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

feat(pluto): add keychain capabilities #98

Merged
merged 1 commit into from
Sep 4, 2023
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
15 changes: 11 additions & 4 deletions AtalaPrismSDK/Apollo/Sources/Model/Ed25519Key.swift
Original file line number Diff line number Diff line change
Expand Up @@ -32,10 +32,13 @@ extension Ed25519PrivateKey: SignableKey {
}
}

extension Ed25519PrivateKey: StorableKey {
var securityLevel: SecurityLevel { SecurityLevel.high }
extension Ed25519PrivateKey: KeychainStorableKey {
var restorationIdentifier: String { "ed25519+priv" }
var storableData: Data { raw }
var type: Domain.KeychainStorableKeyProperties.KeyAlgorithm { .rawKey }
var keyClass: Domain.KeychainStorableKeyProperties.KeyType { .privateKey }
var accessiblity: Domain.KeychainStorableKeyProperties.Accessability? { .firstUnlock(deviceOnly: true) }
var synchronizable: Bool { false }
}

struct Ed25519PublicKey: PublicKey {
Expand All @@ -56,8 +59,12 @@ struct Ed25519PublicKey: PublicKey {
}
}

extension Ed25519PublicKey: StorableKey {
var securityLevel: SecurityLevel { SecurityLevel.low }
extension Ed25519PublicKey: KeychainStorableKey {
var restorationIdentifier: String { "ed25519+pub" }
var storableData: Data { raw }
var type: Domain.KeychainStorableKeyProperties.KeyAlgorithm { .rawKey }
var keyClass: Domain.KeychainStorableKeyProperties.KeyType { .publicKey }
var accessiblity: Domain.KeychainStorableKeyProperties.Accessability? { .firstUnlock(deviceOnly: true) }
var synchronizable: Bool { false }
}

14 changes: 10 additions & 4 deletions AtalaPrismSDK/Apollo/Sources/Model/Secp256k1Key.swift
Original file line number Diff line number Diff line change
Expand Up @@ -37,10 +37,13 @@ extension Secp256k1PrivateKey: SignableKey {
}
}

extension Secp256k1PrivateKey: StorableKey {
var securityLevel: SecurityLevel { SecurityLevel.high }
extension Secp256k1PrivateKey: KeychainStorableKey {
var restorationIdentifier: String { "secp256k1+priv" }
var storableData: Data { raw }
var type: Domain.KeychainStorableKeyProperties.KeyAlgorithm { .rawKey }
var keyClass: Domain.KeychainStorableKeyProperties.KeyType { .privateKey }
var accessiblity: Domain.KeychainStorableKeyProperties.Accessability? { .firstUnlock(deviceOnly: true) }
var synchronizable: Bool { false }
}

struct Secp256k1PublicKey: PublicKey {
Expand Down Expand Up @@ -75,8 +78,11 @@ struct Secp256k1PublicKey: PublicKey {
}
}

extension Secp256k1PublicKey: StorableKey {
var securityLevel: SecurityLevel { SecurityLevel.low }
extension Secp256k1PublicKey: KeychainStorableKey {
var restorationIdentifier: String { "secp256k1+pub" }
var storableData: Data { raw }
var type: Domain.KeychainStorableKeyProperties.KeyAlgorithm { .rawKey }
var keyClass: Domain.KeychainStorableKeyProperties.KeyType { .publicKey }
var accessiblity: Domain.KeychainStorableKeyProperties.Accessability? { .firstUnlock(deviceOnly: true) }
var synchronizable: Bool { false }
}
14 changes: 10 additions & 4 deletions AtalaPrismSDK/Apollo/Sources/Model/X25519Key.swift
Original file line number Diff line number Diff line change
Expand Up @@ -20,10 +20,13 @@ struct X25519PrivateKey: PrivateKey {
}
}

extension X25519PrivateKey: StorableKey {
var securityLevel: SecurityLevel { SecurityLevel.high }
extension X25519PrivateKey: KeychainStorableKey {
var restorationIdentifier: String { "x25519+priv" }
var storableData: Data { raw }
var type: Domain.KeychainStorableKeyProperties.KeyAlgorithm { .rawKey }
var keyClass: Domain.KeychainStorableKeyProperties.KeyType { .privateKey }
var accessiblity: Domain.KeychainStorableKeyProperties.Accessability? { .firstUnlock(deviceOnly: true) }
var synchronizable: Bool { false }
}

struct X25519PublicKey: PublicKey {
Expand All @@ -44,8 +47,11 @@ struct X25519PublicKey: PublicKey {
}
}

extension X25519PublicKey: StorableKey {
var securityLevel: SecurityLevel { SecurityLevel.low }
extension X25519PublicKey: KeychainStorableKey {
var restorationIdentifier: String { "x25519+pub" }
var storableData: Data { raw }
var type: Domain.KeychainStorableKeyProperties.KeyAlgorithm { .rawKey }
var keyClass: Domain.KeychainStorableKeyProperties.KeyType { .publicKey }
var accessiblity: Domain.KeychainStorableKeyProperties.Accessability? { .firstUnlock(deviceOnly: true) }
var synchronizable: Bool { false }
}
15 changes: 6 additions & 9 deletions AtalaPrismSDK/Builders/Sources/MercuryBuilder.swift
Original file line number Diff line number Diff line change
@@ -1,24 +1,22 @@
import Combine
import Domain
import Foundation
import Mercury

public struct MercuryBuilder {
let apollo: Apollo
let castor: Castor
let pluto: Pluto
let secretsStream: AnyPublisher<[Domain.Secret], Error>
let session: URLSession
let timeout: TimeInterval

public init(
apollo: Apollo,
castor: Castor,
pluto: Pluto,
secretsStream: AnyPublisher<[Domain.Secret], Error>,
session: URLSession = .shared,
timeout: TimeInterval = 30
) {
self.apollo = apollo
self.castor = castor
self.pluto = pluto
self.secretsStream = secretsStream
self.session = session
self.timeout = timeout
}
Expand All @@ -27,9 +25,8 @@ public struct MercuryBuilder {
MercuryImpl(
session: session,
timeout: timeout,
apollo: apollo,
castor: castor,
pluto: pluto
secretsStream: secretsStream,
castor: castor
)
}
}
6 changes: 2 additions & 4 deletions AtalaPrismSDK/Builders/Sources/PlutoBuilder.swift
Original file line number Diff line number Diff line change
Expand Up @@ -3,14 +3,12 @@ import Pluto

public struct PlutoBuilder {
let setup: PlutoImpl.PlutoSetup
let keyRestoration: KeyRestoration

public init(setup: PlutoImpl.PlutoSetup = .init(), keyRestoration: KeyRestoration) {
public init(setup: PlutoImpl.PlutoSetup = .init()) {
self.setup = setup
self.keyRestoration = keyRestoration
}

public func build() -> Pluto {
PlutoImpl(setup: setup, keyRestoration: keyRestoration)
PlutoImpl(setup: setup)
}
}
4 changes: 2 additions & 2 deletions AtalaPrismSDK/Castor/Sources/DID/PeerDID/PeerDID.swift
Original file line number Diff line number Diff line change
Expand Up @@ -44,8 +44,8 @@ struct PeerDID {
let type = try container.decode(String.self, forKey: .type)
self.type = type == "dm" ? "DIDCommMessaging" : type
self.serviceEndpoint = try container.decode(String.self, forKey: .serviceEndpoint)
self.routingKeys = (try? container.decode([String].self, forKey: .routingKeys)) ?? []
self.accept = (try? container.decode([String].self, forKey: .accept)) ?? []
self.routingKeys = try container.decodeIfPresent([String].self, forKey: .routingKeys) ?? []
self.accept = try container.decodeIfPresent([String].self, forKey: .accept) ?? []
}
}

Expand Down
20 changes: 10 additions & 10 deletions AtalaPrismSDK/Domain/Sources/BBs/Pluto.swift
Original file line number Diff line number Diff line change
Expand Up @@ -23,7 +23,7 @@ public protocol Pluto {
/// - Returns: A publisher that completes when the operation finishes.
func storePeerDID(
did: DID,
privateKeys: [PrivateKey & StorableKey],
privateKeys: [StorableKey],
alias: String?
) -> AnyPublisher<Void, Error>

Expand All @@ -35,7 +35,7 @@ public protocol Pluto {
/// - Returns: A publisher that completes when the operation finishes.
func storeDID(
did: DID,
privateKeys: [PrivateKey & StorableKey],
privateKeys: [StorableKey],
alias: String?
) -> AnyPublisher<Void, Error>

Expand Down Expand Up @@ -108,41 +108,41 @@ public protocol Pluto {

/// Returns all stored peer DIDs, along with their associated private keys and aliases (if any).
/// - Returns: A publisher that emits an array of tuples representing the stored peer DIDs, along with their associated private keys and aliases (if any).
func getAllPeerDIDs() -> AnyPublisher<[(did: DID, privateKeys: [PrivateKey], alias: String?)], Error>
func getAllPeerDIDs() -> AnyPublisher<[(did: DID, privateKeys: [StorableKey], alias: String?)], Error>

/// Returns the stored information for a given peer DID, including the associated private keys and alias (if any).
/// - Parameter did: The peer DID to retrieve information for.
/// - Returns: A publisher that emits an optional tuple containing the stored information for the given peer DID.
func getPeerDIDInfo(did: DID) -> AnyPublisher<(did: DID, privateKeys: [PrivateKey], alias: String?)?, Error>
func getPeerDIDInfo(did: DID) -> AnyPublisher<(did: DID, privateKeys: [StorableKey], alias: String?)?, Error>

/// Returns the stored information for all peer DIDs that have a given alias, including the associated DIDs and private keys.
/// - Parameter alias: The alias to search for.
/// - Returns: A publisher that emits an array of tuples containing the stored information for the peer DIDs that have the given alias.
func getPeerDIDInfo(alias: String) -> AnyPublisher<[(did: DID, privateKeys: [PrivateKey], alias: String?)], Error>
func getPeerDIDInfo(alias: String) -> AnyPublisher<[(did: DID, privateKeys: [StorableKey], alias: String?)], Error>

/// Returns the private keys associated with a given peer DID.
/// - Parameter did: The peer DID to retrieve the private keys for.
/// - Returns: A publisher that emits an optional array of private keys associated with the given peer DID.
func getPeerDIDPrivateKeys(did: DID) -> AnyPublisher<[PrivateKey]?, Error>
func getPeerDIDPrivateKeys(did: DID) -> AnyPublisher<[StorableKey]?, Error>

/// Returns all stored peer DIDs, along with their associated private keys and aliases (if any).
/// - Returns: A publisher that emits an array of tuples representing the stored peer DIDs, along with their associated private keys and aliases (if any).
func getAllDIDs() -> AnyPublisher<[(did: DID, privateKeys: [PrivateKey], alias: String?)], Error>
func getAllDIDs() -> AnyPublisher<[(did: DID, privateKeys: [StorableKey], alias: String?)], Error>

/// Returns the stored information for a given peer DID, including the associated private keys and alias (if any).
/// - Parameter did: The peer DID to retrieve information for.
/// - Returns: A publisher that emits an optional tuple containing the stored information for the given peer DID.
func getDIDInfo(did: DID) -> AnyPublisher<(did: DID, privateKeys: [PrivateKey], alias: String?)?, Error>
func getDIDInfo(did: DID) -> AnyPublisher<(did: DID, privateKeys: [StorableKey], alias: String?)?, Error>

/// Returns the stored information for all peer DIDs that have a given alias, including the associated DIDs and private keys.
/// - Parameter alias: The alias to search for.
/// - Returns: A publisher that emits an array of tuples containing the stored information for the peer DIDs that have the given alias.
func getDIDInfo(alias: String) -> AnyPublisher<[(did: DID, privateKeys: [PrivateKey], alias: String?)], Error>
func getDIDInfo(alias: String) -> AnyPublisher<[(did: DID, privateKeys: [StorableKey], alias: String?)], Error>

/// Returns the private keys associated with a given peer DID.
/// - Parameter did: The peer DID to retrieve the private keys for.
/// - Returns: A publisher that emits an optional array of private keys associated with the given peer DID.
func getDIDPrivateKeys(did: DID) -> AnyPublisher<[PrivateKey]?, Error>
func getDIDPrivateKeys(did: DID) -> AnyPublisher<[StorableKey]?, Error>

/// Returns all stored DID pairs.
/// - Returns: A publisher that emits an array of DID pairs.
Expand Down
83 changes: 83 additions & 0 deletions AtalaPrismSDK/Domain/Sources/Models/Errors.swift
Original file line number Diff line number Diff line change
Expand Up @@ -579,6 +579,49 @@ public enum PlutoError: KnownPrismError {
/// An error case representing invalid JSON in a credential.
case invalidCredentialJsonError

/// An error case representing an invalid combination of algorithm and key type.
///
/// - Parameters:
/// - algorithm: The algorithm used.
/// - keyType: The key type that is associated with the algorithm.
case algorithmOrKeyTypeNotValid(algorithm: String, keyType: String)

/// An error case representing an issue with saving a key to the keychain.
///
/// - Parameter status: The status code representing the error encountered while saving the key.
case errorSavingKeyOnKeychainWithStatus(OSStatus)

/// An error case indicating that while a key's item could be retrieved from the keychain, its data could not.
case errorRetrievingKeyDataInvalid

/// An error case indicating that a specific key was not found in the keychain.
///
/// - Parameters:
/// - service: Optional identifier for the service associated with the key.
/// - account: Optional identifier for the account associated with the key.
/// - applicationLabel: Optional label for the application associated with the key.
case errorRetrivingKeyFromKeychainKeyNotFound(service: String? = nil, account: String? = nil, applicationLabel: String? = nil)

/// An error case representing an issue with retrieving a key from the keychain.
///
/// - Parameter status: The status code representing the error encountered while retrieving the key.
case errorRetrivingKeyFromKeychainWithStatus(OSStatus)

/// An error case indicating a failure to retrieve data from a `SecKey` object.
///
/// - Parameters:
/// - service: Optional identifier for the service associated with the key.
/// - account: Optional identifier for the account associated with the key.
/// - applicationLabel: Optional label for the application associated with the key.
case errorCouldNotRetrieveDataFromSecKeyObject(service: String? = nil, account: String? = nil, applicationLabel: String? = nil)

/// An error case indicating an issue with creating a `SecKey` object.
///
/// - Parameters:
/// - keyType: The type of the key that was being created.
/// - keyClass: The class of the key being created.
case errorCreatingSecKey(keyType: String, keyClass: String)

/// The error code returned by the server.
public var code: Int {
switch self {
Expand All @@ -592,6 +635,20 @@ public enum PlutoError: KnownPrismError {
return 44
case .invalidCredentialJsonError:
return 45
case .algorithmOrKeyTypeNotValid:
return 46
case .errorSavingKeyOnKeychainWithStatus:
return 47
case .errorRetrievingKeyDataInvalid:
return 48
case .errorRetrivingKeyFromKeychainKeyNotFound:
return 49
case .errorRetrivingKeyFromKeychainWithStatus:
return 50
case .errorCouldNotRetrieveDataFromSecKeyObject:
return 51
case .errorCreatingSecKey:
return 52
}
}

Expand All @@ -614,6 +671,32 @@ public enum PlutoError: KnownPrismError {
return "Could not decode the credential JSON"
case .unknownCredentialTypeError:
return "The credential type needs to be JWT or W3C"
case .algorithmOrKeyTypeNotValid(algorithm: let algorithm, keyType: let keyType):
return "This algorithm (\(algorithm)) or key type (\(keyType)) are not valid on the platform"
case .errorSavingKeyOnKeychainWithStatus(let status):
return "Error saving key on keychain with the following status code: \(status)"
case .errorRetrievingKeyDataInvalid:
return "Could retrieve item from keychain but could not retrieve Data of key"
case .errorRetrivingKeyFromKeychainKeyNotFound(let service, let account, let applicationLabel):
var message = "Key not found"
if let service, let account {
message += " service: \(service) and account: \(account)"
} else if let applicationLabel {
message += " applicationLabel: \(applicationLabel)"
}
return message
case .errorRetrivingKeyFromKeychainWithStatus(let status):
return "Error retrieving key from keychain with the following status code: \(status)"
case .errorCouldNotRetrieveDataFromSecKeyObject(let service, let account, let applicationLabel):
var message = "Key not found"
if let service, let account {
message += " service: \(service) and account: \(account)"
} else if let applicationLabel {
message += " applicationLabel: \(applicationLabel)"
}
return message
case .errorCreatingSecKey(let keyType, let keyClass):
return "Error creating sec key of type \(keyType) and class \(keyClass)"
}
}
}
Expand Down
Loading