Skip to content

Commit

Permalink
feat(anoncreds): add support for anoncreds issuance flow
Browse files Browse the repository at this point in the history
Fixes ATL-4294
  • Loading branch information
goncalo-frade-iohk committed Oct 12, 2023
1 parent 90c56ce commit 579c603
Show file tree
Hide file tree
Showing 148 changed files with 2,147 additions and 4,549 deletions.
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>
}
1 change: 1 addition & 0 deletions AtalaPrismSDK/Pluto/Sources/Domain/StorableKeyModel.swift
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

0 comments on commit 579c603

Please sign in to comment.