diff --git a/Package.swift b/Package.swift index a5e153b..4b13c75 100644 --- a/Package.swift +++ b/Package.swift @@ -5,24 +5,24 @@ let package = Package( name: "tbDEX", platforms: [ .iOS(.v13), - .macOS(.v10_15) + .macOS(.v10_15), ], products: [ .library( name: "tbDEX", targets: ["tbDEX"] - ), + ) ], dependencies: [ .package(url: "https://github.com/GigaBitcoin/secp256k1.swift.git", from: "0.14.0"), - .package(url: "https://github.com/swift-extras/swift-extras-base64.git", from: "0.7.0") + .package(url: "https://github.com/swift-extras/swift-extras-base64.git", from: "0.7.0"), ], targets: [ .target( name: "tbDEX", dependencies: [ .product(name: "secp256k1", package: "secp256k1.swift"), - .product(name: "ExtrasBase64", package: "swift-extras-base64") + .product(name: "ExtrasBase64", package: "swift-extras-base64"), ] ), .testTarget( @@ -30,7 +30,7 @@ let package = Package( dependencies: ["tbDEX"], resources: [ .copy("TestVectors/ed25519"), - .copy("TestVectors/secp256k1") + .copy("TestVectors/secp256k1"), ] ), ] diff --git a/Sources/tbDEX/crypto/Ed25519.swift b/Sources/tbDEX/crypto/Ed25519.swift index 99aca77..af13d35 100644 --- a/Sources/tbDEX/crypto/Ed25519.swift +++ b/Sources/tbDEX/crypto/Ed25519.swift @@ -80,7 +80,8 @@ public enum Ed25519: KeyGenerator, Signer { /// Verifies an RFC8032-compliant EdDSA signature against given data using an Ed25519 public key in JSON Web Key /// (JWK) format. - public static func verify(publicKey: Jwk, signature: S, signedPayload: D) throws -> Bool where S: DataProtocol, D: DataProtocol { + public static func verify(publicKey: Jwk, signature: S, signedPayload: D) throws -> Bool + where S: DataProtocol, D: DataProtocol { guard let x = publicKey.x else { throw Ed25519Error.invalidPublicJwk } @@ -89,7 +90,6 @@ public enum Ed25519: KeyGenerator, Signer { return publicKey.isValidSignature(signature, for: signedPayload) } - // MARK: - Private Functions private static func generatePrivateJwk(privateKey: Curve25519.Signing.PrivateKey) throws -> Jwk { diff --git a/Sources/tbDEX/crypto/JWK.swift b/Sources/tbDEX/crypto/JWK.swift index 3686b87..df3c662 100644 --- a/Sources/tbDEX/crypto/JWK.swift +++ b/Sources/tbDEX/crypto/JWK.swift @@ -1,6 +1,6 @@ -import Foundation import CryptoKit import ExtrasBase64 +import Foundation public struct Jwk: Codable, Equatable { diff --git a/Sources/tbDEX/crypto/KeyManager.swift b/Sources/tbDEX/crypto/KeyManager.swift index aa1c43c..4de236d 100644 --- a/Sources/tbDEX/crypto/KeyManager.swift +++ b/Sources/tbDEX/crypto/KeyManager.swift @@ -20,7 +20,7 @@ public protocol KeyManager { /// Generates and securely stores a private key based on the provided keyType, /// returning a unique alias that can be utilized to reference the generated key for future operations. - /// + /// /// - Parameter keyType: The `KeyType` to use for key generation /// - Returns: A unique alias that can be used to reference the stored key. func generatePrivateKey(keyType: KeyType) throws -> String @@ -40,7 +40,7 @@ public protocol KeyManager { func sign(keyAlias: String, payload: D) throws -> Data where D: DataProtocol /// Return the alias of `publicKey`, as was originally returned by `generatePrivateKey`. - /// + /// /// - Parameter publicKey: A public key in JSON Web Key (JWK) format /// - Returns: The alias belonging to `publicKey` func getDeterministicAlias(publicKey: Jwk) -> String diff --git a/Sources/tbDEX/crypto/Secp256k1.swift b/Sources/tbDEX/crypto/Secp256k1.swift index e935c23..7cdacb1 100644 --- a/Sources/tbDEX/crypto/Secp256k1.swift +++ b/Sources/tbDEX/crypto/Secp256k1.swift @@ -36,7 +36,7 @@ public enum Secp256k1: KeyGenerator, Signer { /// Generates an Secp256k1 private key in JSON Web Key (JWK) format. public static func generatePrivateKey() throws -> Jwk { return try generatePrivateJwk( - privateKey:secp256k1.Signing.PrivateKey() + privateKey: secp256k1.Signing.PrivateKey() ) } @@ -58,7 +58,6 @@ public enum Secp256k1: KeyGenerator, Signer { return try generatePrivateJwk(privateKey: privateKey) } - /// Converts a raw Secp256k1 public key in bytes to its corresponding JSON Web Key (JWK) format. public static func bytesToPublicKey(_ bytes: Data) throws -> Jwk { let publicKey = try secp256k1.Signing.PublicKey( @@ -81,7 +80,7 @@ public enum Secp256k1: KeyGenerator, Signer { /// Converts a Secp256k1 public key from JSON Web Key (JWK) format to a raw bytes. public static func publicKeyToBytes(_ publicKey: Jwk) throws -> Data { guard let x = publicKey.x, - let y = publicKey.y + let y = publicKey.y else { throw Secpsecp256k1Error.invalidPublicJwk } @@ -101,7 +100,7 @@ public enum Secp256k1: KeyGenerator, Signer { /// Converts a Secp256k1 raw public key to its compressed form. public static func compressPublicKey(publicKeyBytes: Data) throws -> Data { guard publicKeyBytes.count == Self.uncompressedKeySize, - publicKeyBytes.first == Self.uncompressedKeyID + publicKeyBytes.first == Self.uncompressedKeyID else { throw Secpsecp256k1Error.internalError(reason: "Public key must be 65 bytes long an start with 0x04") } @@ -109,11 +108,12 @@ public enum Secp256k1: KeyGenerator, Signer { let xBytes = publicKeyBytes[1...32] let yBytes = publicKeyBytes[33...64] - let prefix = if yBytes.last! % 2 == 0 { - Self.compressedKeyEvenYID - } else { - Self.compressedKeyOddYID - } + let prefix = + if yBytes.last! % 2 == 0 { + Self.compressedKeyEvenYID + } else { + Self.compressedKeyOddYID + } var data = Data() data.append(prefix) @@ -145,7 +145,8 @@ public enum Secp256k1: KeyGenerator, Signer { /// Verifies an RFC6979-compliant ECDSA signature against given data and a Secp256k1 public key in JSON Web Key /// (JWK) format. - public static func verify(publicKey: Jwk, signature: S, signedPayload: D) throws -> Bool where S: DataProtocol, D: DataProtocol { + public static func verify(publicKey: Jwk, signature: S, signedPayload: D) throws -> Bool + where S: DataProtocol, D: DataProtocol { let publicKeyBytes = try publicKeyToBytes(publicKey) let publicKey = try secp256k1.Signing.PublicKey(dataRepresentation: publicKeyBytes, format: .uncompressed) @@ -239,20 +240,20 @@ public enum Secpsecp256k1Error: Error { // MARK: - Helper extensions -private extension Data { - func isCompressed() -> Bool { +extension Data { + fileprivate func isCompressed() -> Bool { return self.count == Secp256k1.compressedKeySize } } -private extension secp256k1.Signing.PublicKey { +extension secp256k1.Signing.PublicKey { /// Get the uncompressed bytes for a given public key. /// /// With a compressed public key, there's no direct access to the y-coordinate for use within /// a Jwk. To avoid doing manual computations along the curve to compute the y-coordinate, this /// function offloads the work to the `secp256k1` library to compute it for us. - func uncompressedBytes() -> Data { + fileprivate func uncompressedBytes() -> Data { switch self.format { case .uncompressed: return self.dataRepresentation diff --git a/Sources/tbDEX/crypto/Signer.swift b/Sources/tbDEX/crypto/Signer.swift index f16ebdd..0ffe918 100644 --- a/Sources/tbDEX/crypto/Signer.swift +++ b/Sources/tbDEX/crypto/Signer.swift @@ -2,7 +2,7 @@ import Foundation /// Protocol defining the contract for signing and verifying signatures on payloads protocol Signer { - + /// Sign a given payload using a private key. /// /// - Parameters: @@ -18,5 +18,6 @@ protocol Signer { /// - signature: The signature to be verified against the payload and public key. /// - signedPayload: The original payload that was signed, to be verified. /// - Returns: Boolean indicating if the publicKey and signature are valid for the given payload. - static func verify(publicKey: Jwk, signature: S, signedPayload: D) throws -> Bool where S: DataProtocol, D: DataProtocol + static func verify(publicKey: Jwk, signature: S, signedPayload: D) throws -> Bool + where S: DataProtocol, D: DataProtocol } diff --git a/Sources/tbDEX/extensions/Base64URL.swift b/Sources/tbDEX/extensions/Base64URL.swift index 185d4e9..661286d 100644 --- a/Sources/tbDEX/extensions/Base64URL.swift +++ b/Sources/tbDEX/extensions/Base64URL.swift @@ -1,5 +1,5 @@ -import Foundation import ExtrasBase64 +import Foundation extension Collection where Element == UInt8 { /// Encodes a collection of bytes to a Base64URL encoded string diff --git a/Tests/tbDEXTests/TestVector.swift b/Tests/tbDEXTests/TestVector.swift index a1f5b07..86c9a68 100644 --- a/Tests/tbDEXTests/TestVector.swift +++ b/Tests/tbDEXTests/TestVector.swift @@ -4,11 +4,13 @@ public func loadTestVector( fileName: String, subdirectory: String? = nil ) throws -> TestVector { - guard let url = Bundle.module.url( - forResource: fileName, - withExtension: "json", - subdirectory: subdirectory - ) else { + guard + let url = Bundle.module.url( + forResource: fileName, + withExtension: "json", + subdirectory: subdirectory + ) + else { fatalError("Missing file: \(fileName).json") } diff --git a/Tests/tbDEXTests/crypto/Ed25519Tests.swift b/Tests/tbDEXTests/crypto/Ed25519Tests.swift index 37977cf..ec51dce 100644 --- a/Tests/tbDEXTests/crypto/Ed25519Tests.swift +++ b/Tests/tbDEXTests/crypto/Ed25519Tests.swift @@ -134,7 +134,7 @@ final class Ed25519Tests: XCTestCase { fileName: "verify", subdirectory: "ed25519" ) - + for vector in testVector.vectors { let isValid = try Ed25519.verify( publicKey: vector.input.key, diff --git a/Tests/tbDEXTests/crypto/Secp256k1Tests.swift b/Tests/tbDEXTests/crypto/Secp256k1Tests.swift index 412f0e8..1d0c2d0 100644 --- a/Tests/tbDEXTests/crypto/Secp256k1Tests.swift +++ b/Tests/tbDEXTests/crypto/Secp256k1Tests.swift @@ -60,7 +60,9 @@ final class Secp256k1Tests: XCTestCase { } func test_bytesToPublicKey_returnedInJwkFormat() throws { - let publicKeyBytes = Data.fromHexString("043752951274023296c8a74b0ffe42f82ff4b4d4bba4326477422703f761f59258c26a7465b9a77ac0c3f1cedb139c428b0b1fbb5516867b527636f3286f705553")! + let publicKeyBytes = Data.fromHexString( + "043752951274023296c8a74b0ffe42f82ff4b4d4bba4326477422703f761f59258c26a7465b9a77ac0c3f1cedb139c428b0b1fbb5516867b527636f3286f705553" + )! let publicKey = try Secp256k1.bytesToPublicKey(publicKeyBytes) XCTAssertEqual(publicKey.curve, .secp256k1) @@ -85,8 +87,11 @@ final class Secp256k1Tests: XCTestCase { } func test_compressPublicKey() throws { - let compressedPublicKeyBytes = Data.fromHexString("026bcdccc644b309921d3b0c266183a20786650c1634d34e8dfa1ed74cd66ce214")! - let uncompressedPublicKeyBytes = Data.fromHexString("046bcdccc644b309921d3b0c266183a20786650c1634d34e8dfa1ed74cd66ce21465062296011dd076ae4e8ce5163ccf69d01496d3147656dcc96645b95211f3c6")! + let compressedPublicKeyBytes = Data.fromHexString( + "026bcdccc644b309921d3b0c266183a20786650c1634d34e8dfa1ed74cd66ce214")! + let uncompressedPublicKeyBytes = Data.fromHexString( + "046bcdccc644b309921d3b0c266183a20786650c1634d34e8dfa1ed74cd66ce21465062296011dd076ae4e8ce5163ccf69d01496d3147656dcc96645b95211f3c6" + )! let output = try Secp256k1.compressPublicKey(publicKeyBytes: uncompressedPublicKeyBytes) XCTAssertEqual(output.count, 33) @@ -94,7 +99,9 @@ final class Secp256k1Tests: XCTestCase { } func test_compressPublicKey_throwsForInvalidUncompressedPublickey() throws { - let invalidUncompressedPublicKeyBytes = Data.fromHexString("dfebc16793a5737ac51f606a43524df8373c063e41d5a99b2f1530afd987284bd1c7cde1658a9a756e71f44a97b4783ea9dee5ccb7f1447eb4836d8de9bd4f81fd")! + let invalidUncompressedPublicKeyBytes = Data.fromHexString( + "dfebc16793a5737ac51f606a43524df8373c063e41d5a99b2f1530afd987284bd1c7cde1658a9a756e71f44a97b4783ea9dee5ccb7f1447eb4836d8de9bd4f81fd" + )! do { let _ = try Secp256k1.compressPublicKey(publicKeyBytes: invalidUncompressedPublicKeyBytes) @@ -105,8 +112,11 @@ final class Secp256k1Tests: XCTestCase { } func test_decompressPublicKey() throws { - let compressedPublicKeyBytes = Data.fromHexString("026bcdccc644b309921d3b0c266183a20786650c1634d34e8dfa1ed74cd66ce214")! - let uncompressedPublicKeyBytes = Data.fromHexString("046bcdccc644b309921d3b0c266183a20786650c1634d34e8dfa1ed74cd66ce21465062296011dd076ae4e8ce5163ccf69d01496d3147656dcc96645b95211f3c6")! + let compressedPublicKeyBytes = Data.fromHexString( + "026bcdccc644b309921d3b0c266183a20786650c1634d34e8dfa1ed74cd66ce214")! + let uncompressedPublicKeyBytes = Data.fromHexString( + "046bcdccc644b309921d3b0c266183a20786650c1634d34e8dfa1ed74cd66ce21465062296011dd076ae4e8ce5163ccf69d01496d3147656dcc96645b95211f3c6" + )! let output = try Secp256k1.decompressPublicKey(publicKeyBytes: compressedPublicKeyBytes) XCTAssertEqual(output.count, 65) @@ -114,7 +124,8 @@ final class Secp256k1Tests: XCTestCase { } func test_decompressPublicKey_throwsForInvalidCompressedPublicKey() throws { - let invalidCompressedPublicKeyBytes = Data.fromHexString("fef0b998921eafb58f49efdeb0adc47123aa28a4042924236f08274d50c72fe7b0")! + let invalidCompressedPublicKeyBytes = Data.fromHexString( + "fef0b998921eafb58f49efdeb0adc47123aa28a4042924236f08274d50c72fe7b0")! do { let _ = try Secp256k1.decompressPublicKey(publicKeyBytes: invalidCompressedPublicKeyBytes)