Skip to content

Commit 62c6785

Browse files
committed
Initial commit.
0 parents  commit 62c6785

18 files changed

+1115
-0
lines changed

.gitignore

Lines changed: 22 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,22 @@
1+
# Created by https://www.gitignore.io
2+
3+
### Swift ###
4+
# Xcode
5+
#
6+
build/
7+
*.pbxuser
8+
!default.pbxuser
9+
*.mode1v3
10+
!default.mode1v3
11+
*.mode2v3
12+
!default.mode2v3
13+
*.perspectivev3
14+
!default.perspectivev3
15+
xcuserdata
16+
*.xccheckout
17+
*.moved-aside
18+
DerivedData
19+
*.hmap
20+
*.ipa
21+
*.xcuserstate
22+
.DS_Store

README.md

Lines changed: 12 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,12 @@
1+
# SecureDataKit
2+
SecureDataKit is a library for storing sensitive data such as private keys. It has two primary classes: SecureData and OSKeyChainSecureDataStore (which implements the SecureDataStore protocol).
3+
4+
SecureData
5+
============
6+
The SecureData object is used for storing sensitive data securely in memory. It ensures that the memory will automatically be cleared when the object is deallocated so an attacker can't try to read the memory after it has been freed. It also prevents the memory from being paged to disk.
7+
8+
OSKeyChainSecureDataStore
9+
============
10+
The OSKeyChainSecureDataStore class is used to easily persist sensitive data such as private keys to the keychain. The keychain is a tool provided by the operating system. It stores the data on a secure enclave so it is nearly impossible for attackers to access it.
11+
12+
More information about the Apple keychain can be found here: https://developer.apple.com/library/mac/documentation/Security/Conceptual/keychainServConcepts/01introduction/introduction.html

SecureDataKit.xcodeproj/project.pbxproj

Lines changed: 458 additions & 0 deletions
Large diffs are not rendered by default.

SecureDataKit.xcodeproj/project.xcworkspace/contents.xcworkspacedata

Lines changed: 7 additions & 0 deletions
Some generated files are not rendered by default. Learn more about customizing how changed files appear on GitHub.

SecureDataKit/Info.plist

