diff --git a/BitcoinKit.xcodeproj/project.pbxproj b/BitcoinKit.xcodeproj/project.pbxproj index dd14c97c..b097cf18 100644 --- a/BitcoinKit.xcodeproj/project.pbxproj +++ b/BitcoinKit.xcodeproj/project.pbxproj @@ -7,6 +7,8 @@ objects = { /* Begin PBXBuildFile section */ + 1419E83A202CDBE500FCB0BE /* BitcoinKitInternal.h in Headers */ = {isa = PBXBuildFile; fileRef = 1419E838202CDBE500FCB0BE /* BitcoinKitInternal.h */; }; + 1419E83B202CDBE500FCB0BE /* BitcoinKitInternal.m in Sources */ = {isa = PBXBuildFile; fileRef = 1419E839202CDBE500FCB0BE /* BitcoinKitInternal.m */; }; 141FE2E22022588C00A08B04 /* PublicKey.swift in Sources */ = {isa = PBXBuildFile; fileRef = 141FE2E12022588C00A08B04 /* PublicKey.swift */; }; 141FE2E42022589E00A08B04 /* PrivateKey.swift in Sources */ = {isa = PBXBuildFile; fileRef = 141FE2E32022589E00A08B04 /* PrivateKey.swift */; }; 1463E6B42025E9480033DAAE /* BlockStore.swift in Sources */ = {isa = PBXBuildFile; fileRef = 1463E6B32025E9480033DAAE /* BlockStore.swift */; }; @@ -47,6 +49,8 @@ /* End PBXContainerItemProxy section */ /* Begin PBXFileReference section */ + 1419E838202CDBE500FCB0BE /* BitcoinKitInternal.h */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.h; path = BitcoinKitInternal.h; sourceTree = ""; }; + 1419E839202CDBE500FCB0BE /* BitcoinKitInternal.m */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.objc; path = BitcoinKitInternal.m; sourceTree = ""; }; 141FE2E12022588C00A08B04 /* PublicKey.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = PublicKey.swift; sourceTree = ""; }; 141FE2E32022589E00A08B04 /* PrivateKey.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = PrivateKey.swift; sourceTree = ""; }; 1463E6B32025E9480033DAAE /* BlockStore.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = BlockStore.swift; sourceTree = ""; }; @@ -144,6 +148,8 @@ 147F9728201FC89E0071F49D /* BloomFilter.swift */, 147494ED201FADAC006D1CF8 /* MurmurHash.swift */, 14CDC38B2021EF5000C01556 /* Helpers.swift */, + 1419E838202CDBE500FCB0BE /* BitcoinKitInternal.h */, + 1419E839202CDBE500FCB0BE /* BitcoinKitInternal.m */, 147494CB201F9A29006D1CF8 /* Info.plist */, ); path = BitcoinKit; @@ -166,6 +172,7 @@ buildActionMask = 2147483647; files = ( 147494D8201F9A29006D1CF8 /* BitcoinKit.h in Headers */, + 1419E83A202CDBE500FCB0BE /* BitcoinKitInternal.h in Headers */, ); runOnlyForDeploymentPostprocessing = 0; }; @@ -279,7 +286,7 @@ ); runOnlyForDeploymentPostprocessing = 0; shellPath = /bin/sh; - shellScript = "if [ ! -d \"$SRCROOT/Libraries/secp256k1/lib\" ] || [ ! -d \"$SRCROOT/Libraries/crypto/lib\" ]; then\n env -i PATH=$PATH sh \"$SRCROOT/setup/build_libraries.sh\"\nfi\n"; + shellScript = "if [ ! -d \"$SRCROOT/Libraries/secp256k1/lib\" ] || [ ! -d \"$SRCROOT/Libraries/openssl/lib\" ]; then\n env -i PATH=$PATH sh \"$SRCROOT/setup/build_libraries.sh\"\nfi\n"; showEnvVarsInLog = 0; }; /* End PBXShellScriptBuildPhase section */ @@ -302,6 +309,7 @@ 14CDC3862021824200C01556 /* Wallet.swift in Sources */, 141FE2E42022589E00A08B04 /* PrivateKey.swift in Sources */, 14CDC38C2021EF5000C01556 /* Helpers.swift in Sources */, + 1419E83B202CDBE500FCB0BE /* BitcoinKitInternal.m in Sources */, 147494F0201FAE30006D1CF8 /* Serialization.swift in Sources */, 147494E4201F9B85006D1CF8 /* Crypto.swift in Sources */, 1463E6B62025E99C0033DAAE /* BlockChain.swift in Sources */, @@ -389,7 +397,6 @@ ONLY_ACTIVE_ARCH = YES; SDKROOT = iphoneos; SWIFT_ACTIVE_COMPILATION_CONDITIONS = DEBUG; - SWIFT_INCLUDE_PATHS = "$(SRCROOT)/Libraries"; SWIFT_OPTIMIZATION_LEVEL = "-Onone"; VERSIONING_SYSTEM = "apple-generic"; VERSION_INFO_PREFIX = ""; @@ -442,7 +449,6 @@ IPHONEOS_DEPLOYMENT_TARGET = 8.0; MTL_ENABLE_DEBUG_INFO = NO; SDKROOT = iphoneos; - SWIFT_INCLUDE_PATHS = "$(SRCROOT)/Libraries"; SWIFT_OPTIMIZATION_LEVEL = "-Owholemodule"; VALIDATE_PRODUCT = YES; VERSIONING_SYSTEM = "apple-generic"; @@ -463,19 +469,20 @@ DYLIB_CURRENT_VERSION = 1; DYLIB_INSTALL_NAME_BASE = "@rpath"; HEADER_SEARCH_PATHS = ( - "$(SRCROOT)/Libraries/crypto/include", + "$(SRCROOT)/Libraries/openssl/include", "$(SRCROOT)/Libraries/secp256k1/include", ); INFOPLIST_FILE = BitcoinKit/Info.plist; INSTALL_PATH = "$(LOCAL_LIBRARY_DIR)/Frameworks"; LD_RUNPATH_SEARCH_PATHS = "$(inherited) @executable_path/Frameworks @loader_path/Frameworks"; LIBRARY_SEARCH_PATHS = ( - "$(SRCROOT)/Libraries/crypto/lib", + "$(SRCROOT)/Libraries/openssl/lib", "$(SRCROOT)/Libraries/secp256k1/lib", ); PRODUCT_BUNDLE_IDENTIFIER = com.kishikawakatsumi.BitcoinKit; PRODUCT_NAME = "$(TARGET_NAME:c99extidentifier)"; SKIP_INSTALL = YES; + SWIFT_INCLUDE_PATHS = "$(SRCROOT)/Libraries $(SRCROOT)/BitcoinKit/BitcoinKitInternal"; SWIFT_OPTIMIZATION_LEVEL = "-Onone"; SWIFT_VERSION = 4.0; TARGETED_DEVICE_FAMILY = "1,2"; @@ -495,19 +502,20 @@ DYLIB_CURRENT_VERSION = 1; DYLIB_INSTALL_NAME_BASE = "@rpath"; HEADER_SEARCH_PATHS = ( - "$(SRCROOT)/Libraries/crypto/include", + "$(SRCROOT)/Libraries/openssl/include", "$(SRCROOT)/Libraries/secp256k1/include", ); INFOPLIST_FILE = BitcoinKit/Info.plist; INSTALL_PATH = "$(LOCAL_LIBRARY_DIR)/Frameworks"; LD_RUNPATH_SEARCH_PATHS = "$(inherited) @executable_path/Frameworks @loader_path/Frameworks"; LIBRARY_SEARCH_PATHS = ( - "$(SRCROOT)/Libraries/crypto/lib", + "$(SRCROOT)/Libraries/openssl/lib", "$(SRCROOT)/Libraries/secp256k1/lib", ); PRODUCT_BUNDLE_IDENTIFIER = com.kishikawakatsumi.BitcoinKit; PRODUCT_NAME = "$(TARGET_NAME:c99extidentifier)"; SKIP_INSTALL = YES; + SWIFT_INCLUDE_PATHS = "$(SRCROOT)/Libraries $(SRCROOT)/BitcoinKit/BitcoinKitInternal"; SWIFT_VERSION = 4.0; TARGETED_DEVICE_FAMILY = "1,2"; }; diff --git a/BitcoinKit/BitcoinKitInternal.h b/BitcoinKit/BitcoinKitInternal.h new file mode 100644 index 00000000..cf63a707 --- /dev/null +++ b/BitcoinKit/BitcoinKitInternal.h @@ -0,0 +1,24 @@ +// +// OpenSSL.h +// BitcoinKit +// +// Created by kishikawakatsumi on 2018/02/09. +// Copyright © 2018 Kishikawa Katsumi. All rights reserved. +// + +#import + +NS_ASSUME_NONNULL_BEGIN +@interface BitcoinKitInternal : NSObject + ++ (NSData *)sha256:(NSData *)data; ++ (NSData *)ripemd160:(NSData *)data; ++ (NSData *)hmacsha512:(NSData *)data key:(NSData *)key; + ++ (NSData *)computePublicKeyFromPrivateKey:(NSData *)privateKey compression:(BOOL)compression; + ++ (NSData *)deriveKey:(NSData *)password salt:(NSData *)salt iterations:(NSInteger)iterations keyLength:(NSInteger)keyLength; ++ (nullable NSArray *)deriveKey:(nullable NSData *)privateKey publicKey:(NSData *)publicKey chainCode:(NSData *)chainCode atIndex:(uint32_t)index hardened:(BOOL)hardened; + +@end +NS_ASSUME_NONNULL_END diff --git a/BitcoinKit/BitcoinKitInternal.m b/BitcoinKit/BitcoinKitInternal.m new file mode 100644 index 00000000..94b7d21b --- /dev/null +++ b/BitcoinKit/BitcoinKitInternal.m @@ -0,0 +1,154 @@ +// +// OpenSSL.m +// BitcoinKit +// +// Created by kishikawakatsumi on 2018/02/09. +// Copyright © 2018 Kishikawa Katsumi. All rights reserved. +// + +#import "BitcoinKitInternal.h" +#import +#import +#import +#import + +@implementation BitcoinKitInternal + ++ (NSData *)sha256:(NSData *)data { + NSMutableData *result = [NSMutableData dataWithLength:SHA256_DIGEST_LENGTH]; + SHA256(data.bytes, data.length, result.mutableBytes); + return result; +} + ++ (NSData *)ripemd160:(NSData *)data { + NSMutableData *result = [NSMutableData dataWithLength:RIPEMD160_DIGEST_LENGTH]; + RIPEMD160(data.bytes, data.length, result.mutableBytes); + return result; +} + ++ (NSData *)hmacsha512:(NSData *)data key:(NSData *)key { + unsigned int length = SHA512_DIGEST_LENGTH; + NSMutableData *result = [NSMutableData dataWithLength:length]; + HMAC(EVP_sha512(), key.bytes, (int)key.length, data.bytes, data.length, result.mutableBytes, &length); + return result; +} + ++ (NSData *)generatePrivateKey { + return [NSData data]; +} + ++ (NSData *)computePublicKeyFromPrivateKey:(NSData *)privateKey compression:(BOOL)compression { + BN_CTX *ctx = BN_CTX_new(); + EC_KEY *key = EC_KEY_new_by_curve_name(NID_secp256k1); + const EC_GROUP *group = EC_KEY_get0_group(key); + + BIGNUM *prv = BN_new(); + BN_bin2bn(privateKey.bytes, (int)privateKey.length, prv); + + EC_POINT *pub = EC_POINT_new(group); + EC_POINT_mul(group, pub, prv, nil, nil, ctx); + EC_KEY_set_private_key(key, prv); + EC_KEY_set_public_key(key, pub); + + NSMutableData *result; + if (compression) { + EC_KEY_set_conv_form(key, POINT_CONVERSION_COMPRESSED); + unsigned char *bytes = NULL; + int length = i2o_ECPublicKey(key, &bytes); + result = [NSMutableData dataWithBytesNoCopy:bytes length:length]; + } else { + result = [NSMutableData dataWithLength:65]; + BIGNUM *n = BN_new(); + EC_POINT_point2bn(group, pub, POINT_CONVERSION_UNCOMPRESSED, n, ctx); + BN_bn2bin(n, result.mutableBytes); + BN_free(n); + } + + BN_free(prv); + EC_POINT_free(pub); + EC_KEY_free(key); + BN_CTX_free(ctx); + + return result; +} + ++ (NSData *)deriveKey:(NSData *)password salt:(NSData *)salt iterations:(NSInteger)iterations keyLength:(NSInteger)keyLength { + NSMutableData *result = [NSMutableData dataWithLength:keyLength]; + PKCS5_PBKDF2_HMAC(password.bytes, (int)password.length, salt.bytes, (int)salt.length, (int)iterations, EVP_sha512(), (int)keyLength, result.mutableBytes); + return result; +} + ++ (NSArray *)deriveKey:(nullable NSData *)privateKey publicKey:(NSData *)publicKey chainCode:(NSData *)chainCode atIndex:(uint32_t)index hardened:(BOOL)hardened { + BN_CTX *ctx = BN_CTX_new(); + + NSMutableData *data = [NSMutableData data]; + if (hardened) { + uint8_t padding = 0; + [data appendBytes:&padding length:1]; + [data appendData:privateKey]; + } else { + [data appendData:publicKey]; + } + + uint32_t i = OSSwapHostToBigInt32(hardened ? (0x80000000 | index) : index); + [data appendBytes:&i length:sizeof(i)]; + + NSData *digest = [self hmacsha512:data key:chainCode]; + NSData *prv = [digest subdataWithRange:NSMakeRange(0, 32)]; + NSData *derivedChainCode = [digest subdataWithRange:NSMakeRange(32, 32)]; + + BIGNUM *curveOrder = BN_new(); + BN_hex2bn(&curveOrder, "FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFEBAAEDCE6AF48A03BBFD25E8CD0364141"); + + BIGNUM *factor = BN_new(); + BN_bin2bn(prv.bytes, (int)prv.length, factor); + if (BN_cmp(factor, curveOrder) < 0) { + return nil; + } + + NSMutableData *result; + if (privateKey) { + BIGNUM *pkNum = BN_new(); + BN_bin2bn(privateKey.bytes, (int)privateKey.length, pkNum); + + BN_mod_add(pkNum, pkNum, factor, curveOrder, ctx); + if (BN_is_zero(pkNum)) { + return nil; + } + + int numBytes = BN_num_bytes(pkNum); + result = [NSMutableData dataWithLength:numBytes]; + BN_bn2bin(pkNum, result.mutableBytes); + + BN_free(pkNum); + } else { + BIGNUM *pubNum = BN_new(); + BN_bin2bn(publicKey.bytes, (int)publicKey.length, pubNum); + EC_GROUP *group = EC_GROUP_new_by_curve_name(NID_secp256k1); + + EC_POINT *point = EC_POINT_new(group); + EC_POINT_bn2point(group, pubNum, point, ctx); + EC_POINT_mul(group, point, factor, point, BN_value_one(), ctx); + if (EC_POINT_is_at_infinity(group, point) == 0) { + return nil; + } + + BIGNUM *pointNum = BN_new(); + result = [NSMutableData dataWithLength:33]; + + EC_POINT_point2bn(group, point, POINT_CONVERSION_COMPRESSED, pointNum, ctx); + BN_bn2bin(pointNum, result.mutableBytes); + + BN_free(pointNum); + EC_POINT_free(point); + EC_GROUP_free(group); + } + + BN_free(factor); + BN_free(curveOrder); + BN_CTX_free(ctx); + + return @[result, derivedChainCode]; +} + +@end diff --git a/BitcoinKit/BitcoinKitInternal/module.modulemap b/BitcoinKit/BitcoinKitInternal/module.modulemap new file mode 100644 index 00000000..1e849272 --- /dev/null +++ b/BitcoinKit/BitcoinKitInternal/module.modulemap @@ -0,0 +1,4 @@ +module BitcoinKitInternal { + header "../BitcoinKitInternal.h" + link "crypto" +} diff --git a/BitcoinKit/Crypto.swift b/BitcoinKit/Crypto.swift index 31ddc19d..dd8078b3 100644 --- a/BitcoinKit/Crypto.swift +++ b/BitcoinKit/Crypto.swift @@ -7,41 +7,28 @@ // import Foundation -import crypto +import BitcoinKitInternal import secp256k1 public struct Crypto { - public static func sha256(_ plain: Data) -> Data { - let length = Int(SHA256_DIGEST_LENGTH) - var result = [UInt8](repeating: 0, count: length) - _ = plain.withUnsafeBytes { SHA256($0, plain.count, &result) } - return Data(bytes: result, count: length) + public static func sha256(_ data: Data) -> Data { + return BitcoinKitInternal.sha256(data) } - public static func sha256sha256(_ plain: Data) -> Data { - return sha256(sha256(plain)) + public static func sha256sha256(_ data: Data) -> Data { + return sha256(sha256(data)) } - public static func ripemd160(_ plain: Data) -> Data { - let length = Int(RIPEMD160_DIGEST_LENGTH) - var result = [UInt8](repeating: 0, count: length) - _ = plain.withUnsafeBytes { RIPEMD160($0, plain.count, &result) } - return Data(bytes: result, count: length) + public static func ripemd160(_ data: Data) -> Data { + return BitcoinKitInternal.ripemd160(data) } - public static func sha256ripemd160(_ plain: Data) -> Data { - return ripemd160(sha256(plain)) + public static func sha256ripemd160(_ data: Data) -> Data { + return ripemd160(sha256(data)) } - public static func hmacsha512(key: Data, data: Data) -> Data { - var length = UInt32(SHA512_DIGEST_LENGTH) - var result = [UInt8](repeating: 0, count: Int(length)) - _ = key.withUnsafeBytes { (keyPtr) in - data.withUnsafeBytes { (dataPtr) in - HMAC(EVP_sha512(), keyPtr, Int32(key.count), dataPtr, data.count, &result, &length) - } - } - return Data(result) + public static func hmacsha512(data: Data, key: Data) -> Data { + return BitcoinKitInternal.hmacsha512(data, key: key) } public static func sign(_ data: Data, privateKey: PrivateKey) throws -> Data { diff --git a/BitcoinKit/HDPrivateKey.swift b/BitcoinKit/HDPrivateKey.swift index 8e073b83..7e0953a9 100644 --- a/BitcoinKit/HDPrivateKey.swift +++ b/BitcoinKit/HDPrivateKey.swift @@ -7,7 +7,7 @@ // import Foundation -import crypto +import BitcoinKitInternal public class HDPrivateKey { public let network: Network @@ -28,7 +28,7 @@ public class HDPrivateKey { } public convenience init(seed: Data, network: Network = .testnet) { - let hmac = Crypto.hmacsha512(key: "Bitcoin seed".data(using: .ascii)!, data: seed) + let hmac = Crypto.hmacsha512(data: seed, key: "Bitcoin seed".data(using: .ascii)!) let privateKey = hmac[0..<32] let chainCode = hmac[32..<64] self.init(privateKey: privateKey, chainCode: chainCode, network: network) @@ -65,58 +65,16 @@ public class HDPrivateKey { if ((0x80000000 & index) != 0) { throw KeyChainError.invalidChildIndex } - - let ctx = BN_CTX_new() - defer { BN_CTX_free(ctx) } - - var data = Data() - if hardened { - data += UInt8(0) - data += raw - } else { - data += publicKey().raw - } - - var index = UInt32(hardened ? (0x80000000 | index) : index).bigEndian - data += index - - let digest = Crypto.hmacsha512(key: chainCode, data: data) - - let curveOrder = BN_new() - defer { BN_free(curveOrder) } - let curveData = Data(hex: "FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFEBAAEDCE6AF48A03BBFD25E8CD0364141")! - _ = curveData.withUnsafeBytes { BN_bin2bn($0, Int32(curveData.count), curveOrder) } - - let factor = BN_new() - defer { BN_free(factor) } - let pk = digest[0..<32] - _ = pk.withUnsafeBytes { BN_bin2bn($0, Int32(pk.count), factor) } - guard BN_cmp(factor, curveOrder) < 0 else { - throw KeyChainError.derivateionFailed + if let keys = BitcoinKitInternal.deriveKey(raw, publicKey: publicKey().raw, chainCode: chainCode, at: index, hardened: false) { + let fingerPrint: UInt32 = Crypto.sha256ripemd160(publicKey().raw).withUnsafeBytes { $0.pointee } + return HDPrivateKey(privateKey: keys[0], + chainCode: keys[1], + network: network, + depth: depth + 1, + fingerprint: fingerPrint, + childIndex: index) } - - let derivedChainCode = digest[32..<64] - - let pkNum = BN_new() - defer { BN_free(pkNum) } - _ = raw.withUnsafeBytes { BN_bin2bn($0, Int32(raw.count), pkNum) } - - BN_mod_add(pkNum, pkNum, factor, curveOrder, ctx) - guard pkNum!.pointee.top > 0 else { - throw KeyChainError.derivateionFailed - } - - let numBytes = Int((BN_num_bits(pkNum) + 7) / 8) - var derivedPrivateKey = Data(count: numBytes) - _ = derivedPrivateKey.withUnsafeMutableBytes { BN_bn2bin(pkNum, $0) } - - let derivedFingerPrint: UInt32 = Crypto.sha256ripemd160(publicKey().raw).withUnsafeBytes { $0.pointee } - return HDPrivateKey(privateKey: derivedPrivateKey, - chainCode: derivedChainCode, - network: network, - depth: depth + 1, - fingerprint: derivedFingerPrint, - childIndex: index) + throw KeyChainError.derivateionFailed } } diff --git a/BitcoinKit/HDPublicKey.swift b/BitcoinKit/HDPublicKey.swift index 032e25b2..6d2566a0 100644 --- a/BitcoinKit/HDPublicKey.swift +++ b/BitcoinKit/HDPublicKey.swift @@ -7,7 +7,7 @@ // import Foundation -import crypto +import BitcoinKitInternal public class HDPublicKey { public let network: Network @@ -67,61 +67,15 @@ public class HDPublicKey { if ((0x80000000 & index) != 0) { throw KeyChainError.invalidChildIndex } - - let ctx = BN_CTX_new() - defer { BN_CTX_free(ctx) } - - var data = Data() - data += raw - data += index.bigEndian - - let digest = Crypto.hmacsha512(key: chainCode, data: data) - - let curveOrder = BN_new() - defer { BN_free(curveOrder) } - let curveData = Data(hex: "FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFEBAAEDCE6AF48A03BBFD25E8CD0364141")! - _ = curveData.withUnsafeBytes { BN_bin2bn($0, Int32(curveData.count), curveOrder) } - - let factor = BN_new() - defer { BN_free(factor) } - let pk = digest[0..<32] - _ = pk.withUnsafeBytes { BN_bin2bn($0, Int32(pk.count), factor) } - guard BN_cmp(factor, curveOrder) < 0 else { - throw KeyChainError.derivateionFailed + if let keys = BitcoinKitInternal.deriveKey(nil, publicKey: raw, chainCode: chainCode, at: index, hardened: false) { + let fingerPrint: UInt32 = Crypto.sha256ripemd160(raw).withUnsafeBytes { $0.pointee } + return HDPublicKey(raw: keys[0], + chainCode: keys[1], + network: network, + depth: depth + 1, + fingerprint: fingerPrint, + childIndex: index) } - - let derivedChainCode = digest[32..<64] - - let pubNum = BN_new() - defer { BN_free(pubNum) } - _ = raw.withUnsafeBytes { BN_bin2bn($0, Int32(raw.count), pubNum) } - - let group = EC_GROUP_new_by_curve_name(NID_secp256k1) - defer { EC_GROUP_free(group) } - - let point = EC_POINT_new(group) - defer { EC_POINT_free(point) } - - EC_POINT_bn2point(group, pubNum, point, ctx) - EC_POINT_mul(group, point, factor, point, BN_value_one(), ctx) - - guard EC_POINT_is_at_infinity(group, point) != 1 else { - throw KeyChainError.derivateionFailed - } - - var derivedPublicKey = Data(count: 33) - let pointNum = BN_new() - defer { BN_free(pointNum) } - EC_POINT_point2bn(group, point, POINT_CONVERSION_COMPRESSED, pointNum, ctx) - _ = derivedPublicKey.withUnsafeMutableBytes { BN_bn2bin(pointNum, $0) } - - let derivedFingerPrint: UInt32 = Crypto.sha256ripemd160(raw).withUnsafeBytes { $0.pointee } - - return HDPublicKey(raw: derivedPublicKey, - chainCode: derivedChainCode, - network: network, - depth: depth + 1, - fingerprint: derivedFingerPrint, - childIndex: index) + throw KeyChainError.derivateionFailed } } diff --git a/BitcoinKit/Mnemonic.swift b/BitcoinKit/Mnemonic.swift index e8b4c90a..63b10bd3 100644 --- a/BitcoinKit/Mnemonic.swift +++ b/BitcoinKit/Mnemonic.swift @@ -7,7 +7,7 @@ // import Foundation -import crypto +import BitcoinKitInternal public struct Mnemonic { public enum Strength : Int { @@ -60,12 +60,7 @@ public struct Mnemonic { public static func seed(mnemonic m: [String], passphrase: String = "") -> Data { let mnemonic = m.joined(separator: " ").decomposedStringWithCompatibilityMapping.data(using: .utf8)! let salt = ("mnemonic" + passphrase).decomposedStringWithCompatibilityMapping.data(using: .utf8)! - var seed = Data(count: 64) - _ = seed.withUnsafeMutableBytes { (seedPtr) in - mnemonic.withUnsafeBytes { (mnemonicPtr) in - PKCS5_PBKDF2_HMAC(mnemonicPtr, Int32(mnemonic.count), UnsafePointer(salt.map { $0 }), Int32(salt.count), 2048, EVP_sha512(), Int32(seed.count), seedPtr) - } - } + let seed = BitcoinKitInternal.deriveKey(mnemonic, salt: salt, iterations: 2048, keyLength: 64) return seed } diff --git a/BitcoinKit/PrivateKey.swift b/BitcoinKit/PrivateKey.swift index 3c54e6f6..35f68094 100644 --- a/BitcoinKit/PrivateKey.swift +++ b/BitcoinKit/PrivateKey.swift @@ -7,7 +7,6 @@ // import Foundation -import crypto public struct PrivateKey { let raw: Data @@ -16,14 +15,6 @@ public struct PrivateKey { public init(network: Network = .testnet) { self.network = network - let ctx = BN_CTX_new(); - defer { BN_CTX_free(ctx) } - let start = BN_new() - defer { - BN_clear(start) - BN_free(start) - } - func check(_ vch: [UInt8]) -> Bool { let max: [UInt8] = [ 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, @@ -52,10 +43,12 @@ public struct PrivateKey { return true } - var key = Data(count: 32) + let count = 32 + var key = Data(count: count) + var status: Int32 = 0 repeat { - _ = key.withUnsafeMutableBytes { RAND_bytes($0, 32) } - } while (!check([UInt8](key))) + status = key.withUnsafeMutableBytes { SecRandomCopyBytes(kSecRandomDefault, count, $0) } + } while (status != 0 || !check([UInt8](key))) self.raw = key } @@ -94,13 +87,9 @@ public struct PrivateKey { } public func toWIF() -> String { - let d1 = raw - let d2 = Data([network.privatekey]) + d1 - let h = Crypto.sha256sha256(d2) - let checksum = Data(h.prefix(4)) - let d4 = d2 + checksum - let wif = Base58.encode(d4) - return wif + let data = Data([network.privatekey]) + raw + let checksum = Crypto.sha256sha256(data).prefix(4) + return Base58.encode(data + checksum) } } diff --git a/BitcoinKit/PublicKey.swift b/BitcoinKit/PublicKey.swift index f7ba5047..a91740a8 100644 --- a/BitcoinKit/PublicKey.swift +++ b/BitcoinKit/PublicKey.swift @@ -7,7 +7,7 @@ // import Foundation -import crypto +import BitcoinKitInternal public struct PublicKey { let raw: Data @@ -33,39 +33,7 @@ public struct PublicKey { } static func from(privateKey raw: Data, compression: Bool = false) -> Data { - let ctx = BN_CTX_new(); - defer { BN_CTX_free(ctx) } - - let eckey = EC_KEY_new_by_curve_name(NID_secp256k1); - defer { EC_KEY_free(eckey) } - let group = EC_KEY_get0_group(eckey); - - let privateKey = BN_new() - defer { BN_free(privateKey) } - _ = raw.withUnsafeBytes { BN_bin2bn($0, Int32(raw.count), privateKey) } - - let point = EC_POINT_new(group); - defer { EC_POINT_free(point) } - EC_POINT_mul(group, point, privateKey, nil, nil, ctx) - EC_KEY_set_private_key(eckey, privateKey) - EC_KEY_set_public_key(eckey, point) - - if compression { - EC_KEY_set_conv_form(eckey, POINT_CONVERSION_COMPRESSED) - var result: UnsafeMutablePointer? = nil - let length = i2o_ECPublicKey(eckey, &result) - let publicKey = result!.buffer(withLength: Int(length)) - var data = Data(publicKey) - return data - } else { - var data = Data(count: 65) - let publicKey = BN_new() - defer { BN_free(publicKey) } - - EC_POINT_point2bn(group, point, POINT_CONVERSION_UNCOMPRESSED, publicKey, ctx) - _ = data.withUnsafeMutableBytes { BN_bn2bin(publicKey, $0) } - return data - } + return BitcoinKitInternal.computePublicKey(fromPrivateKey: raw, compression: compression) } } @@ -80,13 +48,3 @@ extension PublicKey : CustomStringConvertible { return raw.hex } } - -extension UnsafeMutablePointer { - func buffer(withLength length: Int) -> [Pointee] { - var buff = Array(repeating: nil, count: length) - for i in 0 ..< length { - buff[i] = self[i] - } - return buff - } -} diff --git a/Examples/Wallet/Wallet.xcodeproj/project.pbxproj b/Examples/Wallet/Wallet.xcodeproj/project.pbxproj index 68587fb3..522de317 100644 --- a/Examples/Wallet/Wallet.xcodeproj/project.pbxproj +++ b/Examples/Wallet/Wallet.xcodeproj/project.pbxproj @@ -338,8 +338,8 @@ "$(inherited)", "$(PROJECT_DIR)/Carthage/Build/iOS", ); - HEADER_SEARCH_PATHS = "$(PROJECT_DIR)/../../Libraries/crypto/include"; INFOPLIST_FILE = Wallet/Info.plist; + IPHONEOS_DEPLOYMENT_TARGET = 10.0; LD_RUNPATH_SEARCH_PATHS = "$(inherited) @executable_path/Frameworks"; PRODUCT_BUNDLE_IDENTIFIER = com.kishikawakatsumi.Wallet; PRODUCT_NAME = "$(TARGET_NAME)"; @@ -358,8 +358,8 @@ "$(inherited)", "$(PROJECT_DIR)/Carthage/Build/iOS", ); - HEADER_SEARCH_PATHS = "$(PROJECT_DIR)/../../Libraries/crypto/include"; INFOPLIST_FILE = Wallet/Info.plist; + IPHONEOS_DEPLOYMENT_TARGET = 10.0; LD_RUNPATH_SEARCH_PATHS = "$(inherited) @executable_path/Frameworks"; PRODUCT_BUNDLE_IDENTIFIER = com.kishikawakatsumi.Wallet; PRODUCT_NAME = "$(TARGET_NAME)"; diff --git a/Libraries/crypto/module.modulemap b/Libraries/crypto/module.modulemap deleted file mode 100644 index 0c1d9506..00000000 --- a/Libraries/crypto/module.modulemap +++ /dev/null @@ -1,13 +0,0 @@ -module crypto { - private header "include/openssl/bn.h" - private header "include/openssl/ec.h" - private header "include/openssl/ecdsa.h" - private header "include/openssl/evp.h" - private header "include/openssl/e_os2.h" - private header "include/openssl/hmac.h" - private header "include/openssl/obj_mac.h" - private header "include/openssl/rand.h" - private header "include/openssl/ripemd.h" - private header "include/openssl/sha.h" - link "crypto" -} diff --git a/setup/build_crypto.sh b/setup/build_crypto.sh index 84cc33e5..d546d8e1 100644 --- a/setup/build_crypto.sh +++ b/setup/build_crypto.sh @@ -1,7 +1,7 @@ #!/bin/sh set -ex -SCRIPT_DIR="$PWD/`dirname $0`" +SCRIPT_DIR="`pwd`/`dirname $0`" OPENSSL_VERSION=1.0.2n TDIR=`mktemp -d` @@ -14,20 +14,21 @@ tar zxf openssl-$OPENSSL_VERSION.tar.gz cd openssl-$OPENSSL_VERSION -sh "$SCRIPT_DIR/build_crypto_iphoneos_arm64.sh" -sh "$SCRIPT_DIR/build_crypto_iphoneos_armv7s.sh" -sh "$SCRIPT_DIR/build_crypto_iphoneos_armv7.sh" -sh "$SCRIPT_DIR/build_crypto_iphonesimulator_x86_64.sh" -sh "$SCRIPT_DIR/build_crypto_iphonesimulator_i386.sh" - -mkdir -p "$SCRIPT_DIR/../Libraries/crypto/lib" -xcrun lipo -create .build/iphonesimulator/i386/lib/libcrypto.a \ - .build/iphonesimulator/x86_64/lib/libcrypto.a \ - .build/iphoneos/armv7/lib/libcrypto.a \ - .build/iphoneos/armv7s/lib/libcrypto.a \ - .build/iphoneos/arm64/lib/libcrypto.a \ - -o "$SCRIPT_DIR/../Libraries/crypto/lib/libcrypto.a" -cp -rf $TDIR/openssl-$OPENSSL_VERSION/include $SCRIPT_DIR/../Libraries/crypto/ +sh "$SCRIPT_DIR/build_crypto_common.sh" iphoneos arm64 +sh "$SCRIPT_DIR/build_crypto_common.sh" iphoneos armv7s +sh "$SCRIPT_DIR/build_crypto_common.sh" iphoneos armv7 +sh "$SCRIPT_DIR/build_crypto_common.sh" iphonesimulator x86_64 +sh "$SCRIPT_DIR/build_crypto_common.sh" iphonesimulator i386 + + +mkdir -p "$SCRIPT_DIR/../Libraries/openssl/lib" +xcrun lipo -create .build/iphoneos/arm64/libcrypto.a \ + .build/iphoneos/armv7s/libcrypto.a \ + .build/iphoneos/armv7/libcrypto.a \ + .build/iphonesimulator/x86_64/libcrypto.a \ + .build/iphonesimulator/i386/libcrypto.a \ + -o "$SCRIPT_DIR/../Libraries/openssl/lib/libcrypto.a" +cp -rf $TDIR/openssl-$OPENSSL_VERSION/include "$SCRIPT_DIR/../Libraries/openssl/" cd - rm -rf $TDIR diff --git a/setup/build_crypto_common.sh b/setup/build_crypto_common.sh new file mode 100644 index 00000000..e99ddb67 --- /dev/null +++ b/setup/build_crypto_common.sh @@ -0,0 +1,31 @@ +#!/bin/sh + +if [ $# -ne 2 ]; then + echo "Usage: sh $0 [iphoneos|iphonesimulator] [arm64|armv7s|armv7|x86_64|i386]" 1>&2 + exit 1 +fi + +set -ex + +SDK=$1 +ARCH=$2 +CURRENTPATH="`pwd`" + +PLATFORM="`xcrun -sdk $SDK --show-sdk-platform-path`" +SDK_PATH="`xcrun -sdk $SDK --show-sdk-path`" + +export CROSS_TOP="$PLATFORM/Developer" +export CROSS_SDK="`basename $SDK_PATH`" +export BUILD_TOOLS="`xcode-select --print-path`" +export CC="`xcrun -find gcc` -arch $ARCH -isysroot $SDK_PATH -Wno-ignored-optimization-argument" + +TARGETDIR="$CURRENTPATH/.build/$SDK/$ARCH" +mkdir -p "$TARGETDIR" + +./Configure iphoneos-cross no-shared no-dso no-hw no-engine no-ssl2 no-ssl3 no-comp no-idea no-asm no-dtls no-dtls1 no-threads no-err no-npn no-psk no-srp no-ec2m no-weak-ssl-ciphers -fembed-bitcode -miphoneos-version-min=8.0 + +make clean +make depend +make build_crypto + +cp libcrypto.a "$TARGETDIR/libcrypto.a" diff --git a/setup/build_crypto_iphoneos_arm64.sh b/setup/build_crypto_iphoneos_arm64.sh deleted file mode 100644 index 8baf3cf8..00000000 --- a/setup/build_crypto_iphoneos_arm64.sh +++ /dev/null @@ -1,22 +0,0 @@ -#!/bin/sh -set -ex - -CURRENTPATH=$(pwd) - -PLATFORM=`xcrun -sdk iphoneos --show-sdk-platform-path` -SDK=`xcrun -sdk iphoneos --show-sdk-path` - -export CROSS_TOP=$PLATFORM/Developer -export CROSS_SDK=`basename $SDK` -export BUILD_TOOLS=`xcode-select --print-path` -export CC="`xcrun -find gcc` -arch arm64 -isysroot `xcrun -sdk iphoneos --show-sdk-path`" - -TARGETDIR="$CURRENTPATH"/.build/iphoneos/arm64 -mkdir -p "$TARGETDIR" - -./Configure iphoneos-cross no-shared no-dso no-hw no-engine no-ssl2 no-ssl3 no-comp no-idea no-asm no-dtls no-dtls1 no-threads no-err no-npn no-psk no-srp no-ec2m no-weak-ssl-ciphers -fembed-bitcode -miphoneos-version-min=8.0 --openssldir="$TARGETDIR" - -make clean -make depend -make -make install_sw diff --git a/setup/build_crypto_iphoneos_armv7.sh b/setup/build_crypto_iphoneos_armv7.sh deleted file mode 100644 index 207b7cb1..00000000 --- a/setup/build_crypto_iphoneos_armv7.sh +++ /dev/null @@ -1,21 +0,0 @@ -#!/bin/sh -set -ex - -CURRENTPATH=$(pwd) - -PLATFORM=`xcrun -sdk iphoneos --show-sdk-platform-path` -SDK=`xcrun -sdk iphoneos --show-sdk-path` - -export CROSS_TOP=$PLATFORM/Developer -export CROSS_SDK=`basename $SDK` -export BUILD_TOOLS=`xcode-select --print-path` -export CC="`xcrun -find gcc` -arch armv7 -isysroot `xcrun -sdk iphoneos --show-sdk-path`" - -TARGETDIR="$CURRENTPATH"/.build/iphoneos/armv7 -mkdir -p "$TARGETDIR" - -./Configure iphoneos-cross no-shared no-dso no-hw no-engine no-ssl2 no-ssl3 no-comp no-idea no-asm no-dtls no-dtls1 no-threads no-err no-npn no-psk no-srp no-ec2m no-weak-ssl-ciphers -fembed-bitcode -miphoneos-version-min=8.0 --openssldir="$TARGETDIR" - -make clean -make -make install_sw diff --git a/setup/build_crypto_iphoneos_armv7s.sh b/setup/build_crypto_iphoneos_armv7s.sh deleted file mode 100644 index 080b6507..00000000 --- a/setup/build_crypto_iphoneos_armv7s.sh +++ /dev/null @@ -1,21 +0,0 @@ -#!/bin/sh -set -ex - -CURRENTPATH=$(pwd) - -PLATFORM=`xcrun -sdk iphoneos --show-sdk-platform-path` -SDK=`xcrun -sdk iphoneos --show-sdk-path` - -export CROSS_TOP=$PLATFORM/Developer -export CROSS_SDK=`basename $SDK` -export BUILD_TOOLS=`xcode-select --print-path` -export CC="`xcrun -find gcc` -arch armv7s -isysroot `xcrun -sdk iphoneos --show-sdk-path`" - -TARGETDIR="$CURRENTPATH"/.build/iphoneos/armv7s -mkdir -p "$TARGETDIR" - -./Configure iphoneos-cross no-shared no-dso no-hw no-engine no-ssl2 no-ssl3 no-comp no-idea no-asm no-dtls no-dtls1 no-threads no-err no-npn no-psk no-srp no-ec2m no-weak-ssl-ciphers -fembed-bitcode -miphoneos-version-min=8.0 --openssldir="$TARGETDIR" - -make clean -make -make install_sw diff --git a/setup/build_crypto_iphonesimulator_i386.sh b/setup/build_crypto_iphonesimulator_i386.sh deleted file mode 100644 index b8acacb4..00000000 --- a/setup/build_crypto_iphonesimulator_i386.sh +++ /dev/null @@ -1,21 +0,0 @@ -#!/bin/sh -set -ex - -CURRENTPATH=$(pwd) - -PLATFORM=`xcrun -sdk iphonesimulator --show-sdk-platform-path` -SDK=`xcrun -sdk iphonesimulator --show-sdk-path` - -export CROSS_TOP=$PLATFORM/Developer -export CROSS_SDK=`basename $SDK` -export BUILD_TOOLS=`xcode-select --print-path` -export CC="`xcrun -find gcc` -arch i386 -isysroot `xcrun -sdk iphonesimulator --show-sdk-path`" - -TARGETDIR="$CURRENTPATH"/.build/iphonesimulator/i386 -mkdir -p "$TARGETDIR" - -./Configure iphoneos-cross no-shared no-dso no-hw no-engine no-ssl2 no-ssl3 no-comp no-idea no-asm no-dtls no-dtls1 no-threads no-err no-npn no-psk no-srp no-ec2m no-weak-ssl-ciphers -fembed-bitcode-marker -miphoneos-version-min=8.0 --openssldir="$TARGETDIR" - -make clean -make -make install_sw diff --git a/setup/build_crypto_iphonesimulator_x86_64.sh b/setup/build_crypto_iphonesimulator_x86_64.sh deleted file mode 100644 index 872a20e9..00000000 --- a/setup/build_crypto_iphonesimulator_x86_64.sh +++ /dev/null @@ -1,21 +0,0 @@ -#!/bin/sh -set -ex - -CURRENTPATH=$(pwd) - -PLATFORM=`xcrun -sdk iphonesimulator --show-sdk-platform-path` -SDK=`xcrun -sdk iphonesimulator --show-sdk-path` - -export CROSS_TOP=$PLATFORM/Developer -export CROSS_SDK=`basename $SDK` -export BUILD_TOOLS=`xcode-select --print-path` -export CC="`xcrun -find gcc` -arch x86_64 -isysroot `xcrun -sdk iphonesimulator --show-sdk-path`" - -TARGETDIR="$CURRENTPATH"/.build/iphonesimulator/x86_64 -mkdir -p "$TARGETDIR" - -./Configure iphoneos-cross no-shared no-dso no-hw no-engine no-ssl2 no-ssl3 no-comp no-idea no-asm no-dtls no-dtls1 no-threads no-err no-npn no-psk no-srp no-ec2m no-weak-ssl-ciphers -fembed-bitcode-marker -miphoneos-version-min=8.0 --openssldir="$TARGETDIR" - -make clean -make -make install_sw diff --git a/setup/build_libraries.sh b/setup/build_libraries.sh index b8cbfa1a..b8907a3d 100644 --- a/setup/build_libraries.sh +++ b/setup/build_libraries.sh @@ -1,7 +1,7 @@ #!/bin/sh set -ex -SCRIPT_DIR=`dirname $0` +SCRIPT_DIR=`dirname "$0"` (cd "$SCRIPT_DIR" && sh build_secp256k1.sh) (cd "$SCRIPT_DIR" && sh build_crypto.sh) diff --git a/setup/build_secp256k1.sh b/setup/build_secp256k1.sh index 30777c28..49375311 100644 --- a/setup/build_secp256k1.sh +++ b/setup/build_secp256k1.sh @@ -1,7 +1,7 @@ #!/bin/sh set -ex -SCRIPT_DIR=`dirname $0` +SCRIPT_DIR=`dirname "$0"` TDIR=`mktemp -d` trap "{ cd - ; rm -rf $TDIR; exit 255; }" SIGINT @@ -10,12 +10,12 @@ cd $TDIR git clone https://github.com/bitcoin-core/secp256k1.git src -CURRENTPATH=$(pwd) +CURRENTPATH=`pwd` -TARGETDIR_IPHONEOS="$CURRENTPATH"/.build/iphoneos +TARGETDIR_IPHONEOS="$CURRENTPATH/.build/iphoneos" mkdir -p "$TARGETDIR_IPHONEOS" -TARGETDIR_SIMULATOR="$CURRENTPATH"/.build/iphonesimulator +TARGETDIR_SIMULATOR="$CURRENTPATH/.build/iphonesimulator" mkdir -p "$TARGETDIR_SIMULATOR" (cd src && ./autogen.sh) @@ -24,10 +24,10 @@ mkdir -p "$TARGETDIR_SIMULATOR" cd - -mkdir -p $SCRIPT_DIR/../Libraries/secp256k1/lib +mkdir -p "$SCRIPT_DIR/../Libraries/secp256k1/lib" xcrun lipo -create "$TARGETDIR_IPHONEOS/lib/libsecp256k1.a" \ "$TARGETDIR_SIMULATOR/lib/libsecp256k1.a" \ -o "$SCRIPT_DIR/../Libraries/secp256k1/lib/libsecp256k1.a" -cp -rf $TDIR/src/include $SCRIPT_DIR/../Libraries/secp256k1/ +cp -rf $TDIR/src/include "$SCRIPT_DIR/../Libraries/secp256k1" rm -rf $TDIR