Skip to content

Commit

Permalink
[BREAK API] ECDSA.Recoverable to ECDSAWithKeyRecovery & ECDSA.NonReco…
Browse files Browse the repository at this point in the history
…verable to ECDSA (#34)
  • Loading branch information
Sajjon committed May 12, 2023
1 parent a54a100 commit 9e6c271
Show file tree
Hide file tree
Showing 33 changed files with 332 additions and 243 deletions.
24 changes: 12 additions & 12 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -14,8 +14,8 @@ The API of K1 maps almost 1:1 with Apple's [CryptoKit][ck], vendoring a set of k
Just like that K1 vendors these key pairs:
- `K1.KeyAgreement.PrivateKey` / `K1.KeyAgreement.PublicKey` for key agreement (ECDH)
- `K1.Schnorr.PrivateKey` / `K1.Schnorr.PublicKey` for sign / verify methods using Schnorr signature scheme
- `K1.ECDSA.Recoverable.PrivateKey` / `K1.ECDSA.Recoverable.PublicKey` for sign / verify methods using ECDSA (producing/validating signature where public key is recoverable)
- `K1.ECDSA.NonRecoverable.PrivateKey` / `K1.ECDSA.NonRecoverable.PublicKey` for sign / verify methods using ECDSA (producing/validating signature where public key is **not** recoverable)
- `K1.ECDSAWithKeyRecovery.PrivateKey` / `K1.ECDSAWithKeyRecovery.PublicKey` for sign / verify methods using ECDSA (producing/validating signature where public key is recoverable)
- `K1.ECDSA.PrivateKey` / `K1.ECDSA.PublicKey` for sign / verify methods using ECDSA (producing/validating signature where public key is **not** recoverable)

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.

Expand Down Expand Up @@ -64,8 +64,8 @@ Furthermore, all PublicKeys's have these additional APIs:
## ECDSA (Elliptic Curve Digital Signature Algorithm)

There exists two set of ECDSA key pairs:
- A key pair for signatures from which you can recover the public key, specifically: `K1.ECDSA.Recoverable.PrivateKey` and `K1.ECDSA.Recoverable.PublicKey`
- A key pair for signatures from which you can **not** recover the public key, specifically: `K1.ECDSA.NonRecoverable.PrivateKey` and `K1.ECDSA.NonRecoverable.PublicKey`
- A key pair for signatures from which you can recover the public key, specifically: `K1.ECDSAWithKeyRecovery.PrivateKey` and `K1.ECDSAWithKeyRecovery.PublicKey`
- A key pair for signatures from which you can **not** recover the public key, specifically: `K1.ECDSA.PrivateKey` and `K1.ECDSA.PublicKey`

For each private key there exists two different `signature:for:options` (one taking hashed data and taking `Digest` as argument) methods and one `signature:forUnhashed:options`.

Expand All @@ -76,7 +76,7 @@ The `option` is a `K1.ECDSA.SigningOptions` struct, which by default specifies [
#### Sign

```swift
let alice = K1.ECDA.NonRecovarable.PrivateKey()
let alice = K1.ECDSA.PrivateKey()
```

##### Hashed (Data)
Expand Down Expand Up @@ -109,8 +109,8 @@ let signature = try alice.signature(forUnhashed: message)

```swift
let hashedMessage: Data = // from somewhere
let publicKey: K1.ECDSA.NonRecoverable.PublicKey = alice.publcKey
let signature: K1.ECDSA.NonRecoverable.Signature // from above
let publicKey: K1.ECDSA.PublicKey = alice.publcKey
let signature: K1.ECDSA.Signature // from above

assert(
publicKey.isValidSignature(signature, hashed: hashedMessage)
Expand All @@ -122,7 +122,7 @@ assert(
```swift
let message: Data = // from somewhere
let digest = SHA256.hash(data: message)
let signature: K1.ECDSA.NonRecoverable.Signature // from above
let signature: K1.ECDSA.Signature // from above

assert(
publicKey.isValidSignature(signature, digest: digest)
Expand All @@ -133,7 +133,7 @@ assert(

```swift
let message: Data = // from somewhere
let signature: K1.ECDSA.NonRecoverable.Signature // from above
let signature: K1.ECDSA.Signature // from above

assert(
publicKey.isValidSignature(signature, unhashed: message)
Expand All @@ -146,11 +146,11 @@ assert(
All signing and validation APIs are identical to the `NonRecoverable` namespace.

```swift
let alice = K1.ECDA.Recovarable.PrivateKey()
let alice = K1.ECDSA.PrivateKey()
let message: Data = // from somewhere
let digest = SHA256.hash(data: message)
let signature: K1.ECDSA.Recoverable.Signature = try alice.signature(for: digest)
let publicKey: K1.ECDSA.Recoverable.PublicKey = alice.publicKey
let signature: K1.ECDSAWithKeyRecovery.Signature = try alice.signature(for: digest)
let publicKey: K1.ECDSAWithKeyRecovery.PublicKey = alice.publicKey
assert(
publicKey.isValidSignature(signature, digest: digest)
) // PASS
Expand Down
2 changes: 1 addition & 1 deletion Sources/K1/K1/ECDSA/ECDSA.swift
Original file line number Diff line number Diff line change
@@ -1,6 +1,6 @@
// MARK: - K1.ECDSA
extension K1 {
/// A mechanism used to create or verify a cryptographic signature using the `secp256k1` elliptic curve digital signature algorithm (ECDSA).
/// A mechanism used to create or verify a cryptographic signature using the `secp256k1` elliptic curve digital signature algorithm (ECDSA), signatures that do not offer recovery of the public key.
public enum ECDSA {}
}

Expand Down
34 changes: 13 additions & 21 deletions Sources/K1/K1/ECDSA/ECDSASignatureNonRecoverable.swift
Original file line number Diff line number Diff line change
Expand Up @@ -2,20 +2,12 @@ import protocol CryptoKit.Digest
import struct CryptoKit.SHA256
import Foundation

// MARK: - K1.ECDSA.NonRecoverable
// MARK: - K1.ECDSA.Signature
extension K1.ECDSA {
/// A mechanism used to create or verify a cryptographic signature using the `secp256k1` elliptic curve digital signature algorithm (ECDSA), signatures that do not offer recovery of the public key.
public enum NonRecoverable {
// Just a namespace
}
}

// MARK: - K1.ECDSA.NonRecoverable.Signature
extension K1.ECDSA.NonRecoverable {
/// A `secp256k1` elliptic curve digital signature algorithm (ECDSA) signature,
/// from which users can recover a public key with the message that was signed.
public struct Signature: Sendable, Hashable, ContiguousBytes {
typealias Wrapped = FFI.ECDSA.NonRecoverable.Wrapped
typealias Wrapped = FFI.ECDSA.Wrapped
internal let wrapped: Wrapped

init(wrapped: Wrapped) {
Expand All @@ -25,12 +17,12 @@ extension K1.ECDSA.NonRecoverable {
}

// MARK: Inits
extension K1.ECDSA.NonRecoverable.Signature {
extension K1.ECDSA.Signature {
/// Creates a `secp256k1` ECDSA signature from a Distinguished Encoding Rules (DER) encoded representation.
/// - Parameter derRepresentation: A DER-encoded representation of the signature.
public init(derRepresentation: some DataProtocol) throws {
try self.init(
wrapped: FFI.ECDSA.NonRecoverable.from(derRepresentation: [UInt8](derRepresentation))
wrapped: FFI.ECDSA.from(derRepresentation: [UInt8](derRepresentation))
)
}

Expand All @@ -44,20 +36,20 @@ extension K1.ECDSA.NonRecoverable.Signature {
/// [rfc]: https://tools.ietf.org/html/rfc4754
public init(rawRepresentation: some DataProtocol) throws {
try self.init(
wrapped: FFI.ECDSA.NonRecoverable.from(compactBytes: [UInt8](rawRepresentation))
wrapped: FFI.ECDSA.from(compactBytes: [UInt8](rawRepresentation))
)
}
}

// MARK: ContiguousBytes
extension K1.ECDSA.NonRecoverable.Signature {
extension K1.ECDSA.Signature {
public func withUnsafeBytes<R>(_ body: (UnsafeRawBufferPointer) throws -> R) rethrows -> R {
try wrapped.withUnsafeBytes(body)
}
}

// MARK: Serialize
extension K1.ECDSA.NonRecoverable.Signature {
extension K1.ECDSA.Signature {
var internalRepresentation: Data {
Data(wrapped.bytes)
}
Expand All @@ -68,7 +60,7 @@ extension K1.ECDSA.NonRecoverable.Signature {
/// `libsecp256k1` this representation is called "compact".
public var rawRepresentation: Data {
do {
return try FFI.ECDSA.NonRecoverable.compact(wrapped)
return try FFI.ECDSA.compact(wrapped)
} catch {
fatalError("Should never fail to convert ECDSA signatures to rawRepresentation.")
}
Expand All @@ -78,19 +70,19 @@ extension K1.ECDSA.NonRecoverable.Signature {
/// `secp256k1` ECDSA non recoverable signature.
public var derRepresentation: Data {
do {
return try FFI.ECDSA.NonRecoverable.der(wrapped)
return try FFI.ECDSA.der(wrapped)
} catch {
fatalError("Should never fail to convert ECDSA signatures to DER representation.")
}
}
}

extension K1.ECDSA.NonRecoverable.Signature {
internal static let byteCount = FFI.ECDSA.Recoverable.byteCount
extension K1.ECDSA.Signature {
internal static let byteCount = FFI.ECDSAWithKeyRecovery.byteCount
}

// MARK: Equatable
extension K1.ECDSA.NonRecoverable.Signature {
extension K1.ECDSA.Signature {
/// Compares two ECDSA signatures.
public static func == (lhs: Self, rhs: Self) -> Bool {
lhs.wrapped.withUnsafeBytes { lhsBytes in
Expand All @@ -102,7 +94,7 @@ extension K1.ECDSA.NonRecoverable.Signature {
}

// MARK: Hashable
extension K1.ECDSA.NonRecoverable.Signature {
extension K1.ECDSA.Signature {
public func hash(into hasher: inout Hasher) {
wrapped.withUnsafeBytes {
hasher.combine(bytes: $0)
Expand Down
48 changes: 24 additions & 24 deletions Sources/K1/K1/ECDSA/ECDSASignatureRecoverable.swift
Original file line number Diff line number Diff line change
Expand Up @@ -2,20 +2,20 @@ import protocol CryptoKit.Digest
import struct CryptoKit.SHA256
import Foundation

// MARK: - K1.ECDSA.Recoverable
extension K1.ECDSA {
// MARK: - K1.ECDSAWithKeyRecovery
extension K1 {
/// A mechanism used to create or verify a cryptographic signature using the `secp256k1` elliptic curve digital signature algorithm (ECDSA), signatures that do offers recovery of the public key.
public enum Recoverable {
public enum ECDSAWithKeyRecovery {
// Just a namespace
}
}

// MARK: - K1.ECDSA.Recoverable.Signature
extension K1.ECDSA.Recoverable {
// MARK: - K1.ECDSAWithKeyRecovery.Signature
extension K1.ECDSAWithKeyRecovery {
/// A `secp256k1` elliptic curve digital signature algorithm (ECDSA) signature,
/// from which users **cannot** recover the public key, not without the `RecoveryID`.
public struct Signature: Sendable, Hashable, ContiguousBytes {
typealias Wrapped = FFI.ECDSA.Recoverable.Wrapped
typealias Wrapped = FFI.ECDSAWithKeyRecovery.Wrapped
internal let wrapped: Wrapped

internal init(wrapped: Wrapped) {
Expand All @@ -25,11 +25,11 @@ extension K1.ECDSA.Recoverable {
}

// MARK: Init
extension K1.ECDSA.Recoverable.Signature {
extension K1.ECDSAWithKeyRecovery.Signature {
/// Compact aka `IEEE P1363` aka `R||S`.
public init(compact: Compact) throws {
try self.init(
wrapped: FFI.ECDSA.Recoverable.deserialize(
wrapped: FFI.ECDSAWithKeyRecovery.deserialize(
compact: [UInt8](compact.compact),
recoveryID: compact.recoveryID.recid
)
Expand All @@ -40,27 +40,27 @@ extension K1.ECDSA.Recoverable.Signature {
internalRepresentation: some DataProtocol
) throws {
try self.init(
wrapped: FFI.ECDSA.Recoverable.deserialize(rawRepresentation: internalRepresentation)
wrapped: FFI.ECDSAWithKeyRecovery.deserialize(rawRepresentation: internalRepresentation)
)
}
}

// MARK: ContiguousBytes
extension K1.ECDSA.Recoverable.Signature {
extension K1.ECDSAWithKeyRecovery.Signature {
public func withUnsafeBytes<R>(_ body: (UnsafeRawBufferPointer) throws -> R) rethrows -> R {
try wrapped.withUnsafeBytes(body)
}
}

// MARK: Serialize
extension K1.ECDSA.Recoverable.Signature {
extension K1.ECDSAWithKeyRecovery.Signature {
internal var internalRepresentation: Data {
Data(wrapped.bytes)
}

/// Compact aka `IEEE P1363` aka `R||S` and `V` (`RecoveryID`).
public func compact() throws -> Compact {
let (rs, recid) = try FFI.ECDSA.Recoverable.serializeCompact(
let (rs, recid) = try FFI.ECDSAWithKeyRecovery.serializeCompact(
wrapped
)
return try .init(
Expand Down Expand Up @@ -92,7 +92,7 @@ extension K1.ECDSA.Recoverable.Signature {
}
}

extension K1.ECDSA.Recoverable.Signature.Compact {
extension K1.ECDSAWithKeyRecovery.Signature.Compact {
public static let byteCountRS = 2 * Curve.Field.byteCount
public static let byteCount = Self.byteCountRS + 1

Expand Down Expand Up @@ -162,7 +162,7 @@ extension K1.ECDSA.Recoverable.Signature.Compact {
}
}

extension K1.ECDSA.Recoverable.Signature.RecoveryID {
extension K1.ECDSAWithKeyRecovery.Signature.RecoveryID {
var vData: Data {
Data(
[UInt8(rawValue)]
Expand All @@ -171,7 +171,7 @@ extension K1.ECDSA.Recoverable.Signature.RecoveryID {
}

// MARK: Recovery
extension K1.ECDSA.Recoverable.Signature {
extension K1.ECDSAWithKeyRecovery.Signature {
/// Recovers a public key from a `secp256k1` this ECDSA signature and the message signed.
///
/// - Parameters:
Expand All @@ -180,27 +180,27 @@ extension K1.ECDSA.Recoverable.Signature {
/// signature by signing the `message`.
public func recoverPublicKey(
message: some DataProtocol
) throws -> K1.ECDSA.Recoverable.PublicKey {
let wrapped = try FFI.ECDSA.Recoverable.recover(wrapped, message: [UInt8](message))
) throws -> K1.ECDSAWithKeyRecovery.PublicKey {
let wrapped = try FFI.ECDSAWithKeyRecovery.recover(wrapped, message: [UInt8](message))
let impl = K1._PublicKeyImplementation(wrapped: wrapped)
return K1.ECDSA.Recoverable.PublicKey(
return K1.ECDSAWithKeyRecovery.PublicKey(
impl: impl
)
}
}

// MARK: Conversion
extension K1.ECDSA.Recoverable.Signature {
extension K1.ECDSAWithKeyRecovery.Signature {
/// Converts this recoverable ECDSA signature to a non-recoverable version.
public func nonRecoverable() throws -> K1.ECDSA.NonRecoverable.Signature {
try K1.ECDSA.NonRecoverable.Signature(
wrapped: FFI.ECDSA.Recoverable.nonRecoverable(self.wrapped)
public func nonRecoverable() throws -> K1.ECDSA.Signature {
try K1.ECDSA.Signature(
wrapped: FFI.ECDSAWithKeyRecovery.nonRecoverable(self.wrapped)
)
}
}

// MARK: Equatable
extension K1.ECDSA.Recoverable.Signature {
extension K1.ECDSAWithKeyRecovery.Signature {
/// Compares two ECDSA signatures.
public static func == (lhs: Self, rhs: Self) -> Bool {
lhs.wrapped.withUnsafeBytes { lhsBytes in
Expand All @@ -212,7 +212,7 @@ extension K1.ECDSA.Recoverable.Signature {
}

// MARK: Hashable
extension K1.ECDSA.Recoverable.Signature {
extension K1.ECDSAWithKeyRecovery.Signature {
public func hash(into hasher: inout Hasher) {
wrapped.withUnsafeBytes {
hasher.combine(bytes: $0)
Expand Down
6 changes: 3 additions & 3 deletions Sources/K1/K1/ECDSA/RecoveryID.swift
Original file line number Diff line number Diff line change
@@ -1,7 +1,7 @@
import Foundation

// MARK: - K1.ECDSA.Recoverable.Signature.RecoveryID
extension K1.ECDSA.Recoverable.Signature {
// MARK: - K1.ECDSAWithKeyRecovery.Signature.RecoveryID
extension K1.ECDSAWithKeyRecovery.Signature {
public enum RecoveryID: UInt8, Sendable, Hashable, Codable {
case _0 = 0
case _1 = 1
Expand All @@ -14,7 +14,7 @@ extension K1.ECDSA.Recoverable.Signature {
}
}

extension K1.ECDSA.Recoverable.Signature.RecoveryID {
extension K1.ECDSAWithKeyRecovery.Signature.RecoveryID {
public init(byte: UInt8) throws {
guard let self_ = Self(rawValue: byte) else {
throw K1.Error.invalidParameter
Expand Down
Loading

0 comments on commit 9e6c271

Please sign in to comment.