Lines changed: 28 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,28 @@
1+
<?xml version="1.0" encoding="UTF-8"?>
2+
<!DOCTYPE plist PUBLIC "-//Apple//DTD PLIST 1.0//EN" "http://www.apple.com/DTDs/PropertyList-1.0.dtd">
3+
<plist version="1.0">
4+
<dict>
5+
<key>CFBundleDevelopmentRegion</key>
6+
<string>en</string>
7+
<key>CFBundleExecutable</key>
8+
<string>$(EXECUTABLE_NAME)</string>
9+
<key>CFBundleIdentifier</key>
10+
<string>com.BitcoinSwift.$(PRODUCT_NAME:rfc1034identifier)</string>
11+
<key>CFBundleInfoDictionaryVersion</key>
12+
<string>6.0</string>
13+
<key>CFBundleName</key>
14+
<string>$(PRODUCT_NAME)</string>
15+
<key>CFBundlePackageType</key>
16+
<string>FMWK</string>
17+
<key>CFBundleShortVersionString</key>
18+
<string>1.0</string>
19+
<key>CFBundleSignature</key>
20+
<string>????</string>
21+
<key>CFBundleVersion</key>
22+
<string>$(CURRENT_PROJECT_VERSION)</string>
23+
<key>NSHumanReadableCopyright</key>
24+
<string>Copyright © 2015 Kevin Greene. All rights reserved.</string>
25+
<key>NSPrincipalClass</key>
26+
<string></string>
27+
</dict>
28+
</plist>
Lines changed: 17 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,17 @@
1+
//
2+
// MemoryLockController.h
3+
// SecureDataKit
4+
//
5+
// Created by Kevin Greene on 1/3/15.
6+
// Copyright (c) 2015 DoubleSha. All rights reserved.
7+
//
8+
9+
#import <Foundation/Foundation.h>
10+
11+
@interface MemoryLockController : NSObject
12+
13+
+ (MemoryLockController *)instance;
14+
- (void)lockMemory:(void *)ptr size:(int)size;
15+
- (void)unlockMemory:(void *)ptr size:(int)size;
16+
17+
@end
Lines changed: 84 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,84 @@
1+
//
2+
// MemoryLockController.m
3+
// SecureDataKit
4+
//
5+
// Created by Kevin Greene on 1/3/15.
6+
// Copyright (c) 2015 DoubleSha. All rights reserved.
7+
//
8+
9+
#import "MemoryLockController.h"
10+
11+
#import <sys/mman.h>
12+
#import <sys/unistd.h>
13+
14+
@interface MemoryLockController()
15+
16+
// Stores how many references there are to each locked page. Keyed by the memory address of a given
17+
// page. When there are 0 references to a page, the page is unlocked and removed from
18+
// lockedPageRefs.
19+
@property(nonatomic, strong) NSMutableDictionary *lockedPageRefs;
20+
@property(nonatomic, assign) size_t pageSize;
21+
@property(nonatomic, assign) size_t pageMask;
22+
23+
@end
24+
25+
@implementation MemoryLockController
26+
27+
- (instancetype)init {
28+
self = [super init];
29+
if (self) {
30+
_lockedPageRefs = [[NSMutableDictionary alloc] init];
31+
_pageSize = getpagesize();
32+
NSAssert(!(_pageSize & (_pageSize - 1)), @"Page size must be a power of two (%zu)", _pageSize);
33+
_pageMask = ~(_pageSize - 1);
34+
}
35+
return self;
36+
}
37+
38+
+ (MemoryLockController *)instance {
39+
static MemoryLockController *instance = nil;
40+
static dispatch_once_t onceToken = 0;
41+
dispatch_once(&onceToken, ^{
42+
instance = [[self alloc] init];
43+
});
44+
return instance;
45+
}
46+
47+
- (void)lockMemory:(void *)ptr size:(int)size {
48+
NSAssert(size >= 0, @"Cannot lock memory with negative size %d", size);
49+
if (size == 0) {
50+
return;
51+
}
52+
size_t ptrAddr = (size_t) ptr;
53+
size_t startPageAddr = ptrAddr & _pageMask;
54+
size_t endPageAddr = (ptrAddr + size - 1) & _pageMask;
55+
for (size_t pageAddr = startPageAddr; pageAddr <= endPageAddr; pageAddr += _pageSize) {
56+
NSValue *page = [NSValue valueWithPointer:(void *) pageAddr];
57+
NSNumber *refCount = _lockedPageRefs[page];
58+
if (refCount == nil) {
59+
mlock((void *) pageAddr, _pageSize);
60+
_lockedPageRefs[page] = [NSNumber numberWithInt:1];
61+
} else {
62+
_lockedPageRefs[page] = [NSNumber numberWithInt:(refCount.intValue + 1)];
63+
}
64+
}
65+
}
66+
67+
- (void)unlockMemory:(void *)ptr size:(int)size {
68+
NSAssert(size >= 0, @"Cannot lock memory with negative size %d", size);
69+
if (size == 0) {
70+
return;
71+
}
72+
size_t ptrAddr = (size_t) ptr;
73+
size_t startPageAddr = ptrAddr & _pageMask;
74+
size_t endPageAddr = (ptrAddr + size - 1) & _pageMask;
75+
for (size_t pageAddr = startPageAddr; pageAddr <= endPageAddr; pageAddr += _pageSize) {
76+
NSValue *page = [NSValue valueWithPointer:(void *) pageAddr];
77+
NSNumber *refCount = _lockedPageRefs[page];
78+
NSAssert(refCount != nil, @"Asymmetric call to unlockMemory");
79+
munlock((void *) pageAddr, _pageSize);
80+
_lockedPageRefs[page] = [NSNumber numberWithInt:(refCount.intValue - 1)];
81+
}
82+
}
83+
84+
@end
Lines changed: 63 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,63 @@
1+
//
2+
// OSKeyChainSecureDataStore.swift
3+
// SecureDataKit
4+
//
5+
// Created by Kevin Greene on 1/27/15.
6+
// Copyright (c) 2015 DoubleSha. All rights reserved.
7+
//
8+
9+
import Foundation
10+
import Security
11+
12+
/// Stores the data using the OSX/iOS secure keychain service.
13+
public class OSKeyChainSecureDataStore: SecureDataStore {
14+
15+
/// The name for your service. This should be a unique value, such as "com.mycompany.myapp".
16+
public let service: String
17+
18+
public init(service: String) {
19+
self.service = service
20+
}
21+
22+
public func dataForKey(key: String) -> SecureData? {
23+
var query = NSMutableDictionary()
24+
query.setObject("\(kSecClassGenericPassword)", forKey: "\(kSecClass)")
25+
query.setObject(service, forKey: "\(kSecAttrService)")
26+
query.setObject(key, forKey: "\(kSecAttrAccount)")
27+
query.setObject(true, forKey: "\(kSecReturnData)")
28+
var result = UnsafeMutablePointer<Unmanaged<AnyObject>?>.alloc(1)
29+
result.initialize(nil)
30+
let status = SecItemCopyMatching(query, result)
31+
if status == errSecItemNotFound {
32+
return nil
33+
}
34+
if status != noErr {
35+
return nil
36+
}
37+
let data = result.memory!.takeUnretainedValue() as! NSData
38+
let secureData = SecureData(data: data)
39+
result.dealloc(1)
40+
return secureData
41+
}
42+
43+
public func saveData(data: SecureData, forKey key: String) -> Bool {
44+
precondition(dataForKey(key) == nil, "SecureData already exists with for key \(key)")
45+
var item = NSMutableDictionary()
46+
// This prevents the key from being copied to Apple servers.
47+
item.setObject("\(kSecAttrAccessibleWhenUnlockedThisDeviceOnly)",
48+
forKey: "\(kSecAttrAccessible)")
49+
item.setObject("\(kSecClassGenericPassword)", forKey: "\(kSecClass)")
50+
item.setObject(service, forKey: "\(kSecAttrService)")
51+
item.setObject(key, forKey: "\(kSecAttrAccount)")
52+
item.setObject(data.data, forKey: "\(kSecValueData)")
53+
return SecItemAdd(item, nil) == noErr
54+
}
55+
56+
public func deleteDataForKey(key: String) -> Bool {
57+
var query = NSMutableDictionary()
58+
query.setObject("\(kSecClassGenericPassword)", forKey: "\(kSecClass)")
59+
query.setObject(service, forKey: "\(kSecAttrService)")
60+
query.setObject(key, forKey: "\(kSecAttrAccount)")
61+
return SecItemDelete(query) == noErr
62+
}
63+
}
Lines changed: 27 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,27 @@
1+
//
2+
// SecureData+swift.swift
3+
// SecureDataKit
4+
//
5+
// Created by Kevin Greene on 12/30/14.
6+
// Copyright (c) 2014 DoubleSha. All rights reserved.
7+
//
8+
9+
import Foundation
10+
11+
public func ==(left: SecureData, right: SecureData) -> Bool {
12+
return left.mutableData == right.mutableData
13+
}
14+
15+
extension SecureData: Equatable {
16+
17+
public subscript(subRange: Range<Int>) -> SecureData {
18+
precondition(subRange.startIndex >= 0 && subRange.startIndex < mutableData.length)
19+
precondition(subRange.endIndex > subRange.startIndex &&
20+
subRange.endIndex <= mutableData.length)
21+
let length = subRange.endIndex - subRange.startIndex
22+
let range = NSRange(location: subRange.startIndex, length: length)
23+
var subData = SecureData(length: UInt(length))
24+
memcpy(subData.mutableBytes, mutableBytes.advancedBy(subRange.startIndex), length)
25+
return subData
26+
}
27+
}

