Skip to content

Commit 7a5a78d

Browse files
author
akifuji
authored
Merge pull request #161 from yenom/remove-direct-dependency-to-secp256k1
♻️ Remove the direct dependency to secp256k1
2 parents 62b5aec + 48c46ad commit 7a5a78d

File tree

7 files changed

+108
-43
lines changed

7 files changed

+108
-43
lines changed

BitcoinKit/BitcoinKit.modulemap

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -7,5 +7,6 @@ framework module BitcoinKit {
77
explicit module Private {
88
header "BitcoinKitPrivate.h"
99
link "crypto"
10+
link "secp256k1"
1011
}
1112
}

BitcoinKit/BitcoinKitPrivate.h

Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -57,4 +57,8 @@ NS_ASSUME_NONNULL_BEGIN
5757

5858
@end
5959

60+
@interface _Crypto : NSObject
61+
+ (NSData *)signMessage:(NSData *)message withPrivateKey:(NSData *)privateKey;
62+
+ (BOOL)verifySignature:(NSData *)signature message:(NSData *)message publicKey:(NSData *)publicKey;
63+
@end
6064
NS_ASSUME_NONNULL_END

BitcoinKit/BitcoinKitPrivate.m

Lines changed: 35 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -29,6 +29,7 @@
2929
#import <openssl/hmac.h>
3030
#import <openssl/ec.h>
3131
#import <openssl/bn.h>
32+
#import <secp256k1.h>
3233

3334
@implementation _Hash
3435

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

208209
@end
210+
211+
@implementation _Crypto
212+
213+
+ (NSData *)signMessage:(NSData *)message withPrivateKey:(NSData *)privateKey {
214+
secp256k1_context *ctx = secp256k1_context_create(SECP256K1_CONTEXT_SIGN);
215+
secp256k1_ecdsa_signature signature;
216+
secp256k1_ecdsa_signature normalizedSignature;
217+
secp256k1_ecdsa_sign(ctx, &signature, message.bytes, privateKey.bytes, NULL, NULL);
218+
secp256k1_ecdsa_signature_normalize(ctx, &normalizedSignature, &signature);
219+
size_t siglen = 74;
220+
NSMutableData *der = [NSMutableData dataWithLength:siglen];
221+
secp256k1_ecdsa_signature_serialize_der(ctx, der.mutableBytes, &siglen, &normalizedSignature);
222+
der.length = siglen;
223+
secp256k1_context_destroy(ctx);
224+
return der;
225+
}
226+
227+
+ (BOOL)verifySignature:(NSData *)sigData message:(NSData *)message publicKey:(NSData *)pubkeyData {
228+
secp256k1_context *ctx = secp256k1_context_create(SECP256K1_CONTEXT_VERIFY);
229+
secp256k1_ecdsa_signature signature;
230+
secp256k1_pubkey pubkey;
231+
232+
secp256k1_ecdsa_signature_parse_der(ctx, &signature, sigData.bytes, sigData.length);
233+
if (secp256k1_ec_pubkey_parse(ctx, &pubkey, pubkeyData.bytes, pubkeyData.length) != 1) {
234+
return FALSE;
235+
};
236+
237+
if (secp256k1_ecdsa_verify(ctx, &signature, message.bytes, &pubkey) != 1) {
238+
return FALSE;
239+
};
240+
secp256k1_context_destroy(ctx);
241+
return TRUE;
242+
}
243+
@end

