Skip to content

Commit

Permalink
Merge pull request #161 from yenom/remove-direct-dependency-to-secp256k1
Browse files Browse the repository at this point in the history
♻️ Remove the direct dependency to secp256k1
  • Loading branch information
akifuji authored Sep 20, 2018
2 parents 62b5aec + 48c46ad commit 7a5a78d
Show file tree
Hide file tree
Showing 7 changed files with 108 additions and 43 deletions.
1 change: 1 addition & 0 deletions BitcoinKit/BitcoinKit.modulemap
Original file line number Diff line number Diff line change
Expand Up @@ -7,5 +7,6 @@ framework module BitcoinKit {
explicit module Private {
header "BitcoinKitPrivate.h"
link "crypto"
link "secp256k1"
}
}
4 changes: 4 additions & 0 deletions BitcoinKit/BitcoinKitPrivate.h
Original file line number Diff line number Diff line change
Expand Up @@ -57,4 +57,8 @@ NS_ASSUME_NONNULL_BEGIN

@end

@interface _Crypto : NSObject
+ (NSData *)signMessage:(NSData *)message withPrivateKey:(NSData *)privateKey;
+ (BOOL)verifySignature:(NSData *)signature message:(NSData *)message publicKey:(NSData *)publicKey;
@end
NS_ASSUME_NONNULL_END
35 changes: 35 additions & 0 deletions BitcoinKit/BitcoinKitPrivate.m
Original file line number Diff line number Diff line change
Expand Up @@ -29,6 +29,7 @@
#import <openssl/hmac.h>
#import <openssl/ec.h>
#import <openssl/bn.h>
#import <secp256k1.h>

@implementation _Hash

Expand Down Expand Up @@ -206,3 +207,37 @@ - (_HDKey *)derivedAtIndex:(uint32_t)index hardened:(BOOL)hardened {
}

@end

@implementation _Crypto

+ (NSData *)signMessage:(NSData *)message withPrivateKey:(NSData *)privateKey {
secp256k1_context *ctx = secp256k1_context_create(SECP256K1_CONTEXT_SIGN);
secp256k1_ecdsa_signature signature;
secp256k1_ecdsa_signature normalizedSignature;
secp256k1_ecdsa_sign(ctx, &signature, message.bytes, privateKey.bytes, NULL, NULL);
secp256k1_ecdsa_signature_normalize(ctx, &normalizedSignature, &signature);
size_t siglen = 74;
NSMutableData *der = [NSMutableData dataWithLength:siglen];
secp256k1_ecdsa_signature_serialize_der(ctx, der.mutableBytes, &siglen, &normalizedSignature);
der.length = siglen;
secp256k1_context_destroy(ctx);
return der;
}

