Skip to content

Commit

Permalink
Unit test for VerifiablePresentationBuilder to examplify use
Browse files Browse the repository at this point in the history
  • Loading branch information
soerenbf committed Oct 2, 2024

Verified

This commit was created on GitHub.com and signed with GitHub’s verified signature. The key has expired.
1 parent d6d468c commit 74983b5
Showing 3 changed files with 102 additions and 18 deletions.
16 changes: 16 additions & 0 deletions Sources/Concordium/Domain/Queries.swift
Original file line number Diff line number Diff line change
@@ -14,6 +14,22 @@ extension CryptographicParameters: FromGRPC {
}
}

extension CryptographicParameters: @retroactive Decodable {
private enum CodingKeys: CodingKey {
case onChainCommitmentKey
case bulletproofGenerators
case genesisString
}

public init(from decoder: any Decoder) throws {
let container = try decoder.container(keyedBy: CodingKeys.self)
let key = try Data(hex: container.decode(String.self, forKey: .onChainCommitmentKey))
let gen = try Data(hex: container.decode(String.self, forKey: .bulletproofGenerators))
let genesisString = try container.decode(String.self, forKey: .genesisString)
self = .init(onChainCommitmentKey: key, bulletproofGenerators: gen, genesisString: genesisString)
}
}

