This commit is contained in:
Sydney Morton 2022-04-13 12:53:50 -07:00
Родитель feb7fe356d
Коммит 38e4423770
8 изменённых файлов: 97 добавлений и 46 удалений

Просмотреть файл

@ -6,6 +6,7 @@
import Foundation
public enum KeychainStoreError: Error {
case deleteFromStoreError(status: OSStatus)
case saveToStoreError(status: Int32)
case itemNotFound
case readFromStoreError(status: OSStatus)
@ -58,8 +59,9 @@ struct KeychainSecretStore : SecretStoring {
/// - Parameters:
/// - id: The Id of the secret
/// - itemTypeCode: The secret type code (4 chars)
/// - accessGroup: The access group to use to save secret.
/// - value: The value of the secret
func saveSecret(id: UUID, itemTypeCode: String, accessGroup: String? = nil, value: inout Data) throws {
func saveSecret(id: UUID, itemTypeCode: String, accessGroup: String? = nil, value: inout Data) throws {
defer {
let secretSize = value.count
value.withUnsafeMutableBytes { (secretPtr) in
@ -89,4 +91,42 @@ struct KeychainSecretStore : SecretStoring {
guard status == errSecSuccess else {
throw KeychainStoreError.saveToStoreError(status: status)}
}
/// Delete the secret from keychain
/// Note: the value passed in will be zeroed out after the secret is deleted
/// - Parameters:
/// - id: The Id of the secret
/// - itemTypeCode: The secret type code (4 chars)
/// - accessGroup: The access group of the secret.
/// - value: The value of the secret
func deleteSecret(id: UUID, itemTypeCode: String, accessGroup: String? = nil, value: inout Data) throws {
defer {
let secretSize = value.count
value.withUnsafeMutableBytes { (secretPtr) in
memset_s(secretPtr.baseAddress, secretSize, 0, secretSize)
return
}
}
guard itemTypeCode.count == 4 else { throw KeychainStoreError.invalidType }
// kSecAttrAccount is used to store the secret Id so that we can look it up later
// kSecAttrService is always set to vcService to enable us to lookup all our secrets later if needed
// kSecAttrType is used to store the secret type to allow us to cast it to the right Type on search
var query = [kSecClass: kSecClassGenericPassword,
kSecAttrAccount: id.uuidString,
kSecAttrService: vcService,
kSecAttrType: itemTypeCode,
kSecAttrAccessible: kSecAttrAccessibleWhenUnlockedThisDeviceOnly,
kSecValueData: value] as [String: Any]
if let accessGroup = accessGroup,
accessGroup != "" {
query[kSecAttrAccessGroup as String] = accessGroup
}
let status = SecItemDelete(query as CFDictionary)
guard status == errSecSuccess else {
throw KeychainStoreError.deleteFromStoreError(status: status)}
}
}

Просмотреть файл

@ -71,12 +71,37 @@ final class Random32BytesSecret: Secret {
}
}
func updateAccessGroup() throws {
var value = try self.store.getSecret(id: id, itemTypeCode: Random32BytesSecret.itemTypeCode, accessGroup: nil)
func migrateKey(fromAccessGroup oldAccessGroup: String?) throws {
/// If old access group is equal to new access group, no action required.
if oldAccessGroup == accessGroup
{
return
}
var value = try self.store.getSecret(id: id,
itemTypeCode: Random32BytesSecret.itemTypeCode,
accessGroup: oldAccessGroup)
defer {
let secretSize = value.count
value.withUnsafeMutableBytes { (secretPtr) in
memset_s(secretPtr.baseAddress, secretSize, 0, secretSize)
return
}
}
/// Save to new access group
try self.store.saveSecret(id: id,
itemTypeCode: Random32BytesSecret.itemTypeCode,
accessGroup: accessGroup,
value: &value)
/// Delete from old access group
try self.store.deleteSecret(id: id,
itemTypeCode: Random32BytesSecret.itemTypeCode,
accessGroup: oldAccessGroup,
value: &value)
}
}

Просмотреть файл

@ -7,14 +7,14 @@ import Foundation
internal protocol Secret: VCCryptoSecret & InternalSecret {}
public protocol VCCryptoSecret{
public protocol VCCryptoSecret {
/// The secret id
var id:UUID { get }
func isValidKey() -> Bool
func updateAccessGroup() throws
func migrateKey(fromAccessGroup oldAccessGroup: String?) throws
}
protocol InternalSecret {

Просмотреть файл

@ -10,4 +10,5 @@ public protocol SecretStoring {
func getSecret(id: UUID, itemTypeCode: String, accessGroup: String?) throws -> Data
func saveSecret(id: UUID, itemTypeCode: String, accessGroup: String?, value: inout Data) throws
func deleteSecret(id: UUID, itemTypeCode: String, accessGroup: String?, value: inout Data) throws
}

Просмотреть файл

@ -34,14 +34,18 @@ public struct KeyContainer {
return keyReference.isValidKey()
}
public func updateAccessGroup() throws {
public func migrateKey(fromAccessGroup oldAccessGroup: String?) throws {
do {
try keyReference.updateAccessGroup()
} catch
try keyReference.migrateKey(fromAccessGroup: oldAccessGroup)
}
catch
{
if case KeychainStoreError.itemNotFound = error {
if case KeychainStoreError.itemNotFound = error
{
throw KeyContainerError.noSigningKeyFoundInStorage
} else {
}
else
{
throw error
}
}

Просмотреть файл

@ -53,15 +53,17 @@ public class IdentifierService {
}
/// updates access group for keys if it needs to be updated.
public func updateAccessGroupForKeys(for identifier: Identifier) throws {
try identifier.recoveryKey.updateAccessGroup()
try identifier.updateKey.updateAccessGroup()
public func migrateKeys(fromAccessGroup oldAccessGroup: String?) throws {
let identifier = try fetchMasterIdentifier()
try identifier.recoveryKey.migrateKey(fromAccessGroup: oldAccessGroup)
try identifier.updateKey.migrateKey(fromAccessGroup: oldAccessGroup)
try identifier.didDocumentKeys.forEach { keyContainer in
try keyContainer.updateAccessGroup()
try keyContainer.migrateKey(fromAccessGroup: oldAccessGroup)
}
}
public func areKeysValid(for identifier: Identifier) -> Bool {
public func areKeysValid() throws -> Bool {
let identifier = try fetchMasterIdentifier()
return identifier.recoveryKey.isValidKey() &&
identifier.updateKey.isValidKey() &&
(identifier.didDocumentKeys.first?.isValidKey() ?? false)

Просмотреть файл

@ -7,11 +7,8 @@ import VCEntities
public enum VCSDKInitStatus
{
case firstTimeMasterIdentifierCreated
case newMasterIdentifierCreated
case accessGroupForKeysUpdated
case success
case error(error: Error)
}
public class VerifiableCredentialSDK {
@ -22,11 +19,10 @@ public class VerifiableCredentialSDK {
/// Returns: TRUE, if needed to create Master Identifier
/// FALSE, if Master Identifier is able to be fetched (included private keys from KeyStore)
public static func initialize(logConsumer: VCLogConsumer = DefaultVCLogConsumer(),
accessGroupIdentifier: String? = nil) -> VCSDKInitStatus {
accessGroupIdentifier: String? = nil) -> Result<VCSDKInitStatus, Error> {
VCSDKLog.sharedInstance.add(consumer: logConsumer)
/// Get access group identifier for app.
if let accessGroupIdentifier = accessGroupIdentifier {
VCSDKConfiguration.sharedInstance.setAccessGroupIdentifier(with: accessGroupIdentifier)
@ -42,38 +38,21 @@ public class VerifiableCredentialSDK {
/// If unable to fetch master identifier, create a new one.
VCSDKLog.sharedInstance.logWarning(message: "Failed to fetch master identifier with: \(String(describing: error))")
return createNewIdentifier(status: .firstTimeMasterIdentifierCreated)
return createNewIdentifier()
}
/// Make sure keys are valid for master identifier, if they are not valid, update access group.
do {
if !identifierService.areKeysValid(for: identifier) {
try identifierService.updateAccessGroupForKeys(for: identifier)
return .accessGroupForKeysUpdated
}
} catch {
/// if keys are not found in storage, create a new master identifier.
if case KeyContainerError.noSigningKeyFoundInStorage = error
{
VCSDKLog.sharedInstance.logWarning(message: "Failed find signing keys in storage with: \(String(describing: error))")
return createNewIdentifier(status: .newMasterIdentifierCreated)
}
return .error(error: error)
}
/// VC sdk initialization successful.
return .success
return .success(.success)
}
private static func createNewIdentifier(status: VCSDKInitStatus) -> VCSDKInitStatus {
private static func createNewIdentifier() -> Result<VCSDKInitStatus, Error> {
do {
_ = try identifierService.createAndSaveIdentifier(forId: VCEntitiesConstants.MASTER_ID, andRelyingParty: VCEntitiesConstants.MASTER_ID)
return status
_ = try identifierService.createAndSaveIdentifier(forId: VCEntitiesConstants.MASTER_ID,
andRelyingParty: VCEntitiesConstants.MASTER_ID)
return .success(.newMasterIdentifierCreated)
} catch {
return .error(error: error)
return .failure(error)
}
}
}

Просмотреть файл

@ -6,7 +6,7 @@
import VCCrypto
public struct KeyId: VCCryptoSecret {
public let id: UUID
public init(id: UUID) {
@ -17,5 +17,5 @@ public struct KeyId: VCCryptoSecret {
return true
}
public func updateAccessGroup() throws { }
public func migrateKey(fromAccessGroup oldAccessGroup: String?) throws { }
}