From 2034db589a380e4943891310c1a0340594447fe4 Mon Sep 17 00:00:00 2001 From: Hatter Jiang Date: Sat, 9 Dec 2023 11:43:21 +0800 Subject: [PATCH] feat: secure enclave is on going --- .gitignore | 1 + build.rs | 2 +- src/cmd_version.rs | 2 +- src/util_keychainkey.rs | 13 +++++- swift-lib/Package.swift | 2 +- swift-lib/src/lib.swift | 90 ++++++++++++++++++++++++++++++++++++++++- 6 files changed, 104 insertions(+), 6 deletions(-) diff --git a/.gitignore b/.gitignore index 030576c..d862f80 100644 --- a/.gitignore +++ b/.gitignore @@ -1,3 +1,4 @@ +.swiftpm/ .build/ _tinyencrypt_config-rs.json *.tinyenc diff --git a/build.rs b/build.rs index 05d9a0e..0a53bcf 100644 --- a/build.rs +++ b/build.rs @@ -4,7 +4,7 @@ use swift_rs::SwiftLinker; fn main() { // Ensure this matches the versions set in your `Package.swift` file. #[cfg(feature = "secure-enclave")] - SwiftLinker::new("10.15") + SwiftLinker::new("11") .with_ios("11") .with_package("swift-lib", "./swift-lib/") .link(); diff --git a/src/cmd_version.rs b/src/cmd_version.rs index 5644966..353e73a 100644 --- a/src/cmd_version.rs +++ b/src/cmd_version.rs @@ -15,7 +15,7 @@ pub fn version(_cmd_version: CmdVersion) -> XResult<()> { #[cfg(feature = "macos")] features.push("macos".to_string()); #[cfg(feature = "secure-enclave")] - features.push(format!("secure-enclave{}", iff!(util_keychainkey::is_support_secure_enclave(), "*", ""))); + features.push(format!("secure-enclave{}", iff!(util_keychainkey::is_support_se(), "*", ""))); if features.is_empty() { features.push("-".to_string()); } println!( "User-Agent: {} [with features: {}]\n{}", diff --git a/src/util_keychainkey.rs b/src/util_keychainkey.rs index fd6ec67..44f145c 100644 --- a/src/util_keychainkey.rs +++ b/src/util_keychainkey.rs @@ -1,3 +1,12 @@ -pub fn is_support_secure_enclave() -> bool { - false +use swift_rs::{Bool, SRString}; +use swift_rs::swift; + +swift!(fn is_support_secure_enclave() -> Bool); +swift!(fn print_greeting(name: SRString) -> Bool); + +pub fn is_support_se() -> bool { + unsafe { + print_greeting(SRString::from("hatter")); + } + unsafe { is_support_secure_enclave() } } diff --git a/swift-lib/Package.swift b/swift-lib/Package.swift index ab35b13..997dad6 100644 --- a/swift-lib/Package.swift +++ b/swift-lib/Package.swift @@ -6,7 +6,7 @@ import PackageDescription let package = Package( name: "swift-lib", platforms: [ - .macOS(.v10_15), // macOS Catalina. Earliest version that is officially supported by Apple. + .macOS(.v11), // macOS Catalina. Earliest version that is officially supported by Apple. ], products: [ // Products define the executables and libraries a package produces, and make them visible to other packages. diff --git a/swift-lib/src/lib.swift b/swift-lib/src/lib.swift index baaa3bc..f82f111 100644 --- a/swift-lib/src/lib.swift +++ b/swift-lib/src/lib.swift @@ -1,5 +1,93 @@ import SwiftRs import AppKit +import CryptoKit +import LocalAuthentication + +// reference: +// https://zenn.dev/iceman/scraps/380f69137c7ea2 +// https://www.andyibanez.com/posts/cryptokit-secure-enclave/ +@_cdecl("is_support_secure_enclave") +func isSupportSecureEnclave() -> Bool { + // TODO pending delete + let epub = "MFkwEwYHKoZIzj0CAQYIKoZIzj0DAQcDQgAE76jmqKrSs8tIVcvYYLpCA2za9GG7VxLdaI8FqynT+G65QgakCjT/P2ey7plz4KEl6ffORfZtZXO+lq2qQaaBHw==" + + guard let ephemeralPublicKeyRepresentation = Data( + base64Encoded: epub + ) else { + print("err:ephemeral public key base64 decode failed") + return false + } + do { + let a = try CryptoKit.P256.KeyAgreement.PublicKey.init(derRepresentation: ephemeralPublicKeyRepresentation) + print("\(a)") + } catch { + print("error: \(error)") + } + return SecureEnclave.isAvailable +} + +// TODO delete print_greeting +@_cdecl("print_greeting") +func printGreeting(name: SRString) { + print("Hello \(name.toString())!") +} + +@_cdecl("generate_secure_enclave_p256_keypair") +func generateSecureEnclaveP256KeyPair() -> SRString { + var error: Unmanaged? = nil; + guard let accessCtrl = SecAccessControlCreateWithFlags( + nil, + kSecAttrAccessibleWhenUnlockedThisDeviceOnly, + [.privateKeyUsage, .biometryCurrentSet], + &error + ) else { + return SRString("err:\(error.debugDescription)") + } + do { + let privateKeyReference = try CryptoKit.SecureEnclave.P256.KeyAgreement.PrivateKey.init( + accessControl: accessCtrl + ); + let dataRepresentation = privateKeyReference.dataRepresentation; + print("Private key reference: \(privateKeyReference)"); + print("Private key reference - publicKey: \(privateKeyReference.publicKey)"); + print("Private key reference - dataRepresentation: \(privateKeyReference.dataRepresentation)"); + print("Private key reference - dataRepresentation: \(privateKeyReference.dataRepresentation.base64EncodedString())"); + return SRString("") + } catch { + return SRString("err:\(error)") + } +} + +func computeSecureEnclaveP256Ecdh(privateKeyDataRepresentation: SRString, ephemeraPublicKey: SRString) -> SRString { + guard let dataRepresentation = Data( + base64Encoded: privateKeyDataRepresentation.toString() + ) else { + return SRString("err:private key base64 decode failed") + } + guard let ephemeralPublicKeyRepresentation = Data( + base64Encoded: ephemeraPublicKey.toString() + ) else { + return SRString("err:ephemeral public key base64 decode failed") + } + do { + let context = LAContext(); + let p = try SecureEnclave.P256.KeyAgreement.PrivateKey( + dataRepresentation: dataRepresentation, + authenticationContext: context + ) + + let ephemeralPublicKey = try CryptoKit.P256.KeyAgreement.PublicKey.init(derRepresentation: ephemeralPublicKeyRepresentation) + + let sharedSecret = try p.sharedSecretFromKeyAgreement( + with: ephemeralPublicKey) + print("Shared secret: \(sharedSecret)") + + return SRString("ok:\(sharedSecret.description)") + } catch { + return SRString("err:\(error)") + } +} + @_cdecl("get_file_thumbnail_base64") func getFileThumbnailBase64(path: SRString) -> SRString { @@ -91,4 +179,4 @@ func returnNullable(null: Bool) -> Test? { if (null == true) { return nil } return Test(null) -} \ No newline at end of file +}