forked from EWC-consortium/eudi-wallet-oid4vc-ios
-
Notifications
You must be signed in to change notification settings - Fork 1
Commit
This commit does not belong to any branch on this repository, and may belong to a fork outside of the repository.
* Changes for encryption key management Segregated the code for encryption key generation and data signing to separate class to enable scalability to other encryption key generation and storage techniques. * Changing the interface descriptions Changed the interface descriptions to match the new changes related to key generation handler * Cleaning up spare code Removing test code for secure enclave * Changes related to secure enclave integratio * Changes for secure enclave - Fixed issue with creating jwk - code cleanup and adding necessary comments
- Loading branch information
1 parent
a4ec354
commit 711c3aa
Showing
8 changed files
with
317 additions
and
35 deletions.
There are no files selected for viewing
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
95 changes: 95 additions & 0 deletions
95
...s/eudiWalletOidcIos/Helper/EncryptionKeyHandler/Secure Enclave/SecureEnclaveHandler.swift
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,95 @@ | ||
// | ||
// File.swift | ||
// | ||
// | ||
// Created by Arun Raj on 27/06/24. | ||
// | ||
|
||
import Foundation | ||
|
||
public class SecureEnclaveHandler: NSObject, SecureKeyProtocol{ | ||
|
||
var privateKeyLabel = "" | ||
var publicKeyLabel = "" | ||
var organisationID = "" | ||
var secureEnclaveHandler: SecureEnclave? | ||
public var keyStorageType: SecureKeyTypes = .cryptoKit | ||
|
||
public init(organisationID: String) { | ||
super.init() | ||
self.organisationID = organisationID | ||
self.keyStorageType = .secureEnclave | ||
} | ||
|
||
//Creating secure enclave instance with unique identifer for the keys | ||
private func createSecureEnclaveHandlerFor() -> Bool{ | ||
if !organisationID.isEmpty{ | ||
secureEnclaveHandler = nil | ||
privateKeyLabel = "com.EudiWallet.\(organisationID).PrivateKey" | ||
secureEnclaveHandler = SecureEnclave(privateKeyApplicationTag: privateKeyLabel) | ||
return true | ||
} else{ | ||
// invalid organisation id | ||
return false | ||
} | ||
} | ||
|
||
//Generate private and public keys from secure enclave and pass the public key back | ||
//private key is stored securely within secure enclave is not accessible directly | ||
public func generateSecureKey() -> SecureKeyData?{ | ||
if createSecureEnclaveHandlerFor(){ | ||
do{ | ||
let anyExistingKey = try SecureEnclave.loadKeyPair(with: privateKeyLabel) | ||
|
||
if let publicKeyData = convertSecKeyToData(key: anyExistingKey.publicKey){ | ||
return SecureKeyData(publicKey: publicKeyData, privateKey: nil) | ||
} | ||
} | ||
catch{ | ||
|
||
do{ | ||
let newKeys = try SecureEnclave.generateKeyPair(with: privateKeyLabel) | ||
if let publicKeyData = convertSecKeyToData(key: newKeys.publicKey){ | ||
return SecureKeyData(publicKey: publicKeyData, privateKey: nil) | ||
} | ||
} | ||
catch{ | ||
print("Error retrieving the key") | ||
return nil | ||
} | ||
|
||
} | ||
|
||
} | ||
return nil | ||
} | ||
|
||
func convertSecKeyToData(key: SecKey) -> Data?{ | ||
var error: Unmanaged<CFError>? | ||
guard let publicKeydata = SecKeyCopyExternalRepresentation(key, &error) as? Data else { | ||
return nil | ||
} | ||
return publicKeydata | ||
} | ||
|
||
public func sign(payload: String, header: Data, withKey privateKey: Data?) -> String?{ | ||
if createSecureEnclaveHandlerFor(){ | ||
do{ | ||
if let signedData = try secureEnclaveHandler?.sign(payload, header: header){ | ||
|
||
return signedData | ||
} | ||
} | ||
catch{ | ||
return nil | ||
} | ||
} | ||
return nil | ||
} | ||
|
||
public func getJWK(publicKey: Data) -> [String:Any]?{ | ||
let jwk = secureEnclaveHandler?.getJWK(publicKey: publicKey) | ||
return jwk | ||
} | ||
|
||
} |
132 changes: 132 additions & 0 deletions
132
...es/eudiWalletOidcIos/Helper/EncryptionKeyHandler/Secure Enclave/SecureEnclaveHelper.swift
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,132 @@ | ||
//Class implementing the high level key generation and signing of data | ||
//using JOSESwift library for secure enclave | ||
|
||
import Foundation | ||
import Security | ||
import JOSESwift | ||
|
||
|
||
enum SecureEnclaveError: Error { | ||
case couldNotLoadKeyPair | ||
case couldNotGenerateKeyPair(description: String) | ||
case couldNotCreateSigner | ||
case couldNotCreateVerifier | ||
} | ||
|
||
class SecureEnclave { | ||
let keyPair: KeyPair | ||
|
||
init(privateKeyApplicationTag: String) { | ||
do { | ||
keyPair = try SecureEnclave.loadKeyPair(with: privateKeyApplicationTag) | ||
} catch { | ||
keyPair = try! SecureEnclave.generateKeyPair(with: privateKeyApplicationTag) | ||
} | ||
} | ||
|
||
//signing of data securely with the private key | ||
func sign(_ message: String, header: Data) throws -> String { | ||
if let headerParams = JWSHeader(header){ | ||
let payload = Payload(message.data(using: .utf8)!) | ||
|
||
guard let signer = Signer(signingAlgorithm: .ES256, privateKey: keyPair.privateKey) else { | ||
throw SecureEnclaveError.couldNotCreateSigner | ||
} | ||
|
||
return try JWS(header: headerParams, payload: payload, signer: signer).compactSerializedString | ||
}else{ | ||
throw SecureEnclaveError.couldNotCreateSigner | ||
} | ||
} | ||
|
||
func verify(_ compactSerialization: String) throws -> Bool { | ||
guard let verifier = Verifier(verifyingAlgorithm: .ES256, publicKey: keyPair.publicKey) else { | ||
throw SecureEnclaveError.couldNotCreateVerifier | ||
} | ||
|
||
let jws = try JWS(compactSerialization: compactSerialization) | ||
|
||
return jws.isValid(for: verifier) | ||
} | ||
|
||
} | ||
|
||
extension SecureEnclave { | ||
typealias KeyPair = (privateKey: SecKey, publicKey: SecKey) | ||
|
||
//Load the private and public keys if already available in secure enclave | ||
static func loadKeyPair(with applicationTag: String) throws -> KeyPair { | ||
let query: [String: Any] = [ | ||
kSecClass as String: kSecClassKey, | ||
kSecAttrApplicationTag as String: applicationTag, | ||
kSecAttrKeyType as String: kSecAttrKeyTypeECSECPrimeRandom, | ||
kSecAttrTokenID as String: kSecAttrTokenIDSecureEnclave, | ||
kSecReturnRef as String: true | ||
] | ||
|
||
var item: CFTypeRef? | ||
let status = SecItemCopyMatching(query as CFDictionary, &item) | ||
guard status == errSecSuccess else { | ||
throw SecureEnclaveError.couldNotLoadKeyPair | ||
} | ||
|
||
let privateKey = item as! SecKey | ||
let publicKey = SecKeyCopyPublicKey(privateKey)! | ||
|
||
return (privateKey, publicKey) | ||
} | ||
|
||
//generate new pair of public and private keys from secure enclave | ||
static func generateKeyPair(with applicationTag: String) throws -> KeyPair { | ||
let access = SecAccessControlCreateWithFlags( | ||
kCFAllocatorDefault, | ||
kSecAttrAccessibleWhenUnlockedThisDeviceOnly, | ||
.privateKeyUsage, | ||
nil | ||
)! | ||
|
||
let attributes: [String: Any] = [ | ||
kSecAttrKeyType as String: kSecAttrKeyTypeECSECPrimeRandom, | ||
kSecAttrKeySizeInBits as String: 256, | ||
kSecAttrTokenID as String: kSecAttrTokenIDSecureEnclave, | ||
kSecPrivateKeyAttrs as String: [ | ||
kSecAttrIsPermanent as String: true, | ||
kSecAttrApplicationTag as String: applicationTag, | ||
kSecAttrAccessControl as String: access | ||
] | ||
] | ||
|
||
var error: Unmanaged<CFError>? | ||
guard let privateKey = SecKeyCreateRandomKey(attributes as CFDictionary, &error) else { | ||
throw SecureEnclaveError.couldNotGenerateKeyPair( | ||
description: error!.takeRetainedValue().localizedDescription | ||
) | ||
} | ||
|
||
let publicKey = SecKeyCopyPublicKey(privateKey)! | ||
|
||
return (privateKey, publicKey) | ||
} | ||
|
||
//create jason web key for the given public key | ||
func getJWK(publicKey:Data) -> [String:Any]?{ | ||
let jwk = try! ECPublicKey(publicKey: publicKey) | ||
if let jsonData = jwk.jsonData(){ | ||
if let jwkDict = convertToDictionary(data: jsonData){ | ||
return jwkDict | ||
} | ||
} | ||
return nil | ||
} | ||
|
||
func convertToDictionary(data: Data) -> [String: Any]? { | ||
|
||
do { | ||
return try JSONSerialization.jsonObject(with: data, options: []) as? [String: Any] | ||
} catch { | ||
print(error.localizedDescription) | ||
} | ||
|
||
return nil | ||
} | ||
} |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Oops, something went wrong.