diff --git a/Sources/Packages/Sources/SecretKit/OpenSSH/OpenSSHKeyWriter.swift b/Sources/Packages/Sources/SecretKit/OpenSSH/OpenSSHKeyWriter.swift index 223b935..ef69b0c 100644 --- a/Sources/Packages/Sources/SecretKit/OpenSSH/OpenSSHKeyWriter.swift +++ b/Sources/Packages/Sources/SecretKit/OpenSSH/OpenSSHKeyWriter.swift @@ -64,6 +64,8 @@ extension OpenSSHKeyWriter { switch algorithm { case .ellipticCurve: return "ecdsa-sha2-nistp" + String(describing: length) + case .rsa: + return "rsa-sha2-512" } } @@ -76,6 +78,8 @@ extension OpenSSHKeyWriter { switch algorithm { case .ellipticCurve: return "nistp" + String(describing: length) + case .rsa: + return "rsa-sha2-512" } } diff --git a/Sources/Packages/Sources/SecretKit/Types/Secret.swift b/Sources/Packages/Sources/SecretKit/Types/Secret.swift index 6fc57a1..8f9656c 100644 --- a/Sources/Packages/Sources/SecretKit/Types/Secret.swift +++ b/Sources/Packages/Sources/SecretKit/Types/Secret.swift @@ -20,6 +20,7 @@ public protocol Secret: Identifiable, Hashable { public enum Algorithm: Hashable { case ellipticCurve + case rsa /// Initializes the Algorithm with a secAttr representation of an algorithm. /// - Parameter secAttr: the secAttr, represented as an NSNumber. @@ -28,8 +29,19 @@ public enum Algorithm: Hashable { switch secAttrString { case kSecAttrKeyTypeEC: self = .ellipticCurve + case kSecAttrKeyTypeRSA: + self = .rsa default: fatalError() } } + + public var secAttrKeyType: CFString { + switch self { + case .ellipticCurve: + return kSecAttrKeyTypeEC + case .rsa: + return kSecAttrKeyTypeRSA + } + } } diff --git a/Sources/Packages/Sources/SmartCardSecretKit/SmartCardStore.swift b/Sources/Packages/Sources/SmartCardSecretKit/SmartCardStore.swift index 28026ce..25f8509 100644 --- a/Sources/Packages/Sources/SmartCardSecretKit/SmartCardStore.swift +++ b/Sources/Packages/Sources/SmartCardSecretKit/SmartCardStore.swift @@ -74,6 +74,10 @@ extension SmartCard { signatureAlgorithm = .ecdsaSignatureMessageX962SHA256 case (.ellipticCurve, 384): signatureAlgorithm = .ecdsaSignatureMessageX962SHA384 + case (.rsa, 1024): + signatureAlgorithm = .rsaSignatureMessagePKCS1v15SHA512 + case (.rsa, 2048): + signatureAlgorithm = .rsaSignatureMessagePKCS1v15SHA512 default: fatalError() } @@ -82,6 +86,112 @@ extension SmartCard { } return signature as Data } + + public func verify(data: Data, signature: Data, with secret: SecretType) throws -> Bool { + + let attributes = KeychainDictionary([ + kSecAttrKeyType: secret.algorithm.secAttrKeyType, + kSecAttrKeySizeInBits: secret.keySize, + kSecAttrKeyClass: kSecAttrKeyClassPublic + ]) + var encryptError: SecurityError? + var untyped: CFTypeRef? = SecKeyCreateWithData(secret.publicKey as CFData, attributes, &encryptError) + guard let untypedSafe = untyped else { + throw KeychainError(statusCode: errSecSuccess) + } + let key = untypedSafe as! SecKey + let signatureAlgorithm: SecKeyAlgorithm + switch (secret.algorithm, secret.keySize) { + case (.ellipticCurve, 256): + signatureAlgorithm = .ecdsaSignatureMessageX962SHA256 + case (.ellipticCurve, 384): + signatureAlgorithm = .ecdsaSignatureMessageX962SHA384 + case (.rsa, 1024): + signatureAlgorithm = .rsaSignatureMessagePKCS1v15SHA512 + case (.rsa, 2048): + signatureAlgorithm = .rsaSignatureMessagePKCS1v15SHA512 + default: + fatalError() + } + let signature = SecKeyVerifySignature(key, signatureAlgorithm, data as CFData, signature as CFData, &encryptError) + if !signature { + throw SigningError(error: encryptError) + } + return signature + } + + public func encrypt(data: Data, with secret: SecretType) throws -> Data { + let attributes = KeychainDictionary([ + kSecAttrKeyType: secret.algorithm.secAttrKeyType, + kSecAttrKeySizeInBits: secret.keySize, + kSecAttrKeyClass: kSecAttrKeyClassPublic + ]) + var encryptError: SecurityError? + let untyped: CFTypeRef? = SecKeyCreateWithData(secret.publicKey as CFData, attributes, &encryptError) + guard let untypedSafe = untyped else { + throw KeychainError(statusCode: errSecSuccess) + } + let key = untypedSafe as! SecKey + let signatureAlgorithm: SecKeyAlgorithm + switch (secret.algorithm, secret.keySize) { + case (.ellipticCurve, 256): + signatureAlgorithm = .eciesEncryptionCofactorVariableIVX963SHA256AESGCM + case (.ellipticCurve, 384): + signatureAlgorithm = .eciesEncryptionCofactorVariableIVX963SHA256AESGCM + case (.rsa, 1024): + signatureAlgorithm = .rsaEncryptionOAEPSHA512AESGCM + case (.rsa, 2048): + signatureAlgorithm = .rsaEncryptionOAEPSHA512AESGCM + default: + fatalError() + } + guard let signature = SecKeyCreateEncryptedData(key, signatureAlgorithm, data as CFData, &encryptError) else { + throw SigningError(error: encryptError) + } + return signature as Data + } + + public func decrypt(data: Data, with secret: SecretType) throws -> Data { + guard let tokenID = tokenID else { fatalError() } + let context = LAContext() + context.localizedReason = "decrypt a file using secret \"\(secret.name)\"" + context.localizedCancelTitle = "Deny" + let attributes = KeychainDictionary([ + kSecClass: kSecClassKey, + kSecAttrKeyClass: kSecAttrKeyClassPrivate, + kSecAttrApplicationLabel: secret.id as CFData, + kSecAttrTokenID: tokenID, + kSecUseAuthenticationContext: context, + kSecReturnRef: true + ]) + var untyped: CFTypeRef? + let status = SecItemCopyMatching(attributes, &untyped) + if status != errSecSuccess { + throw KeychainError(statusCode: status) + } + guard let untypedSafe = untyped else { + throw KeychainError(statusCode: errSecSuccess) + } + let key = untypedSafe as! SecKey + var encryptError: SecurityError? + let signatureAlgorithm: SecKeyAlgorithm + switch (secret.algorithm, secret.keySize) { + case (.ellipticCurve, 256): + signatureAlgorithm = .eciesEncryptionStandardX963SHA256AESGCM + case (.ellipticCurve, 384): + signatureAlgorithm = .eciesEncryptionStandardX963SHA384AESGCM + case (.rsa, 1024): + signatureAlgorithm = .rsaEncryptionOAEPSHA512AESGCM + case (.rsa, 2048): + signatureAlgorithm = .rsaEncryptionOAEPSHA512AESGCM + default: + fatalError() + } + guard let signature = SecKeyCreateDecryptedData(key, signatureAlgorithm, data as CFData, &encryptError) else { + throw SigningError(error: encryptError) + } + return signature as Data + } public func existingPersistedAuthenticationContext(secret: SmartCard.Secret) -> PersistedAuthenticationContext? { nil @@ -140,7 +250,6 @@ extension SmartCard.Store { let attributes = KeychainDictionary([ kSecClass: kSecClassKey, kSecAttrTokenID: tokenID, - kSecAttrKeyType: kSecAttrKeyTypeEC, // Restrict to EC kSecReturnRef: true, kSecMatchLimit: kSecMatchLimitAll, kSecReturnAttributes: true