2022-01-02 00:43:29 +00:00
|
|
|
import Foundation
|
2020-03-24 06:22:22 +00:00
|
|
|
import SecretKit
|
|
|
|
import CryptoKit
|
|
|
|
|
|
|
|
struct Stub {}
|
|
|
|
|
|
|
|
extension Stub {
|
|
|
|
|
2023-12-11 10:13:08 +00:00
|
|
|
public final class Store: SecretStore {
|
2020-03-24 06:22:22 +00:00
|
|
|
|
|
|
|
public let isAvailable = true
|
|
|
|
public let id = UUID()
|
|
|
|
public let name = "Stub"
|
|
|
|
public var secrets: [Secret] = []
|
|
|
|
public var shouldThrow = false
|
|
|
|
|
|
|
|
public init() {
|
|
|
|
// try! create(size: 256)
|
|
|
|
// try! create(size: 384)
|
|
|
|
}
|
|
|
|
|
|
|
|
public func create(size: Int) throws {
|
|
|
|
let flags: SecAccessControlCreateFlags = []
|
|
|
|
let access =
|
|
|
|
SecAccessControlCreateWithFlags(kCFAllocatorDefault,
|
|
|
|
kSecAttrAccessibleWhenUnlockedThisDeviceOnly,
|
|
|
|
flags,
|
|
|
|
nil) as Any
|
|
|
|
|
2023-02-19 01:37:38 +00:00
|
|
|
let attributes = KeychainDictionary([
|
2020-03-24 06:22:22 +00:00
|
|
|
kSecAttrLabel: name,
|
|
|
|
kSecAttrKeyType: kSecAttrKeyTypeECSECPrimeRandom,
|
|
|
|
kSecAttrKeySizeInBits: size,
|
|
|
|
kSecPrivateKeyAttrs: [
|
|
|
|
kSecAttrIsPermanent: true,
|
|
|
|
kSecAttrAccessControl: access
|
|
|
|
]
|
2023-02-19 01:37:38 +00:00
|
|
|
])
|
2020-03-24 06:22:22 +00:00
|
|
|
|
2023-02-19 01:37:38 +00:00
|
|
|
let privateKey = SecKeyCreateRandomKey(attributes, nil)!
|
|
|
|
let publicKey = SecKeyCopyPublicKey(privateKey)!
|
2020-03-24 06:22:22 +00:00
|
|
|
let publicAttributes = SecKeyCopyAttributes(publicKey) as! [CFString: Any]
|
|
|
|
let privateAttributes = SecKeyCopyAttributes(privateKey) as! [CFString: Any]
|
|
|
|
let publicData = (publicAttributes[kSecValueData] as! Data)
|
|
|
|
let privateData = (privateAttributes[kSecValueData] as! Data)
|
|
|
|
let secret = Secret(keySize: size, publicKey: publicData, privateKey: privateData)
|
|
|
|
print(secret)
|
|
|
|
print("Public Key OpenSSH: \(OpenSSHKeyWriter().openSSHString(secret: secret))")
|
|
|
|
}
|
|
|
|
|
2022-02-25 06:59:35 +00:00
|
|
|
public func sign(data: Data, with secret: Secret, for provenance: SigningRequestProvenance) throws -> Data {
|
2020-03-24 06:22:22 +00:00
|
|
|
guard !shouldThrow else {
|
2021-09-23 04:10:04 +00:00
|
|
|
throw NSError(domain: "test", code: 0, userInfo: nil)
|
2020-03-24 06:22:22 +00:00
|
|
|
}
|
2023-02-19 01:37:38 +00:00
|
|
|
let privateKey = SecKeyCreateWithData(secret.privateKey as CFData, KeychainDictionary([
|
2020-03-24 06:22:22 +00:00
|
|
|
kSecAttrKeyType: kSecAttrKeyTypeECSECPrimeRandom,
|
|
|
|
kSecAttrKeySizeInBits: secret.keySize,
|
|
|
|
kSecAttrKeyClass: kSecAttrKeyClassPrivate
|
2023-02-19 01:37:38 +00:00
|
|
|
])
|
2020-03-24 06:22:22 +00:00
|
|
|
, nil)!
|
2023-03-12 01:58:39 +00:00
|
|
|
return SecKeyCreateSignature(privateKey, signatureAlgorithm(for: secret), data as CFData, nil)! as Data
|
2022-02-25 06:59:35 +00:00
|
|
|
}
|
|
|
|
|
2023-03-12 00:21:09 +00:00
|
|
|
public func verify(signature: Data, for data: Data, with secret: Stub.Secret) throws -> Bool {
|
|
|
|
let attributes = KeychainDictionary([
|
|
|
|
kSecAttrKeyType: secret.algorithm.secAttrKeyType,
|
|
|
|
kSecAttrKeySizeInBits: secret.keySize,
|
|
|
|
kSecAttrKeyClass: kSecAttrKeyClassPublic
|
|
|
|
])
|
|
|
|
var verifyError: Unmanaged<CFError>?
|
|
|
|
let untyped: CFTypeRef? = SecKeyCreateWithData(secret.publicKey as CFData, attributes, &verifyError)
|
|
|
|
guard let untypedSafe = untyped else {
|
|
|
|
throw NSError(domain: "test", code: 0, userInfo: nil)
|
|
|
|
}
|
|
|
|
let key = untypedSafe as! SecKey
|
2023-03-12 01:58:39 +00:00
|
|
|
let verified = SecKeyVerifySignature(key, signatureAlgorithm(for: secret), data as CFData, signature as CFData, &verifyError)
|
2023-03-12 00:21:09 +00:00
|
|
|
if let verifyError {
|
|
|
|
if verifyError.takeUnretainedValue() ~= .verifyError {
|
|
|
|
return false
|
|
|
|
} else {
|
|
|
|
throw NSError(domain: "test", code: 0, userInfo: nil)
|
|
|
|
}
|
|
|
|
}
|
|
|
|
return verified
|
|
|
|
}
|
|
|
|
|
2022-02-25 06:59:35 +00:00
|
|
|
public func existingPersistedAuthenticationContext(secret: Stub.Secret) -> PersistedAuthenticationContext? {
|
|
|
|
nil
|
2021-11-08 01:41:59 +00:00
|
|
|
}
|
|
|
|
|
|
|
|
public func persistAuthentication(secret: Stub.Secret, forDuration duration: TimeInterval) throws {
|
2020-03-24 06:22:22 +00:00
|
|
|
}
|
|
|
|
|
2022-12-18 07:16:56 +00:00
|
|
|
public func reloadSecrets() {
|
|
|
|
}
|
|
|
|
|
2020-03-24 06:22:22 +00:00
|
|
|
}
|
|
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
extension Stub {
|
|
|
|
|
|
|
|
struct Secret: SecretKit.Secret, CustomDebugStringConvertible {
|
|
|
|
|
|
|
|
let id = UUID().uuidString.data(using: .utf8)!
|
|
|
|
let name = UUID().uuidString
|
|
|
|
let algorithm = Algorithm.ellipticCurve
|
|
|
|
|
|
|
|
let keySize: Int
|
|
|
|
let publicKey: Data
|
2022-02-25 06:59:35 +00:00
|
|
|
let requiresAuthentication = false
|
2020-03-24 06:22:22 +00:00
|
|
|
let privateKey: Data
|
|
|
|
|
|
|
|
init(keySize: Int, publicKey: Data, privateKey: Data) {
|
|
|
|
self.keySize = keySize
|
|
|
|
self.publicKey = publicKey
|
|
|
|
self.privateKey = privateKey
|
|
|
|
}
|
|
|
|
|
|
|
|
var debugDescription: String {
|
|
|
|
"""
|
|
|
|
Key Size \(keySize)
|
|
|
|
Private: \(privateKey.base64EncodedString())
|
|
|
|
Public: \(publicKey.base64EncodedString())
|
|
|
|
"""
|
|
|
|
}
|
|
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
extension Stub.Store {
|
|
|
|
|
|
|
|
struct StubError: Error {
|
|
|
|
}
|
|
|
|
|
|
|
|
}
|