+ (BOOL)verifySignature:(NSData *)sigData message:(NSData *)message publicKey:(NSData *)pubkeyData {
secp256k1_context *ctx = secp256k1_context_create(SECP256K1_CONTEXT_VERIFY);
secp256k1_ecdsa_signature signature;
secp256k1_pubkey pubkey;

secp256k1_ecdsa_signature_parse_der(ctx, &signature, sigData.bytes, sigData.length);
if (secp256k1_ec_pubkey_parse(ctx, &pubkey, pubkeyData.bytes, pubkeyData.length) != 1) {
return FALSE;
};

if (secp256k1_ecdsa_verify(ctx, &signature, message.bytes, &pubkey) != 1) {
return FALSE;
};
secp256k1_context_destroy(ctx);
return TRUE;
}
@end
3 changes: 2 additions & 1 deletion Package.swift
Original file line number Diff line number Diff line change
Expand Up @@ -17,7 +17,8 @@ let package = Package(
dependencies: ["BitcoinKitPrivate", "secp256k1", "Random"]
),
.target(
name: "BitcoinKitPrivate"
name: "BitcoinKitPrivate",
dependencies: ["COpenSSL", "secp256k1"]
),
.testTarget(
name: "BitcoinKitTests",
Expand Down
51 changes: 10 additions & 41 deletions Sources/BitcoinKit/Core/Crypto.swift
Original file line number Diff line number Diff line change
Expand Up @@ -29,7 +29,6 @@ import BitcoinKit.Private
#else
import BitcoinKitPrivate
#endif
import secp256k1

public struct Crypto {
public static func sha1(_ data: Data) -> Data {
Expand Down Expand Up @@ -57,49 +56,19 @@ public struct Crypto {
}

public static func sign(_ data: Data, privateKey: PrivateKey) throws -> Data {
let ctx = secp256k1_context_create(UInt32(SECP256K1_CONTEXT_SIGN))!
defer { secp256k1_context_destroy(ctx) }

let signature = UnsafeMutablePointer<secp256k1_ecdsa_signature>.allocate(capacity: 1)
defer { signature.deallocate() }
let status = data.withUnsafeBytes { (ptr: UnsafePointer<UInt8>) in
privateKey.data.withUnsafeBytes { secp256k1_ecdsa_sign(ctx, signature, ptr, $0, nil, nil) }
}
guard status == 1 else { throw CryptoError.signFailed }

let normalizedsig = UnsafeMutablePointer<secp256k1_ecdsa_signature>.allocate(capacity: 1)
defer { normalizedsig.deallocate() }
secp256k1_ecdsa_signature_normalize(ctx, normalizedsig, signature)

var length: size_t = 128
var der = Data(count: length)
guard der.withUnsafeMutableBytes({ return secp256k1_ecdsa_signature_serialize_der(ctx, $0, &length, normalizedsig) }) == 1 else { throw CryptoError.noEnoughSpace }
der.count = length

return der
#if BitcoinKitXcode
return _Crypto.signMessage(data, withPrivateKey: privateKey.data)
#else
return try _Crypto.signMessage(data, withPrivateKey: privateKey.data)
#endif
}

public static func verifySignature(_ signature: Data, message: Data, publicKey: Data) throws -> Bool {
let ctx = secp256k1_context_create(UInt32(SECP256K1_CONTEXT_VERIFY))!
defer { secp256k1_context_destroy(ctx) }

let signaturePointer = UnsafeMutablePointer<secp256k1_ecdsa_signature>.allocate(capacity: 1)
defer { signaturePointer.deallocate() }
guard signature.withUnsafeBytes({ secp256k1_ecdsa_signature_parse_der(ctx, signaturePointer, $0, signature.count) }) == 1 else {
throw CryptoError.signatureParseFailed
}

let pubkeyPointer = UnsafeMutablePointer<secp256k1_pubkey>.allocate(capacity: 1)
defer { pubkeyPointer.deallocate() }
guard publicKey.withUnsafeBytes({ secp256k1_ec_pubkey_parse(ctx, pubkeyPointer, $0, publicKey.count) }) == 1 else {
throw CryptoError.publicKeyParseFailed
}

guard message.withUnsafeBytes ({ secp256k1_ecdsa_verify(ctx, signaturePointer, $0, pubkeyPointer) }) == 1 else {
return false
}

return true
#if BitcoinKitXcode
return _Crypto.verifySignature(signature, message: message, publicKey: publicKey)
#else
return try _Crypto.verifySignature(signature, message: message, publicKey: publicKey)
#endif
}

public static func verifySigData(for tx: Transaction, inputIndex: Int, utxo: TransactionOutput, sigData: Data, pubKeyData: Data) throws -> Bool {
Expand Down
1 change: 0 additions & 1 deletion Sources/BitcoinKit/Scripts/ScriptMachine.swift
Original file line number Diff line number Diff line change
Expand Up @@ -23,7 +23,6 @@
//

import Foundation
import secp256k1

public enum ScriptVerification {
case StrictEncoding // enforce strict conformance to DER and SEC2 for signatures and pubkeys (aka SCRIPT_VERIFY_STRICTENC)
Expand Down
56 changes: 56 additions & 0 deletions Sources/BitcoinKitPrivate/BitcoinKit.Private.swift
Original file line number Diff line number Diff line change
Expand Up @@ -8,6 +8,7 @@

import Foundation
import COpenSSL
import secp256k1

public class _Hash {
public static func sha1(_ data: Data) -> Data {
Expand Down Expand Up @@ -243,3 +244,58 @@ public class _HDKey {
}
}
}

public class _Crypto {
public static func signMessage(_ data: Data, withPrivateKey privateKey: Data) throws -> Data {
let ctx = secp256k1_context_create(UInt32(SECP256K1_CONTEXT_SIGN))!
defer { secp256k1_context_destroy(ctx) }

let signature = UnsafeMutablePointer<secp256k1_ecdsa_signature>.allocate(capacity: 1)
defer { signature.deallocate() }
let status = data.withUnsafeBytes { (ptr: UnsafePointer<UInt8>) in
privateKey.withUnsafeBytes { secp256k1_ecdsa_sign(ctx, signature, ptr, $0, nil, nil) }
}
guard status == 1 else { throw CryptoError.signFailed }

let normalizedsig = UnsafeMutablePointer<secp256k1_ecdsa_signature>.allocate(capacity: 1)
defer { normalizedsig.deallocate() }
secp256k1_ecdsa_signature_normalize(ctx, normalizedsig, signature)

var length: size_t = 128
var der = Data(count: length)
guard der.withUnsafeMutableBytes({ return secp256k1_ecdsa_signature_serialize_der(ctx, $0, &length, normalizedsig) }) == 1 else { throw CryptoError.noEnoughSpace }
der.count = length

return der
}

public static func verifySignature(_ signature: Data, message: Data, publicKey: Data) throws -> Bool {
let ctx = secp256k1_context_create(UInt32(SECP256K1_CONTEXT_VERIFY))!
defer { secp256k1_context_destroy(ctx) }

let signaturePointer = UnsafeMutablePointer<secp256k1_ecdsa_signature>.allocate(capacity: 1)
defer { signaturePointer.deallocate() }
guard signature.withUnsafeBytes({ secp256k1_ecdsa_signature_parse_der(ctx, signaturePointer, $0, signature.count) }) == 1 else {
throw CryptoError.signatureParseFailed
}

let pubkeyPointer = UnsafeMutablePointer<secp256k1_pubkey>.allocate(capacity: 1)
defer { pubkeyPointer.deallocate() }
guard publicKey.withUnsafeBytes({ secp256k1_ec_pubkey_parse(ctx, pubkeyPointer, $0, publicKey.count) }) == 1 else {
throw CryptoError.publicKeyParseFailed
}

guard message.withUnsafeBytes ({ secp256k1_ecdsa_verify(ctx, signaturePointer, $0, pubkeyPointer) }) == 1 else {
return false
}

return true
}

public enum CryptoError: Error {
case signFailed
case noEnoughSpace
case signatureParseFailed
case publicKeyParseFailed
}
}

0 comments on commit 7a5a78d

Please sign in to comment.