diff --git a/Sources/Crypto/Keys/EC/Ed25519.swift b/Sources/Crypto/Keys/EC/Ed25519.swift index 14d11ca23..4b0e4c3de 100644 --- a/Sources/Crypto/Keys/EC/Ed25519.swift +++ b/Sources/Crypto/Keys/EC/Ed25519.swift @@ -57,7 +57,7 @@ extension Curve25519 { } } - public struct PublicKey { + public struct PublicKey: Hashable { private var baseKey: Curve25519.Signing.Curve25519PublicKeyImpl public init(rawRepresentation: D) throws { @@ -75,6 +75,18 @@ extension Curve25519 { var keyBytes: [UInt8] { return self.baseKey.keyBytes } + + private func withUnsafeBytes(_ body: (UnsafeRawBufferPointer) throws -> R) rethrows -> R { + return try self.baseKey.keyBytes.withUnsafeBytes(body) + } + + public static func ==(lhs: Self, rhs: Self) -> Bool { + return lhs.rawRepresentation == rhs.rawRepresentation + } + + public func hash(into hasher: inout Hasher) { + return self.withUnsafeBytes { hasher.combine(bytes: $0) } + } } } } diff --git a/Sources/Crypto/Keys/EC/X25519Keys.swift b/Sources/Crypto/Keys/EC/X25519Keys.swift index 250eec355..4dc90a3e2 100644 --- a/Sources/Crypto/Keys/EC/X25519Keys.swift +++ b/Sources/Crypto/Keys/EC/X25519Keys.swift @@ -26,7 +26,7 @@ extension Curve25519 { typealias Curve25519PublicKeyImpl = Curve25519.KeyAgreement.OpenSSLCurve25519PublicKeyImpl #endif - public struct PublicKey: ECPublicKey { + public struct PublicKey: ECPublicKey, Hashable { fileprivate var baseKey: Curve25519PublicKeyImpl /// Initializes a Curve25519 Key for Key Agreement. @@ -54,6 +54,14 @@ extension Curve25519 { private func withUnsafeBytes(_ body: (UnsafeRawBufferPointer) throws -> R) rethrows -> R { return try self.baseKey.keyBytes.withUnsafeBytes(body) } + + public static func ==(lhs: Self, rhs: Self) -> Bool { + return lhs.rawRepresentation == rhs.rawRepresentation + } + + public func hash(into hasher: inout Hasher) { + self.withUnsafeBytes { hasher.combine(bytes: $0) } + } } public struct PrivateKey: ECPrivateKey, DiffieHellmanKeyAgreement { diff --git a/Tests/CryptoTests/Signatures/EdDSA/EdDSATests.swift b/Tests/CryptoTests/Signatures/EdDSA/EdDSATests.swift index 233e30146..3ae789b79 100644 --- a/Tests/CryptoTests/Signatures/EdDSA/EdDSATests.swift +++ b/Tests/CryptoTests/Signatures/EdDSA/EdDSATests.swift @@ -75,4 +75,54 @@ class EdDSATests: XCTestCase { // This signature should be invalid XCTAssertFalse(privateKey.publicKey.isValidSignature(DispatchData.empty, for: DispatchData.empty)) } + + func testCurve25519SigningPublicKeyEquatable() throws { + // Equality + let publicKey = Curve25519.Signing.PrivateKey().publicKey + XCTAssertEqual(publicKey, publicKey) + + // Inequality + + // The probability of this inequality check loop + // accidentally failing is... 1/2^246, i.e. low. + for _ in 0..<1024 { + XCTAssertNotEqual( + publicKey, + Curve25519.Signing.PrivateKey().publicKey + ) + } + } + + func testCurve25519KeyAgreementPublicKeyEquatable() throws { + // Equality + let publicKey = Curve25519.KeyAgreement.PrivateKey().publicKey + XCTAssertEqual(publicKey, publicKey) + + // Inequality + + // The probability of this inequality check loop + // accidentally failing is... 1/2^246, i.e. low. + for _ in 0..<1024 { + XCTAssertNotEqual( + publicKey, + Curve25519.KeyAgreement.PrivateKey().publicKey + ) + } + } + + func testCurve25519SigningPublicKeyHashable() throws { + let expectedCount = 1000 + let set = Set((0..