/// Describes the possible status variants of a transaction submitted to a node.
public enum TransactionStatus {
case received
50 changes: 32 additions & 18 deletions Sources/Concordium/Proofs/Web3Id.swift
Original file line number Diff line number Diff line change
@@ -433,7 +433,7 @@ extension Web3IdCredential: Codable {

init(id: String) {
self.id = id
self.type = "JsonSchema2023"
type = "JsonSchema2023"
}
}

@@ -450,9 +450,9 @@ extension Web3IdCredential: Codable {
let verificationMethod: DID // .publicKey

init(proofValue: Data, verificationMethod: DID) {
self.proofPurpose = "assertionMethod"
proofPurpose = "assertionMethod"
self.proofValue = proofValue.hex
self.type = "Ed25519Signature2020"
type = "Ed25519Signature2020"
self.verificationMethod = verificationMethod
}
}
@@ -462,21 +462,21 @@ extension Web3IdCredential: Codable {
var container = encoder.singleValueContainer()
let dateFormatter = getDateFormatter()

let schema = JSON.CredentialSchema(id: self.credentialSchema)
let subject = JSON.CredentialSubject(attributes: self.values, id: DID(network: self.network, idType: IdentifierType.publicKey(key: self.holderId)))
let id = DID(network: self.network, idType: .contractData(address: self.registry, entrypoint: "credentialEntry", parameter: self.holderId))
let issuer = DID(network: self.network, idType: .contractData(address: self.registry, entrypoint: "issuer", parameter: Data()))
let proof = JSON.Proof(proofValue: self.signature, verificationMethod: DID(network: self.network, idType: .publicKey(key: self.issuerKey)))
let schema = JSON.CredentialSchema(id: credentialSchema)
let subject = JSON.CredentialSubject(attributes: values, id: DID(network: network, idType: IdentifierType.publicKey(key: holderId)))
let id = DID(network: network, idType: .contractData(address: registry, entrypoint: "credentialEntry", parameter: holderId))
let issuer = DID(network: network, idType: .contractData(address: registry, entrypoint: "issuer", parameter: Data()))
let proof = JSON.Proof(proofValue: signature, verificationMethod: DID(network: network, idType: .publicKey(key: issuerKey)))
let json = JSON(
credentialSchema: schema,
credentialSubject: subject,
id: id,
issuer: issuer,
proof: proof,
randomness: self.randomness.mapValues(\.hex),
type: self.credentialType,
validFrom: dateFormatter.string(from: self.validFrom),
validUntil: self.validUntil.map { dateFormatter.string(from: $0) }
randomness: randomness.mapValues(\.hex),
type: credentialType,
validFrom: dateFormatter.string(from: validFrom),
validUntil: validUntil.map { dateFormatter.string(from: $0) }
)
try container.encode(json)
}
@@ -488,7 +488,7 @@ extension Web3IdCredential: Codable {

let validFrom = try dateFormatter.date(from: json.validFrom) ?! DecodingError.dataCorruptedError(in: container, debugDescription: "Expected 'validFrom' to contain ISO8601 formatted date")
let validUntil = try json.validUntil.map {
try dateFormatter.date(from: $0) ?! DecodingError.dataCorruptedError(in: container, debugDescription: "Expected 'validFrom' to contain ISO8601 formatted date")
try dateFormatter.date(from: $0) ?! DecodingError.dataCorruptedError(in: container, debugDescription: "Expected 'validFrom' to contain ISO8601 formatted date")
}
let signature = try Data(hex: json.proof.proofValue)

@@ -499,14 +499,14 @@ extension Web3IdCredential: Codable {

let holderId: Data
switch json.credentialSubject.id.idType {
case .publicKey(let key): holderId = key
case let .publicKey(key): holderId = key
default:
throw DecodingError.dataCorruptedError(in: container, debugDescription: "Field 'id' must be smart contract DID with entrypoint 'credentialEntry'")
}

let issuerKey: Data
switch json.proof.verificationMethod.idType {
case .publicKey(let key): issuerKey = key
case let .publicKey(key): issuerKey = key
default:
throw DecodingError.dataCorruptedError(in: container, debugDescription: "Field 'id' must be smart contract DID with entrypoint 'credentialEntry'")
}
@@ -572,15 +572,29 @@ public struct VerifiablePresentationBuilder {
}

public mutating func verify(_ statement: [AtomicStatementV1], for idObject: IdentityObject, cred: AccountCredentialWithRandomness, issuer: UInt32) throws {
try verify(statement, for: idObject, credId: CredentialRegistrationID(cred.credential.credId), randomness: cred.randomness, issuer: issuer)
try verify(statement, values: idObject.attributeList.chosenAttributes, randomness: cred.randomness.attributesRand, credId: CredentialRegistrationID(cred.credential.credId), issuer: issuer)
}

public mutating func verify(_ statement: [AtomicStatementV1], for idObject: IdentityObject, credId: CredentialRegistrationID, randomness: Randomness, issuer: UInt32) throws {
try verify(statement, values: idObject.attributeList.chosenAttributes, randomness: randomness.attributesRand, credId: credId, issuer: issuer)
}

public mutating func verify(_ statement: [AtomicStatementV1], for values: [AttributeTag: String], wallet: WalletSeed, credIndices: AccountCredentialSeedIndexes, global: CryptographicParameters) throws {
let attributes = statement.map(\.attributeTag)
let randomness = try attributes.reduce(into: [AttributeTag: Data]()) { acc, tag in
acc[tag] = try wallet.attributeCommitmentRandomness(accountCredentialIndexes: credIndices, attribute: tag.rawValue)
}
let credId = try wallet.id(accountCredentialIndexes: credIndices, commitmentKey: global.onChainCommitmentKey)
try verify(statement, values: values, randomness: randomness, credId: credId, issuer: credIndices.identity.providerID)
}

public mutating func verify(_ statement: [AtomicStatementV1], values: [AttributeTag: String], randomness: [AttributeTag: Data], credId: CredentialRegistrationID, issuer: UInt32) throws {
let attributes = statement.map(\.attributeTag)
let (values, randomness) = try attributes.reduce(into: ([AttributeTag: String](), [AttributeTag: Data]())) { acc, tag in
acc.0[tag] = try idObject.attributeList.chosenAttributes[tag] ?! VerifiablePresentationBuilderError.missingValue(tag: tag.description)
acc.1[tag] = try randomness.attributesRand[tag] ?! VerifiablePresentationBuilderError.missingRandomness(tag: tag.description)
acc.0[tag] = try values[tag] ?! VerifiablePresentationBuilderError.missingValue(tag: tag.description)
acc.1[tag] = try randomness[tag] ?! VerifiablePresentationBuilderError.missingRandomness(tag: tag.description)
}

let verifiableStatement = VerifiableCredentialStatement.account(network: network, credId: credId.value, statement: statement)
let commitmentInputs = VerifiableCredentialCommitmentInputs.account(issuer: issuer, values: values, randomness: randomness)
statements.insert(PresentationInput(statement: verifiableStatement, commitmentInputs: commitmentInputs))
54 changes: 54 additions & 0 deletions Tests/ConcordiumTests/Proofs/Web3IdTest.swift
Original file line number Diff line number Diff line change
@@ -232,6 +232,60 @@ final class Web3IdTest: XCTestCase {

let parsed = try decoder.decode(Web3IdCredential.self, from: json)
XCTAssertEqual(parsed, try decoder.decode(Web3IdCredential.self, from: encoder.encode(parsed)))
}

// Cryptographic values have been generated with the rust SDK.
func testProveStatement() throws {
let network = Network.testnet
let challenge = try Data(hex: "94d3e85bbc8ff0091e562ad8ef6c30d57f29b19f17c98ce155df2a30100dAAAA")

let accountStatements = [
AtomicStatementV1.attributeInSet(statement: AttributeInSetStatementV1(attributeTag: .nationality, set: ["DK", "NO", "SE", "FI"])),
AtomicStatementV1.attributeInRange(statement: AttributeInRangeStatementV1(attributeTag: .dateOfBirth, lower: "16000101", upper: "20000101")),
]
let web3IdStatements = [
AtomicStatementV2.attributeNotInSet(statement: AttributeNotInSetStatementV2(attributeTag: "tag", set: [.numeric(value: 0), .numeric(value: 1), .numeric(value: 2)])),
AtomicStatementV2.attributeInRange(statement: AttributeInRangeStatementV2(attributeTag: "other", lower: .timestamp(value: .init(timeIntervalSince1970: 1_000_000_000)), upper: .timestamp(value: .init(timeIntervalSince1970: 1_727_870_991)))),
]

let idValues: [AttributeTag: String] = [
.nationality: "DK",
.dateOfBirth: "18000101",
]
let idRand: [AttributeTag: Data] = try [
.dateOfBirth: Data(hex: "237205f67b5dd0b87a3f45bea51e55de0dda64af6082dceafc43855a317249d6"),
.nationality: Data(hex: "25de8bfad4a90ca67d7c0be368355b3401f24dc2172f05b610001299d3c84898"),
]
let credId = try CredentialRegistrationID(Data(hex: "94b55cf320993917927c77c1e8037b868e279ca7a97905115516c8366d8ad1fc27bbe5fd9c87e1ac27d7166216f6afbc"))
let issuer = UInt32(17)

let web3Values: [String: Web3IdAttribute] = [
"tag": .numeric(value: 3),
"other": .timestamp(value: .init(timeIntervalSince1970: 1_727_870_891)),
]
let web3Rand: [String: Data] = try [
"other": Data(hex: "2a989b5b483bcb7adcf070617c83c8daea8536282946b616bb78db179ed606aa"),
"tag": Data(hex: "6a0951d214b82be6a26f6f5abadff0f22788f6b5bf5c08f1799d65addcc464e4"),
]

let web3IdCred = try Web3IdCredential(
holderId: Data(hex: "1dce1025acbc8002ee9403f635073b49f1b93cb43cf9509fefc43d14bedb6834"),
network: Network.testnet,
registry: ContractAddress(index: 1337, subindex: 42),
credentialType: ["VerifiableCredential", "TestCredential", "ConcordiumVerifiableCredential"],
credentialSchema: "N/A",
issuerKey: Data(hex: "c954ee1e182b2ff7cdefd25bfab25437d184e45c20df0a94b9e8f60143929575"),
validFrom: Date(),
validUntil: nil,
values: web3Values,
randomness: web3Rand,
signature: Data(hex: "7013df1d6d7a260b2a6c4a2eabe62ab2595cf01a65be0a846cf54d0f24464ad181d47f41ebe1f3fb3b7d78208ba4dc56b8930d0b6218d35d608ddc0a773dbf0c")
)
let signer = try Data(hex: "cfd166ecd740d2aefab053c1dca27e01ad55d3c4a730bced4e3a8ed476ff2fbc")

var builder = VerifiablePresentationBuilder(challenge: challenge, network: network)
try builder.verify(accountStatements, values: idValues, randomness: idRand, credId: credId, issuer: issuer)
try builder.verify(web3IdStatements, for: web3IdCred, signer: signer)
let _ = try builder.finalize(global: GLOBAL)
}
}

0 comments on commit 74983b5

Please sign in to comment.