SecureDataKit/SecureData.h

Lines changed: 36 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,36 @@
1+
//
2+
// SecureData.h
3+
// SecureDataKit
4+
//
5+
// Created by Kevin Greene on 1/11/15.
6+
// Copyright (c) 2015 DoubleSha. All rights reserved.
7+
//
8+
9+
#import <Foundation/Foundation.h>
10+
11+
/// A wrapper around NSMutableData that uses the SecureAllocator to allocate memory.
12+
///
13+
/// The memory associated with a SecureData object is automatically cleared when deallocated, and
14+
/// the memory is also prevented from being paged to disk.
15+
///
16+
/// NOTE: Your application is only allowed a maximum of 64kb of secure data, so it is important to
17+
/// allocate a limited number of SecureData objects at a time, and any SecureData objects you create
18+
/// should be as short-lived as possible.
19+
@interface SecureData : NSObject
20+
21+
@property(nonatomic, readonly) NSData *data;
22+
@property(nonatomic, readonly) NSMutableData *mutableData;
23+
@property(nonatomic, readonly) const void *bytes;
24+
@property(nonatomic, readonly) void *mutableBytes;
25+
@property(nonatomic, readonly) NSUInteger length;
26+
27+
- (instancetype)init;
28+
- (instancetype)initWithLength:(NSUInteger)length;
29+
- (instancetype)initWithData:(NSData *)data;
30+
- (instancetype)initWithBytes:(const void *)bytes length:(NSUInteger)length;
31+
32+
- (void)appendBytes:(const void *)bytes length:(NSUInteger)length;
33+
- (void)appendData:(NSData *)data;
34+
- (void)appendSecureData:(SecureData *)secureData;
35+
36+
@end

0 commit comments

Comments
 (0)