Skip to content

Commit

Permalink
Merge branch 'main' into gh-pages
Browse files Browse the repository at this point in the history
  • Loading branch information
phisakel committed Sep 9, 2024
2 parents 87dd182 + 373a59f commit 5382ad6
Show file tree
Hide file tree
Showing 9 changed files with 53 additions and 30 deletions.
8 changes: 4 additions & 4 deletions Package.resolved
Original file line number Diff line number Diff line change
Expand Up @@ -5,8 +5,8 @@
"kind" : "remoteSourceControl",
"location" : "https://github.com/eu-digital-identity-wallet/eudi-lib-ios-iso18013-data-model.git",
"state" : {
"revision" : "bf62cc73ae2cea61e98020d2d037c153500207e7",
"version" : "0.2.5"
"revision" : "e604f0f0b67c86c3360f848defe85c9a9939b716",
"version" : "0.3.1"
}
},
{
Expand Down Expand Up @@ -41,8 +41,8 @@
"kind" : "remoteSourceControl",
"location" : "https://github.com/apple/swift-log.git",
"state" : {
"revision" : "e97a6fcb1ab07462881ac165fdbb37f067e205d5",
"version" : "1.5.4"
"revision" : "9cb486020ebf03bfa5b5df985387a14a98744537",
"version" : "1.6.1"
}
},
{
Expand Down
2 changes: 1 addition & 1 deletion Package.swift
Original file line number Diff line number Diff line change
Expand Up @@ -16,7 +16,7 @@ let package = Package(
dependencies: [
.package(url: "https://github.com/apple/swift-docc-plugin", from: "1.0.0"),
.package(url: "https://github.com/apple/swift-log.git", from: "1.5.3"),
.package(url: "https://github.com/eu-digital-identity-wallet/eudi-lib-ios-iso18013-data-model.git", from: "0.2.5"),
.package(url: "https://github.com/eu-digital-identity-wallet/eudi-lib-ios-iso18013-data-model.git", exact: "0.3.1"),
],
targets: [
// Targets are the basic building blocks of a package, defining a module or a test suite.
Expand Down
7 changes: 5 additions & 2 deletions Sources/WalletStorage/Document.swift
Original file line number Diff line number Diff line change
Expand Up @@ -18,8 +18,8 @@ import Foundation
import MdocDataModel18013

/// wallet document structure
public struct Document {
public init(id: String = UUID().uuidString, docType: String, docDataType: DocDataType, data: Data, privateKeyType: PrivateKeyType?, privateKey: Data?, createdAt: Date?, modifiedAt: Date? = nil, status: DocumentStatus) {
public struct Document: DocumentProtocol {
public init(id: String = UUID().uuidString, docType: String, docDataType: DocDataType, data: Data, privateKeyType: PrivateKeyType?, privateKey: Data?, createdAt: Date?, modifiedAt: Date? = nil, displayName: String?, status: DocumentStatus) {
self.id = id
self.docType = docType
self.docDataType = docDataType
Expand All @@ -28,6 +28,7 @@ public struct Document {
self.privateKey = privateKey
self.createdAt = createdAt ?? Date()
self.modifiedAt = modifiedAt
self.displayName = displayName
self.status = status
}

Expand All @@ -39,7 +40,9 @@ public struct Document {
public let privateKey: Data?
public let createdAt: Date
public let modifiedAt: Date?
public let displayName: String?
public let status: DocumentStatus
public var statusDescription: String? { status.rawValue }
public var isDeferred: Bool { status == .deferred }

/// get CBOR data and private key from document
Expand Down
3 changes: 2 additions & 1 deletion Sources/WalletStorage/Enumerations.swift
Original file line number Diff line number Diff line change
Expand Up @@ -49,7 +49,8 @@ public enum PrivateKeyType: String {


/// document status
public enum DocumentStatus: String {
public enum DocumentStatus: String, CaseIterable {
case issued
case deferred
case pending
}
6 changes: 5 additions & 1 deletion Sources/WalletStorage/IssueRequest.swift
Original file line number Diff line number Diff line change
Expand Up @@ -35,6 +35,7 @@ public struct IssueRequest {
self.privateKeyType = privateKeyType
if let keyData {
self.keyData = keyData
// key-data already created, exit
return
}
switch privateKeyType {
Expand All @@ -51,11 +52,14 @@ public struct IssueRequest {
let secureEnclaveKey = try SecureEnclave.P256.KeyAgreement.PrivateKey()
self.keyData = secureEnclaveKey.dataRepresentation
}
logger.info("Created private key of type \(privateKeyType)")
if let docType { logger.info(" and docType: \(docType)") }
}

public func saveToStorage(_ storageService: any DataStorageService, status: DocumentStatus) throws {
// save key data to storage with id
let docKey = Document(id: id, docType: docType ?? "P256", docDataType: .cbor, data: Data(), privateKeyType: privateKeyType, privateKey: keyData, createdAt: Date(), status: status)
logger.info("Saving Issue request with id: \(id) and document status: \(status)")
let docKey = Document(id: id, docType: docType ?? "P256", docDataType: .cbor, data: Data(), privateKeyType: privateKeyType, privateKey: keyData, createdAt: Date(), displayName: nil, status: status)
try storageService.saveDocument(docKey, allowOverwrite: true)
}

Expand Down
37 changes: 24 additions & 13 deletions Sources/WalletStorage/KeyChainStorageService.swift
Original file line number Diff line number Diff line change
Expand Up @@ -32,10 +32,12 @@ public class KeyChainStorageService: DataStorageService {
/// - Parameter id: Document identifier
/// - Returns: The document if exists
public func loadDocument(id: String, status: DocumentStatus) throws -> Document? {
try loadDocuments(id: id, status: status)?.first
logger.info("Load document with status: \(status), id: \(id)")
return try loadDocuments(id: id, status: status)?.first
}
public func loadDocuments(status: DocumentStatus) throws -> [Document]? {
try loadDocuments(id: nil, status: status)
logger.info("Load documents with status: \(status)")
return try loadDocuments(id: nil, status: status)
}
// use is-negative to denote type of data
static func isDocumentDataRow(_ d: [String: Any]) -> Bool { if let b = d[kSecAttrIsNegative as String] as? Bool { !b } else { true } }
Expand All @@ -52,23 +54,20 @@ public class KeyChainStorageService: DataStorageService {
return documents
}

func loadDocumentsData(id: String?, docStatus: DocumentStatus, dataToLoadType: SavedKeyChainDataType = .doc, bCompatOldVersion: Bool = false) throws -> [[String: Any]]? {
var query = makeQuery(id: id, bForSave: false, status: docStatus, dataType: dataToLoadType)
if bCompatOldVersion { query[kSecAttrService as String] = if dataToLoadType == .doc { serviceName } else { serviceName + "_key" } } // to be removed in version 1
func loadDocumentsData(id: String?, docStatus: DocumentStatus, dataToLoadType: SavedKeyChainDataType = .doc) throws -> [[String: Any]]? {
let query = makeQuery(id: id, bForSave: false, status: docStatus, dataType: dataToLoadType)
var result: CFTypeRef?
let status = SecItemCopyMatching(query as CFDictionary, &result)
if status == errSecItemNotFound { return nil }
let statusMessage = SecCopyErrorMessageString(status, nil) as? String
guard status == errSecSuccess else {
logger.error("Error code: \(Int(status)), description: \(statusMessage ?? "")")
throw StorageError(description: statusMessage ?? "", code: Int(status))
}
var res = result as! [[String: Any]]
if !bCompatOldVersion, dataToLoadType == .doc {
if let dicts2 = try loadDocumentsData(id: id, docStatus: docStatus, dataToLoadType: .key, bCompatOldVersion: bCompatOldVersion) { res.append(contentsOf: dicts2) }
if dataToLoadType == .doc {
if let dicts2 = try loadDocumentsData(id: id, docStatus: docStatus, dataToLoadType: .key) { res.append(contentsOf: dicts2) }
}
// following lines to be removed in version 1
if !bCompatOldVersion, dataToLoadType == .doc { if let dicts1 = try loadDocumentsData(id: id, docStatus: docStatus, dataToLoadType: .doc, bCompatOldVersion: true) { res.append(contentsOf: dicts1) } }
if !bCompatOldVersion, dataToLoadType == .key { if let dicts2 = try loadDocumentsData(id: id, docStatus: docStatus, dataToLoadType: .key, bCompatOldVersion: true) {dicts2.forEach { d in var d2 = d; d2[kSecAttrIsNegative as String] = true; res.append(d2) } } }
return res
}

Expand Down Expand Up @@ -107,6 +106,7 @@ public class KeyChainStorageService: DataStorageService {
public func saveDocumentData(_ document: Document, dataToSaveType: SavedKeyChainDataType, dataType: String, allowOverwrite: Bool = true) throws {
// kSecAttrAccount is used to store the secret Id (we save the document ID)
// kSecAttrService is a key whose value is a string indicating the item's service.
logger.info("Save document for status: \(document.status), id: \(document.id), docType: \(document.docType), displayName: \(document.displayName ?? "")")
guard dataType.count == 4 else { throw StorageError(description: "Invalid type") }
if dataToSaveType == .key && document.privateKey == nil { throw StorageError(description: "Private key not available") }
var query: [String: Any] = makeQuery(id: document.id, bForSave: true, status: document.status, dataType: dataToSaveType)
Expand All @@ -117,15 +117,18 @@ public class KeyChainStorageService: DataStorageService {
// use this attribute to differentiate between document and key data
query[kSecAttrIsNegative as String] = Self.getIsNegativeValueToUse(dataToSaveType)
query[kSecAttrLabel as String] = document.docType
if let dn = document.displayName { query[kSecAttrDescription as String] = dn }
query[kSecAttrType as String] = dataType
var status = SecItemAdd(query as CFDictionary, nil)
if allowOverwrite && status == errSecDuplicateItem {
let updated: [String: Any] = [kSecValueData: query[kSecValueData as String] as! Data, kSecAttrIsNegative: Self.getIsNegativeValueToUse(dataToSaveType), kSecAttrLabel: document.docType, kSecAttrType: dataType] as [String: Any]
var updated: [String: Any] = [kSecValueData: query[kSecValueData as String] as! Data, kSecAttrIsNegative: Self.getIsNegativeValueToUse(dataToSaveType), kSecAttrLabel: document.docType, kSecAttrDescription: document.displayName ?? "", kSecAttrType: dataType] as [String: Any]
if let dn = document.displayName { updated[kSecAttrDescription as String] = dn }
query = makeQuery(id: document.id, bForSave: true, status: document.status, dataType: dataToSaveType)
status = SecItemUpdate(query as CFDictionary, updated as CFDictionary)
}
let statusMessage = SecCopyErrorMessageString(status, nil) as? String
guard status == errSecSuccess else {
logger.error("Error code: \(Int(status)), description: \(statusMessage ?? "")")
throw StorageError(description: statusMessage ?? "", code: Int(status))
}
}
Expand All @@ -135,21 +138,29 @@ public class KeyChainStorageService: DataStorageService {
/// - Parameters:
/// - id: The Id of the secret
public func deleteDocument(id: String, status: DocumentStatus) throws {
logger.info("Delete document with status: \(status), id: \(id)")
try deleteDocumentData(id: id, docStatus: status)
}

public func deleteDocumentData(id: String?, docStatus: DocumentStatus, dataType: SavedKeyChainDataType = .doc) throws {
let query: [String: Any] = makeQuery(id: id, bForSave: true, status: docStatus, dataType: dataType)
let status = SecItemDelete(query as CFDictionary)
let statusMessage = SecCopyErrorMessageString(status, nil) as? String
guard status == errSecSuccess else { throw StorageError(description: statusMessage ?? "", code: Int(status)) }
if status == errSecItemNotFound, id == nil {
let msg = statusMessage ?? "No items found"
logger.warning("\(msg)")
} else if status != errSecSuccess {
logger.error("Error code: \(Int(status)), description: \(statusMessage ?? "")")
throw StorageError(description: statusMessage ?? "", code: Int(status))
}
if dataType == .doc { try deleteDocumentData(id: id, docStatus: docStatus, dataType: .key) }
}

/// Delete all documents from keychain
/// - Parameters:
/// - id: The Id of the secret
public func deleteDocuments(status: DocumentStatus) throws {
logger.info("Delete documents with status: \(status)")
try deleteDocumentData(id: nil, docStatus: status)
}

Expand All @@ -164,6 +175,6 @@ public class KeyChainStorageService: DataStorageService {
keyType = PrivateKeyType(rawValue: dict2[kSecAttrType as String] as? String ?? PrivateKeyType.derEncodedP256.rawValue)!
privateKeyData = (dict2[kSecValueData as String] as! Data)
}
return Document(id: dict1[kSecAttrAccount as String] as! String, docType: dict1[kSecAttrLabel as String] as? String ?? "", docDataType: DocDataType(rawValue: dict1[kSecAttrType as String] as? String ?? DocDataType.cbor.rawValue) ?? DocDataType.cbor, data: data, privateKeyType: keyType, privateKey: privateKeyData, createdAt: (dict1[kSecAttrCreationDate as String] as! Date), modifiedAt: dict1[kSecAttrModificationDate as String] as? Date, status: status)
return Document(id: dict1[kSecAttrAccount as String] as! String, docType: dict1[kSecAttrLabel as String] as? String ?? "", docDataType: DocDataType(rawValue: dict1[kSecAttrType as String] as? String ?? DocDataType.cbor.rawValue) ?? DocDataType.cbor, data: data, privateKeyType: keyType, privateKey: privateKeyData, createdAt: (dict1[kSecAttrCreationDate as String] as! Date), modifiedAt: dict1[kSecAttrModificationDate as String] as? Date, displayName: dict1[kSecAttrDescription as String] as? String, status: status)
}
}
Loading

0 comments on commit 5382ad6

Please sign in to comment.