From d7ff2d3504d7a6ed7bdd33c2cf807446d281141b Mon Sep 17 00:00:00 2001 From: Chris Date: Thu, 20 Apr 2023 06:13:17 +0000 Subject: [PATCH] Signature Updates (#338) --- README.md | 14 +-- Sources/zkp/Asymmetric.swift | 30 ----- Sources/zkp/ECDSA.swift | 66 ++--------- Sources/zkp/Recovery.swift | 178 ++++++++++++++++++++-------- Sources/zkp/Schnorr.swift | 126 +++++++++++++++----- Tests/zkpTests/secp256k1Tests.swift | 32 ++--- 6 files changed, 260 insertions(+), 186 deletions(-) diff --git a/README.md b/README.md index 9478c1e..20a1094 100644 --- a/README.md +++ b/README.md @@ -20,7 +20,7 @@ Long-term goals are: This repository primarily uses Swift package manager as its build tool, so we recommend using that as well. Xcode comes with [built-in support](https://developer.apple.com/documentation/xcode/adding-package-dependencies-to-your-app) for Swift packages. From the menu bar, goto: `File > Add Packages...` If you manage packages via a `Package.swift` file, simply add `secp256k1.swift` as a dependencies' clause in your Swift manifest: ```swift -.package(url: "https://github.com/GigaBitcoin/secp256k1.swift.git", .upToNextMajor(from: "0.10.0")) +.package(url: "https://github.com/GigaBitcoin/secp256k1.swift.git", from: "0.11.0"), ``` Try in a [playground](spi-playgrounds://open?dependencies=GigaBitcoin/secp256k1.swift) using the [SPI Playgrounds app](https://swiftpackageindex.com/try-in-a-playground) or 🏟 [Arena](https://github.com/finestructure/arena) @@ -46,7 +46,7 @@ print(String(bytes: privateKey.publicKey.rawRepresentation)) // ECDSA let messageData = "We're all Satoshi.".data(using: .utf8)! -let signature = try! privateKey.ecdsa.signature(for: messageData) +let signature = try! privateKey.signature(for: messageData) // DER signature print(try! signature.derRepresentation.base64EncodedString()) @@ -55,14 +55,14 @@ print(try! signature.derRepresentation.base64EncodedString()) ## Schnorr ```swift -let privateKey = try! secp256k1.Signing.PrivateKey() +let privateKey = try! secp256k1.Schnorr.PrivateKey() // Extra params for custom signing var auxRand = try! "C87AA53824B4D7AE2EB035A2B5BBBCCC080E76CDC6D1692C4B0B62D798E6D906".bytes var messageDigest = try! "7E2D58D8B3BCDF1ABADEC7829054F90DDA9805AAB56C77333024B9D0A508B75C".bytes // API allows for signing variable length messages -let signature = try! privateKey.schnorr.signature(message: &messageDigest, auxiliaryRand: &auxRand) +let signature = try! privateKey.signature(message: &messageDigest, auxiliaryRand: &auxRand) ``` ## Tweak @@ -111,11 +111,11 @@ let privateTweak1 = try! sharedSecretSign1.add(xonly: privateSign1.publicKey.xon ## Recovery ```swift -let privateKey = try! secp256k1.Signing.PrivateKey() +let privateKey = try! secp256k1.Recovery.PrivateKey() let messageData = "We're all Satoshi.".data(using: .utf8)! // Create a recoverable ECDSA signature -let recoverySignature = try! privateKey.ecdsa.recoverableSignature(for: messageData) +let recoverySignature = try! privateKey.signature(for: messageData) // Recover an ECDSA public key from a signature let publicKey = try! secp256k1.Recovery.PublicKey(messageData, signature: recoverySignature) @@ -126,5 +126,5 @@ let signature = try! recoverySignature.normalize # Danger -These APIs should not be considered stable and may change at any time, libsecp256k1 is still experimental and has not been formally released. +These APIs should not be considered stable and may change at any time. diff --git a/Sources/zkp/Asymmetric.swift b/Sources/zkp/Asymmetric.swift index 42436c3..4c3a8d3 100644 --- a/Sources/zkp/Asymmetric.swift +++ b/Sources/zkp/Asymmetric.swift @@ -25,21 +25,6 @@ public extension secp256k1 { baseKey.key } - /// The ECDSA signing object. - /// - /// You can use this object to create a digital signature for a given piece of data. - public var ecdsa: secp256k1.Signing.ECDSASigner { - ECDSASigner(signingKey: baseKey) - } - - /// The Schnorr signing object. - /// - /// You can use this object to create a digital signature for a given piece of data - /// using the Schnorr algorithm. - public var schnorr: secp256k1.Signing.SchnorrSigner { - SchnorrSigner(signingKey: baseKey) - } - /// The associated public key for verifying signatures created with this private key. /// /// - Returns: The associated public key. @@ -95,21 +80,6 @@ public extension secp256k1 { baseKey.rawRepresentation } - /// The ECDSA validating object. - /// - /// You can use this object to verify digital signatures created by the corresponding private key. - public var ecdsa: secp256k1.Signing.ECDSAValidator { - ECDSAValidator(validatingKey: baseKey) - } - - /// The Schnorr validating object. - /// - /// You can use this object to verify digital signatures created by the corresponding private key - /// using the Schnorr algorithm. - public var schnorr: secp256k1.Signing.SchnorrValidator { - SchnorrValidator(validatingKey: baseKey) - } - /// The associated x-only public key for verifying Schnorr signatures. /// /// - Returns: The associated x-only public key. diff --git a/Sources/zkp/ECDSA.swift b/Sources/zkp/ECDSA.swift index f0c407c..39c0a97 100644 --- a/Sources/zkp/ECDSA.swift +++ b/Sources/zkp/ECDSA.swift @@ -137,36 +137,7 @@ public extension secp256k1.Signing { // MARK: - secp256k1 + Signing Key -public extension secp256k1.Signing { - struct ECDSASigner { - /// Generated secp256k1 Signing Key. - var signingKey: PrivateKeyImplementation - } -} - -extension secp256k1.Signing.ECDSASigner: DigestSigner, Signer { - /// Generates a recoverable ECDSA signature. - /// - /// - Parameter digest: The digest to sign. - /// - Returns: The recoverable ECDSA Signature. - /// - Throws: If there is a failure producing the signature - public func recoverableSignature(for digest: D) throws -> secp256k1.Recovery.ECDSASignature { - var signature = secp256k1_ecdsa_recoverable_signature() - - guard secp256k1_ecdsa_sign_recoverable( - secp256k1.Context.raw, - &signature, - Array(digest), - Array(signingKey.rawRepresentation), - nil, - nil - ).boolValue else { - throw secp256k1Error.underlyingCryptoError - } - - return try secp256k1.Recovery.ECDSASignature(signature.dataValue) - } - +extension secp256k1.Signing.PrivateKey: DigestSigner { /// Generates an ECDSA signature over the secp256k1 elliptic curve. /// /// - Parameter digest: The digest to sign. @@ -179,7 +150,7 @@ extension secp256k1.Signing.ECDSASigner: DigestSigner, Signer { secp256k1.Context.raw, &signature, Array(digest), - Array(signingKey.rawRepresentation), + Array(rawRepresentation), nil, nil ).boolValue else { @@ -188,17 +159,9 @@ extension secp256k1.Signing.ECDSASigner: DigestSigner, Signer { return try secp256k1.Signing.ECDSASignature(signature.dataValue) } +} - /// Generates an ECDSA signature over the secp256k1 elliptic curve. - /// SHA256 is used as the hash function. - /// - /// - Parameter data: The data to sign. - /// - Returns: The ECDSA Signature. - /// - Throws: If there is a failure producing the signature. - public func recoverableSignature(for data: D) throws -> secp256k1.Recovery.ECDSASignature { - try recoverableSignature(for: SHA256.hash(data: data)) - } - +extension secp256k1.Signing.PrivateKey: Signer { /// Generates an ECDSA signature over the secp256k1 elliptic curve. /// SHA256 is used as the hash function. /// @@ -212,14 +175,7 @@ extension secp256k1.Signing.ECDSASigner: DigestSigner, Signer { // MARK: - secp256k1 + Validating Key -public extension secp256k1.Signing { - struct ECDSAValidator { - /// Generated secp256k1 Validating Key. - var validatingKey: PublicKeyImplementation - } -} - -extension secp256k1.Signing.ECDSAValidator: DigestValidator, DataValidator { +extension secp256k1.Signing.PublicKey: DigestValidator { /// Verifies an ECDSA signature over the secp256k1 elliptic curve. /// /// - Parameters: @@ -227,15 +183,17 @@ extension secp256k1.Signing.ECDSAValidator: DigestValidator, DataValidator { /// - digest: The digest that was signed. /// - Returns: True if the signature is valid, false otherwise. public func isValidSignature(_ signature: secp256k1.Signing.ECDSASignature, for digest: D) -> Bool { - var secp256k1Signature = secp256k1_ecdsa_signature() - var secp256k1PublicKey = secp256k1_pubkey() + var ecdsaSignature = secp256k1_ecdsa_signature() + var publicKey = secp256k1_pubkey() - signature.rawRepresentation.copyToUnsafeMutableBytes(of: &secp256k1Signature.data) + signature.rawRepresentation.copyToUnsafeMutableBytes(of: &ecdsaSignature.data) - return secp256k1_ec_pubkey_parse(secp256k1.Context.raw, &secp256k1PublicKey, validatingKey.bytes, validatingKey.bytes.count).boolValue && - secp256k1_ecdsa_verify(secp256k1.Context.raw, &secp256k1Signature, Array(digest), &secp256k1PublicKey).boolValue + return secp256k1_ec_pubkey_parse(secp256k1.Context.raw, &publicKey, bytes, bytes.count).boolValue && + secp256k1_ecdsa_verify(secp256k1.Context.raw, &ecdsaSignature, Array(digest), &publicKey).boolValue } +} +extension secp256k1.Signing.PublicKey: DataValidator { /// Verifies an ECDSA signature over the secp256k1 elliptic curve. /// SHA256 is used as the hash function. /// diff --git a/Sources/zkp/Recovery.swift b/Sources/zkp/Recovery.swift index 906e15f..4b10e7e 100644 --- a/Sources/zkp/Recovery.swift +++ b/Sources/zkp/Recovery.swift @@ -15,9 +15,58 @@ import Foundation /// An extension for secp256k1 with a nested Recovery enum. public extension secp256k1 { enum Recovery { + /// A representation of a secp256k1 private key used for signing. + public struct PrivateKey: Equatable { + /// Generated secp256k1 Signing Key. + private let baseKey: PrivateKeyImplementation + + /// The associated public key for verifying signatures created with this private key. + /// + /// - Returns: The associated public key. + public var publicKey: PublicKey { + PublicKey(baseKey: baseKey.publicKey) + } + + /// A data representation of the private key. + public var rawRepresentation: Data { + baseKey.rawRepresentation + } + + /// Creates a random secp256k1 private key for signing. + /// + /// - Parameter format: The key format, default is .compressed. + /// - Throws: An error if the private key cannot be generated. + public init(format: secp256k1.Format = .compressed) throws { + self.baseKey = try PrivateKeyImplementation(format: format) + } + + /// Creates a secp256k1 private key for signing from a data representation. + /// + /// - Parameter data: A raw representation of the key. + /// - Parameter format: The key format, default is .compressed. + /// - Throws: An error if the raw representation does not create a private key for signing. + public init(rawRepresentation data: D, format: secp256k1.Format = .compressed) throws { + self.baseKey = try PrivateKeyImplementation(rawRepresentation: data, format: format) + } + + /// Determines if two private keys are equal. + /// + /// - Parameters: + /// - lhs: The left-hand side private key. + /// - rhs: The right-hand side private key. + /// - Returns: True if the private keys are equal, false otherwise. + public static func == (lhs: Self, rhs: Self) -> Bool { + lhs.baseKey.key == rhs.baseKey.key + } + } + /// A struct representing a secp256k1 public key for recovery purposes. public struct PublicKey { - let baseKey: PublicKeyImplementation + /// A data representation of the public key. + public var rawRepresentation: Data { baseKey.rawRepresentation } + + /// Generated secp256k1 Public Key. + private let baseKey: PublicKeyImplementation /// Initializes a secp256k1 public key using a data message and a recovery signature. /// - Parameters: @@ -54,12 +103,6 @@ public extension secp256k1 { init(baseKey: PublicKeyImplementation) { self.baseKey = baseKey } - - /// A data representation of the public key. - public var rawRepresentation: Data { baseKey.rawRepresentation } - - /// Implementation public key object. - var bytes: [UInt8] { baseKey.bytes } } } } @@ -76,6 +119,54 @@ public extension secp256k1.Recovery { /// Returns the raw signature. public var rawRepresentation: Data + /// Serialize an ECDSA signature in compact (64 byte) format. + /// - Throws: If there is a failure parsing signature + /// - Returns: a 64-byte data representation of the compact serialization + public var compactRepresentation: ECDSACompactSignature { + get throws { + let compactSignatureLength = 64 + var recoveryId = Int32() + var recoverableSignature = secp256k1_ecdsa_recoverable_signature() + var compactSignature = [UInt8](repeating: 0, count: compactSignatureLength) + + rawRepresentation.copyToUnsafeMutableBytes(of: &recoverableSignature.data) + + guard secp256k1_ecdsa_recoverable_signature_serialize_compact( + secp256k1.Context.raw, + &compactSignature, + &recoveryId, + &recoverableSignature + ).boolValue else { + throw secp256k1Error.underlyingCryptoError + } + + return secp256k1.Recovery.ECDSACompactSignature( + signature: Data(bytes: &compactSignature, count: compactSignatureLength), + recoveryId: recoveryId + ) + } + } + + /// Convert a recoverable signature into a normal signature. + public var normalize: secp256k1.Signing.ECDSASignature { + get throws { + var normalizedSignature = secp256k1_ecdsa_signature() + var recoverableSignature = secp256k1_ecdsa_recoverable_signature() + + rawRepresentation.copyToUnsafeMutableBytes(of: &recoverableSignature.data) + + guard secp256k1_ecdsa_recoverable_signature_convert( + secp256k1.Context.raw, + &normalizedSignature, + &recoverableSignature + ).boolValue else { + throw secp256k1Error.underlyingCryptoError + } + + return try secp256k1.Signing.ECDSASignature(normalizedSignature.dataValue) + } + } + /// Initializes ECDSASignature from the raw representation. /// - Parameters: /// - rawRepresentation: A raw representation of the key as a collection of contiguous bytes. @@ -125,53 +216,44 @@ public extension secp256k1.Recovery { public func withUnsafeBytes(_ body: (UnsafeRawBufferPointer) throws -> R) rethrows -> R { try rawRepresentation.withUnsafeBytes(body) } + } +} - /// Serialize an ECDSA signature in compact (64 byte) format. - /// - Throws: If there is a failure parsing signature - /// - Returns: a 64-byte data representation of the compact serialization - public var compactRepresentation: ECDSACompactSignature { - get throws { - let compactSignatureLength = 64 - var recoveryId = Int32() - var recoverableSignature = secp256k1_ecdsa_recoverable_signature() - var compactSignature = [UInt8](repeating: 0, count: compactSignatureLength) +// MARK: - secp256k1 + Recovery - rawRepresentation.copyToUnsafeMutableBytes(of: &recoverableSignature.data) +extension secp256k1.Recovery.PrivateKey: DigestSigner { + public typealias Signature = secp256k1.Recovery.ECDSASignature - guard secp256k1_ecdsa_recoverable_signature_serialize_compact( - secp256k1.Context.raw, - &compactSignature, - &recoveryId, - &recoverableSignature - ).boolValue else { - throw secp256k1Error.underlyingCryptoError - } + /// Generates a recoverable ECDSA signature. + /// + /// - Parameter digest: The digest to sign. + /// - Returns: The recoverable ECDSA Signature. + /// - Throws: If there is a failure producing the signature + public func signature(for digest: D) throws -> Signature { + var signature = secp256k1_ecdsa_recoverable_signature() - return secp256k1.Recovery.ECDSACompactSignature( - signature: Data(bytes: &compactSignature, count: compactSignatureLength), - recoveryId: recoveryId - ) - } + guard secp256k1_ecdsa_sign_recoverable( + secp256k1.Context.raw, + &signature, + Array(digest), + Array(rawRepresentation), + nil, + nil + ).boolValue else { + throw secp256k1Error.underlyingCryptoError } - /// Convert a recoverable signature into a normal signature. - public var normalize: secp256k1.Signing.ECDSASignature { - get throws { - var normalizedSignature = secp256k1_ecdsa_signature() - var recoverableSignature = secp256k1_ecdsa_recoverable_signature() - - rawRepresentation.copyToUnsafeMutableBytes(of: &recoverableSignature.data) - - guard secp256k1_ecdsa_recoverable_signature_convert( - secp256k1.Context.raw, - &normalizedSignature, - &recoverableSignature - ).boolValue else { - throw secp256k1Error.underlyingCryptoError - } + return try secp256k1.Recovery.ECDSASignature(signature.dataValue) + } +} - return try secp256k1.Signing.ECDSASignature(normalizedSignature.dataValue) - } - } +extension secp256k1.Recovery.PrivateKey: Signer { + /// Generates a recoverable ECDSA signature. SHA256 is used as the hash function. + /// + /// - Parameter data: The data to sign. + /// - Returns: The ECDSA Signature. + /// - Throws: If there is a failure producing the signature. + public func signature(for data: D) throws -> Signature { + try signature(for: SHA256.hash(data: data)) } } diff --git a/Sources/zkp/Schnorr.swift b/Sources/zkp/Schnorr.swift index d9df49d..fa896bc 100644 --- a/Sources/zkp/Schnorr.swift +++ b/Sources/zkp/Schnorr.swift @@ -10,8 +10,8 @@ import Foundation -extension secp256k1 { - @usableFromInline enum Schnorr { +public extension secp256k1 { + enum Schnorr { /// Fixed number of bytes for Schnorr signature /// /// [BIP340](https://github.com/bitcoin/bips/blob/master/bip-0340.mediawiki#abstract) @@ -28,10 +28,88 @@ extension secp256k1 { } } +/// An elliptic curve that enables secp256k1 signatures and key agreement. +public extension secp256k1.Schnorr { + /// A representation of a secp256k1 private key used for signing. + struct PrivateKey: Equatable { + /// Generated secp256k1 Signing Key. + private let baseKey: PrivateKeyImplementation + + /// The associated x-only public key for verifying Schnorr signatures. + /// + /// - Returns: The associated x-only public key. + public var xonly: XonlyKey { + XonlyKey(baseKey: baseKey.publicKey.xonly) + } + + /// A data representation of the private key. + public var rawRepresentation: Data { + baseKey.rawRepresentation + } + + /// Creates a random secp256k1 private key for signing. + /// + /// - Parameter format: The key format, default is .compressed. + /// - Throws: An error if the private key cannot be generated. + public init(format: secp256k1.Format = .compressed) throws { + self.baseKey = try PrivateKeyImplementation(format: format) + } + + /// Creates a secp256k1 private key for signing from a data representation. + /// + /// - Parameter data: A raw representation of the key. + /// - Parameter format: The key format, default is .compressed. + /// - Throws: An error if the raw representation does not create a private key for signing. + public init(rawRepresentation data: D, format: secp256k1.Format = .compressed) throws { + self.baseKey = try PrivateKeyImplementation(rawRepresentation: data, format: format) + } + + /// Determines if two private keys are equal. + /// + /// - Parameters: + /// - lhs: The left-hand side private key. + /// - rhs: The right-hand side private key. + /// - Returns: True if the private keys are equal, false otherwise. + public static func == (lhs: Self, rhs: Self) -> Bool { + lhs.baseKey.key == rhs.baseKey.key + } + } + + /// The corresponding x-only public key for the secp256k1 curve. + struct XonlyKey { + /// Generated secp256k1 x-only public key. + private let baseKey: XonlyKeyImplementation + + /// The secp256k1 x-only public key object. + public var bytes: [UInt8] { + baseKey.bytes + } + + /// Schnorr x-only public key are implicit of the point being even, therefore this will always return `false`.` + public var parity: Bool { + false + } + + /// Generates a secp256k1 x-only public key. + /// + /// - Parameter baseKey: Generated secp256k1 x-only public key. + fileprivate init(baseKey: XonlyKeyImplementation) { + self.baseKey = baseKey + } + + /// Generates a secp256k1 x-only public key from a raw representation. + /// + /// - Parameter data: A raw representation of the x-only public key. + public init(rawRepresentation data: D) { + self.baseKey = XonlyKeyImplementation(rawRepresentation: data, keyParity: 0) + } + } +} + // MARK: - Schnorr Signatures /// A Schnorr (Schnorr Digital Signature Scheme) Signature -public extension secp256k1.Signing { +public extension secp256k1.Schnorr { struct SchnorrSignature: ContiguousBytes, RawSignature { /// Returns the raw signature in a fixed 64-byte format. public var rawRepresentation: Data @@ -71,16 +149,9 @@ public extension secp256k1.Signing { } } -// MARK: - secp256k1 + Signing Key +// MARK: - secp256k1 + Schnorr -public extension secp256k1.Signing { - struct SchnorrSigner { - /// Generated secp256k1 Signing Key. - var signingKey: PrivateKeyImplementation - } -} - -extension secp256k1.Signing.SchnorrSigner: DigestSigner, Signer { +extension secp256k1.Schnorr.PrivateKey: DigestSigner, Signer { /// Generates an Schnorr signature from a hash of a variable length data object /// /// This function uses SHA256 to create a hash of the variable length the data argument to ensure only 32-byte messages are signed. @@ -92,7 +163,7 @@ extension secp256k1.Signing.SchnorrSigner: DigestSigner, Signer { /// - data: The data object to hash and sign. /// - Returns: The Schnorr Signature. /// - Throws: If there is a failure producing the signature. - public func signature(for data: D) throws -> secp256k1.Signing.SchnorrSignature { + public func signature(for data: D) throws -> secp256k1.Schnorr.SchnorrSignature { try signature(for: data, auxiliaryRand: SecureBytes(count: secp256k1.Schnorr.xonlyByteCount).bytes) } @@ -107,7 +178,7 @@ extension secp256k1.Signing.SchnorrSigner: DigestSigner, Signer { /// - digest: The digest to sign. /// - Returns: The Schnorr Signature. /// - Throws: If there is a failure producing the signature. - public func signature(for digest: D) throws -> secp256k1.Signing.SchnorrSignature { + public func signature(for digest: D) throws -> secp256k1.Schnorr.SchnorrSignature { try signature(for: digest, auxiliaryRand: SecureBytes(count: secp256k1.Schnorr.xonlyByteCount).bytes) } @@ -123,7 +194,7 @@ extension secp256k1.Signing.SchnorrSigner: DigestSigner, Signer { /// - auxiliaryRand: Auxiliary randomness. /// - Returns: The Schnorr Signature. /// - Throws: If there is a failure producing the signature. - public func signature(for data: D, auxiliaryRand: [UInt8]) throws -> secp256k1.Signing.SchnorrSignature { + public func signature(for data: D, auxiliaryRand: [UInt8]) throws -> secp256k1.Schnorr.SchnorrSignature { try signature(for: SHA256.hash(data: data), auxiliaryRand: auxiliaryRand) } @@ -139,7 +210,7 @@ extension secp256k1.Signing.SchnorrSigner: DigestSigner, Signer { /// - auxiliaryRand: Auxiliary randomness; BIP340 requires 32-bytes. /// - Returns: The Schnorr Signature. /// - Throws: If there is a failure producing the signature. - public func signature(for digest: D, auxiliaryRand: [UInt8]) throws -> secp256k1.Signing.SchnorrSignature { + public func signature(for digest: D, auxiliaryRand: [UInt8]) throws -> secp256k1.Schnorr.SchnorrSignature { var hashDataBytes = Array(digest).bytes var randomBytes = auxiliaryRand @@ -158,31 +229,24 @@ extension secp256k1.Signing.SchnorrSigner: DigestSigner, Signer { /// - auxiliaryRand: Auxiliary randomness; BIP340 requires 32-bytes. /// - Returns: The Schnorr Signature. /// - Throws: If there is a failure creating the context or signature. - public func signature(message: inout [UInt8], auxiliaryRand: UnsafeMutableRawPointer?) throws -> secp256k1.Signing.SchnorrSignature { + public func signature(message: inout [UInt8], auxiliaryRand: UnsafeMutableRawPointer?) throws -> secp256k1.Schnorr.SchnorrSignature { var keypair = secp256k1_keypair() var signature = [UInt8](repeating: 0, count: secp256k1.Schnorr.signatureByteCount) var extraParams = secp256k1_schnorrsig_extraparams(magic: secp256k1.Schnorr.magic, noncefp: nil, ndata: auxiliaryRand) - guard secp256k1_keypair_create(secp256k1.Context.raw, &keypair, signingKey.key.bytes).boolValue, + guard secp256k1_keypair_create(secp256k1.Context.raw, &keypair, Array(rawRepresentation)).boolValue, secp256k1_schnorrsig_sign_custom(secp256k1.Context.raw, &signature, &message, message.count, &keypair, &extraParams).boolValue else { throw secp256k1Error.underlyingCryptoError } - return try secp256k1.Signing.SchnorrSignature(Data(bytes: signature, count: secp256k1.Schnorr.signatureByteCount)) + return try secp256k1.Schnorr.SchnorrSignature(Data(bytes: signature, count: secp256k1.Schnorr.signatureByteCount)) } } // MARK: - Schnorr + Validating Key -public extension secp256k1.Signing { - struct SchnorrValidator { - /// Generated Schnorr Validating Key. - var validatingKey: PublicKeyImplementation - } -} - -extension secp256k1.Signing.SchnorrValidator: DigestValidator, DataValidator { +extension secp256k1.Schnorr.XonlyKey: DigestValidator, DataValidator { /// Verifies a Schnorr signature with a variable length data object /// /// This function uses SHA256 to create a hash of the variable length the data argument to ensure only 32-byte messages are verified. @@ -194,7 +258,7 @@ extension secp256k1.Signing.SchnorrValidator: DigestValidator, DataValidator { /// - signature: The signature to verify /// - data: The data that was signed. /// - Returns: True if the signature is valid, false otherwise. - public func isValidSignature(_ signature: secp256k1.Signing.SchnorrSignature, for data: D) -> Bool { + public func isValidSignature(_ signature: secp256k1.Schnorr.SchnorrSignature, for data: D) -> Bool { isValidSignature(signature, for: SHA256.hash(data: data)) } @@ -209,7 +273,7 @@ extension secp256k1.Signing.SchnorrValidator: DigestValidator, DataValidator { /// - signature: The signature to verify. /// - digest: The digest that was signed. /// - Returns: True if the signature is valid, false otherwise. - public func isValidSignature(_ signature: secp256k1.Signing.SchnorrSignature, for digest: D) -> Bool { + public func isValidSignature(_ signature: secp256k1.Schnorr.SchnorrSignature, for digest: D) -> Bool { var hashDataBytes = Array(digest).bytes return isValid(signature, for: &hashDataBytes) @@ -225,10 +289,10 @@ extension secp256k1.Signing.SchnorrValidator: DigestValidator, DataValidator { /// - signature: The signature to verify. /// - message: The message that was signed. /// - Returns: True if the signature is valid, false otherwise. - public func isValid(_ signature: secp256k1.Signing.SchnorrSignature, for message: inout [UInt8]) -> Bool { + public func isValid(_ signature: secp256k1.Schnorr.SchnorrSignature, for message: inout [UInt8]) -> Bool { var pubKey = secp256k1_xonly_pubkey() - return secp256k1_xonly_pubkey_parse(secp256k1.Context.raw, &pubKey, validatingKey.xonly.bytes).boolValue && + return secp256k1_xonly_pubkey_parse(secp256k1.Context.raw, &pubKey, bytes).boolValue && secp256k1_schnorrsig_verify(secp256k1.Context.raw, signature.rawRepresentation.bytes, message, message.count, &pubKey).boolValue } } diff --git a/Tests/zkpTests/secp256k1Tests.swift b/Tests/zkpTests/secp256k1Tests.swift index 67faaae..d4f49da 100644 --- a/Tests/zkpTests/secp256k1Tests.swift +++ b/Tests/zkpTests/secp256k1Tests.swift @@ -182,7 +182,7 @@ final class secp256k1Tests: XCTestCase { let privateKey = try! secp256k1.Signing.PrivateKey(rawRepresentation: privateKeyBytes) let messageData = "We're all Satoshi Nakamoto and a bit of Harold Thomas Finney II.".data(using: .utf8)! - let signature = try! privateKey.ecdsa.signature(for: messageData) + let signature = try! privateKey.signature(for: messageData) // Verify the signature matches the expected output XCTAssertEqual(expectedSignature, signature.rawRepresentation.base64EncodedString()) @@ -195,10 +195,10 @@ final class secp256k1Tests: XCTestCase { let expectedSignature = "rPnhleCU8vQOthm5h4gX/5UbmxH6w3zw1ykAmLvvtXT4YGKBoiMaP8eBBF8upN8IaTYmO7+o0Vyhf+cODD1uVg==" let expectedPrivateKey = "5f6d5afecc677d66fb3d41eee7a8ad8195659ceff588edaf416a9a17daf38fdd" let privateKeyBytes = try! expectedPrivateKey.bytes - let privateKey = try! secp256k1.Signing.PrivateKey(rawRepresentation: privateKeyBytes) + let privateKey = try! secp256k1.Recovery.PrivateKey(rawRepresentation: privateKeyBytes) let messageData = "We're all Satoshi Nakamoto and a bit of Harold Thomas Finney II.".data(using: .utf8)! - let recoverySignature = try! privateKey.ecdsa.recoverableSignature(for: messageData) + let recoverySignature = try! privateKey.signature(for: messageData) // Verify the recovery signature matches the expected output XCTAssertEqual(expectedRecoverySignature, recoverySignature.rawRepresentation.base64EncodedString()) @@ -214,10 +214,10 @@ final class secp256k1Tests: XCTestCase { let expectedRecoverySignature = "rPnhleCU8vQOthm5h4gX/5UbmxH6w3zw1ykAmLvvtXT4YGKBoiMaP8eBBF8upN8IaTYmO7+o0Vyhf+cODD1uVgE=" let expectedPrivateKey = "5f6d5afecc677d66fb3d41eee7a8ad8195659ceff588edaf416a9a17daf38fdd" let privateKeyBytes = try! expectedPrivateKey.bytes - let privateKey = try! secp256k1.Signing.PrivateKey(rawRepresentation: privateKeyBytes) + let privateKey = try! secp256k1.Recovery.PrivateKey(rawRepresentation: privateKeyBytes) let messageData = "We're all Satoshi Nakamoto and a bit of Harold Thomas Finney II.".data(using: .utf8)! - let recoverySignature = try! privateKey.ecdsa.recoverableSignature(for: messageData) + let recoverySignature = try! privateKey.signature(for: messageData) // Verify the recovery signature matches the expected output XCTAssertEqual(expectedRecoverySignature, recoverySignature.rawRepresentation.base64EncodedString()) @@ -233,12 +233,12 @@ final class secp256k1Tests: XCTestCase { let expectedSignature = "e907831f80848d1069a5371b402410364bdf1c5f8307b0084c55f1ce2dca821525f66a4a85ea8b71e482a74f382d2ce5ebeee8fdb2172f477df4900d310536c0" let expectedPrivateKey = "0000000000000000000000000000000000000000000000000000000000000003" let privateKeyBytes = try! expectedPrivateKey.bytes - let privateKey = try! secp256k1.Signing.PrivateKey(rawRepresentation: privateKeyBytes) + let privateKey = try! secp256k1.Schnorr.PrivateKey(rawRepresentation: privateKeyBytes) var messageDigest = try! "0000000000000000000000000000000000000000000000000000000000000000".bytes var auxRand = try! "0000000000000000000000000000000000000000000000000000000000000000".bytes - let signature = try! privateKey.schnorr.signature(message: &messageDigest, auxiliaryRand: &auxRand) + let signature = try! privateKey.signature(message: &messageDigest, auxiliaryRand: &auxRand) // Verify the signature matches the expected output XCTAssertEqual(expectedSignature, String(bytes: Array(signature.rawRepresentation))) @@ -251,38 +251,38 @@ final class secp256k1Tests: XCTestCase { let privateKey = try! secp256k1.Signing.PrivateKey(rawRepresentation: privateKeyBytes) let messageData = "We're all Satoshi Nakamoto and a bit of Harold Thomas Finney II.".data(using: .utf8)! - let signature = try! privateKey.ecdsa.signature(for: messageData) + let signature = try! privateKey.signature(for: messageData) // Test the verification of the signature output - XCTAssertTrue(privateKey.publicKey.ecdsa.isValidSignature(signature, for: SHA256.hash(data: messageData))) + XCTAssertTrue(privateKey.publicKey.isValidSignature(signature, for: SHA256.hash(data: messageData))) } func testSchnorrVerifyingPre() { let expectedPrivateKey = "4894b8087f428971b55ff96e16f7127340138bc84e7973821a224cad02055975" let expectedSignature = "ad57c21d383ef8ac799adfd469a221c40ef9f09563a16682b9ab1edc46c33d6d6a1d719761d269e87ab971e0ffafc1618a4666a4f9aef4abddc3ea9fc0cd5b12" let privateKeyBytes = try! expectedPrivateKey.bytes - let privateKey = try! secp256k1.Signing.PrivateKey(rawRepresentation: privateKeyBytes) + let privateKey = try! secp256k1.Schnorr.PrivateKey(rawRepresentation: privateKeyBytes) var messageDigest = "We're all Satoshi Nakamoto and a bit of Harold Thomas Finney II.".data(using: .utf8)!.bytes var auxRand = try! "f50c8c99e39a82f125fa83186b5f2483f39fb0fb56269c755689313a177be6ea".bytes - let signature = try! privateKey.schnorr.signature(message: &messageDigest, auxiliaryRand: &auxRand) + let signature = try! privateKey.signature(message: &messageDigest, auxiliaryRand: &auxRand) // Test the verification of the signature output XCTAssertEqual(expectedSignature, String(bytes: signature.rawRepresentation.bytes)) - XCTAssertTrue(privateKey.publicKey.schnorr.isValid(signature, for: &messageDigest)) + XCTAssertTrue(privateKey.xonly.isValid(signature, for: &messageDigest)) } func testSchnorrVerifying() { let expectedPrivateKey = "0000000000000000000000000000000000000000000000000000000000000003" let privateKeyBytes = try! expectedPrivateKey.bytes - let privateKey = try! secp256k1.Signing.PrivateKey(rawRepresentation: privateKeyBytes) + let privateKey = try! secp256k1.Schnorr.PrivateKey(rawRepresentation: privateKeyBytes) var messageDigest = try! "0000000000000000000000000000000000000000000000000000000000000000".bytes var auxRand = try! "0000000000000000000000000000000000000000000000000000000000000000".bytes - let signature = try! privateKey.schnorr.signature(message: &messageDigest, auxiliaryRand: &auxRand) + let signature = try! privateKey.signature(message: &messageDigest, auxiliaryRand: &auxRand) // Test the verification of the signature output - XCTAssertTrue(privateKey.publicKey.schnorr.isValid(signature, for: &messageDigest)) + XCTAssertTrue(privateKey.xonly.isValid(signature, for: &messageDigest)) } func testVerifyingDER() { @@ -295,7 +295,7 @@ final class secp256k1Tests: XCTestCase { let signature = try! secp256k1.Signing.ECDSASignature(derRepresentation: expectedDerSignature) // Test the verification of the signature output - XCTAssertTrue(privateKey.publicKey.ecdsa.isValidSignature(signature, for: SHA256.hash(data: messageData))) + XCTAssertTrue(privateKey.publicKey.isValidSignature(signature, for: SHA256.hash(data: messageData))) } func testPrivateKey() {