Package.swift

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -17,7 +17,8 @@ let package = Package(
1717
dependencies: ["BitcoinKitPrivate", "secp256k1", "Random"]
1818
),
1919
.target(
20-
name: "BitcoinKitPrivate"
20+
name: "BitcoinKitPrivate",
21+
dependencies: ["COpenSSL", "secp256k1"]
2122
),
2223
.testTarget(
2324
name: "BitcoinKitTests",

Sources/BitcoinKit/Core/Crypto.swift

Lines changed: 10 additions & 41 deletions
Original file line numberDiff line numberDiff line change
@@ -29,7 +29,6 @@ import BitcoinKit.Private
2929
#else
3030
import BitcoinKitPrivate
3131
#endif
32-
import secp256k1
3332

3433
public struct Crypto {
3534
public static func sha1(_ data: Data) -> Data {
@@ -57,49 +56,19 @@ public struct Crypto {
5756
}
5857

5958
public static func sign(_ data: Data, privateKey: PrivateKey) throws -> Data {
60-
let ctx = secp256k1_context_create(UInt32(SECP256K1_CONTEXT_SIGN))!
61-
defer { secp256k1_context_destroy(ctx) }
62-
63-
let signature = UnsafeMutablePointer<secp256k1_ecdsa_signature>.allocate(capacity: 1)
64-
defer { signature.deallocate() }
65-
let status = data.withUnsafeBytes { (ptr: UnsafePointer<UInt8>) in
66-
privateKey.data.withUnsafeBytes { secp256k1_ecdsa_sign(ctx, signature, ptr, $0, nil, nil) }
67-
}
68-
guard status == 1 else { throw CryptoError.signFailed }
69-
70-
let normalizedsig = UnsafeMutablePointer<secp256k1_ecdsa_signature>.allocate(capacity: 1)
71-
defer { normalizedsig.deallocate() }
72-
secp256k1_ecdsa_signature_normalize(ctx, normalizedsig, signature)
73-
74-
var length: size_t = 128
75-
var der = Data(count: length)
76-
guard der.withUnsafeMutableBytes({ return secp256k1_ecdsa_signature_serialize_der(ctx, $0, &length, normalizedsig) }) == 1 else { throw CryptoError.noEnoughSpace }
77-
der.count = length
78-
79-
return der
59+
#if BitcoinKitXcode
60+
return _Crypto.signMessage(data, withPrivateKey: privateKey.data)
61+
#else
62+
return try _Crypto.signMessage(data, withPrivateKey: privateKey.data)
63+
#endif
8064
}
8165

8266
public static func verifySignature(_ signature: Data, message: Data, publicKey: Data) throws -> Bool {
83-
let ctx = secp256k1_context_create(UInt32(SECP256K1_CONTEXT_VERIFY))!
84-
defer { secp256k1_context_destroy(ctx) }
85-
86-
let signaturePointer = UnsafeMutablePointer<secp256k1_ecdsa_signature>.allocate(capacity: 1)
87-
defer { signaturePointer.deallocate() }
88-
guard signature.withUnsafeBytes({ secp256k1_ecdsa_signature_parse_der(ctx, signaturePointer, $0, signature.count) }) == 1 else {
89-
throw CryptoError.signatureParseFailed
90-
}
91-
92-
let pubkeyPointer = UnsafeMutablePointer<secp256k1_pubkey>.allocate(capacity: 1)
93-
defer { pubkeyPointer.deallocate() }
94-
guard publicKey.withUnsafeBytes({ secp256k1_ec_pubkey_parse(ctx, pubkeyPointer, $0, publicKey.count) }) == 1 else {
95-
throw CryptoError.publicKeyParseFailed
96-
}
97-
98-
guard message.withUnsafeBytes ({ secp256k1_ecdsa_verify(ctx, signaturePointer, $0, pubkeyPointer) }) == 1 else {
99-
return false
100-
}
101-
102-
return true
67+
#if BitcoinKitXcode
68+
return _Crypto.verifySignature(signature, message: message, publicKey: publicKey)
69+
#else
70+
return try _Crypto.verifySignature(signature, message: message, publicKey: publicKey)
71+
#endif
10372
}
10473

10574
public static func verifySigData(for tx: Transaction, inputIndex: Int, utxo: TransactionOutput, sigData: Data, pubKeyData: Data) throws -> Bool {

Sources/BitcoinKit/Scripts/ScriptMachine.swift

Lines changed: 0 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -23,7 +23,6 @@
2323
//
2424

2525
import Foundation
26-
import secp256k1
2726

2827
public enum ScriptVerification {
2928
case StrictEncoding // enforce strict conformance to DER and SEC2 for signatures and pubkeys (aka SCRIPT_VERIFY_STRICTENC)

Sources/BitcoinKitPrivate/BitcoinKit.Private.swift

Lines changed: 56 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -8,6 +8,7 @@
88

99
import Foundation
1010
import COpenSSL
11+
import secp256k1
1112

1213
public class _Hash {
1314
public static func sha1(_ data: Data) -> Data {
@@ -243,3 +244,58 @@ public class _HDKey {
243244
}
244245
}
245246
}
247+
248+
public class _Crypto {
249+
public static func signMessage(_ data: Data, withPrivateKey privateKey: Data) throws -> Data {
250+
let ctx = secp256k1_context_create(UInt32(SECP256K1_CONTEXT_SIGN))!
251+
defer { secp256k1_context_destroy(ctx) }
252+
253+
let signature = UnsafeMutablePointer<secp256k1_ecdsa_signature>.allocate(capacity: 1)
254+
defer { signature.deallocate() }
255+
let status = data.withUnsafeBytes { (ptr: UnsafePointer<UInt8>) in
256+
privateKey.withUnsafeBytes { secp256k1_ecdsa_sign(ctx, signature, ptr, $0, nil, nil) }
257+
}
258+
guard status == 1 else { throw CryptoError.signFailed }
259+
260+
let normalizedsig = UnsafeMutablePointer<secp256k1_ecdsa_signature>.allocate(capacity: 1)
261+
defer { normalizedsig.deallocate() }
262+
secp256k1_ecdsa_signature_normalize(ctx, normalizedsig, signature)
263+
264+
var length: size_t = 128
265+
var der = Data(count: length)
266+
guard der.withUnsafeMutableBytes({ return secp256k1_ecdsa_signature_serialize_der(ctx, $0, &length, normalizedsig) }) == 1 else { throw CryptoError.noEnoughSpace }
267+
der.count = length
268+
269+
return der
270+
}
271+
272+
public static func verifySignature(_ signature: Data, message: Data, publicKey: Data) throws -> Bool {
273+
let ctx = secp256k1_context_create(UInt32(SECP256K1_CONTEXT_VERIFY))!
274+
defer { secp256k1_context_destroy(ctx) }
275+
276+
let signaturePointer = UnsafeMutablePointer<secp256k1_ecdsa_signature>.allocate(capacity: 1)
277+
defer { signaturePointer.deallocate() }
278+
guard signature.withUnsafeBytes({ secp256k1_ecdsa_signature_parse_der(ctx, signaturePointer, $0, signature.count) }) == 1 else {
279+
throw CryptoError.signatureParseFailed
280+
}
281+
282+
let pubkeyPointer = UnsafeMutablePointer<secp256k1_pubkey>.allocate(capacity: 1)
283+
defer { pubkeyPointer.deallocate() }
284+
guard publicKey.withUnsafeBytes({ secp256k1_ec_pubkey_parse(ctx, pubkeyPointer, $0, publicKey.count) }) == 1 else {
285+
throw CryptoError.publicKeyParseFailed
286+
}
287+
288+
guard message.withUnsafeBytes ({ secp256k1_ecdsa_verify(ctx, signaturePointer, $0, pubkeyPointer) }) == 1 else {
289+
return false
290+
}
291+
292+
return true
293+
}
294+
295+
public enum CryptoError: Error {
296+
case signFailed
297+
case noEnoughSpace
298+
case signatureParseFailed
299+
case publicKeyParseFailed
300+
}
301+
}

0 commit comments

Comments
 (0)