Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
58 changes: 58 additions & 0 deletions account-kit/rn-signer/ios/implementation/KeychainHelper.swift
Original file line number Diff line number Diff line change
@@ -0,0 +1,58 @@
import Foundation
import Security
import CryptoKit

let ACCOUNT_KIT_EPHEMERAL_PRIVATE_KEY = "accountKitEphemeralPrivateKey"

let save_ephemeral_private_key_query: [String: Any] = [
kSecClass as String: kSecClassGenericPassword,
kSecAttrAccount as String: ACCOUNT_KIT_EPHEMERAL_PRIVATE_KEY,
kSecAttrAccessible as String: kSecAttrAccessibleAfterFirstUnlock
]

let get_ephemeral_private_key_query: [String: Any] = [
kSecClass as String: kSecClassGenericPassword,
kSecAttrAccount as String: ACCOUNT_KIT_EPHEMERAL_PRIVATE_KEY,
kSecReturnData as String: true,
kSecMatchLimit as String: kSecMatchLimitOne
]

let delete_ephemeral_private_key_query: [String: Any] = [
kSecClass as String: kSecClassGenericPassword,
kSecAttrAccount as String: ACCOUNT_KIT_EPHEMERAL_PRIVATE_KEY
]


struct PrivateKeyChainUtilities {
@available(*, unavailable) private init() {}

static func savePrivateKeyToKeychain(_ privateKey: P256.KeyAgreement.PrivateKey) {
let keyData = privateKey.rawRepresentation

var _saveEphemeralPrivateKeyQuery = save_ephemeral_private_key_query
_saveEphemeralPrivateKeyQuery.updateValue(keyData, forKey: kSecValueData as String)

SecItemDelete(_saveEphemeralPrivateKeyQuery as CFDictionary)
SecItemAdd(_saveEphemeralPrivateKeyQuery as CFDictionary, nil)
}

static func getPrivateKeyFromKeychain() -> P256.KeyAgreement.PrivateKey? {
var result: AnyObject?
let status = SecItemCopyMatching(get_ephemeral_private_key_query as CFDictionary, &result)

if status == errSecSuccess, let keyData = result as? Data {
do {
return try P256.KeyAgreement.PrivateKey(rawRepresentation: keyData)
} catch {
print("❌ Failed to decode private key: \(error)")
return nil
}
}

return nil
}

static func deletePrivateKeyFromKeychain() {
SecItemDelete(delete_ephemeral_private_key_query as CFDictionary)
}
}
Original file line number Diff line number Diff line change
@@ -1,3 +1,4 @@

import CryptoKit
import Foundation

Expand All @@ -11,22 +12,28 @@ enum StamperError: Error {
@objc public class NativeTEKStamperImpl: NSObject {
// TODO: we probably want to keep this longer term somewhere, because the RN session manager will
// hold on to the bundle and try to recreate a session if a user is still logged in
var ephemeralPrivateKey: P256.KeyAgreement.PrivateKey? = nil;
var ephemeralPrivateKey: P256.KeyAgreement.PrivateKey? = nil

// These can be ephemeral and held in memory because the session manager will handle re-authenticating
var apiPublicKey: P256.Signing.PublicKey? = nil;
var apiPrivateKey: P256.Signing.PrivateKey? = nil;

@objc public func create() async throws -> NSString {
if (ephemeralPrivateKey == nil) {
ephemeralPrivateKey = P256.KeyAgreement.PrivateKey()
}
let targetPublicKey = try ephemeralPrivateKey!.publicKey.toString(representation: .x963)
var _ephemeralPrivateKey: P256.KeyAgreement.PrivateKey? = PrivateKeyChainUtilities.getPrivateKeyFromKeychain()

if (_ephemeralPrivateKey == nil) {
_ephemeralPrivateKey = P256.KeyAgreement.PrivateKey()
PrivateKeyChainUtilities.savePrivateKeyToKeychain(_ephemeralPrivateKey!)
}

let targetPublicKey = try _ephemeralPrivateKey!.publicKey.toString(representation: .x963)
ephemeralPrivateKey = _ephemeralPrivateKey

return NSString(string: targetPublicKey)
}

@objc public func clear() {
PrivateKeyChainUtilities.deletePrivateKeyFromKeychain()
ephemeralPrivateKey = nil
apiPublicKey = nil
apiPrivateKey = nil
Expand All @@ -41,18 +48,17 @@ enum StamperError: Error {
}
}

@objc public func injectCredentialBundle(bundle: NSString) async throws -> ObjCBool {
if let ephemeralPrivateKey = ephemeralPrivateKey {
let (bundlePrivateKey, bundlePublicKey) = try AuthManager.decryptBundle(encryptedBundle: bundle as String, ephemeralPrivateKey: ephemeralPrivateKey)
@objc public func injectCredentialBundle(bundle: NSString) async throws -> ObjCBool {
if let ephemeralPrivateKey = ephemeralPrivateKey {
let (bundlePrivateKey, bundlePublicKey) = try AuthManager.decryptBundle(encryptedBundle: bundle as String, ephemeralPrivateKey: ephemeralPrivateKey)
apiPublicKey = bundlePublicKey
apiPrivateKey = bundlePrivateKey

apiPublicKey = bundlePublicKey
apiPrivateKey = bundlePrivateKey

return true;
} else {
throw StamperError.notInitialized
}
}
return true
} else {
throw StamperError.notInitialized
}
}

// TODO: we should use the turnkey stamper for all of this, but we need it published as a pod
// and it shouldn't require use_frameworks!
Expand All @@ -70,7 +76,6 @@ enum StamperError: Error {

let signatureHex = signature.derRepresentation.toHexString()

print(apiPublicKey.compressedRepresentation.toHexString())
let stamp: [String: Any] = [
"publicKey": apiPublicKey.compressedRepresentation.toHexString(),
"scheme": "SIGNATURE_SCHEME_TK_API_P256",
Expand Down