From d8799da503be1dd95cb9128478b5c3b22451a4b2 Mon Sep 17 00:00:00 2001 From: Rory Smith Date: Wed, 2 Oct 2024 16:40:39 +0100 Subject: [PATCH] Support SecKey Certificate private keys (#195) Motivation: I would like to create Certificates using a SecKey so that I can create certificates signed by hardware-backed keys. Currently, Swift Certificates requires the private key be provided in an exported representation, but this isn't possible when a SecKey is marked as non-exportable. Modifications: Adds a new SecKeyWrapper backing to Certificate.PrivateKey to support initalisation with SecKey. This wrapper struct handles key validation and signing operations by calling the appropriate SecKey interfaces. Adds additional tests to support new backing type. Result: Certifcate.PrivateKey can be initialised with a SecKey, allowing existing hardware-backed SecKey instances to be used as the issuer private key for new Certificate instances. Co-authored-by: Rory Smith Co-authored-by: Cory Benfield --- Sources/X509/CMakeLists.txt | 1 + Sources/X509/CertificatePrivateKey.swift | 38 ++- Sources/X509/SecKeyWrapper.swift | 329 +++++++++++++++++++++++ Tests/X509Tests/SecKeyWrapperTests.swift | 73 +++++ Tests/X509Tests/SignatureTests.swift | 286 ++++++++++++++++++++ 5 files changed, 726 insertions(+), 1 deletion(-) create mode 100644 Sources/X509/SecKeyWrapper.swift create mode 100644 Tests/X509Tests/SecKeyWrapperTests.swift diff --git a/Sources/X509/CMakeLists.txt b/Sources/X509/CMakeLists.txt index dd9b2c8a..45d1b65c 100644 --- a/Sources/X509/CMakeLists.txt +++ b/Sources/X509/CMakeLists.txt @@ -82,6 +82,7 @@ add_library(X509 "RandomNumberGenerator+bytes.swift" "RelativeDistinguishedName.swift" "SEC1PrivateKey.swift" + "SecKeyWrapper.swift" "Signature.swift" "SignatureAlgorithm.swift" "Verifier/AllOfPolicies.swift" diff --git a/Sources/X509/CertificatePrivateKey.swift b/Sources/X509/CertificatePrivateKey.swift index f1082ed1..928565b7 100644 --- a/Sources/X509/CertificatePrivateKey.swift +++ b/Sources/X509/CertificatePrivateKey.swift @@ -21,7 +21,7 @@ extension Certificate { /// A private key that can be used with a certificate. /// /// This type provides an opaque wrapper around the various private key types - /// provided by `swift-crypto`. Users are expected to construct this key from + /// provided by `swift-crypto` and `Security`. Users are expected to construct this key from /// one of those types. /// /// As private keys are never sent over the wire, this type does not offer @@ -70,6 +70,13 @@ extension Certificate { public init(_ secureEnclaveP256: SecureEnclave.P256.Signing.PrivateKey) { self.backing = .secureEnclaveP256(secureEnclaveP256) } + + /// Construct a private key wrapping a SecKey private key. + /// - Parameter secKey: The SecKey private key to wrap. + @inlinable + public init(_ secKey: SecKey) throws { + self.backing = .secKey(try SecKeyWrapper(key: secKey)) + } #endif @inlinable @@ -93,6 +100,8 @@ extension Certificate { #if canImport(Darwin) case .secureEnclaveP256(let secureEnclaveP256): return try secureEnclaveP256.signature(for: bytes, digestAlgorithm: digestAlgorithm) + case .secKey(let secKeyWrapper): + return try secKeyWrapper.signature(for: bytes, digestAlgorithm: digestAlgorithm) #endif } } @@ -113,6 +122,8 @@ extension Certificate { #if canImport(Darwin) case .secureEnclaveP256(let secureEnclaveP256): return PublicKey(secureEnclaveP256.publicKey) + case .secKey(let secKeyWrapper): + return secKeyWrapper.publicKey #endif } } @@ -139,6 +150,21 @@ extension Certificate { reason: "Cannot use \(algorithm) with ECDSA key \(self)" ) } + case .secKey(let key): + switch key.type { + case .ECDSA: + if !algorithm.isECDSA { + throw CertificateError.unsupportedSignatureAlgorithm( + reason: "Cannot use \(algorithm) with ECDSA key \(self)" + ) + } + case .RSA: + if !algorithm.isRSA { + throw CertificateError.unsupportedSignatureAlgorithm( + reason: "Cannot use \(algorithm) with RSA key \(self)" + ) + } + } #endif } @@ -164,6 +190,8 @@ extension Certificate.PrivateKey: CustomStringConvertible { #if canImport(Darwin) case .secureEnclaveP256: return "SecureEnclave.P256.PrivateKey" + case .secKey: + return "SecKey" #endif } } @@ -178,6 +206,7 @@ extension Certificate.PrivateKey { case rsa(_CryptoExtras._RSA.Signing.PrivateKey) #if canImport(Darwin) case secureEnclaveP256(SecureEnclave.P256.Signing.PrivateKey) + case secKey(SecKeyWrapper) #endif @inlinable @@ -194,6 +223,8 @@ extension Certificate.PrivateKey { #if canImport(Darwin) case (.secureEnclaveP256(let l), .secureEnclaveP256(let r)): return l.dataRepresentation == r.dataRepresentation + case (.secKey(let l), .secKey(let r)): + return l.publicKey.backing == r.publicKey.backing #endif default: return false @@ -219,6 +250,10 @@ extension Certificate.PrivateKey { case .secureEnclaveP256(let digest): hasher.combine(4) hasher.combine(digest.dataRepresentation) + case .secKey(let secKeyWrapper): + hasher.combine(5) + hasher.combine(secKeyWrapper.privateKey.hashValue) + hasher.combine(secKeyWrapper.publicKey.hashValue) #endif } } @@ -305,6 +340,7 @@ extension Certificate.PrivateKey { throw CertificateError.unsupportedPrivateKey( reason: "secure enclave private keys can not be serialised as PEM" ) + case .secKey(let key): return try key.pemDocument() #endif } } diff --git a/Sources/X509/SecKeyWrapper.swift b/Sources/X509/SecKeyWrapper.swift new file mode 100644 index 00000000..82059442 --- /dev/null +++ b/Sources/X509/SecKeyWrapper.swift @@ -0,0 +1,329 @@ +//===----------------------------------------------------------------------===// +// +// 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 +// +//===----------------------------------------------------------------------===// + +#if canImport(Darwin) +import SwiftASN1 +import Foundation +@preconcurrency import Crypto +@preconcurrency import _CryptoExtras +@preconcurrency import Security + +extension Certificate.PrivateKey { + /// A wrapper around ``Security.SecKey`` to allow the use of `SecKey` with certificates. + @usableFromInline + struct SecKeyWrapper: Sendable { + @usableFromInline + let privateKey: SecKey + @usableFromInline + let publicKey: Certificate.PublicKey + @usableFromInline + let type: KeyType + @usableFromInline + let attributes: [String: any Sendable] + + @usableFromInline + enum ECKeySize: Sendable { + case P256 + case P384 + case P521 + } + + @usableFromInline + enum KeyType: Sendable { + case RSA + case ECDSA(ECKeySize) + } + + /// Init with an existing `SecKey`. + /// + /// This parses the given `SecKey` and attempts to pre-emptively extract + /// data that will be needed at later points. Importantly, some of these operations + /// can throw, so these are performs during initialisation rather than at later + /// stages where throwing is unacceptable. + @inlinable + init(key: SecKey) throws { + self.privateKey = key + + self.attributes = try Self.keyAttributes(key: key) + + try Self.validateSecKey(attributes: self.attributes) + + self.type = try Self.keyType(attributes: self.attributes) + + self.publicKey = try Self.publicKey(privateKey: key, type: self.type) + } + + @usableFromInline + static func keyAttributes(key: SecKey) throws -> [String: any Sendable] { + guard let attributes = SecKeyCopyAttributes(key) as? [CFString: Any] else { + throw CertificateError.unsupportedPrivateKey( + reason: "cannot copy SecKey attributes" + ) + } + + return attributes as [String: any Sendable] + } + + @usableFromInline + static func validateSecKey(attributes: [String: any Sendable]) throws { + guard let keyClassType = attributes[kSecAttrKeyClass as String] as? String else { + throw CertificateError.unsupportedPrivateKey( + reason: "cannot determine class of SecKey" + ) + } + + let privateKeyClassType = kSecAttrKeyClassPrivate as String + if keyClassType != privateKeyClassType { + throw CertificateError.unsupportedPrivateKey( + reason: "SecKey class must be \(privateKeyClassType), not \(keyClassType)" + ) + } + } + + @usableFromInline + static func publicKeyData(privateKey: SecKey) throws -> Data { + var error: Unmanaged? = nil + guard let publicSecKey = SecKeyCopyPublicKey(privateKey), + let publicKeyData = SecKeyCopyExternalRepresentation(publicSecKey, &error) as Data? + else { + if let error = error?.takeRetainedValue() { + throw CertificateError.unsupportedPrivateKey( + reason: "cannot get public key from SecKey instance: \(error)" + ) + } + + throw CertificateError.unsupportedPrivateKey( + reason: "SecKeyCopyExternalRepresentation returned empty data" + ) + } + + return publicKeyData + } + + @usableFromInline + static func keyType(attributes: [String: any Sendable]) throws -> KeyType { + guard let privateKeyType = attributes[kSecAttrKeyType as String] as? String else { + throw CertificateError.unsupportedPrivateKey( + reason: "cannot get SecKey type" + ) + } + + if privateKeyType == (kSecAttrKeyTypeECDSA as String) { + let keySize = attributes[kSecAttrKeySizeInBits as String] as? Int ?? -1 + + if keySize == 256 { + return .ECDSA(.P256) + } else if keySize == 384 { + return .ECDSA(.P384) + } else if keySize == 521 { + return .ECDSA(.P521) + } else { + throw CertificateError.unsupportedPrivateKey( + reason: "unsupported SecKey key size: \(keySize)" + ) + } + } else if privateKeyType == (kSecAttrKeyTypeRSA as String) { + return .RSA + } else { + throw CertificateError.unsupportedPrivateKey( + reason: "unsupported SecKey key type: \(privateKeyType)" + ) + } + } + + @usableFromInline + static func publicKey(privateKey: SecKey, type: KeyType) throws -> Certificate.PublicKey { + let publicKeyData = try Self.publicKeyData(privateKey: privateKey) + + do { + switch type { + case .ECDSA(let keySize): + if keySize == .P256 { + return Certificate.PublicKey(try P256.Signing.PublicKey(x963Representation: publicKeyData)) + } else if keySize == .P384 { + return Certificate.PublicKey(try P384.Signing.PublicKey(x963Representation: publicKeyData)) + } else if keySize == .P521 { + return Certificate.PublicKey(try P521.Signing.PublicKey(x963Representation: publicKeyData)) + } else { + throw CertificateError.unsupportedPrivateKey( + reason: "unsupported SecKey ECDSA key size: \(keySize)" + ) + } + case .RSA: + return Certificate.PublicKey(try _RSA.Signing.PublicKey(derRepresentation: publicKeyData)) + } + } catch { + throw CertificateError.unsupportedPrivateKey( + reason: "cannot get public key from SecKey instance \(error)" + ) + } + } + + static func signatureData( + key: SecKey, + type: KeyType, + digestAlgorithm: AlgorithmIdentifier, + bytes: Bytes + ) throws -> Data { + + let signatureAlgorithm = try Self.signatureAlgorithm(digestAlgorithm: digestAlgorithm, type: type) + + var error: Unmanaged? + guard + let signatureData = SecKeyCreateSignature( + key, + signatureAlgorithm, + Data(bytes) as CFData, + &error + ) as Data? + else { + if let error = error?.takeRetainedValue() { + throw CertificateError.unsupportedPrivateKey( + reason: "could not create signature with SecKey: \(error)" + ) + } + + throw CertificateError.unsupportedPrivateKey(reason: "SecKeyCreateSignature returned empty data") + } + + return signatureData + } + + static func signatureAlgorithm(digestAlgorithm: AlgorithmIdentifier, type: KeyType) throws -> SecKeyAlgorithm { + let algorithm: SecKeyAlgorithm + switch type { + case .RSA: + switch digestAlgorithm { + case .sha1, .sha1UsingNil: + algorithm = .rsaSignatureMessagePKCS1v15SHA1 + case .sha256, .sha256UsingNil: + algorithm = .rsaSignatureMessagePKCS1v15SHA256 + case .sha384, .sha384UsingNil: + algorithm = .rsaSignatureMessagePKCS1v15SHA384 + case .sha512, .sha512UsingNil: + algorithm = .rsaSignatureMessagePKCS1v15SHA512 + default: + throw CertificateError.unsupportedPrivateKey( + reason: "unsupported SecKey RSA digest algorithm: \(digestAlgorithm)" + ) + } + case .ECDSA: + switch digestAlgorithm { + case .sha1, .sha1UsingNil: + algorithm = .ecdsaSignatureMessageX962SHA1 + case .sha256, .sha256UsingNil: + algorithm = .ecdsaSignatureMessageX962SHA256 + case .sha384, .sha384UsingNil: + algorithm = .ecdsaSignatureMessageX962SHA384 + case .sha512, .sha512UsingNil: + algorithm = .ecdsaSignatureMessageX962SHA512 + default: + throw CertificateError.unsupportedPrivateKey( + reason: "unsupported SecKey ECDSA digest algorithm: \(digestAlgorithm)" + ) + } + } + + return algorithm + } + + @usableFromInline + func signature( + for bytes: Bytes, + digestAlgorithm: AlgorithmIdentifier + ) throws -> Certificate.Signature { + + let signatureData = try Self.signatureData( + key: self.privateKey, + type: self.type, + digestAlgorithm: digestAlgorithm, + bytes: bytes + ) + + switch self.type { + case .RSA: + let signature = _RSA.Signing.RSASignature(rawRepresentation: signatureData) + return Certificate.Signature(backing: .rsa(signature)) + case .ECDSA(let keySize): + switch keySize { + case .P256: + let signature = try P256.Signing.ECDSASignature(derRepresentation: signatureData) + return Certificate.Signature(backing: .ecdsa(.init(signature))) + case .P384: + let signature = try P384.Signing.ECDSASignature(derRepresentation: signatureData) + return Certificate.Signature(backing: .ecdsa(.init(signature))) + case .P521: + let signature = try P521.Signing.ECDSASignature(derRepresentation: signatureData) + return Certificate.Signature(backing: .ecdsa(.init(signature))) + } + } + } + + @usableFromInline + var isSerializable: Bool { + if let extractable = self.attributes[kSecAttrIsExtractable as String] as? Bool { + return extractable + } + + return false + } + + @available(macOS 11.0, iOS 14, tvOS 14, watchOS 7, *) + @inlinable + func pemDocument() throws -> PEMDocument { + if !self.isSerializable { + throw CertificateError.unsupportedPrivateKey( + reason: "SecKey private key PEM cannot be extracted" + ) + } + + var error: Unmanaged? + guard let keyData = SecKeyCopyExternalRepresentation(self.privateKey, &error) as Data? else { + if let error = error?.takeRetainedValue() { + throw CertificateError.unsupportedPrivateKey( + reason: "cannot get external representation of SecKey: \(error)" + ) + } + + throw CertificateError.unsupportedPrivateKey( + reason: "SecKeyCopyExternalRepresentation returned empty data" + ) + } + + let derData: Data + let type: String + + switch self.type { + case .RSA: + type = "RSA PRIVATE KEY" + // keyData is DER-encoded private key + derData = keyData + case .ECDSA(let keySize): + type = "EC PRIVATE KEY" + switch keySize { + case .P256: + derData = try P256.Signing.PrivateKey(x963Representation: keyData).derRepresentation + case .P384: + derData = try P384.Signing.PrivateKey(x963Representation: keyData).derRepresentation + case .P521: + derData = try P521.Signing.PrivateKey(x963Representation: keyData).derRepresentation + } + } + + let array = [UInt8](derData) + return PEMDocument(type: type, derBytes: array) + } + } +} +#endif diff --git a/Tests/X509Tests/SecKeyWrapperTests.swift b/Tests/X509Tests/SecKeyWrapperTests.swift new file mode 100644 index 00000000..64353762 --- /dev/null +++ b/Tests/X509Tests/SecKeyWrapperTests.swift @@ -0,0 +1,73 @@ +//===----------------------------------------------------------------------===// +// +// 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 XCTest +@_spi(Testing) @testable import X509 + +#if canImport(Darwin) +final class SecKeyWrapperTests: XCTestCase { + struct CandidateKey { + let key: SecKey + let type: String + let keySize: Int + let sep: Bool + } + + func generateCandidateKeys() throws -> [CandidateKey] { + var keys: [CandidateKey] = [] + + // RSA + keys.append( + CandidateKey( + key: try SignatureTests.generateSecKey(keyType: kSecAttrKeyTypeRSA, keySize: 2048, useSEP: false), + type: "RSA", + keySize: 2048, + sep: false + ) + ) + + // eliptic curves + for keyType in [kSecAttrKeyTypeECSECPrimeRandom, kSecAttrKeyTypeEC, kSecAttrKeyTypeECDSA] { + for keySize in [256, 384] { + for useSEP in [true, false] { + keys.append( + CandidateKey( + key: try SignatureTests.generateSecKey(keyType: keyType, keySize: keySize, useSEP: useSEP), + type: "EC-\(keyType)", + keySize: keySize, + sep: useSEP + ) + ) + } + } + } + return keys + } + + func testPEMExport() throws { + for candidate in try generateCandidateKeys() { + try XCTContext.runActivity(named: "Testing \(candidate.type) key (size: \(candidate.keySize))") { _ in + let secKeyWrapper = try Certificate.PrivateKey.SecKeyWrapper(key: candidate.key) + + if !candidate.sep { + let pemString = try secKeyWrapper.pemDocument() + XCTAssertNotNil(pemString) + } else { + XCTAssertThrowsError(try secKeyWrapper.pemDocument()) + } + } + } + } +} +#endif diff --git a/Tests/X509Tests/SignatureTests.swift b/Tests/X509Tests/SignatureTests.swift index c94ccefa..e810a88a 100644 --- a/Tests/X509Tests/SignatureTests.swift +++ b/Tests/X509Tests/SignatureTests.swift @@ -29,6 +29,21 @@ final class SignatureTests: XCTestCase { static let rsaKey = try! _RSA.Signing.PrivateKey(keySize: .bits2048) #if canImport(Darwin) static let secureEnclaveP256 = try? SecureEnclave.P256.Signing.PrivateKey() + static let secKeyRSA = try? generateSecKey(keyType: kSecAttrKeyTypeRSA, keySize: 2048, useSEP: false) + static let secKeyEC256 = try? generateSecKey(keyType: kSecAttrKeyTypeECSECPrimeRandom, keySize: 256, useSEP: false) + static let secKeyEC384 = try? generateSecKey(keyType: kSecAttrKeyTypeECSECPrimeRandom, keySize: 384, useSEP: false) + static let secKeyEC521 = try? generateSecKey(keyType: kSecAttrKeyTypeECSECPrimeRandom, keySize: 521, useSEP: false) + // SEP currently supports 256 and 384 bit EC keys + static let secKeyEnclaveEC256 = try? generateSecKey( + keyType: kSecAttrKeyTypeECSECPrimeRandom, + keySize: 256, + useSEP: true + ) + static let secKeyEnclaveEC384 = try? generateSecKey( + keyType: kSecAttrKeyTypeECSECPrimeRandom, + keySize: 384, + useSEP: true + ) #endif func testP384Signature() throws { @@ -382,6 +397,277 @@ final class SignatureTests: XCTestCase { } #if canImport(Darwin) + static func generateSecKey(keyType: CFString, keySize: Int, useSEP: Bool) throws -> SecKey { + let access = SecAccessControlCreateWithFlags( + kCFAllocatorDefault, + kSecAttrAccessibleWhenUnlockedThisDeviceOnly, + .privateKeyUsage, + nil + )! + + let attributes: NSMutableDictionary = [ + kSecAttrKeyType: keyType, + kSecAttrKeySizeInBits: keySize, + kSecPrivateKeyAttrs: [ + kSecAttrIsPermanent: false, + kSecAttrAccessControl: access, + ], + ] + + if useSEP { + attributes[kSecAttrTokenID] = kSecAttrTokenIDSecureEnclave + } + + var error: Unmanaged? = nil + guard let secKey = SecKeyCreateRandomKey(attributes, &error) else { + throw error!.takeRetainedValue() as Error + } + + return secKey + } + + func testHashFunctionMismatch_secKeyRSA_sha1WithRSAEncryption() throws { + guard let secKeyRSA = Self.secKeyRSA else { + throw XCTSkip("Key Error") + } + try self.hashFunctionMismatchTest( + privateKey: .init(secKeyRSA), + signatureAlgorithm: .sha1WithRSAEncryption, + validCombination: true + ) + } + + func testHashFunctionMismatch_secKeyRSA_sha256WithRSAEncryption() throws { + guard let secKeyRSA = Self.secKeyRSA else { + throw XCTSkip("Key Error") + } + try self.hashFunctionMismatchTest( + privateKey: .init(secKeyRSA), + signatureAlgorithm: .sha256WithRSAEncryption, + validCombination: true + ) + } + + func testHashFunctionMismatch_secKeyRSA_sha384WithRSAEncryption() throws { + guard let secKeyRSA = Self.secKeyRSA else { + throw XCTSkip("Key Error") + } + try self.hashFunctionMismatchTest( + privateKey: .init(secKeyRSA), + signatureAlgorithm: .sha384WithRSAEncryption, + validCombination: true + ) + } + + func testHashFunctionMismatch_secKeyRSA_sha512WithRSAEncryption() throws { + guard let secKeyRSA = Self.secKeyRSA else { + throw XCTSkip("Key Error") + } + try self.hashFunctionMismatchTest( + privateKey: .init(secKeyRSA), + signatureAlgorithm: .sha512WithRSAEncryption, + validCombination: true + ) + } + + func testHashFunctionMismatch_secKeyRSA_ecdsaWithSHA256() throws { + guard let secKeyRSA = Self.secKeyRSA else { + throw XCTSkip("Key Error") + } + try self.hashFunctionMismatchTest( + privateKey: .init(secKeyRSA), + signatureAlgorithm: .ecdsaWithSHA256, + validCombination: false + ) + } + + func testHashFunctionMismatch_secKeyEC256_ecdsaWithSHA256() throws { + guard let secKeyEC256 = Self.secKeyEC256 else { + throw XCTSkip("Key Error") + } + try self.hashFunctionMismatchTest( + privateKey: .init(secKeyEC256), + signatureAlgorithm: .ecdsaWithSHA256, + validCombination: true + ) + } + + func testHashFunctionMismatch_secKeyEC256_ecdsaWithSHA384() throws { + guard let secKeyEC256 = Self.secKeyEC256 else { + throw XCTSkip("Key Error") + } + try self.hashFunctionMismatchTest( + privateKey: .init(secKeyEC256), + signatureAlgorithm: .ecdsaWithSHA384, + validCombination: true + ) + } + + func testHashFunctionMismatch_secKeyEC256_ecdsaWithSHA512() throws { + guard let secKeyEC256 = Self.secKeyEC256 else { + throw XCTSkip("Key Error") + } + try self.hashFunctionMismatchTest( + privateKey: .init(secKeyEC256), + signatureAlgorithm: .ecdsaWithSHA512, + validCombination: true + ) + } + + func testHashFunctionMismatch_secKeyEC384_ecdsaWithSHA256() throws { + guard let secKeyEC384 = Self.secKeyEC384 else { + throw XCTSkip("Key Error") + } + try self.hashFunctionMismatchTest( + privateKey: .init(secKeyEC384), + signatureAlgorithm: .ecdsaWithSHA256, + validCombination: true + ) + } + + func testHashFunctionMismatch_secKeyEC384_ecdsaWithSHA384() throws { + guard let secKeyEC384 = Self.secKeyEC384 else { + throw XCTSkip("Key Error") + } + try self.hashFunctionMismatchTest( + privateKey: .init(secKeyEC384), + signatureAlgorithm: .ecdsaWithSHA384, + validCombination: true + ) + } + + func testHashFunctionMismatch_secKeyEC384_ecdsaWithSHA512() throws { + guard let secKeyEC384 = Self.secKeyEC384 else { + throw XCTSkip("Key Error") + } + try self.hashFunctionMismatchTest( + privateKey: .init(secKeyEC384), + signatureAlgorithm: .ecdsaWithSHA512, + validCombination: true + ) + } + + func testHashFunctionMismatch_secKeyEC521_ecdsaWithSHA256() throws { + guard let secKeyEC521 = Self.secKeyEC521 else { + throw XCTSkip("Key Error") + } + try self.hashFunctionMismatchTest( + privateKey: .init(secKeyEC521), + signatureAlgorithm: .ecdsaWithSHA256, + validCombination: true + ) + } + + func testHashFunctionMismatch_secKeyEC521_ecdsaWithSHA384() throws { + guard let secKeyEC521 = Self.secKeyEC521 else { + throw XCTSkip("Key Error") + } + try self.hashFunctionMismatchTest( + privateKey: .init(secKeyEC521), + signatureAlgorithm: .ecdsaWithSHA384, + validCombination: true + ) + } + + func testHashFunctionMismatch_secKeyEC521_ecdsaWithSHA512() throws { + guard let secKeyEC521 = Self.secKeyEC521 else { + throw XCTSkip("Key Error") + } + try self.hashFunctionMismatchTest( + privateKey: .init(secKeyEC521), + signatureAlgorithm: .ecdsaWithSHA512, + validCombination: true + ) + } + + func testHashFunctionMismatch_secKeyEC521_sha512WithRSAEncryption() throws { + guard let secKeyEC521 = Self.secKeyEC521 else { + throw XCTSkip("Key Error") + } + try self.hashFunctionMismatchTest( + privateKey: .init(secKeyEC521), + signatureAlgorithm: .sha512WithRSAEncryption, + validCombination: false + ) + } + + func testHashFunctionMismatch_secKeyEnclaveEC256_ecdsaWithSHA256() throws { + guard let secKeyEnclaveEC256 = Self.secKeyEnclaveEC256 else { + throw XCTSkip("No SEP") + } + try self.hashFunctionMismatchTest( + privateKey: .init(secKeyEnclaveEC256), + signatureAlgorithm: .ecdsaWithSHA256, + validCombination: true + ) + } + + func testHashFunctionMismatch_secKeyEnclaveEC256_ecdsaWithSHA384() throws { + guard let secKeyEnclaveEC256 = Self.secKeyEnclaveEC256 else { + throw XCTSkip("No SEP") + } + try self.hashFunctionMismatchTest( + privateKey: .init(secKeyEnclaveEC256), + signatureAlgorithm: .ecdsaWithSHA384, + validCombination: true + ) + } + + func testHashFunctionMismatch_secKeyEnclaveEC256_ecdsaWithSHA512() throws { + guard let secKeyEnclaveEC256 = Self.secKeyEnclaveEC256 else { + throw XCTSkip("No SEP") + } + try self.hashFunctionMismatchTest( + privateKey: .init(secKeyEnclaveEC256), + signatureAlgorithm: .ecdsaWithSHA512, + validCombination: true + ) + } + + func testHashFunctionMismatch_secKeyEnclaveEC384_ecdsaWithSHA256() throws { + guard let secKeyEnclaveEC384 = Self.secKeyEnclaveEC384 else { + throw XCTSkip("No SEP") + } + try self.hashFunctionMismatchTest( + privateKey: .init(secKeyEnclaveEC384), + signatureAlgorithm: .ecdsaWithSHA256, + validCombination: true + ) + } + + func testHashFunctionMismatch_secKeyEnclaveEC384_ecdsaWithSHA384() throws { + guard let secKeyEnclaveEC384 = Self.secKeyEnclaveEC384 else { + throw XCTSkip("No SEP") + } + try self.hashFunctionMismatchTest( + privateKey: .init(secKeyEnclaveEC384), + signatureAlgorithm: .ecdsaWithSHA384, + validCombination: true + ) + } + + func testHashFunctionMismatch_secKeyEnclaveEC384_ecdsaWithSHA512() throws { + guard let secKeyEnclaveEC384 = Self.secKeyEnclaveEC384 else { + throw XCTSkip("No SEP") + } + try self.hashFunctionMismatchTest( + privateKey: .init(secKeyEnclaveEC384), + signatureAlgorithm: .ecdsaWithSHA512, + validCombination: true + ) + } + + func testHashFunctionMismatch_secKeyEnclaveEC384_sha512WithRSAEncryption() throws { + guard let secKeyEnclaveEC384 = Self.secKeyEnclaveEC384 else { + throw XCTSkip("No SEP") + } + try self.hashFunctionMismatchTest( + privateKey: .init(secKeyEnclaveEC384), + signatureAlgorithm: .sha512WithRSAEncryption, + validCombination: false + ) + } + func testHashFunctionMismatch_secureEnclaveP256_ecdsaWithSHA256() throws { guard let secureEnclaveP256 = Self.secureEnclaveP256 else { throw XCTSkip("No SEP")