diff --git a/Sources/X509/CertificatePrivateKey.swift b/Sources/X509/CertificatePrivateKey.swift index 928565b..1dfb7c2 100644 --- a/Sources/X509/CertificatePrivateKey.swift +++ b/Sources/X509/CertificatePrivateKey.swift @@ -63,6 +63,13 @@ extension Certificate { self.backing = .rsa(rsa) } + /// Construct a private key wrapping an Ed25519 private key. + /// - Parameter ed25519: The Ed25519 private key to wrap. + @inlinable + public init(_ ed25519: Curve25519.Signing.PrivateKey) { + self.backing = .ed25519(ed25519) + } + #if canImport(Darwin) /// Construct a private key wrapping a SecureEnclave.P256 private key. /// - Parameter secureEnclaveP256: The SecureEnclave.P256 private key to wrap. @@ -85,24 +92,31 @@ extension Certificate { signatureAlgorithm: SignatureAlgorithm ) throws -> Signature { try self.validateAlgorithmForKey(algorithm: signatureAlgorithm) - let digestAlgorithm = try AlgorithmIdentifier(digestAlgorithmFor: signatureAlgorithm) switch self.backing { case .p256(let p256): + let digestAlgorithm = try AlgorithmIdentifier(digestAlgorithmFor: signatureAlgorithm) return try p256.signature(for: bytes, digestAlgorithm: digestAlgorithm) case .p384(let p384): + let digestAlgorithm = try AlgorithmIdentifier(digestAlgorithmFor: signatureAlgorithm) return try p384.signature(for: bytes, digestAlgorithm: digestAlgorithm) case .p521(let p521): + let digestAlgorithm = try AlgorithmIdentifier(digestAlgorithmFor: signatureAlgorithm) return try p521.signature(for: bytes, digestAlgorithm: digestAlgorithm) case .rsa(let rsa): + let digestAlgorithm = try AlgorithmIdentifier(digestAlgorithmFor: signatureAlgorithm) let padding = try _RSA.Signing.Padding(forSignatureAlgorithm: signatureAlgorithm) return try rsa.signature(for: bytes, digestAlgorithm: digestAlgorithm, padding: padding) #if canImport(Darwin) case .secureEnclaveP256(let secureEnclaveP256): + let digestAlgorithm = try AlgorithmIdentifier(digestAlgorithmFor: signatureAlgorithm) return try secureEnclaveP256.signature(for: bytes, digestAlgorithm: digestAlgorithm) case .secKey(let secKeyWrapper): + let digestAlgorithm = try AlgorithmIdentifier(digestAlgorithmFor: signatureAlgorithm) return try secKeyWrapper.signature(for: bytes, digestAlgorithm: digestAlgorithm) #endif + case .ed25519(let ed25519): + return try ed25519.signature(for: bytes) } } @@ -125,6 +139,8 @@ extension Certificate { case .secKey(let secKeyWrapper): return secKeyWrapper.publicKey #endif + case .ed25519(let ed25519): + return PublicKey(ed25519.publicKey) } } @@ -166,6 +182,12 @@ extension Certificate { } } #endif + case .ed25519: + if algorithm != .ed25519 { + throw CertificateError.unsupportedSignatureAlgorithm( + reason: "Cannot use \(algorithm) with Ed25519 key \(self)" + ) + } } } @@ -193,6 +215,8 @@ extension Certificate.PrivateKey: CustomStringConvertible { case .secKey: return "SecKey" #endif + case .ed25519: + return "Ed25519.PrivateKey" } } } @@ -208,6 +232,7 @@ extension Certificate.PrivateKey { case secureEnclaveP256(SecureEnclave.P256.Signing.PrivateKey) case secKey(SecKeyWrapper) #endif + case ed25519(Crypto.Curve25519.Signing.PrivateKey) @inlinable static func == (lhs: BackingPrivateKey, rhs: BackingPrivateKey) -> Bool { @@ -226,6 +251,8 @@ extension Certificate.PrivateKey { case (.secKey(let l), .secKey(let r)): return l.publicKey.backing == r.publicKey.backing #endif + case (.ed25519(let l), .ed25519(let r)): + return l.rawRepresentation == r.rawRepresentation default: return false } @@ -255,6 +282,9 @@ extension Certificate.PrivateKey { hasher.combine(secKeyWrapper.privateKey.hashValue) hasher.combine(secKeyWrapper.publicKey.hashValue) #endif + case .ed25519(let digest): + hasher.combine(6) + hasher.combine(digest.rawRepresentation) } } } @@ -300,6 +330,8 @@ extension Certificate.PrivateKey { case .rsaKey: self = try .init(_CryptoExtras._RSA.Signing.PrivateKey(derRepresentation: pkcs8.privateKey.bytes)) + case .ed25519: + self = try .init(Curve25519.Signing.PrivateKey(pkcs8Key: pkcs8)) default: throw CertificateError.unsupportedPrivateKey(reason: "unknown algorithm \(pkcs8.algorithm)") } @@ -342,6 +374,7 @@ extension Certificate.PrivateKey { ) case .secKey(let key): return try key.pemDocument() #endif + case .ed25519(let key): return key.pemRepresentation } } } diff --git a/Sources/X509/CertificatePublicKey.swift b/Sources/X509/CertificatePublicKey.swift index 9d9c4dd..c81e57e 100644 --- a/Sources/X509/CertificatePublicKey.swift +++ b/Sources/X509/CertificatePublicKey.swift @@ -46,6 +46,9 @@ extension Certificate { _ = try RSAPKCS1PublicKey(derEncoded: spki.key.bytes) let key = try _RSA.Signing.PublicKey(derRepresentation: spki.key.bytes) self.backing = .rsa(key) + case .ed25519: + let key = try Curve25519.Signing.PublicKey(rawRepresentation: spki.key.bytes) + self.backing = .ed25519(key) default: throw CertificateError.unsupportedPublicKeyAlgorithm(reason: "\(spki.algorithmIdentifier)") } @@ -83,6 +86,13 @@ extension Certificate { public init(_ rsa: _RSA.Signing.PublicKey) { self.backing = .rsa(rsa) } + + /// Construct a public key wrapping an Ed25519 public key. + /// - Parameter ed25519: The Ed25519 public key to wrap. + @inlinable + public init(_ ed25519: Curve25519.Signing.PublicKey) { + self.backing = .ed25519(ed25519) + } } } @@ -127,22 +137,20 @@ extension Certificate.PublicKey { for bytes: Bytes, signatureAlgorithm: Certificate.SignatureAlgorithm ) -> Bool { - let digest: Digest - do { - let digestAlgorithm = try AlgorithmIdentifier(digestAlgorithmFor: signatureAlgorithm) - digest = try Digest.computeDigest(for: bytes, using: digestAlgorithm) - } catch { - return false + var digest: Digest? + + if let digestAlgorithm = try? AlgorithmIdentifier(digestAlgorithmFor: signatureAlgorithm) { + digest = try? Digest.computeDigest(for: bytes, using: digestAlgorithm) } - switch self.backing { - case .p256(let p256): + switch (self.backing, digest) { + case (.p256(let p256), .some(let digest)): return p256.isValidSignature(signature, for: digest) - case .p384(let p384): + case (.p384(let p384), .some(let digest)): return p384.isValidSignature(signature, for: digest) - case .p521(let p521): + case (.p521(let p521), .some(let digest)): return p521.isValidSignature(signature, for: digest) - case .rsa(let rsa): + case (.rsa(let rsa), .some(let digest)): // For now we don't support RSA PSS, as it's not deployed in the WebPKI. // We could, if there are sufficient user needs. do { @@ -151,6 +159,10 @@ extension Certificate.PublicKey { } catch { return false } + case (.ed25519(let ed25519), .none): + return ed25519.isValidSignature(signature, for: bytes) + default: + return false } } } @@ -170,6 +182,8 @@ extension Certificate.PublicKey: CustomStringConvertible { return "P521.PublicKey" case .rsa(let publicKey): return "RSA\(publicKey.keySizeInBits).PublicKey" + case .ed25519: + return "Ed25519.PublicKey" } } } @@ -181,6 +195,7 @@ extension Certificate.PublicKey { case p384(Crypto.P384.Signing.PublicKey) case p521(Crypto.P521.Signing.PublicKey) case rsa(_CryptoExtras._RSA.Signing.PublicKey) + case ed25519(Curve25519.Signing.PublicKey) @inlinable static func == (lhs: BackingPublicKey, rhs: BackingPublicKey) -> Bool { @@ -193,6 +208,8 @@ extension Certificate.PublicKey { return l.rawRepresentation == r.rawRepresentation case (.rsa(let l), .rsa(let r)): return l.derRepresentation == r.derRepresentation + case (.ed25519(let l), .ed25519(let r)): + return l.rawRepresentation == r.rawRepresentation default: return false } @@ -213,6 +230,9 @@ extension Certificate.PublicKey { case .rsa(let digest): hasher.combine(3) hasher.combine(digest.derRepresentation) + case .ed25519(let digest): + hasher.combine(4) + hasher.combine(digest.rawRepresentation) } } } @@ -237,6 +257,9 @@ extension SubjectPublicKeyInfo { case .rsa(let rsa): algorithmIdentifier = .rsaKey key = .init(bytes: ArraySlice(rsa.pkcs1DERRepresentation)) + case .ed25519(let ed25519): + algorithmIdentifier = .ed25519 + key = .init(bytes: ArraySlice(ed25519.rawRepresentation)) } self.algorithmIdentifier = algorithmIdentifier @@ -329,6 +352,21 @@ extension _RSA.Signing.PublicKey { } } +extension Curve25519.Signing.PublicKey { + /// Create a Curve25519 Public Key from a given ``Certificate/PublicKey-swift.struct``. + /// + /// Fails if the key is not a Curve25519 key. + /// + /// - parameters: + /// - key: The key to unwrap. + public init?(_ key: Certificate.PublicKey) { + guard case .ed25519(let inner) = key.backing else { + return nil + } + self = inner + } +} + extension Certificate.PublicKey: PEMParseable, PEMSerializable { @inlinable public static var defaultPEMDiscriminator: String { diff --git a/Sources/X509/Curve25519+DER.swift b/Sources/X509/Curve25519+DER.swift new file mode 100644 index 0000000..431f299 --- /dev/null +++ b/Sources/X509/Curve25519+DER.swift @@ -0,0 +1,44 @@ +//===----------------------------------------------------------------------===// +// +// This source file is part of the SwiftCertificates open source project +// +// Copyright (c) 2024 Apple Inc. and the SwiftCertificates project authors +// Licensed under Apache License v2.0 +// +// See LICENSE.txt for license information +// See CONTRIBUTORS.txt for the list of SwiftCertificates project authors +// +// SPDX-License-Identifier: Apache-2.0 +// +//===----------------------------------------------------------------------===// + +import Foundation +import SwiftASN1 +import Crypto + +extension Curve25519.Signing.PrivateKey { + @inlinable + init(pkcs8Key: PKCS8PrivateKey) throws { + // Annoyingly, the PKCS8 key has the raw bytes wrapped inside an octet string. + let rawRepresentation = try ASN1OctetString(derEncoded: pkcs8Key.privateKey.bytes) + self = try .init(rawRepresentation: rawRepresentation.bytes) + } + + @inlinable + var derRepresentation: [UInt8] { + // The DER representation we want is a PKCS8 private key. Somewhat annoyingly + // for us, we have to wrap the key bytes in an extra layer of ASN1OctetString + // which we encode separately. + let pkcs8Key = PKCS8PrivateKey( + algorithm: .ed25519, privateKey: ASN1OctetString(contentBytes: ArraySlice(self.rawRepresentation)) + ) + var serializer = DER.Serializer() + try! serializer.serialize(pkcs8Key) + return serializer.serializedBytes + } + + @inlinable + var pemRepresentation: PEMDocument { + return PEMDocument(type: "PRIVATE KEY", derBytes: self.derRepresentation) + } +} diff --git a/Sources/X509/Digests.swift b/Sources/X509/Digests.swift index d347cd2..9c84064 100644 --- a/Sources/X509/Digests.swift +++ b/Sources/X509/Digests.swift @@ -136,6 +136,18 @@ extension _RSA.Signing.PublicKey { } } +extension Curve25519.Signing.PublicKey { + @inlinable + func isValidSignature(_ signature: Certificate.Signature, for bytes: Bytes) -> Bool { + guard case .ed25519(let rawInnerSignature) = signature.backing else { + // Signature mismatch + return false + } + + return self.isValidSignature(rawInnerSignature, for: bytes) + } +} + // MARK: Private key operations extension P256.Signing.PrivateKey { @@ -255,3 +267,13 @@ extension _RSA.Signing.PrivateKey { return Certificate.Signature(backing: .rsa(signature)) } } + +extension Curve25519.Signing.PrivateKey { + @inlinable + func signature( + for bytes: Bytes + ) throws -> Certificate.Signature { + let signature: Data = try self.signature(for: bytes) + return Certificate.Signature(backing: .ed25519(.init(signature))) + } +} diff --git a/Sources/X509/PKCS8PrivateKey.swift b/Sources/X509/PKCS8PrivateKey.swift index adc2796..003457e 100644 --- a/Sources/X509/PKCS8PrivateKey.swift +++ b/Sources/X509/PKCS8PrivateKey.swift @@ -73,12 +73,12 @@ struct PKCS8PrivateKey: DERImplicitlyTaggable { // We ignore the attributes _ = try DER.optionalExplicitlyTagged(&nodes, tagNumber: 0, tagClass: .contextSpecific) { _ in } - return try .init(algorithm: algorithm, privateKey: privateKeyBytes) + return .init(algorithm: algorithm, privateKey: privateKeyBytes) } } @inlinable - init(algorithm: AlgorithmIdentifier, privateKey: ASN1OctetString) throws { + init(algorithm: AlgorithmIdentifier, privateKey: ASN1OctetString) { self.privateKey = privateKey self.algorithm = algorithm } diff --git a/Sources/X509/Signature.swift b/Sources/X509/Signature.swift index c9cb580..7ea82b7 100644 --- a/Sources/X509/Signature.swift +++ b/Sources/X509/Signature.swift @@ -48,6 +48,12 @@ extension Certificate { case .sha1WithRSAEncryption, .sha256WithRSAEncryption, .sha384WithRSAEncryption, .sha512WithRSAEncryption: let signature = _RSA.Signing.RSASignature(rawRepresentation: signatureBytes.bytes) self.backing = .rsa(signature) + case .ed25519: + guard signatureBytes.paddingBits == 0 else { + throw CertificateError.invalidSignatureForCertificate(reason: "No padding bits are allowed on Ed25519 signatures") + } + let signature = Data(signatureBytes.bytes) + self.backing = .ed25519(signature) default: throw CertificateError.unsupportedSignatureAlgorithm(reason: "\(signatureAlgorithm)") } @@ -66,6 +72,8 @@ extension Certificate.Signature: CustomStringConvertible { return "ECDSA" case .rsa: return "RSA" + case .ed25519: + return "Ed25519" } } } @@ -75,6 +83,7 @@ extension Certificate.Signature { enum BackingSignature: Hashable, Sendable { case ecdsa(ECDSASignature) case rsa(_CryptoExtras._RSA.Signing.RSASignature) + case ed25519(Data) @inlinable static func == (lhs: BackingSignature, rhs: BackingSignature) -> Bool { @@ -83,6 +92,8 @@ extension Certificate.Signature { return l == r case (.rsa(let l), .rsa(let r)): return l.rawRepresentation == r.rawRepresentation + case (.ed25519(let l), .ed25519(let r)): + return l == r default: return false } @@ -97,6 +108,9 @@ extension Certificate.Signature { case .rsa(let digest): hasher.combine(1) hasher.combine(digest.rawRepresentation) + case .ed25519(let sig): + hasher.combine(2) + hasher.combine(sig) } } } @@ -112,6 +126,8 @@ extension ASN1BitString { self = ASN1BitString(bytes: serializer.serializedBytes[...]) case .rsa(let sig): self = ASN1BitString(bytes: ArraySlice(sig.rawRepresentation)) + case .ed25519(let sig): + self = ASN1BitString(bytes: ArraySlice(sig)) } } } @@ -126,6 +142,8 @@ extension ASN1OctetString { self = ASN1OctetString(contentBytes: serializer.serializedBytes[...]) case .rsa(let sig): self = ASN1OctetString(contentBytes: ArraySlice(sig.rawRepresentation)) + case .ed25519(let sig): + self = ASN1OctetString(contentBytes: ArraySlice(sig)) } } } diff --git a/Sources/X509/SignatureAlgorithm.swift b/Sources/X509/SignatureAlgorithm.swift index 29c0f3a..8becfc2 100644 --- a/Sources/X509/SignatureAlgorithm.swift +++ b/Sources/X509/SignatureAlgorithm.swift @@ -65,6 +65,9 @@ extension Certificate { /// This value represents an RSA signature with PKCS1v1.5 padding and SHA512 as the hash function. public static let sha512WithRSAEncryption = Self(algorithmIdentifier: .sha512WithRSAEncryption) + /// This value represents an EdDSA signature using Curve25519. + public static let ed25519 = Self(algorithmIdentifier: .ed25519) + /// Whether this algorithm represents an ECDSA signature. @inlinable var isECDSA: Bool { diff --git a/Sources/X509/X509BaseTypes/AlgorithmIdentifier.swift b/Sources/X509/X509BaseTypes/AlgorithmIdentifier.swift index 665fd20..1db9791 100644 --- a/Sources/X509/X509BaseTypes/AlgorithmIdentifier.swift +++ b/Sources/X509/X509BaseTypes/AlgorithmIdentifier.swift @@ -207,6 +207,12 @@ extension AlgorithmIdentifier { algorithm: .AlgorithmIdentifier.sha512, parameters: try! ASN1Any(erasing: ASN1Null()) ) + + @usableFromInline + static let ed25519 = AlgorithmIdentifier( + algorithm: .AlgorithmIdentifier.ed25519, + parameters: nil + ) } extension AlgorithmIdentifier: CustomStringConvertible { @@ -264,6 +270,8 @@ extension ASN1ObjectIdentifier.AlgorithmIdentifier { static let sha384: ASN1ObjectIdentifier = [2, 16, 840, 1, 101, 3, 4, 2, 2] static let sha512: ASN1ObjectIdentifier = [2, 16, 840, 1, 101, 3, 4, 2, 3] + + static let ed25519: ASN1ObjectIdentifier = [1, 3, 101, 112] } extension AlgorithmIdentifier { diff --git a/Tests/X509Tests/CertificateTests.swift b/Tests/X509Tests/CertificateTests.swift index 9a7512b..15b8072 100644 --- a/Tests/X509Tests/CertificateTests.swift +++ b/Tests/X509Tests/CertificateTests.swift @@ -681,4 +681,46 @@ final class CertificateTests: XCTestCase { ] ) } + + func testRFC8410Ed25519PublicKey() throws { + let pemKey = """ + -----BEGIN PUBLIC KEY----- + MCowBQYDK2VwAyEAGb9ECWmEzf6FQbrBZ9w7lshQhqowtrbLDFw4rXAxZuE= + -----END PUBLIC KEY----- + """ + let resultingKey = try Certificate.PublicKey(pemEncoded: pemKey) + let unwrappedKey = Curve25519.Signing.PublicKey(resultingKey) + let reWrappedKey = Certificate.PublicKey(unwrappedKey!) + XCTAssertEqual(reWrappedKey, resultingKey) + XCTAssertEqual(try reWrappedKey.serializeAsPEM().pemString, pemKey) + } + + func testRFC8410Ed25519PrivateKey() throws { + let pemKey = """ + -----BEGIN PRIVATE KEY----- + MC4CAQAwBQYDK2VwBCIEINTuctv5E1hK1bbY8fdp+K06/nwoy/HU++CXqI9EdVhC + -----END PRIVATE KEY----- + """ + let resultingKey = try Certificate.PrivateKey(pemEncoded: pemKey) + XCTAssertEqual(try resultingKey.serializeAsPEM().pemString, pemKey) + } + + func testExampleEd25519SelfIssuedSelfSignedCert() throws { + let cert = """ + -----BEGIN CERTIFICATE----- + MIIBCDCBuwIUGW78zw0OL0GptJi++a91dBa7DsQwBQYDK2VwMCcxCzAJBgNVBAYT + AkRFMRgwFgYDVQQDDA93d3cuZXhhbXBsZS5jb20wHhcNMTkwMzMxMTc1MTIyWhcN + MjEwMjI4MTc1MTIyWjAnMQswCQYDVQQGEwJERTEYMBYGA1UEAwwPd3d3LmV4YW1w + bGUuY29tMCowBQYDK2VwAyEAK87g0b8CC1eA5mvKXt9uezZwJYWEyg74Y0xTZEkq + CcwwBQYDK2VwA0EAIIu/aa3Qtr3IE5to/nvWVY9y3ciwG5DnA70X3ALUhFs+U5aL + tfY8sNT1Ng72ht+UBwByuze20UsL9qMsmknQCA== + -----END CERTIFICATE----- + """ + let parsedCert = try Certificate(pemEncoded: cert) + XCTAssertTrue(parsedCert.publicKey.isValidSignature(parsedCert.signature, for: parsedCert)) + XCTAssertNotNil(Curve25519.Signing.PublicKey(parsedCert.publicKey)) + + let reEncoded = try parsedCert.serializeAsPEM().pemString + XCTAssertEqual(cert, reEncoded) + } } diff --git a/Tests/X509Tests/SignatureTests.swift b/Tests/X509Tests/SignatureTests.swift index e810a88..ff7a8c4 100644 --- a/Tests/X509Tests/SignatureTests.swift +++ b/Tests/X509Tests/SignatureTests.swift @@ -27,6 +27,7 @@ final class SignatureTests: XCTestCase { static let p384Key = P384.Signing.PrivateKey() static let p521Key = P521.Signing.PrivateKey() static let rsaKey = try! _RSA.Signing.PrivateKey(keySize: .bits2048) + static let ed25519Key = Curve25519.Signing.PrivateKey() #if canImport(Darwin) static let secureEnclaveP256 = try? SecureEnclave.P256.Signing.PrivateKey() static let secKeyRSA = try? generateSecKey(keyType: kSecAttrKeyTypeRSA, keySize: 2048, useSEP: false) @@ -228,6 +229,14 @@ final class SignatureTests: XCTestCase { ) } + func testHashFunctionMismatch_p256_ed25519() throws { + try self.hashFunctionMismatchTest( + privateKey: .init(Self.p256Key), + signatureAlgorithm: .ed25519, + validCombination: false + ) + } + func testHashFunctionMismatch_p384_ecdsaWithSHA256() throws { try self.hashFunctionMismatchTest( privateKey: .init(Self.p384Key), @@ -284,6 +293,14 @@ final class SignatureTests: XCTestCase { ) } + func testHashFunctionMismatch_p384_ed25519() throws { + try self.hashFunctionMismatchTest( + privateKey: .init(Self.p384Key), + signatureAlgorithm: .ed25519, + validCombination: false + ) + } + func testHashFunctionMismatch_p521_ecdsaWithSHA256() throws { try self.hashFunctionMismatchTest( privateKey: .init(Self.p521Key), @@ -340,6 +357,14 @@ final class SignatureTests: XCTestCase { ) } + func testHashFunctionMismatch_p521_ed25519() throws { + try self.hashFunctionMismatchTest( + privateKey: .init(Self.p521Key), + signatureAlgorithm: .ed25519, + validCombination: false + ) + } + func testHashFunctionMismatch_rsa_ecdsaWithSHA256() throws { try self.hashFunctionMismatchTest( privateKey: .init(Self.rsaKey), @@ -364,6 +389,14 @@ final class SignatureTests: XCTestCase { ) } + func testHashFunctionMismatch_rsa_ed25519() throws { + try self.hashFunctionMismatchTest( + privateKey: .init(Self.rsaKey), + signatureAlgorithm: .ed25519, + validCombination: false + ) + } + func testHashFunctionMismatch_rsa_sha1WithRSAEncryption() throws { try self.hashFunctionMismatchTest( privateKey: .init(Self.rsaKey), @@ -591,6 +624,17 @@ final class SignatureTests: XCTestCase { ) } + func testHashFunctionMismatch_secKeyEC521_ed25519() throws { + guard let secKeyEC521 = Self.secKeyEC521 else { + throw XCTSkip("Key Error") + } + try self.hashFunctionMismatchTest( + privateKey: .init(secKeyEC521), + signatureAlgorithm: .ed25519, + validCombination: false + ) + } + func testHashFunctionMismatch_secKeyEnclaveEC256_ecdsaWithSHA256() throws { guard let secKeyEnclaveEC256 = Self.secKeyEnclaveEC256 else { throw XCTSkip("No SEP") @@ -668,6 +712,17 @@ final class SignatureTests: XCTestCase { ) } + func testHashFunctionMismatch_secKeyEnclaveEC384_ed25519() throws { + guard let secKeyEnclaveEC384 = Self.secKeyEnclaveEC384 else { + throw XCTSkip("No SEP") + } + try self.hashFunctionMismatchTest( + privateKey: .init(secKeyEnclaveEC384), + signatureAlgorithm: .ed25519, + validCombination: false + ) + } + func testHashFunctionMismatch_secureEnclaveP256_ecdsaWithSHA256() throws { guard let secureEnclaveP256 = Self.secureEnclaveP256 else { throw XCTSkip("No SEP") @@ -744,8 +799,83 @@ final class SignatureTests: XCTestCase { validCombination: false ) } + + func testHashFunctionMismatch_secureEnclaveP256_ed25519() throws { + guard let secureEnclaveP256 = Self.secureEnclaveP256 else { + throw XCTSkip("No SEP") + } + try self.hashFunctionMismatchTest( + privateKey: .init(secureEnclaveP256), + signatureAlgorithm: .ed25519, + validCombination: false + ) + } #endif + func testHashFunctionMismatch_ped25519_ecdsaWithSHA256() throws { + try self.hashFunctionMismatchTest( + privateKey: .init(Self.ed25519Key), + signatureAlgorithm: .ecdsaWithSHA256, + validCombination: false + ) + } + + func testHashFunctionMismatch_ed25519_ecdsaWithSHA384() throws { + try self.hashFunctionMismatchTest( + privateKey: .init(Self.ed25519Key), + signatureAlgorithm: .ecdsaWithSHA384, + validCombination: false + ) + } + + func testHashFunctionMismatch_ed25519_ecdsaWithSHA512() throws { + try self.hashFunctionMismatchTest( + privateKey: .init(Self.ed25519Key), + signatureAlgorithm: .ecdsaWithSHA512, + validCombination: false + ) + } + + func testHashFunctionMismatch_ed25519_sha1WithRSAEncryption() throws { + try self.hashFunctionMismatchTest( + privateKey: .init(Self.ed25519Key), + signatureAlgorithm: .sha1WithRSAEncryption, + validCombination: false + ) + } + + func testHashFunctionMismatch_ed25519_sha256WithRSAEncryption() throws { + try self.hashFunctionMismatchTest( + privateKey: .init(Self.ed25519Key), + signatureAlgorithm: .sha256WithRSAEncryption, + validCombination: false + ) + } + + func testHashFunctionMismatch_ed25519_sha384WithRSAEncryption() throws { + try self.hashFunctionMismatchTest( + privateKey: .init(Self.ed25519Key), + signatureAlgorithm: .sha384WithRSAEncryption, + validCombination: false + ) + } + + func testHashFunctionMismatch_ed25519_sha512WithRSAEncryption() throws { + try self.hashFunctionMismatchTest( + privateKey: .init(Self.ed25519Key), + signatureAlgorithm: .sha512WithRSAEncryption, + validCombination: false + ) + } + + func testHashFunctionMismatch_ed25519_ed25519() throws { + try self.hashFunctionMismatchTest( + privateKey: .init(Self.ed25519Key), + signatureAlgorithm: .ed25519, + validCombination: true + ) + } + func testECDSASignatureCorrectlyStripsLeadingZerosFromRawByteRepresentation() throws { // We're testing a round-trip logic here, ensuring that the ECDSA signature correctly round-trips. func testECDSASignatureRoundTrip(rawSignatureBytes: [UInt8]) throws {