diff --git a/Sources/Web5/Crypto/DigitalSignatureAlgorithms/ECDSA+Es256k.swift b/Sources/Web5/Crypto/DigitalSignatureAlgorithms/ECDSA+Es256k.swift index 92e8f30..7699ae3 100644 --- a/Sources/Web5/Crypto/DigitalSignatureAlgorithms/ECDSA+Es256k.swift +++ b/Sources/Web5/Crypto/DigitalSignatureAlgorithms/ECDSA+Es256k.swift @@ -5,7 +5,7 @@ extension ECDSA { /// Crypto operations using the Elliptic Curve Digital Signature Algorithm (ECDSA) /// with the secp256k1 elliptic curve and SHA-256 - enum Es256k: AsymmetricKeyGenerator, Signer { + public enum Es256k: AsymmetricKeyGenerator, Signer { public static func generatePrivateKey() throws -> Jwk { return try secp256k1.Signing.PrivateKey().jwk() @@ -16,6 +16,29 @@ extension ECDSA { return try privateKey.publicKey.jwk() } + public static func privateKeyToBytes(_ privateKey: Jwk) throws -> Data { + let privateKey = try secp256k1.Signing.PrivateKey(privateJwk: privateKey) + return privateKey.dataRepresentation + } + + public static func privateKeyFromBytes(_ bytes: Data) throws -> Jwk { + return try secp256k1.Signing.PrivateKey(dataRepresentation: bytes).jwk() + } + + public static func publicKeyToBytes(_ publicKey: Jwk) throws -> Data { + let publicKey = try secp256k1.Signing.PublicKey(publicJwk: publicKey) + return publicKey.uncompressedBytes() + } + + public static func publicKeyFromBytes(_ bytes: Data) throws -> Jwk { + return try secp256k1.Signing.PublicKey( + dataRepresentation: bytes, + format: bytes.count == secp256k1.Signing.PublicKey.Constants.compressedKeySize + ? .compressed + : .uncompressed + ).jwk() + } + public static func sign(payload: D, privateKey: Jwk) throws -> Data where D: DataProtocol { let privateKey = try secp256k1.Signing.PrivateKey(privateJwk: privateKey) return try privateKey.signature(for: payload).compactRepresentation @@ -74,7 +97,7 @@ extension secp256k1.Signing.PrivateKey { extension secp256k1.Signing.PublicKey { - private enum Constants { + fileprivate enum Constants { /// Uncompressed key leading byte that indicates both the X and Y coordinates are available directly within the key. static let uncompressedKeyID: UInt8 = 0x04 @@ -83,6 +106,12 @@ extension secp256k1.Signing.PublicKey { /// An uncompressed key is represented with a leading 0x04 bytes, /// followed by 32 bytes for the x-coordinate and 32 bytes for the y-coordinate. static let uncompressedKeySize: Int = 65 + + /// Size of a compressed public key, in bytes. + /// + /// A compressed key is represented with a leading 0x02 or 0x03 byte, + /// followed by 32 bytes for the x-coordinate. + static let compressedKeySize: Int = 33 } init(publicJwk: Jwk) throws { diff --git a/Sources/Web5/Crypto/DigitalSignatureAlgorithms/ECDSA.swift b/Sources/Web5/Crypto/DigitalSignatureAlgorithms/ECDSA.swift index 3adaec1..81db071 100644 --- a/Sources/Web5/Crypto/DigitalSignatureAlgorithms/ECDSA.swift +++ b/Sources/Web5/Crypto/DigitalSignatureAlgorithms/ECDSA.swift @@ -1,4 +1,4 @@ import Foundation /// Elliptic Curve Digital Signature Algorithm (ECDSA) -enum ECDSA {} +public enum ECDSA {} diff --git a/Sources/Web5/Crypto/DigitalSignatureAlgorithms/EdDSA+Ed25519.swift b/Sources/Web5/Crypto/DigitalSignatureAlgorithms/EdDSA+Ed25519.swift index a05df63..2464402 100644 --- a/Sources/Web5/Crypto/DigitalSignatureAlgorithms/EdDSA+Ed25519.swift +++ b/Sources/Web5/Crypto/DigitalSignatureAlgorithms/EdDSA+Ed25519.swift @@ -5,7 +5,7 @@ extension EdDSA { /// Cryptographic operations using the Edwards-curve Digital Signature Algorithm (EdDSA) /// with the Ed25519 elliptic curve - enum Ed25519: AsymmetricKeyGenerator, Signer { + public enum Ed25519: AsymmetricKeyGenerator, Signer { enum Error: Swift.Error { case invalidPrivateJwk @@ -21,6 +21,24 @@ extension EdDSA { return try privateKey.publicKey.jwk() } + public static func privateKeyToBytes(_ privateKey: Jwk) throws -> Data { + let privateKey = try Curve25519.Signing.PrivateKey(privateJwk: privateKey) + return privateKey.rawRepresentation + } + + public static func privateKeyFromBytes(_ bytes: Data) throws -> Jwk { + return try Curve25519.Signing.PrivateKey(rawRepresentation: bytes).jwk() + } + + public static func publicKeyToBytes(_ publicKey: Jwk) throws -> Data { + let publicKey = try Curve25519.Signing.PublicKey(publicJwk: publicKey) + return publicKey.rawRepresentation + } + + public static func publicKeyFromBytes(_ bytes: Data) throws -> Jwk { + return try Curve25519.Signing.PublicKey(rawRepresentation: bytes).jwk() + } + public static func sign(payload: D, privateKey: Jwk) throws -> Data where D: DataProtocol { let privateKey = try Curve25519.Signing.PrivateKey(privateJwk: privateKey) return try privateKey.signature(for: payload) diff --git a/Sources/Web5/Crypto/DigitalSignatureAlgorithms/EdDSA.swift b/Sources/Web5/Crypto/DigitalSignatureAlgorithms/EdDSA.swift index e30d133..f4eee7f 100644 --- a/Sources/Web5/Crypto/DigitalSignatureAlgorithms/EdDSA.swift +++ b/Sources/Web5/Crypto/DigitalSignatureAlgorithms/EdDSA.swift @@ -1,4 +1,4 @@ import Foundation /// Edwards-curve Digital Signature Algorithm (EdDSA) -enum EdDSA {} +public enum EdDSA {} diff --git a/Sources/Web5/Crypto/Primitives/AsymmetricKeyGenerator.swift b/Sources/Web5/Crypto/Primitives/AsymmetricKeyGenerator.swift index e8433c5..676d7ad 100644 --- a/Sources/Web5/Crypto/Primitives/AsymmetricKeyGenerator.swift +++ b/Sources/Web5/Crypto/Primitives/AsymmetricKeyGenerator.swift @@ -12,6 +12,26 @@ public protocol AsymmetricKeyGenerator { /// - Returns: Public key in JWK format which corresponds to the provided `privateKey` static func computePublicKey(privateKey: Jwk) throws -> Jwk + /// Convert a JWK private key into its data representation + /// - Parameter privateKey: Public JWK to be converted + /// - Returns: Data representation of the provided `privateKey` + static func privateKeyToBytes(_ privateKey: Jwk) throws -> Data + + /// Convert a data representation of a private key into a JWK + /// - Parameter bytes: Data representation of a private key + /// - Returns: JWK representation of the provided `bytes` + static func privateKeyFromBytes(_ bytes: Data) throws -> Jwk + + /// Convert a JWK public key into its data representation + /// - Parameter publicKey: Private JWK to be converted + /// - Returns: Data representation of the provided `publicKey` + static func publicKeyToBytes(_ publicKey: Jwk) throws -> Data + + /// Convert a data representation of a public key into a JWK + /// - Parameter bytes: Data representation of a public key + /// - Returns: JWK representation of the provided `bytes` + static func publicKeyFromBytes(_ bytes: Data) throws -> Jwk + /// Determine if a given private key is a valid key that was generated by this generator /// - Parameter privateKey: Private key in JWK format /// - Returns: Boolean indicating if the provided `privateKey` is a valid key generated by this generator diff --git a/Tests/Web5Tests/Crypto/DigitalSignatureAlgorithms/ECDSA+Es256kTests.swift b/Tests/Web5Tests/Crypto/DigitalSignatureAlgorithms/ECDSA+Es256kTests.swift index a3bf57c..4c57814 100644 --- a/Tests/Web5Tests/Crypto/DigitalSignatureAlgorithms/ECDSA+Es256kTests.swift +++ b/Tests/Web5Tests/Crypto/DigitalSignatureAlgorithms/ECDSA+Es256kTests.swift @@ -1,3 +1,4 @@ +import CustomDump import XCTest @testable import Web5 @@ -35,6 +36,23 @@ final class ECDSA_Es256kTests: XCTestCase { XCTAssertEqual(publicKey.y, privateKey.y) } + func test_privateKey_toAndFromBytes() throws { + let privateKey = try ECDSA.Es256k.generatePrivateKey() + let privateKeyBytes = try ECDSA.Es256k.privateKeyToBytes(privateKey) + let restoredPrivateKey = try ECDSA.Es256k.privateKeyFromBytes(privateKeyBytes) + + XCTAssertNoDifference(privateKey, restoredPrivateKey) + } + + func test_publicKey_toAndFromBytes() throws { + let privateKey = try ECDSA.Es256k.generatePrivateKey() + let publicKey = try ECDSA.Es256k.computePublicKey(privateKey: privateKey) + let publicKeyBytes = try ECDSA.Es256k.publicKeyToBytes(publicKey) + let restoredPublicKey = try ECDSA.Es256k.publicKeyFromBytes(publicKeyBytes) + + XCTAssertNoDifference(publicKey, restoredPublicKey) + } + func test_sign() throws { let privateKey = try ECDSA.Es256k.generatePrivateKey() let signature = try ECDSA.Es256k.sign(payload: payload, privateKey: privateKey) diff --git a/Tests/Web5Tests/Crypto/DigitalSignatureAlgorithms/EdDSA+Ed25519Tests.swift b/Tests/Web5Tests/Crypto/DigitalSignatureAlgorithms/EdDSA+Ed25519Tests.swift index d2dbe17..991b8fa 100644 --- a/Tests/Web5Tests/Crypto/DigitalSignatureAlgorithms/EdDSA+Ed25519Tests.swift +++ b/Tests/Web5Tests/Crypto/DigitalSignatureAlgorithms/EdDSA+Ed25519Tests.swift @@ -1,3 +1,4 @@ +import CustomDump import XCTest @testable import Web5 @@ -39,6 +40,23 @@ final class EdDSA_Ed25519Tests: XCTestCase { XCTAssertEqual(publicKey.y, privateKey.y) } + func test_privateKey_toAndFromBytes() throws { + let privateKey = try EdDSA.Ed25519.generatePrivateKey() + let privateKeyBytes = try EdDSA.Ed25519.privateKeyToBytes(privateKey) + let restoredPrivateKey = try EdDSA.Ed25519.privateKeyFromBytes(privateKeyBytes) + + XCTAssertNoDifference(privateKey, restoredPrivateKey) + } + + func test_publicKey_toAndFromBytes() throws { + let privateKey = try EdDSA.Ed25519.generatePrivateKey() + let publicKey = try EdDSA.Ed25519.computePublicKey(privateKey: privateKey) + let publicKeyBytes = try EdDSA.Ed25519.publicKeyToBytes(publicKey) + let restoredPublicKey = try EdDSA.Ed25519.publicKeyFromBytes(publicKeyBytes) + + XCTAssertNoDifference(publicKey, restoredPublicKey) + } + func test_sign() throws { let privateKey = try EdDSA.Ed25519.generatePrivateKey() let signature = try EdDSA.Ed25519.sign(payload: payload, privateKey: privateKey)