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(anoncreds): add support for anoncreds issuance flow #103

Merged
merged 1 commit into from
Oct 12, 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
25 changes: 13 additions & 12 deletions AtalaPrismSDK/Apollo/Sources/ApolloImpl+KeyRestoration.swift
Original file line number Diff line number Diff line change
Expand Up @@ -10,29 +10,30 @@ extension ApolloImpl: KeyRestoration {
identifier.hasSuffix("pub")
}

public func restorePrivateKey(identifier: String?, data: Data) throws -> PrivateKey {
guard let identifier else { throw ApolloError.restoratonFailedNoIdentifierOrInvalid }
switch identifier {
public func restorePrivateKey(_ key: StorableKey) throws -> PrivateKey {
switch key.restorationIdentifier {
case "secp256k1+priv":
return Secp256k1PrivateKey(lockedPrivateKey: .init(data: data))
return Secp256k1PrivateKey(
lockedPrivateKey: .init(data: key.storableData),
derivationPath: key.index.map { DerivationPath(index: $0) } ?? DerivationPath(index: 0)
)
case "x25519+priv":
return X25519PrivateKey(appleCurve: try .init(rawRepresentation: data))
return X25519PrivateKey(appleCurve: try .init(rawRepresentation: key.storableData))
case "ed25519+priv":
return Ed25519PrivateKey(appleCurve: try .init(rawRepresentation: data))
return Ed25519PrivateKey(appleCurve: try .init(rawRepresentation: key.storableData))
default:
throw ApolloError.restoratonFailedNoIdentifierOrInvalid
}
}

public func restorePublicKey(identifier: String?, data: Data) throws -> PublicKey {
guard let identifier else { throw ApolloError.restoratonFailedNoIdentifierOrInvalid }
switch identifier {
public func restorePublicKey(_ key: StorableKey) throws -> PublicKey {
switch key.restorationIdentifier {
case "secp256k1+pub":
return Secp256k1PublicKey(lockedPublicKey: .init(bytes: data))
return Secp256k1PublicKey(lockedPublicKey: .init(bytes: key.storableData))
case "x25519+pub":
return X25519PublicKey(appleCurve: try .init(rawRepresentation: data))
return X25519PublicKey(appleCurve: try .init(rawRepresentation: key.storableData))
case "ed25519+pub":
return Ed25519PublicKey(appleCurve: try .init(rawRepresentation: data))
return Ed25519PublicKey(appleCurve: try .init(rawRepresentation: key.storableData))
default:
throw ApolloError.restoratonFailedNoIdentifierOrInvalid
}
Expand Down
10 changes: 6 additions & 4 deletions AtalaPrismSDK/Apollo/Sources/ApolloImpl+Public.swift
Original file line number Diff line number Diff line change
Expand Up @@ -102,9 +102,11 @@ returns random mnemonics nerver returns invalid mnemonics
switch curve {
case .secp256k1:
if
let keyData = parameters[KeyProperties.rawKey.rawValue].flatMap({ Data(base64Encoded: $0) })
let keyData = parameters[KeyProperties.rawKey.rawValue].flatMap({ Data(base64Encoded: $0) }),
let derivationPathStr = parameters[KeyProperties.derivationPath.rawValue]
{
return Secp256k1PrivateKey(lockedPrivateKey: .init(data: keyData))
let derivationPath = try DerivationPath(string: derivationPathStr)
return Secp256k1PrivateKey(lockedPrivateKey: .init(data: keyData), derivationPath: derivationPath)
} else {
guard
let derivationPathStr = parameters[KeyProperties.derivationPath.rawValue],
Expand Down Expand Up @@ -143,7 +145,7 @@ returns random mnemonics nerver returns invalid mnemonics
}
}

public func createNewLinkSecret() -> String {
CreateLinkSecretOperation().create()
public func createNewLinkSecret() throws -> String {
try CreateLinkSecretOperation().create()
}
}
2 changes: 2 additions & 0 deletions AtalaPrismSDK/Apollo/Sources/Model/Ed25519Key.swift
Original file line number Diff line number Diff line change
Expand Up @@ -35,6 +35,7 @@ extension Ed25519PrivateKey: SignableKey {
extension Ed25519PrivateKey: KeychainStorableKey {
var restorationIdentifier: String { "ed25519+priv" }
var storableData: Data { raw }
var index: Int? { nil }
var type: Domain.KeychainStorableKeyProperties.KeyAlgorithm { .rawKey }
var keyClass: Domain.KeychainStorableKeyProperties.KeyType { .privateKey }
var accessiblity: Domain.KeychainStorableKeyProperties.Accessability? { .firstUnlock(deviceOnly: true) }
Expand Down Expand Up @@ -62,6 +63,7 @@ struct Ed25519PublicKey: PublicKey {
extension Ed25519PublicKey: KeychainStorableKey {
var restorationIdentifier: String { "ed25519+pub" }
var storableData: Data { raw }
var index: Int? { nil }
var type: Domain.KeychainStorableKeyProperties.KeyAlgorithm { .rawKey }
var keyClass: Domain.KeychainStorableKeyProperties.KeyType { .publicKey }
var accessiblity: Domain.KeychainStorableKeyProperties.Accessability? { .firstUnlock(deviceOnly: true) }
Expand Down
9 changes: 7 additions & 2 deletions AtalaPrismSDK/Apollo/Sources/Model/Secp256k1Key.swift
Original file line number Diff line number Diff line change
Expand Up @@ -9,11 +9,14 @@ struct Secp256k1PrivateKey: PrivateKey {
let keySpecifications: [String : String]
let size: Int
let raw: Data
let derivationPath: DerivationPath

init(lockedPrivateKey: LockPrivateKey) {
init(lockedPrivateKey: LockPrivateKey, derivationPath: DerivationPath) {
self.lockedPrivateKey = lockedPrivateKey
self.derivationPath = derivationPath
self.keySpecifications = [
KeyProperties.curve.rawValue : "secp256k1"
KeyProperties.curve.rawValue : "secp256k1",
KeyProperties.derivationPath.rawValue : derivationPath.keyPathString()
]

self.raw = lockedPrivateKey.data
Expand All @@ -40,6 +43,7 @@ extension Secp256k1PrivateKey: SignableKey {
extension Secp256k1PrivateKey: KeychainStorableKey {
var restorationIdentifier: String { "secp256k1+priv" }
var storableData: Data { raw }
var index: Int? { derivationPath.index }
var type: Domain.KeychainStorableKeyProperties.KeyAlgorithm { .rawKey }
var keyClass: Domain.KeychainStorableKeyProperties.KeyType { .privateKey }
var accessiblity: Domain.KeychainStorableKeyProperties.Accessability? { .firstUnlock(deviceOnly: true) }
Expand Down Expand Up @@ -81,6 +85,7 @@ struct Secp256k1PublicKey: PublicKey {
extension Secp256k1PublicKey: KeychainStorableKey {
var restorationIdentifier: String { "secp256k1+pub" }
var storableData: Data { raw }
var index: Int? { nil }
var type: Domain.KeychainStorableKeyProperties.KeyAlgorithm { .rawKey }
var keyClass: Domain.KeychainStorableKeyProperties.KeyType { .publicKey }
var accessiblity: Domain.KeychainStorableKeyProperties.Accessability? { .firstUnlock(deviceOnly: true) }
Expand Down
2 changes: 2 additions & 0 deletions AtalaPrismSDK/Apollo/Sources/Model/X25519Key.swift
Original file line number Diff line number Diff line change
Expand Up @@ -23,6 +23,7 @@ struct X25519PrivateKey: PrivateKey {
extension X25519PrivateKey: KeychainStorableKey {
var restorationIdentifier: String { "x25519+priv" }
var storableData: Data { raw }
var index: Int? { nil }
var type: Domain.KeychainStorableKeyProperties.KeyAlgorithm { .rawKey }
var keyClass: Domain.KeychainStorableKeyProperties.KeyType { .privateKey }
var accessiblity: Domain.KeychainStorableKeyProperties.Accessability? { .firstUnlock(deviceOnly: true) }
Expand Down Expand Up @@ -50,6 +51,7 @@ struct X25519PublicKey: PublicKey {
extension X25519PublicKey: KeychainStorableKey {
var restorationIdentifier: String { "x25519+pub" }
var storableData: Data { raw }
var index: Int? { nil }
var type: Domain.KeychainStorableKeyProperties.KeyAlgorithm { .rawKey }
var keyClass: Domain.KeychainStorableKeyProperties.KeyType { .publicKey }
var accessiblity: Domain.KeychainStorableKeyProperties.Accessability? { .firstUnlock(deviceOnly: true) }
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -2,7 +2,7 @@ import AnoncredsSwift
import Foundation

struct CreateLinkSecretOperation {
func create() -> String {
Prover().createLinkSecret().getBigNumber()
func create() throws -> String {
try Prover().createLinkSecret().getValue()
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -16,6 +16,6 @@ struct CreateSec256k1KeyPairOperation {
func compute() throws -> PrivateKey {
let derivedKey = try HDKeychain(seed: seed.value).derivedKey(path: keyPath.keyPathString())

return Secp256k1PrivateKey(lockedPrivateKey: derivedKey.privateKey())
return Secp256k1PrivateKey(lockedPrivateKey: derivedKey.privateKey(), derivationPath: keyPath)
}
}
5 changes: 4 additions & 1 deletion AtalaPrismSDK/Apollo/Tests/ECSigningTests.swift
Original file line number Diff line number Diff line change
Expand Up @@ -6,7 +6,10 @@ import XCTest
final class ECSigningTests: XCTestCase {
func testSigning() throws {
let privKey = Secp256k1PrivateKey(
lockedPrivateKey: .init(data: Data(fromBase64URL: "N_JFgvYaReyRXwassz5FHg33A4I6dczzdXrjdHGksmg")!)
lockedPrivateKey: .init(
data: Data(fromBase64URL: "N_JFgvYaReyRXwassz5FHg33A4I6dczzdXrjdHGksmg")!
),
derivationPath: .init(index: 0)
)
let testMessage = "Test".data(using: .utf8)!

Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -150,8 +150,7 @@ struct CreatePeerDIDOperation {
accept: endpoint.accept
)
}.compactMap { $0 }
let encoder = JSONEncoder()
encoder.outputFormatting = .withoutEscapingSlashes
let encoder = JSONEncoder.didComm()
if
peerDidServices.count == 1,
let peerDidService = peerDidServices.first
Expand Down
2 changes: 1 addition & 1 deletion AtalaPrismSDK/Castor/Tests/PeerDIDCreationTests.swift
Original file line number Diff line number Diff line change
Expand Up @@ -5,7 +5,7 @@ import XCTest

final class PeerDIDCreationTests: XCTestCase {
func testPeerDIDCreation() throws {
let validPeerDID = "did:peer:2.Ez6LSoHkfN1Y4nK9RCjx7vopWsLrMGNFNgTNZgoCNQrTzmb1n.Vz6MknRZmapV7uYZQuZez9n9N3tQotjRN18UGS68Vcfo6gR4h.SeyJyIjpbImRpZDpleGFtcGxlOnNvbWVtZWRpYXRvciNzb21la2V5Il0sInMiOiJodHRwczovL2V4YW1wbGUuY29tL2VuZHBvaW50IiwiYSI6W10sInQiOiJkbSJ9"
let validPeerDID = "did:peer:2.Ez6LSoHkfN1Y4nK9RCjx7vopWsLrMGNFNgTNZgoCNQrTzmb1n.Vz6MknRZmapV7uYZQuZez9n9N3tQotjRN18UGS68Vcfo6gR4h.SeyJhIjpbXSwiciI6WyJkaWQ6ZXhhbXBsZTpzb21lbWVkaWF0b3Ijc29tZWtleSJdLCJzIjoiaHR0cHM6Ly9leGFtcGxlLmNvbS9lbmRwb2ludCIsInQiOiJkbSJ9"
let apollo = ApolloImpl()
let castor = CastorImpl(apollo: apollo)
let keyAgreementPrivateKey = try apollo.createPrivateKey(parameters: [
Expand Down
2 changes: 1 addition & 1 deletion AtalaPrismSDK/Domain/Sources/BBs/Apollo.swift
Original file line number Diff line number Diff line change
Expand Up @@ -55,5 +55,5 @@ public protocol Apollo {
/// - Returns: The decompressed public key
func uncompressedPublicKey(compressedData: Data) -> PublicKey

func createNewLinkSecret() -> String
func createNewLinkSecret() throws -> String
}
6 changes: 3 additions & 3 deletions AtalaPrismSDK/Domain/Sources/BBs/Pollux.swift
Original file line number Diff line number Diff line change
Expand Up @@ -4,9 +4,9 @@ import Foundation
/// Options that can be passed into various operations.
public enum CredentialOperationsOptions {
case schema(id: String, json: String) // The JSON schema.
case schemasStream(stream: AnyPublisher<[(id: String, json: String)], Error>) // Stream of schemas, only the first batch is considered
case schemaDownloader(downloader: Downloader) // Stream of schemas, only the first batch is considered
case credentialDefinition(id: String, json: String) // The JSON Credential Definition
case credentialDefinitionsStream(stream: AnyPublisher<[(id: String, json: String)], Error>) // Stream of credential definitions, only the first batch is considered
case credentialDefinitionDownloader(downloader: Downloader) // Download of credential definitions, only the first batch is considered
case linkSecret(id: String, secret: String) // A secret link.
case subjectDID(DID) // The decentralized identifier of the subject.
case entropy(String) // Entropy for any randomization operation.
Expand All @@ -21,7 +21,7 @@ public protocol Pollux {
/// - Parameter data: The encoded item to parse.
/// - Throws: An error if the item cannot be parsed or decoded.
/// - Returns: An object representing the parsed item.
func parseCredential(issuedCredential: Message) throws -> Credential
func parseCredential(issuedCredential: Message, options: [CredentialOperationsOptions]) async throws -> Credential

/// Restores a previously stored item using the provided restoration identifier and data.
/// - Parameters:
Expand Down
5 changes: 5 additions & 0 deletions AtalaPrismSDK/Domain/Sources/Models/Common/Downloader.swift
Original file line number Diff line number Diff line change
@@ -0,0 +1,5 @@
import Foundation

public protocol Downloader {
func downloadFromEndpoint(urlOrDID: String) async throws -> Data
}
Original file line number Diff line number Diff line change
Expand Up @@ -70,6 +70,8 @@ public protocol Credential {
var claims: [Claim] { get }
/// Additional properties associated with the credential.
var properties: [String: Any] { get }
/// The type of the credential Ex: JWT, Anoncred, W3C
var credentialType: String { get }
}

public extension Credential {
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -165,7 +165,7 @@ extension PEMKey {
/// Returns a PEM-encoded string representation of this `PEMKey`.
/// - Returns: A string representing this key in PEM format.
public func pemEncoded() -> String {
let base64Data = keyData.base64EncodedString(options: [.lineLength64Characters])
let base64Data = keyData.base64EncodedString()
let beginMarker = "-----BEGIN \(keyType)-----"
let endMarker = "-----END \(keyType)-----"

Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -24,32 +24,13 @@ public protocol KeyRestoration {
/// - data: The raw data representing the key.
/// - Throws: If the restoration process fails, this method throws an error.
/// - Returns: The restored `PrivateKey` instance.
func restorePrivateKey(identifier: String?, data: Data) async throws -> PrivateKey
func restorePrivateKey(_ key: StorableKey) async throws -> PrivateKey

/// Restores a public key from the given data.
/// - Parameters:
/// - identifier: An optional string used to identify the key.
/// - data: The raw data representing the key.
/// - Throws: If the restoration process fails, this method throws an error.
/// - Returns: The restored `PublicKey` instance.
func restorePublicKey(identifier: String?, data: Data) async throws -> PublicKey
}

/// Extension of the KeyRestoration protocol to provide additional restoration methods.
public extension KeyRestoration {
/// Restores a private key from the given data, without requiring an identifier.
/// - Parameter data: The raw data representing the key.
/// - Throws: If the restoration process fails, this method throws an error.
/// - Returns: The restored PrivateKey instance.
func restorePrivateKey(data: Data) async throws -> PrivateKey {
try await restorePrivateKey(identifier: nil, data: data)
}

/// Restores a public key from the given data, without requiring an identifier.
/// - Parameter data: The raw data representing the key.
/// - Throws: If the restoration process fails, this method throws an error.
/// - Returns: The restored `PublicKey` instance.
func restorePublicKey(data: Data) async throws -> PublicKey {
try await restorePublicKey(identifier: nil, data: data)
}
func restorePublicKey(_ key: StorableKey) async throws -> PublicKey
}
Original file line number Diff line number Diff line change
Expand Up @@ -7,6 +7,9 @@ public protocol StorableKey {

/// The raw data representation of the key, suitable for storage.
var storableData: Data { get }

/// Indexation of the key is useful to keep track of a derivation index
var index: Int? { get }
}

/// Extension of the `Key` protocol to provide additional functionality related to storage.
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -7,4 +7,5 @@ protocol DIDPrivateKeyProvider {
func getDIDInfo(did: DID) -> AnyPublisher<(did: DID, privateKeys: [StorableKey], alias: String?)?, Error>
func getDIDInfo(alias: String) -> AnyPublisher<[(did: DID, privateKeys: [StorableKey], alias: String?)], Error>
func getPrivateKeys(did: DID) -> AnyPublisher<[StorableKey]?, Error>
func getLastKeyIndex() -> AnyPublisher<Int, Error>
}
Original file line number Diff line number Diff line change
Expand Up @@ -12,5 +12,4 @@ protocol DIDProvider {
func getDIDInfo(
keyPairIndex: Int
) -> AnyPublisher<(did: DID, keyPairIndex: Int, alias: String?)?, Error>
func getLastKeyPairIndex() -> AnyPublisher<Int, Error>
}
Original file line number Diff line number Diff line change
Expand Up @@ -4,4 +4,5 @@ import Foundation
struct StorableKeyModel: StorableKey {
let restorationIdentifier: String
let storableData: Data
let index: Int?
}
Original file line number Diff line number Diff line change
Expand Up @@ -56,6 +56,17 @@ extension CDDIDPrivateKeyDAO: DIDPrivateKeyProvider {
}
.eraseToAnyPublisher()
}

func getLastKeyIndex() -> AnyPublisher<Int, Error> {
keyDao.fetchController(
sorting: NSSortDescriptor(key: "index", ascending: true),
context: readContext
)
.map {
$0.first.map { $0.index?.intValue ?? 0 } ?? 0
}
.eraseToAnyPublisher()
}
}

extension CDKey {
Expand All @@ -79,12 +90,14 @@ extension CDKey {

return StorableKeyModel(
restorationIdentifier: keychainKey.restorationIdentifier,
storableData: keyData
storableData: keyData,
index: keychainKey.index?.intValue
)
case let databaseKey as CDDatabaseKey:
return StorableKeyModel(
restorationIdentifier: databaseKey.restorationIdentifier,
storableData: databaseKey.storableData
storableData: databaseKey.storableData,
index: databaseKey.index?.intValue
)
default:
throw UnknownError.somethingWentWrongError(
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -91,6 +91,7 @@ private extension CDDatabaseKey {
) {
self.identifier = identifier
self.storableData = key.storableData
self.index = key.index.map { NSNumber(integerLiteral: $0) }
self.restorationIdentifier = key.restorationIdentifier
}
}
Expand All @@ -104,6 +105,7 @@ private extension CDKeychainKey {
) {
self.identifier = identifier
self.restorationIdentifier = key.restorationIdentifier
self.index = key.index.map { NSNumber(integerLiteral: $0) }
self.type = key.keyClass.rawValue
self.algorithm = key.type.rawValue
self.service = service
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -6,6 +6,7 @@ struct CDDIDPrivateKeyDAO: CoreDataDAO {
typealias CoreDataObject = CDDIDPrivateKey
let keychain: KeychainStore & KeychainProvider
let keychainService: String
let keyDao: CDKeyDAO
let readContext: NSManagedObjectContext
let writeContext: NSManagedObjectContext
let identifierKey: String? = "did"
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -90,7 +90,7 @@ extension KeychainDAO: KeychainStore {
try key.getSecKeyAddItemDictionary(service: service, account: account, accessGroup: accessGroup),
nil
)
guard status == errSecSuccess else {
guard status == errSecSuccess || status == errSecDuplicateItem else {
throw PlutoError.errorSavingKeyOnKeychainWithStatus(status)
}
}
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -8,6 +8,7 @@ extension CDKey {

@NSManaged var identifier: String
@NSManaged var restorationIdentifier: String
@NSManaged var index: NSNumber?
@NSManaged var did: CDDIDPrivateKey?
}

Expand Down
2 changes: 1 addition & 1 deletion AtalaPrismSDK/Pluto/Sources/PlutoImpl+Public.swift
Original file line number Diff line number Diff line change
Expand Up @@ -75,7 +75,7 @@ extension PlutoImpl: Pluto {
}

public func getPrismLastKeyPairIndex() -> AnyPublisher<Int, Error> {
registeredDIDDao.getLastKeyPairIndex()
privateKeyDIDDao.getLastKeyIndex()
}

public func getAllPeerDIDs() -> AnyPublisher<[(did: DID, privateKeys: [StorableKey], alias: String?)], Error> {
Expand Down
4 changes: 4 additions & 0 deletions AtalaPrismSDK/Pluto/Sources/PlutoImpl.swift
Original file line number Diff line number Diff line change
Expand Up @@ -56,6 +56,10 @@ public struct PlutoImpl {
let privateKeyDao = CDDIDPrivateKeyDAO(
keychain: setup.keychain,
keychainService: setup.keychainService,
keyDao: CDKeyDAO(
readContext: manager.mainContext,
writeContext: manager.editContext
),
readContext: manager.mainContext,
writeContext: manager.editContext
)
Expand Down
Loading
Loading