Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Refactor key storage and issue request handling #25

Merged
merged 15 commits into from
Nov 28, 2024
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
2 changes: 0 additions & 2 deletions .devcontainer/devcontainer.json
Original file line number Diff line number Diff line change
Expand Up @@ -5,8 +5,6 @@
"ghcr.io/devcontainers/features/common-utils:2": {
"installZsh": "false",
"username": "vscode",
"userUid": "1000",
"userGid": "1000",
"upgradePackages": "false"
},
"ghcr.io/devcontainers/features/git:1": {
Expand Down
9 changes: 6 additions & 3 deletions .github/workflows/swift.yml
Original file line number Diff line number Diff line change
Expand Up @@ -2,9 +2,12 @@
# For more information see: https://docs.github.com/en/actions/automating-builds-and-tests/building-and-testing-swift

name: Swift

on: [push]

on:
pull_request:
types: [opened, reopened]
push:
branches: ['main']
tags: [ v* ]
jobs:
build:

Expand Down
79 changes: 79 additions & 0 deletions .swiftpm/xcode/xcshareddata/xcschemes/WalletStorage.xcscheme
Original file line number Diff line number Diff line change
@@ -0,0 +1,79 @@
<?xml version="1.0" encoding="UTF-8"?>
<Scheme
LastUpgradeVersion = "1610"
version = "1.7">
<BuildAction
parallelizeBuildables = "YES"
buildImplicitDependencies = "YES"
buildArchitectures = "Automatic">
<BuildActionEntries>
<BuildActionEntry
buildForTesting = "YES"
buildForRunning = "YES"
buildForProfiling = "YES"
buildForArchiving = "YES"
buildForAnalyzing = "YES">
<BuildableReference
BuildableIdentifier = "primary"
BlueprintIdentifier = "WalletStorage"
BuildableName = "WalletStorage"
BlueprintName = "WalletStorage"
ReferencedContainer = "container:">
</BuildableReference>
</BuildActionEntry>
</BuildActionEntries>
</BuildAction>
<TestAction
buildConfiguration = "Debug"
selectedDebuggerIdentifier = "Xcode.DebuggerFoundation.Debugger.LLDB"
selectedLauncherIdentifier = "Xcode.DebuggerFoundation.Launcher.LLDB"
shouldUseLaunchSchemeArgsEnv = "YES"
shouldAutocreateTestPlan = "YES">
<Testables>
<TestableReference
skipped = "NO">
<BuildableReference
BuildableIdentifier = "primary"
BlueprintIdentifier = "WalletStorageTests"
BuildableName = "WalletStorageTests"
BlueprintName = "WalletStorageTests"
ReferencedContainer = "container:">
</BuildableReference>
</TestableReference>
</Testables>
</TestAction>
<LaunchAction
buildConfiguration = "Debug"
selectedDebuggerIdentifier = "Xcode.DebuggerFoundation.Debugger.LLDB"
selectedLauncherIdentifier = "Xcode.DebuggerFoundation.Launcher.LLDB"
launchStyle = "0"
useCustomWorkingDirectory = "NO"
ignoresPersistentStateOnLaunch = "NO"
debugDocumentVersioning = "YES"
debugServiceExtension = "internal"
allowLocationSimulation = "YES">
</LaunchAction>
<ProfileAction
buildConfiguration = "Release"
shouldUseLaunchSchemeArgsEnv = "YES"
savedToolIdentifier = ""
useCustomWorkingDirectory = "NO"
debugDocumentVersioning = "YES">
<MacroExpansion>
<BuildableReference
BuildableIdentifier = "primary"
BlueprintIdentifier = "WalletStorage"
BuildableName = "WalletStorage"
BlueprintName = "WalletStorage"
ReferencedContainer = "container:">
</BuildableReference>
</MacroExpansion>
</ProfileAction>
<AnalyzeAction
buildConfiguration = "Debug">
</AnalyzeAction>
<ArchiveAction
buildConfiguration = "Release"
revealArchiveInOrganizer = "YES">
</ArchiveAction>
</Scheme>
Original file line number Diff line number Diff line change
@@ -0,0 +1,54 @@
<?xml version="1.0" encoding="UTF-8"?>
<Scheme
LastUpgradeVersion = "1610"
version = "1.7">
<BuildAction
parallelizeBuildables = "YES"
buildImplicitDependencies = "YES"
buildArchitectures = "Automatic">
</BuildAction>
<TestAction
buildConfiguration = "Debug"
selectedDebuggerIdentifier = "Xcode.DebuggerFoundation.Debugger.LLDB"
selectedLauncherIdentifier = "Xcode.DebuggerFoundation.Launcher.LLDB"
shouldUseLaunchSchemeArgsEnv = "YES"
shouldAutocreateTestPlan = "YES">
<Testables>
<TestableReference
skipped = "NO">
<BuildableReference
BuildableIdentifier = "primary"
BlueprintIdentifier = "WalletStorageTests"
BuildableName = "WalletStorageTests"
BlueprintName = "WalletStorageTests"
ReferencedContainer = "container:">
</BuildableReference>
</TestableReference>
</Testables>
</TestAction>
<LaunchAction
buildConfiguration = "Debug"
selectedDebuggerIdentifier = "Xcode.DebuggerFoundation.Debugger.LLDB"
selectedLauncherIdentifier = "Xcode.DebuggerFoundation.Launcher.LLDB"
launchStyle = "0"
useCustomWorkingDirectory = "NO"
ignoresPersistentStateOnLaunch = "NO"
debugDocumentVersioning = "YES"
debugServiceExtension = "internal"
allowLocationSimulation = "YES">
</LaunchAction>
<ProfileAction
buildConfiguration = "Release"
shouldUseLaunchSchemeArgsEnv = "YES"
savedToolIdentifier = ""
useCustomWorkingDirectory = "NO"
debugDocumentVersioning = "YES">
</ProfileAction>
<AnalyzeAction
buildConfiguration = "Debug">
</AnalyzeAction>
<ArchiveAction
buildConfiguration = "Release"
revealArchiveInOrganizer = "YES">
</ArchiveAction>
</Scheme>
6 changes: 3 additions & 3 deletions Package.resolved
Original file line number Diff line number Diff line change
@@ -1,13 +1,13 @@
{
"originHash" : "5899d8de2075d7a83e61827a33fdb4419671b2313f3f6131cd2e3ed570f58a7d",
"originHash" : "fcb82ddcec78f864b92635b39a134e6c2ebc5f4f04274dc94f535c265aaae092",
"pins" : [
{
"identity" : "eudi-lib-ios-iso18013-data-model",
"kind" : "remoteSourceControl",
"location" : "https://github.com/eu-digital-identity-wallet/eudi-lib-ios-iso18013-data-model.git",
"state" : {
"revision" : "c1b4383d6fc3387a8ed4c79177548624c4e34e3a",
"version" : "0.3.3"
"revision" : "29f30a92427733db0c7b9cea9616607a1df24284",
"version" : "0.4.0"
}
},
{
Expand Down
2 changes: 1 addition & 1 deletion Package.swift
Original file line number Diff line number Diff line change
Expand Up @@ -15,7 +15,7 @@ let package = Package(
],
dependencies: [
.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", exact: "0.3.3"),
.package(url: "https://github.com/eu-digital-identity-wallet/eudi-lib-ios-iso18013-data-model.git", exact: "0.4.0"),
],
targets: [
// Targets are the basic building blocks of a package, defining a module or a test suite.
Expand Down
14 changes: 4 additions & 10 deletions Sources/WalletStorage/Document.swift
Original file line number Diff line number Diff line change
Expand Up @@ -19,13 +19,12 @@ import MdocDataModel18013

/// wallet document structure
public struct Document: DocumentProtocol, Sendable {
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) {
public init(id: String = UUID().uuidString, docType: String, docDataType: DocDataType, data: Data, secureAreaName: String?, createdAt: Date?, modifiedAt: Date? = nil, displayName: String?, status: DocumentStatus) {
self.id = id
self.docType = docType
self.docDataType = docDataType
self.data = data
self.privateKeyType = privateKeyType
self.privateKey = privateKey
self.secureAreaName = secureAreaName
self.createdAt = createdAt ?? Date()
self.modifiedAt = modifiedAt
self.displayName = displayName
Expand All @@ -36,8 +35,7 @@ public struct Document: DocumentProtocol, Sendable {
public let docType: String
public let data: Data
public let docDataType: DocDataType
public let privateKeyType: PrivateKeyType?
public let privateKey: Data?
public let secureAreaName: String?
public let createdAt: Date
public let modifiedAt: Date?
public let displayName: String?
Expand All @@ -48,12 +46,8 @@ public struct Document: DocumentProtocol, Sendable {
/// get CBOR data and private key from document
public func getCborData() -> (iss: (String, IssuerSigned), dpk: (String, CoseKeyPrivate))? {
switch docDataType {
case .signupResponseJson:
guard let sr = data.decodeJSON(type: SignUpResponse.self), let dr = sr.deviceResponse, let iss = dr.documents?.first?.issuerSigned, let dpk = sr.devicePrivateKey else { return nil }
let randomId = UUID().uuidString
return ((randomId, iss), (randomId, dpk))
case .cbor:
guard let iss = IssuerSigned(data: [UInt8](data)), let privateKeyType, let privateKey, let dpk = try? IssueRequest(id: id, privateKeyType: privateKeyType, keyData: privateKey).toCoseKeyPrivate() else { return nil }
guard let iss = IssuerSigned(data: [UInt8](data)), case let dpk = CoseKeyPrivate(privateKeyId: id, secureArea: SecureAreaRegistry.shared.get(name: secureAreaName)) else { return nil }
return ((id, iss), (id, dpk))
case .sjwt:
fatalError("Format \(docDataType) not implemented")
Expand Down
17 changes: 3 additions & 14 deletions Sources/WalletStorage/Enumerations.swift
Original file line number Diff line number Diff line change
Expand Up @@ -19,9 +19,10 @@ import Foundation
/// type of data to save in storage
/// ``doc``: Document data
/// ``key``: Private-key
public enum SavedKeyChainDataType: String, Sendable {
public enum SavedKeyChainDataType: String, Sendable, CaseIterable {
case doc = "sdoc"
case key = "skey"
case keyInfo = "skei"
}

/// Format of document data
Expand All @@ -32,19 +33,7 @@ public enum SavedKeyChainDataType: String, Sendable {
public enum DocDataType: String, Sendable {
case cbor = "cbor"
case sjwt = "sjwt"
case signupResponseJson = "srjs"
}

/// Format of private key
/// ``derEncodedP256``: DER encoded
/// ``pemStringDataP256`` PEM string encoded as utf8
/// ``x963EncodedP256``: ANSI x9.63 representation (default)
/// ``secureEnclaveP256``: data representation for the secure enclave
public enum PrivateKeyType: String, Sendable {
case derEncodedP256 = "dep2"
case pemStringDataP256 = "pep2"
case x963EncodedP256 = "x9p2"
case secureEnclaveP256 = "sep2"
// case signupResponseJson = "srjs"
}


Expand Down
83 changes: 9 additions & 74 deletions Sources/WalletStorage/IssueRequest.swift
Original file line number Diff line number Diff line change
Expand Up @@ -21,87 +21,22 @@ import MdocDataModel18013
/// Issue request structure
public struct IssueRequest: Sendable {
public var id: String
public var docType: String?
public var keyData: Data
public var privateKeyType: PrivateKeyType

public var keyOptions: KeyOptions?
public var secureArea: SecureArea
public var secureAreaName: String { type(of: secureArea).name }
/// Initialize issue request with id
///
/// - Parameters:
/// - id: a key identifier (uuid)
public init(id: String = UUID().uuidString, docType: String? = nil, privateKeyType: PrivateKeyType = .secureEnclaveP256, keyData: Data? = nil) throws {
self.id = id
self.docType = docType
self.privateKeyType = privateKeyType
if let keyData {
self.keyData = keyData
// key-data already created, exit
return
}
switch privateKeyType {
case .derEncodedP256:
let p256 = P256.KeyAgreement.PrivateKey()
self.keyData = p256.derRepresentation
case .pemStringDataP256:
let p256 = P256.KeyAgreement.PrivateKey()
self.keyData = p256.pemRepresentation.data(using: .utf8)!
case .x963EncodedP256:
let p256 = P256.KeyAgreement.PrivateKey()
self.keyData = p256.x963Representation
case .secureEnclaveP256:
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 saveTo(storageService: any DataStorageService, status: DocumentStatus) async throws {
// save key data to storage with id
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 await storageService.saveDocument(docKey, allowOverwrite: true)
}

public mutating func loadFrom(storageService: any DataStorageService, id: String, status: DocumentStatus) async throws {
guard let doc = try await storageService.loadDocument(id: id, status: status), let pk = doc.privateKey, let pkt = doc.privateKeyType else { return }
public init(id: String = UUID().uuidString, keyOptions: KeyOptions? = nil) throws {
self.id = id
keyData = pk
privateKeyType = pkt
}

public func toCoseKeyPrivate() throws -> CoseKeyPrivate {
switch privateKeyType {
case .derEncodedP256:
let p256 = try P256.KeyAgreement.PrivateKey(derRepresentation: keyData)
return CoseKeyPrivate(privateKeyx963Data: p256.x963Representation, crv: .p256)
case .x963EncodedP256:
let p256 = try P256.KeyAgreement.PrivateKey(x963Representation: keyData)
return CoseKeyPrivate(privateKeyx963Data: p256.x963Representation, crv: .p256)
case .pemStringDataP256:
let p256 = try P256.KeyAgreement.PrivateKey(pemRepresentation: String(data: keyData, encoding: .utf8)!)
return CoseKeyPrivate(privateKeyx963Data: p256.x963Representation, crv: .p256)
case .secureEnclaveP256:
let se256 = try SecureEnclave.P256.KeyAgreement.PrivateKey(dataRepresentation: keyData)
return CoseKeyPrivate(publicKeyx963Data: se256.publicKey.x963Representation, secureEnclaveKeyID: keyData)
}
self.keyOptions = keyOptions
secureArea = SecureAreaRegistry.shared.get(name: keyOptions?.secureAreaName)
}

public func getPublicKeyPEM() throws -> String {
switch privateKeyType {
case .derEncodedP256:
let p256 = try P256.KeyAgreement.PrivateKey(derRepresentation: keyData)
return p256.publicKey.pemRepresentation
case .pemStringDataP256:
let p256 = try P256.KeyAgreement.PrivateKey(pemRepresentation: String(data: keyData, encoding: .utf8)!)
return p256.publicKey.pemRepresentation
case .x963EncodedP256:
let p256 = try P256.KeyAgreement.PrivateKey(x963Representation: keyData)
return p256.publicKey.pemRepresentation
case .secureEnclaveP256:
let se256 = try SecureEnclave.P256.KeyAgreement.PrivateKey(dataRepresentation: keyData)
return se256.publicKey.pemRepresentation
}
public func createKey() async throws -> CoseKey {
let res = try await secureArea.createKey(id: id, keyOptions: keyOptions)
return res
}

}
Expand Down
Loading