Skip to content

Commit ca73782

Browse files
SajjonCyonAlexRDX
andauthored
Major cleanup of Recovery (#6)
Co-authored-by: Alexander Cyon <[email protected]>
1 parent 202b5ce commit ca73782

16 files changed

+284
-208
lines changed

Sources/K1/K1/ECDSA/ECDSASignatureNonRecoverable+Conversion.swift

+3-4
Original file line numberDiff line numberDiff line change
@@ -16,13 +16,12 @@ extension ECDSASignatureNonRecoverable {
1616
return try Self(rawRepresentation: signatureData)
1717
}
1818

19-
/// `R||S` aka `X9.63` aka `IEEE P1363`
20-
public func p1364() throws -> Data {
21-
try Bridge.compactRepresentationOfSignature(rawRepresentation: _rawRepresentation)
19+
public func compactRepresentation() throws -> Data {
20+
try Bridge.compactRepresentationOfSignature(rawRepresentation: rawRepresentation)
2221
}
2322

2423
public func derRepresentation() throws -> Data {
25-
try Bridge.derRepresentationOfSignature(rawRepresentation: _rawRepresentation)
24+
try Bridge.derRepresentationOfSignature(rawRepresentation: rawRepresentation)
2625
}
2726
}
2827

Sources/K1/K1/ECDSA/ECDSASignatureNonRecoverable.swift

+20-7
Original file line numberDiff line numberDiff line change
@@ -7,15 +7,11 @@
77

88
import Foundation
99
import CryptoKit
10+
import secp256k1
1011

1112
public struct ECDSASignatureNonRecoverable: Sendable, Hashable, ECSignature {
12-
13-
internal let _rawRepresentation: Data
1413

15-
/// Accepts `R||S` format
16-
public init(p1364: Data) throws {
17-
try self.init(rawRepresentation: swapSignatureByteOrder(p1364))
18-
}
14+
public let rawRepresentation: Data
1915

2016
public init<D: DataProtocol>(rawRepresentation: D) throws {
2117
guard
@@ -24,7 +20,24 @@ public struct ECDSASignatureNonRecoverable: Sendable, Hashable, ECSignature {
2420
throw K1.Error.incorrectByteCountOfRawSignature
2521
}
2622

27-
self._rawRepresentation = Data(rawRepresentation)
23+
self.rawRepresentation = Data(rawRepresentation)
24+
}
25+
26+
public init<D: DataProtocol>(compactRepresentation: D) throws {
27+
var signature = secp256k1_ecdsa_signature()
28+
let compactBytes = [UInt8](compactRepresentation)
29+
try Bridge.call(ifFailThrow: .failedToParseSignatureFromCompactRepresentation) { context in
30+
secp256k1_ecdsa_signature_parse_compact(
31+
context,
32+
&signature,
33+
compactBytes
34+
)
35+
}
36+
37+
try self.init(rawRepresentation: Data(
38+
bytes: &signature.data,
39+
count: MemoryLayout.size(ofValue: signature.data)
40+
))
2841
}
2942
}
3043

Sources/K1/K1/ECDSA/ECDSASignatureRecoverable+Conversion.swift

-22
This file was deleted.

Sources/K1/K1/ECDSA/ECDSASignatureRecoverable.swift

+55-29
Original file line numberDiff line numberDiff line change
@@ -7,45 +7,80 @@
77

88
import Foundation
99
import CryptoKit
10+
import secp256k1
1011

1112
public struct ECDSASignatureRecoverable: Sendable, Hashable, ECSignature {
13+
14+
public let rawRepresentation: Data
1215

13-
internal let _rawRepresentation: Data
14-
16+
public init(compactRepresentation: Data, recoveryID: Int32) throws {
17+
guard
18+
compactRepresentation.count == ECDSASignatureNonRecoverable.byteCount
19+
else {
20+
throw K1.Error.incorrectByteCountOfRawSignature
21+
}
22+
var recoverableSignature = secp256k1_ecdsa_recoverable_signature()
23+
let rs = [UInt8](compactRepresentation)
24+
25+
try Bridge.call(ifFailThrow: .failedToParseRecoverableSignatureFromCompactRepresentation) { context in
26+
secp256k1_ecdsa_recoverable_signature_parse_compact(
27+
context,
28+
&recoverableSignature,
29+
rs,
30+
recoveryID
31+
)
32+
}
33+
self.rawRepresentation = Data(
34+
bytes: &recoverableSignature.data,
35+
count: MemoryLayout.size(ofValue: recoverableSignature.data)
36+
)
37+
}
38+
1539
public init<D: DataProtocol>(rawRepresentation: D) throws {
16-
40+
1741
guard
18-
rawRepresentation.count == Self.byteCount
42+
rawRepresentation.count == ECDSASignatureNonRecoverable.byteCount + 1
1943
else {
2044
throw K1.Error.incorrectByteCountOfRawSignature
2145
}
22-
23-
self._rawRepresentation = Data(rawRepresentation)
46+
self.rawRepresentation = Data(rawRepresentation)
2447
}
25-
}
26-
27-
28-
private extension ECDSASignatureRecoverable {
29-
static let byteCount = ECDSASignatureNonRecoverable.byteCount + 1
48+
49+
3050
}
3151

3252
public extension ECDSASignatureRecoverable {
3353

34-
/// `R||S` without `V`
35-
func rs() -> Data {
36-
Data(_rawRepresentation.prefix(64))
37-
}
38-
39-
/// aka Signature `v`, aka `recid`
40-
var recoveryID: Int { Int(_rawRepresentation[64]) }
41-
4254
typealias Scheme = ECDSA
4355
static let scheme: SigningScheme = .ecdsa
4456

4557
func nonRecoverable() throws -> ECDSASignatureNonRecoverable {
4658
try Bridge.convertToNonRecoverable(ecdsaSignature: self)
4759
}
4860

61+
func compact() throws -> (rs: Data, recoveryID: Int) {
62+
var rsBytes = [UInt8](repeating: 0, count: 64)
63+
var recoveryID: Int32 = 0
64+
65+
var recoverableBridgedToC = secp256k1_ecdsa_recoverable_signature()
66+
withUnsafeMutableBytes(of: &recoverableBridgedToC.data) { pointer in
67+
pointer.copyBytes(
68+
from: rawRepresentation.prefix(pointer.count)
69+
)
70+
}
71+
72+
try Bridge.call(
73+
ifFailThrow: .failedSignatureToConvertRecoverableSignatureToCompact) { context in
74+
secp256k1_ecdsa_recoverable_signature_serialize_compact(
75+
context,
76+
&rsBytes,
77+
&recoveryID,
78+
&recoverableBridgedToC
79+
)
80+
}
81+
return (rs: Data(rsBytes), recoveryID: Int(recoveryID))
82+
}
83+
4984
}
5085

5186
public extension ECDSASignatureRecoverable {
@@ -63,16 +98,7 @@ public extension ECDSASignatureRecoverable {
6398
static func by<D>(signing hashed: D, with privateKey: K1.PrivateKey, mode: SigningMode) throws -> Self where D : DataProtocol {
6499
try privateKey.ecdsaSignRecoverable(hashed: hashed, mode: mode)
65100
}
66-
67-
// /// Tosses away V byte
68-
// func compactRepresentation() throws -> Data {
69-
//
70-
// }
71-
//
72-
// func derRepresentation() throws -> Data {
73-
// fatalError()
74-
// }
75-
101+
76102
func wasSigned<D>(by signer: K1.PublicKey, for digest: D) throws -> Bool where D : Digest {
77103
try nonRecoverable().wasSigned(by: signer, for: digest)
78104
}

Sources/K1/K1/ECDSA/Signature+Format+Conversion.swift

+10-6
Original file line numberDiff line numberDiff line change
@@ -42,24 +42,28 @@ extension Bridge {
4242
pointer.copyBytes(from: rawRepresentation.prefix(pointer.count))
4343
}
4444

45+
4546
try Bridge.call(ifFailThrow: .failedToSerializeCompactSignature) { context in
46-
secp256k1_ecdsa_signature_serialize_compact(context, &compactSignature, &signatureBridgedToC)
47+
secp256k1_ecdsa_signature_serialize_compact(
48+
context,
49+
&compactSignature,
50+
&signatureBridgedToC
51+
)
4752

4853
}
4954

50-
return Data(bytes: &compactSignature, count: compactSignatureLength)
55+
return Data(compactSignature)
5156
}
5257

5358
static func derRepresentationOfSignature(rawRepresentation: Data) throws -> Data {
54-
5559
var signatureBridgedToC = secp256k1_ecdsa_signature()
5660
var derMaxLength = 75 // in fact max is 73, but we can have some margin.
5761
var derSignature = [UInt8](repeating: 0, count: derMaxLength)
58-
62+
5963
withUnsafeMutableBytes(of: &signatureBridgedToC.data) { pointer in
6064
pointer.copyBytes(from: rawRepresentation.prefix(pointer.count))
6165
}
62-
66+
6367
try Bridge.call(ifFailThrow: .failedToSerializeDERSignature) { context in
6468
secp256k1_ecdsa_signature_serialize_der(
6569
context,
@@ -68,7 +72,7 @@ extension Bridge {
6872
&signatureBridgedToC
6973
)
7074
}
71-
75+
7276
return Data(bytes: &derSignature, count: derMaxLength)
7377
}
7478
}

Sources/K1/K1/K1/Error.swift

+3
Original file line numberDiff line numberDiff line change
@@ -36,6 +36,9 @@ public extension K1 {
3636
case failedToECDSASignDigest
3737
case recoverPublicKeyDiscrepancyReceivedSignatureContainingRecoveryIDButFunctionSpecifiesANonMatchingOne
3838
case failedToParseRecoverableSignatureFromECDSASignature
39+
case failedToParseRecoverableSignatureFromCompactRepresentation
40+
case failedToParseSignatureFromCompactRepresentation
41+
case failedSignatureToConvertRecoverableSignatureToCompact
3942
case failedToConvertRecoverableSignatureToNonRecoverable
4043
case failedToRecoverPublicKeyFromSignature
4144
case failedToNormalizeECDSASignature

Sources/K1/K1/Keys/PrivateKey/PrivateKey/PrivateKey+Bridge+To+C.swift

+34-39
Original file line numberDiff line numberDiff line change
@@ -9,23 +9,6 @@ import secp256k1
99
import CryptoKit
1010
import Foundation
1111

12-
struct IncorrectByteCount: Swift.Error {}
13-
public func swapSignatureByteOrder<D>(_ data: D) throws -> Data where D: DataProtocol {
14-
guard data.count == 64 || data.count == 65 else {
15-
throw IncorrectByteCount()
16-
}
17-
let invalidByteOrder = Data(data)
18-
let r = Data(invalidByteOrder[0 ..< 32].reversed())
19-
let s = Data(invalidByteOrder[32 ..< 64].reversed())
20-
21-
var vDataOrEmpty = Data()
22-
if data.count > 64 {
23-
vDataOrEmpty = Data([invalidByteOrder[64]])
24-
}
25-
26-
return vDataOrEmpty + r + s
27-
}
28-
2912
extension Bridge {
3013

3114
/// Produces a **recoverable** ECDSA signature.
@@ -67,7 +50,7 @@ extension Bridge {
6750
nonceFunctionArbitraryBytes
6851
)
6952
}
70-
53+
7154
return Data(
7255
bytes: &signatureRecoverableBridgedToC.data,
7356
count: MemoryLayout.size(ofValue: signatureRecoverableBridgedToC.data)
@@ -218,15 +201,10 @@ extension Bridge {
218201
ecdsaSignature: ECDSASignatureRecoverable
219202
) throws -> ECDSASignatureNonRecoverable {
220203
var recoverableBridgedToC = secp256k1_ecdsa_recoverable_signature()
221-
let rs = [UInt8](ecdsaSignature.rs())
222-
try Self.call(
223-
ifFailThrow: .failedToParseRecoverableSignatureFromECDSASignature
224-
) { context in
225-
secp256k1_ecdsa_recoverable_signature_parse_compact(
226-
context,
227-
&recoverableBridgedToC,
228-
rs,
229-
Int32(ecdsaSignature.recoveryID)
204+
205+
withUnsafeMutableBytes(of: &recoverableBridgedToC.data) { pointer in
206+
pointer.copyBytes(
207+
from: ecdsaSignature.rawRepresentation.prefix(pointer.count)
230208
)
231209
}
232210

@@ -257,7 +235,7 @@ extension Bridge {
257235
message: [UInt8]
258236
) throws -> [UInt8] {
259237
try _recoverPublicKey(
260-
rs: ecdsaSignature.p1364(),
238+
rs: ecdsaSignature.compactRepresentation(),
261239
recoveryID: recoveryID,
262240
message: message
263241
)
@@ -268,9 +246,16 @@ extension Bridge {
268246
ecdsaSignature: ECDSASignatureRecoverable,
269247
message: [UInt8]
270248
) throws -> [UInt8] {
271-
try _recoverPublicKey(
272-
rs: ecdsaSignature.rs(),
273-
recoveryID: Int32(ecdsaSignature.recoveryID),
249+
var recoverableBridgedToC = secp256k1_ecdsa_recoverable_signature()
250+
251+
withUnsafeMutableBytes(of: &recoverableBridgedToC.data) { pointer in
252+
pointer.copyBytes(
253+
from: ecdsaSignature.rawRepresentation.prefix(pointer.count)
254+
)
255+
}
256+
257+
return try __recoverPubKeyFrom(
258+
signatureBridgedToC: recoverableBridgedToC,
274259
message: message
275260
)
276261
}
@@ -283,6 +268,7 @@ extension Bridge {
283268
) throws -> [UInt8] {
284269
var signatureBridgedToC = secp256k1_ecdsa_recoverable_signature()
285270
let rs = [UInt8](rsData)
271+
286272
try Self.call(
287273
ifFailThrow: .failedToParseRecoverableSignatureFromECDSASignature
288274
) { context in
@@ -294,6 +280,17 @@ extension Bridge {
294280
)
295281
}
296282

283+
return try __recoverPubKeyFrom(
284+
signatureBridgedToC: signatureBridgedToC,
285+
message: message
286+
)
287+
}
288+
289+
static func __recoverPubKeyFrom(
290+
signatureBridgedToC: secp256k1_ecdsa_recoverable_signature,
291+
message: [UInt8]
292+
) throws -> [UInt8] {
293+
var signatureBridgedToC = signatureBridgedToC
297294
var publicKeyBridgedToC = secp256k1_pubkey()
298295
try Self.call(
299296
ifFailThrow: .failedToRecoverPublicKeyFromSignature
@@ -378,9 +375,9 @@ public extension ECDSASignatureNonRecoverable {
378375
wrapped: .init(uncompressedRaw: uncompressedPublicKeyBytes)
379376
)
380377

381-
// guard try publicKey.isValid(signature: self, hashed: messageThatWasSigned) else {
382-
// throw K1.Error.expectedPublicKeyToBeValidForSignatureAndMessage
383-
// }
378+
guard try publicKey.isValid(signature: self, hashed: messageThatWasSigned) else {
379+
throw K1.Error.expectedPublicKeyToBeValidForSignatureAndMessage
380+
}
384381

385382
return publicKey
386383
}
@@ -394,13 +391,11 @@ public extension K1.PrivateKey {
394391
mode: ECDSASignatureNonRecoverable.SigningMode = .default
395392
) throws -> ECDSASignatureRecoverable {
396393
let messageBytes = [UInt8](message)
397-
let signatureData = try withSecureBytes { (secureBytes: SecureBytes) -> Data in
398-
try Bridge.ecdsaSignRecoverable(message: messageBytes, privateKey: secureBytes, mode: mode)
394+
let raw = try withSecureBytes {
395+
try Bridge.ecdsaSignRecoverable(message: messageBytes, privateKey: $0, mode: mode)
399396
}
400397

401-
return try ECDSASignatureRecoverable(
402-
rawRepresentation: signatureData
403-
)
398+
return try ECDSASignatureRecoverable.init(rawRepresentation: raw)
404399
}
405400

406401
/// Produces a **non recoverable** ECDSA signature.

0 commit comments

Comments
 (0)