Skip to content

Commit a04b89d

Browse files
authored
Adding Tweaking API (#192)
1 parent 21933e7 commit a04b89d

File tree

11 files changed

+282
-142
lines changed

11 files changed

+282
-142
lines changed

Package.swift

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -145,7 +145,6 @@ let package = Package(
145145
"swift-crypto/Tests/Test Vectors"
146146
],
147147
sources: [
148-
"Data.swift",
149148
"Digests.swift",
150149
"ECDSA.swift",
151150
"EdDSA.swift",
@@ -156,12 +155,13 @@ let package = Package(
156155
"Schnorr.swift",
157156
"secp256k1.swift",
158157
"SHA256.swift",
159-
"String.swift",
160158
"swift-crypto/Sources/Crypto/Digests/Digest.swift",
161159
"swift-crypto/Sources/Crypto/Signatures/Signature.swift",
162160
"swift-crypto/Sources/Crypto/Util/BoringSSL/RNG_boring.swift",
163161
"swift-crypto/Sources/Crypto/Util/SecureBytes.swift",
164162
"swift-crypto/Tests/_CryptoExtrasTests/Utils/BytesUtil.swift",
163+
"Tweak.swift",
164+
"Utility.swift",
165165
"Zeroization.swift"
166166
]
167167
),

README.md

Lines changed: 0 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -4,7 +4,6 @@
44
Swift package with elliptic curve public key cryptography, ECDSA, Schnorr Signatures for Bitcoin and C bindings from [libsecp256k1](https://github.com/bitcoin-core/secp256k1).
55

66

7-
87
# Objectives
98

109
Long-term goals are:

Sources/implementation/Data.swift

Lines changed: 0 additions & 29 deletions
This file was deleted.

Sources/implementation/ECDSA.swift

Lines changed: 5 additions & 8 deletions
Original file line numberDiff line numberDiff line change
@@ -76,7 +76,7 @@ public extension secp256k1.Signing {
7676
throw secp256k1Error.underlyingCryptoError
7777
}
7878

79-
self.rawRepresentation = Data(bytes: &signature.data, count: MemoryLayout.size(ofValue: signature.data))
79+
self.rawRepresentation = signature.dataValue
8080
}
8181

8282
/// Initializes ECDSASignature from the Compact representation.
@@ -87,14 +87,13 @@ public extension secp256k1.Signing {
8787

8888
defer { secp256k1_context_destroy(context) }
8989

90-
let compactSignatureBytes = Array(compactRepresentation)
9190
var signature = secp256k1_ecdsa_signature()
9291

93-
guard secp256k1_ecdsa_signature_parse_compact(context, &signature, compactSignatureBytes) == 1 else {
92+
guard secp256k1_ecdsa_signature_parse_compact(context, &signature, Array(compactRepresentation)) == 1 else {
9493
throw secp256k1Error.underlyingCryptoError
9594
}
9695

97-
self.rawRepresentation = Data(bytes: &signature.data, count: MemoryLayout.size(ofValue: signature.data))
96+
self.rawRepresentation = signature.dataValue
9897
}
9998

10099
/// Invokes the given closure with a buffer pointer covering the raw bytes of the digest.
@@ -170,7 +169,6 @@ extension secp256k1.Signing.ECDSASigner: DigestSigner, Signer {
170169
/// - Throws: If there is a failure producing the signature
171170
public func signature<D: Digest>(for digest: D) throws -> secp256k1.Signing.ECDSASignature {
172171
let context = try secp256k1.Context.create()
173-
174172
defer { secp256k1_context_destroy(context) }
175173

176174
var signature = secp256k1_ecdsa_signature()
@@ -179,7 +177,7 @@ extension secp256k1.Signing.ECDSASigner: DigestSigner, Signer {
179177
throw secp256k1Error.underlyingCryptoError
180178
}
181179

182-
return try secp256k1.Signing.ECDSASignature(Data(bytes: &signature.data, count: MemoryLayout.size(ofValue: signature.data)))
180+
return try secp256k1.Signing.ECDSASignature(signature.dataValue)
183181
}
184182

185183
/// Generates an ECDSA signature over the secp256k1 elliptic curve.
@@ -218,11 +216,10 @@ extension secp256k1.Signing.ECDSAValidator: DigestValidator, DataValidator {
218216

219217
var secp256k1Signature = secp256k1_ecdsa_signature()
220218
var secp256k1PublicKey = secp256k1_pubkey()
221-
let pubKey = validatingKey.keyBytes
222219

223220
signature.rawRepresentation.copyToUnsafeMutableBytes(of: &secp256k1Signature.data)
224221

225-
return secp256k1_ec_pubkey_parse(context, &secp256k1PublicKey, pubKey, pubKey.count) == 1 &&
222+
return secp256k1_ec_pubkey_parse(context, &secp256k1PublicKey, validatingKey.bytes, validatingKey.bytes.count) == 1 &&
226223
secp256k1_ecdsa_verify(context, &secp256k1Signature, Array(digest), &secp256k1PublicKey) == 1
227224
}
228225

Sources/implementation/Schnorr.swift

Lines changed: 7 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -20,6 +20,10 @@ extension secp256k1 {
2020
64
2121
}
2222

23+
@inlinable static var xonlyByteCount: Int {
24+
32
25+
}
26+
2327
/// Tuple representation of ``SECP256K1_SCHNORRSIG_EXTRAPARAMS_MAGIC``
2428
///
2529
/// Only used at initialization and has no other function than making sure the object is initialized.
@@ -96,7 +100,7 @@ extension secp256k1.Signing.SchnorrSigner: DigestSigner, Signer {
96100
/// - Returns: The Schnorr Signature.
97101
/// - Throws: If there is a failure producing the signature.
98102
public func signature<D: DataProtocol>(for data: D) throws -> secp256k1.Signing.SchnorrSignature {
99-
try signature(for: data, auxiliaryRand: SecureBytes(count: 32).bytes)
103+
try signature(for: data, auxiliaryRand: SecureBytes(count: secp256k1.Schnorr.xonlyByteCount).bytes)
100104
}
101105

102106
/// Generates an Schnorr signature from the hash digest object
@@ -111,7 +115,7 @@ extension secp256k1.Signing.SchnorrSigner: DigestSigner, Signer {
111115
/// - Returns: The Schnorr Signature.
112116
/// - Throws: If there is a failure producing the signature.
113117
public func signature<D: Digest>(for digest: D) throws -> secp256k1.Signing.SchnorrSignature {
114-
try signature(for: digest, auxiliaryRand: SecureBytes(count: 32).bytes)
118+
try signature(for: digest, auxiliaryRand: SecureBytes(count: secp256k1.Schnorr.xonlyByteCount).bytes)
115119
}
116120

117121
/// Generates an Schnorr signature from a hash of a variable length data object
@@ -239,7 +243,7 @@ extension secp256k1.Signing.SchnorrValidator: DigestValidator, DataValidator {
239243

240244
var pubKey = secp256k1_xonly_pubkey()
241245

242-
return secp256k1_xonly_pubkey_parse(context, &pubKey, validatingKey.xonlyKeyBytes) == 1 &&
246+
return secp256k1_xonly_pubkey_parse(context, &pubKey, validatingKey.xonly.bytes) == 1 &&
243247
secp256k1_schnorrsig_verify(context, signature.rawRepresentation.bytes, message, message.count, &pubKey) == 1
244248
}
245249
}

Sources/implementation/String.swift

Lines changed: 0 additions & 29 deletions
This file was deleted.

Sources/implementation/Tweak.swift

Lines changed: 63 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,63 @@
1+
//
2+
// Tweak.swift
3+
// GigaBitcoin/secp256k1.swift
4+
//
5+
// Copyright (c) 2022 GigaBitcoin LLC
6+
// Distributed under the MIT software license
7+
//
8+
// See the accompanying file LICENSE for information
9+
//
10+
11+
import Foundation
12+
import secp256k1_bindings
13+
14+
public extension secp256k1.Signing.PrivateKey {
15+
/// Create a new `PrivateKey` by adding tweak to the secret key.
16+
/// - Parameter tweak: the 32-byte tweak object
17+
/// - Returns: tweaked `PrivateKey` object
18+
func tweak(_ tweak: [UInt8]) throws -> Self {
19+
let context = try secp256k1.Context.create()
20+
defer { secp256k1_context_destroy(context) }
21+
22+
var keypair = secp256k1_keypair()
23+
var privateBytes = [UInt8](repeating: 0, count: secp256k1.ByteDetails.count)
24+
25+
guard secp256k1_keypair_create(context, &keypair, key.bytes).boolValue,
26+
secp256k1_keypair_xonly_tweak_add(context, &keypair, tweak).boolValue,
27+
secp256k1_keypair_sec(context, &privateBytes, &keypair).boolValue else {
28+
throw secp256k1Error.underlyingCryptoError
29+
}
30+
31+
return try Self(rawRepresentation: privateBytes)
32+
}
33+
}
34+
35+
public extension secp256k1.Signing.PublicKey {
36+
/// Create a new `PublicKey` by adding tweak to the public key.
37+
/// - Parameters:
38+
/// - tweak: the 32-byte tweak object
39+
/// - format: the format of the tweaked `PublicKey` object
40+
/// - Returns: tweaked `PublicKey` object
41+
func tweak(_ tweak: [UInt8], format: secp256k1.Format = .compressed) throws -> Self {
42+
let context = try secp256k1.Context.create()
43+
defer { secp256k1_context_destroy(context) }
44+
45+
var xonlyPubKey = secp256k1_xonly_pubkey()
46+
var pubKey = secp256k1_pubkey()
47+
var pubKeyLen = format.length
48+
var pubBytes = [UInt8](repeating: 0, count: pubKeyLen)
49+
var xonlyPubKeyOutput = secp256k1_xonly_pubkey()
50+
var xonlyBytes = [UInt8](repeating: 0, count: secp256k1.Schnorr.xonlyByteCount)
51+
var keyParity = Int32()
52+
53+
guard secp256k1_xonly_pubkey_parse(context, &xonlyPubKey, xonlyBytes).boolValue,
54+
secp256k1_xonly_pubkey_tweak_add(context, &pubKey, &xonlyPubKey, tweak).boolValue,
55+
secp256k1_ec_pubkey_serialize(context, &pubBytes, &pubKeyLen, &pubKey, format.rawValue).boolValue,
56+
secp256k1_xonly_pubkey_from_pubkey(context, &xonlyPubKeyOutput, &keyParity, &pubKey).boolValue,
57+
secp256k1_xonly_pubkey_serialize(context, &xonlyBytes, &xonlyPubKeyOutput).boolValue else {
58+
throw secp256k1Error.underlyingCryptoError
59+
}
60+
61+
return Self(rawRepresentation: pubBytes, xonly: xonlyBytes, format: format)
62+
}
63+
}
Lines changed: 61 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,61 @@
1+
//
2+
// Utility.swift
3+
// GigaBitcoin/secp256k1.swift
4+
//
5+
// Copyright (c) 2022 GigaBitcoin LLC
6+
// Distributed under the MIT software license
7+
//
8+
// See the accompanying file LICENSE for information
9+
//
10+
11+
import Foundation
12+
import secp256k1_bindings
13+
14+
public extension ContiguousBytes {
15+
@inlinable var bytes: [UInt8] {
16+
withUnsafeBytes { bytesPtr in Array(bytesPtr) }
17+
}
18+
}
19+
20+
public extension Data {
21+
@inlinable var bytes: [UInt8] {
22+
withUnsafeBytes { bytesPtr in Array(bytesPtr) }
23+
}
24+
25+
func copyToUnsafeMutableBytes<T>(of value: inout T) {
26+
_ = Swift.withUnsafeMutableBytes(of: &value) { ptr in
27+
ptr.copyBytes(from: self.prefix(ptr.count))
28+
}
29+
}
30+
}
31+
32+
extension Int32 {
33+
var boolValue: Bool {
34+
Bool(truncating: NSNumber(value: self))
35+
}
36+
}
37+
38+
public extension secp256k1_ecdsa_signature {
39+
var dataValue: Data {
40+
var mutableSig = self
41+
return Data(bytes: &mutableSig.data, count: MemoryLayout.size(ofValue: data))
42+
}
43+
}
44+
45+
public extension String {
46+
/// Public initializer backed by the `BytesUtil.swift` DataProtocol extension property `hexString`
47+
/// - Parameter bytes: byte array to initialize
48+
init<T: DataProtocol>(bytes: T) {
49+
self.init()
50+
self = bytes.hexString
51+
}
52+
53+
/// Public convenience property backed by the `BytesUtil.swift` Array extension initializer
54+
/// - Throws: `ByteHexEncodingErrors` for invalid string or hex value
55+
var bytes: [UInt8] {
56+
get throws {
57+
// The `BytesUtil.swift` Array extension expects lowercase strings
58+
try Array(hexString: lowercased())
59+
}
60+
}
61+
}

0 commit comments

Comments
 (0)