Skip to content

Commit 4305158

Browse files
authored
[API BREAK] Change initializers of ECDSA signatures and add more documentation. (#28)
1 parent 3a9fac9 commit 4305158

18 files changed

+204
-117
lines changed

README.md

Lines changed: 14 additions & 15 deletions
Original file line numberDiff line numberDiff line change
@@ -16,46 +16,45 @@ Just like that K1 vendors these key pairs:
1616

1717
Just like you can convert between e.g. `Curve25519.KeyAgreement.PrivateKey` and `Curve25519.Signing.PrivateKey` back and forth using any of the initializers and serializer, you can convert between all PrivateKeys and all PublicKeys of all features in K1.
1818

19-
All keys conform to `K1KeyExportable` protocol below to serialize the Private/PubliKey:
19+
All keys can be serialized using these computed properties:
2020

2121
```swift
22-
public protocol K1KeyExportable {
22+
{
2323
var rawRepresentation: Data { get }
24-
var x963Representation: Data { get }
2524
var derRepresentation: Data { get }
2625
var pemRepresentation: String { get }
26+
var x963Representation: Data { get }
2727
}
2828
```
2929

30-
All keys conform to `K1KeyImportable` protocol below to deserialize to Private/PubliKey:
30+
All keys can be deserialize using these initializer:
3131

3232
```swift
33-
public protocol K1KeyImportable {
33+
{
3434
init(rawRepresentation: some ContiguousBytes) throws
35-
init(x963Representation: some ContiguousBytes) throws
3635
init(derRepresentation: some RandomAccessCollection<UInt8>) throws
3736
init(pemRepresentation: String) throws
37+
init(x963Representation: some ContiguousBytes) throws
3838
}
3939
```
4040

41-
Furthermore, all PrivateKey's conform to this protocol
41+
Furthermore, all PrivateKey's have these additional APIs:
4242

4343
```swift
44-
protocol K1PrivateKeyProtocol: K1KeyPortable {
45-
associatedtype PublicKey: K1PublicKeyProtocol
46-
var publicKey: PublicKey { get }
44+
{
4745
init()
46+
associatedtype PublicKey
47+
var publicKey: PublicKey { get }
4848
}
4949
```
5050

51-
Furthermore, all PublicKey's conform to this protocol
51+
Furthermore, all PublicKeys's have these additional APIs:
5252

5353
```swift
54-
public protocol K1PublicKeyProtocol: K1KeyPortable {
54+
{
5555
init(compressedRepresentation: some ContiguousBytes) throws
5656
var compressedRepresentation: Data { get }
5757
}
58-
5958
```
6059

6160

@@ -185,8 +184,8 @@ It is worth noting that some Schnorr implementations are incompatible with [BIP3
185184
# ECDH
186185

187186
This library vendors three different EC Diffie-Hellman (ECDH) key exchange functions:
188-
1. `ASN1 x9.63` - No hash, return only the `X` coordinate of the point - `sharedSecretFromKeyAgreement -> SharedSecret`
189-
2. `libsecp256k1` - SHA-256 hash the compressed point - `ecdh -> Data`
187+
1. `ASN1 x9.63` - No hash, return only the `X` coordinate of the point - `sharedSecretFromKeyAgreement:with -> SharedSecret`
188+
2. `libsecp256k1` - SHA-256 hash the compressed point - `ecdh:with -> SharedSecret`
190189
3. Custom - No hash, return point uncompressed - `ecdhPoint -> Data`
191190

192191
```swift

Sources/K1/K1/ECDSA/ECDSASignatureNonRecoverable.swift

Lines changed: 42 additions & 9 deletions
Original file line numberDiff line numberDiff line change
@@ -26,15 +26,25 @@ extension K1.ECDSA.NonRecoverable {
2626

2727
// MARK: Inits
2828
extension K1.ECDSA.NonRecoverable.Signature {
29-
public init(compactRepresentation: some DataProtocol) throws {
29+
/// Creates a `secp256k1` ECDSA signature from a Distinguished Encoding Rules (DER) encoded representation.
30+
/// - Parameter derRepresentation: A DER-encoded representation of the signature.
31+
public init(derRepresentation: some DataProtocol) throws {
3032
try self.init(
31-
wrapped: FFI.ECDSA.NonRecoverable.from(compactBytes: [UInt8](compactRepresentation))
33+
wrapped: FFI.ECDSA.NonRecoverable.from(derRepresentation: [UInt8](derRepresentation))
3234
)
3335
}
3436

35-
public init(derRepresentation: some DataProtocol) throws {
37+
/// Creates a `secp256k1` ECDSA signature from the raw representation.
38+
///
39+
/// Expects 64 bytes on format: `R || S`, as defined in [rfc4754][rfc]. In
40+
/// `libsecp256k1` this representation is called "compact".
41+
///
42+
/// - Parameter rawRepresentation: A raw representation of the ECDSA signature as a collection of contiguous bytes.
43+
///
44+
/// [rfc]: https://tools.ietf.org/html/rfc4754
45+
public init(rawRepresentation: some DataProtocol) throws {
3646
try self.init(
37-
wrapped: FFI.ECDSA.NonRecoverable.from(derRepresentation: [UInt8](derRepresentation))
47+
wrapped: FFI.ECDSA.NonRecoverable.from(compactBytes: [UInt8](rawRepresentation))
3848
)
3949
}
4050
}
@@ -48,21 +58,43 @@ extension K1.ECDSA.NonRecoverable.Signature {
4858

4959
// MARK: Serialize
5060
extension K1.ECDSA.NonRecoverable.Signature {
51-
internal var rawRepresentation: Data {
61+
var internalRepresentation: Data {
5262
Data(wrapped.bytes)
5363
}
5464

55-
public func compactRepresentation() throws -> Data {
56-
try FFI.ECDSA.NonRecoverable.compact(wrapped)
65+
/// A raw data representation of a `secp256k1` ECDSA non recoverable signature.
66+
///
67+
/// Returns 64 bytes on format: `R || S`, as defined in [rfc4754][rfc]. In
68+
/// `libsecp256k1` this representation is called "compact".
69+
public var rawRepresentation: Data {
70+
do {
71+
return try FFI.ECDSA.NonRecoverable.compact(wrapped)
72+
} catch {
73+
fatalError("Should never fail to convert ECDSA signatures to rawRepresentation.")
74+
}
5775
}
5876

59-
public func derRepresentation() throws -> Data {
60-
try FFI.ECDSA.NonRecoverable.der(wrapped)
77+
/// A Distinguished Encoding Rules (DER) encoded representation of a
78+
/// `secp256k1` ECDSA non recoverable signature.
79+
public var derRepresentation: Data {
80+
do {
81+
return try FFI.ECDSA.NonRecoverable.der(wrapped)
82+
} catch {
83+
fatalError("Should never fail to convert ECDSA signatures to DER representation.")
84+
}
6185
}
6286
}
6387

6488
// MARK: Recover
6589
extension K1.ECDSA.NonRecoverable.Signature {
90+
/// Recovers a public key from a `secp256k1` ECDSA signature, the message signed
91+
/// and a `recoveryID`.
92+
///
93+
/// - Parameters:
94+
/// - recoveryID: The recoveryID produced when a recoverable signature was produced.
95+
/// - message: The message that was signed to produce this ECDSA signature.
96+
/// - Returns: The public key which corresponds to the private key which used to produce this
97+
/// signature by signing the `message`.
6698
public func recoverPublicKey(
6799
recoveryID: K1.ECDSA.Recoverable.Signature.RecoveryID,
68100
message: some DataProtocol
@@ -83,6 +115,7 @@ extension K1.ECDSA.NonRecoverable.Signature {
83115

84116
// MARK: Equatable
85117
extension K1.ECDSA.NonRecoverable.Signature {
118+
/// Compares two ECDSA signatures.
86119
public static func == (lhs: Self, rhs: Self) -> Bool {
87120
lhs.wrapped.withUnsafeBytes { lhsBytes in
88121
rhs.wrapped.withUnsafeBytes { rhsBytes in

Sources/K1/K1/ECDSA/ECDSASignatureRecoverable.swift

Lines changed: 13 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -41,11 +41,11 @@ extension K1.ECDSA.Recoverable.Signature {
4141
try self.init(compact: .init(compact: compact, recoveryID: recoveryID))
4242
}
4343

44-
public init(
45-
rawRepresentation: some DataProtocol
44+
init(
45+
internalRepresentation: some DataProtocol
4646
) throws {
4747
try self.init(
48-
wrapped: FFI.ECDSA.Recoverable.deserialize(rawRepresentation: rawRepresentation)
48+
wrapped: FFI.ECDSA.Recoverable.deserialize(rawRepresentation: internalRepresentation)
4949
)
5050
}
5151
}
@@ -59,7 +59,7 @@ extension K1.ECDSA.Recoverable.Signature {
5959

6060
// MARK: Serialize
6161
extension K1.ECDSA.Recoverable.Signature {
62-
internal var rawRepresentation: Data {
62+
internal var internalRepresentation: Data {
6363
Data(wrapped.bytes)
6464
}
6565

@@ -74,6 +74,7 @@ extension K1.ECDSA.Recoverable.Signature {
7474
)
7575
}
7676

77+
/// A tuple of `R||S` and `recoveryID` from a recoverable ECDSA signature.
7778
public struct Compact: Sendable, Hashable {
7879
/// Compact aka `IEEE P1363` aka `R||S`.
7980
public let compact: Data
@@ -163,6 +164,12 @@ extension K1.ECDSA.Recoverable.Signature.RecoveryID {
163164

164165
// MARK: Recovery
165166
extension K1.ECDSA.Recoverable.Signature {
167+
/// Recovers a public key from a `secp256k1` this ECDSA signature and the message signed.
168+
///
169+
/// - Parameters:
170+
/// - message: The message that was signed to produce this ECDSA signature.
171+
/// - Returns: The public key which corresponds to the private key which used to produce this
172+
/// signature by signing the `message`.
166173
public func recoverPublicKey(
167174
message: some DataProtocol
168175
) throws -> K1.ECDSA.Recoverable.PublicKey {
@@ -176,6 +183,7 @@ extension K1.ECDSA.Recoverable.Signature {
176183

177184
// MARK: Conversion
178185
extension K1.ECDSA.Recoverable.Signature {
186+
/// Converts this recoverable ECDSA signature to a non-recoverable version.
179187
public func nonRecoverable() throws -> K1.ECDSA.NonRecoverable.Signature {
180188
try K1.ECDSA.NonRecoverable.Signature(
181189
wrapped: FFI.ECDSA.Recoverable.nonRecoverable(self.wrapped)
@@ -185,6 +193,7 @@ extension K1.ECDSA.Recoverable.Signature {
185193

186194
// MARK: Equatable
187195
extension K1.ECDSA.Recoverable.Signature {
196+
/// Compares two ECDSA signatures.
188197
public static func == (lhs: Self, rhs: Self) -> Bool {
189198
lhs.wrapped.withUnsafeBytes { lhsBytes in
190199
rhs.wrapped.withUnsafeBytes { rhsBytes in

Sources/K1/K1/Keys/Keys.generated.swift

Lines changed: 32 additions & 8 deletions
Original file line numberDiff line numberDiff line change
@@ -7,7 +7,7 @@ import Foundation
77
extension K1.KeyAgreement {
88
// MARK: KeyAgreement + PrivateKey
99
/// A `secp256k1` private key used for key agreement.
10-
public struct PrivateKey: Sendable, Hashable, K1PrivateKeyProtocol {
10+
public struct PrivateKey: Sendable, Hashable {
1111
typealias Impl = K1._PrivateKeyImplementation
1212
public typealias PublicKey = K1.KeyAgreement.PublicKey
1313

@@ -109,7 +109,7 @@ extension K1.KeyAgreement {
109109

110110
// MARK: KeyAgreement + PublicKey
111111
/// A `secp256k1` public key used for key agreement.
112-
public struct PublicKey: Sendable, Hashable, K1PublicKeyProtocol {
112+
public struct PublicKey: Sendable, Hashable {
113113
typealias Impl = K1._PublicKeyImplementation
114114
internal let impl: Impl
115115
internal init(impl: Impl) {
@@ -206,7 +206,7 @@ extension K1.Schnorr {
206206
// MARK: Schnorr + PrivateKey
207207
/// A `secp256k1` private key used to create cryptographic signatures,
208208
/// more specifically Schnorr signatures.
209-
public struct PrivateKey: Sendable, Hashable, K1PrivateKeyProtocol {
209+
public struct PrivateKey: Sendable, Hashable {
210210
typealias Impl = K1._PrivateKeyImplementation
211211
public typealias PublicKey = K1.Schnorr.PublicKey
212212

@@ -309,7 +309,7 @@ extension K1.Schnorr {
309309
// MARK: Schnorr + PublicKey
310310
/// A `secp256k1` public key used to verify cryptographic signatures,
311311
/// more specifically Schnorr signatures
312-
public struct PublicKey: Sendable, Hashable, K1PublicKeyProtocol {
312+
public struct PublicKey: Sendable, Hashable {
313313
typealias Impl = K1._PublicKeyImplementation
314314
internal let impl: Impl
315315
internal init(impl: Impl) {
@@ -406,7 +406,7 @@ extension K1.ECDSA.NonRecoverable {
406406
// MARK: ECDSA.NonRecoverable + PrivateKey
407407
/// A `secp256k1` private key used to create cryptographic signatures,
408408
/// more specifically ECDSA signatures, that do not offer recovery of the public key.
409-
public struct PrivateKey: Sendable, Hashable, K1PrivateKeyProtocol {
409+
public struct PrivateKey: Sendable, Hashable {
410410
typealias Impl = K1._PrivateKeyImplementation
411411
public typealias PublicKey = K1.ECDSA.NonRecoverable.PublicKey
412412

@@ -509,7 +509,7 @@ extension K1.ECDSA.NonRecoverable {
509509
// MARK: ECDSA.NonRecoverable + PublicKey
510510
/// A `secp256k1` public key used to verify cryptographic signatures,
511511
/// more specifically ECDSA signatures, that do not offer recovery of the public key.
512-
public struct PublicKey: Sendable, Hashable, K1PublicKeyProtocol {
512+
public struct PublicKey: Sendable, Hashable {
513513
typealias Impl = K1._PublicKeyImplementation
514514
internal let impl: Impl
515515
internal init(impl: Impl) {
@@ -606,7 +606,7 @@ extension K1.ECDSA.Recoverable {
606606
// MARK: ECDSA.Recoverable + PrivateKey
607607
/// A `secp256k1` private key used to create cryptographic signatures,
608608
/// more specifically ECDSA signatures that offers recovery of the public key.
609-
public struct PrivateKey: Sendable, Hashable, K1PrivateKeyProtocol {
609+
public struct PrivateKey: Sendable, Hashable {
610610
typealias Impl = K1._PrivateKeyImplementation
611611
public typealias PublicKey = K1.ECDSA.Recoverable.PublicKey
612612

@@ -709,7 +709,7 @@ extension K1.ECDSA.Recoverable {
709709
// MARK: ECDSA.Recoverable + PublicKey
710710
/// A `secp256k1` public key used to verify cryptographic signatures.
711711
/// more specifically ECDSA signatures that offers recovery of the public key.
712-
public struct PublicKey: Sendable, Hashable, K1PublicKeyProtocol {
712+
public struct PublicKey: Sendable, Hashable {
713713
typealias Impl = K1._PublicKeyImplementation
714714
internal let impl: Impl
715715
internal init(impl: Impl) {
@@ -801,3 +801,27 @@ extension K1.ECDSA.Recoverable {
801801
}
802802
}
803803
}
804+
805+
// MARK: - K1.KeyAgreement.PrivateKey + _K1PrivateKeyProtocol
806+
extension K1.KeyAgreement.PrivateKey: _K1PrivateKeyProtocol {}
807+
808+
// MARK: - K1.KeyAgreement.PublicKey + _K1PublicKeyProtocol
809+
extension K1.KeyAgreement.PublicKey: _K1PublicKeyProtocol {}
810+
811+
// MARK: - K1.Schnorr.PrivateKey + _K1PrivateKeyProtocol
812+
extension K1.Schnorr.PrivateKey: _K1PrivateKeyProtocol {}
813+
814+
// MARK: - K1.Schnorr.PublicKey + _K1PublicKeyProtocol
815+
extension K1.Schnorr.PublicKey: _K1PublicKeyProtocol {}
816+
817+
// MARK: - K1.ECDSA.NonRecoverable.PrivateKey + _K1PrivateKeyProtocol
818+
extension K1.ECDSA.NonRecoverable.PrivateKey: _K1PrivateKeyProtocol {}
819+
820+
// MARK: - K1.ECDSA.NonRecoverable.PublicKey + _K1PublicKeyProtocol
821+
extension K1.ECDSA.NonRecoverable.PublicKey: _K1PublicKeyProtocol {}
822+
823+
// MARK: - K1.ECDSA.Recoverable.PrivateKey + _K1PrivateKeyProtocol
824+
extension K1.ECDSA.Recoverable.PrivateKey: _K1PrivateKeyProtocol {}
825+
826+
// MARK: - K1.ECDSA.Recoverable.PublicKey + _K1PublicKeyProtocol
827+
extension K1.ECDSA.Recoverable.PublicKey: _K1PublicKeyProtocol {}

Sources/K1/K1/Keys/Keys.swift.gyb

Lines changed: 11 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -47,7 +47,7 @@ import Foundation
4747
extension K1.${FEATURE} {
4848
// MARK: ${FEATURE} + PrivateKey
4949
${PRIVATEKEY_TYPE_DOC}
50-
public struct PrivateKey: Sendable, Hashable, K1PrivateKeyProtocol {
50+
public struct PrivateKey: Sendable, Hashable {
5151

5252
typealias Impl = K1._PrivateKeyImplementation
5353
public typealias PublicKey = K1.${FEATURE}.PublicKey
@@ -151,7 +151,7 @@ extension K1.${FEATURE} {
151151

152152
// MARK: ${FEATURE} + PublicKey
153153
${PUBLICKEY_TYPE_DOC}
154-
public struct PublicKey: Sendable, Hashable, K1PublicKeyProtocol {
154+
public struct PublicKey: Sendable, Hashable {
155155

156156
typealias Impl = K1._PublicKeyImplementation
157157
internal let impl: Impl
@@ -245,6 +245,15 @@ extension K1.${FEATURE} {
245245

246246
}
247247
}
248+
% end
248249

250+
% for KEY_FOR_FEATURE_WITH_DOCS in LIST_KEY_FOR_FEATURE_WITH_DOCS:
251+
%{
252+
FEATURE = KEY_FOR_FEATURE_WITH_DOCS["feature"]
253+
}%
254+
extension K1.${FEATURE}.PrivateKey: _K1PrivateKeyProtocol {}
255+
extension K1.${FEATURE}.PublicKey: _K1PublicKeyProtocol {}
249256
% end
257+
250258
% end
259+

Sources/K1/K1/Keys/PrivateKeyImplementation.swift

Lines changed: 11 additions & 11 deletions
Original file line numberDiff line numberDiff line change
@@ -1,34 +1,34 @@
11
import CryptoKit
22
import Foundation
33

4-
// MARK: - K1KeyExportable
5-
public protocol K1KeyExportable {
4+
// MARK: - _K1KeyExportable
5+
protocol _K1KeyExportable {
66
var rawRepresentation: Data { get }
7-
var x963Representation: Data { get }
87
var derRepresentation: Data { get }
98
var pemRepresentation: String { get }
9+
var x963Representation: Data { get }
1010
}
1111

12-
// MARK: - K1KeyImportable
13-
public protocol K1KeyImportable {
12+
// MARK: - _K1KeyImportable
13+
protocol _K1KeyImportable {
1414
init(rawRepresentation: some ContiguousBytes) throws
15-
init(x963Representation: some ContiguousBytes) throws
1615
init(derRepresentation: some RandomAccessCollection<UInt8>) throws
1716
init(pemRepresentation: String) throws
17+
init(x963Representation: some ContiguousBytes) throws
1818
}
1919

20-
public typealias K1KeyPortable = K1KeyImportable & K1KeyExportable
20+
typealias _K1KeyPortable = _K1KeyImportable & _K1KeyExportable
2121

22-
// MARK: - K1PrivateKeyProtocol
23-
public protocol K1PrivateKeyProtocol: K1KeyPortable {
24-
associatedtype PublicKey: K1PublicKeyProtocol
22+
// MARK: - _K1PrivateKeyProtocol
23+
protocol _K1PrivateKeyProtocol: _K1KeyPortable {
24+
associatedtype PublicKey: _K1PublicKeyProtocol
2525
var publicKey: PublicKey { get }
2626
init()
2727
}
2828

2929
// MARK: - K1._PrivateKeyImplementation
3030
extension K1 {
31-
struct _PrivateKeyImplementation: Sendable, Hashable, K1PrivateKeyProtocol {
31+
struct _PrivateKeyImplementation: Sendable, Hashable, _K1PrivateKeyProtocol {
3232
typealias Wrapped = FFI.PrivateKey.Wrapped
3333
internal let wrapped: Wrapped
3434

0 commit comments

Comments
 (0)