Skip to content

Commit

Permalink
add key rotation use case test
Browse files Browse the repository at this point in the history
  • Loading branch information
daveroga committed Apr 8, 2024
1 parent cc25463 commit a451485
Show file tree
Hide file tree
Showing 3 changed files with 306 additions and 3 deletions.
5 changes: 5 additions & 0 deletions src/identity/identity-wallet.ts
Original file line number Diff line number Diff line change
Expand Up @@ -1391,6 +1391,11 @@ export class IdentityWallet implements IIdentityWallet {
prover?: IZKProver // it will be needed in case of non ethereum identities
): Promise<W3CCredential> {
opts.seed = opts.seed ?? getRandomBytes(32);
opts.revocationOpts.nonce =
opts.revocationOpts.nonce ??
(isOldStateGenesis
? 0
: opts.revocationOpts.nonce ?? new DataView(getRandomBytes(12).buffer).getUint32(0));

const { authClaim, pubKey } = await this.createAuthCoreClaim(
opts.revocationOpts.nonce ?? 0,
Expand Down
301 changes: 300 additions & 1 deletion tests/handlers/auth.test.ts
Original file line number Diff line number Diff line change
Expand Up @@ -39,7 +39,8 @@ import {
W3CCredential,
Sec256k1Provider,
StateInfo,
hexToBytes
hexToBytes,
NativeProver
} from '../../src';
import { Token } from '@iden3/js-jwz';
import { Blockchain, DID, DidMethod, NetworkId } from '@iden3/js-iden3-core';
Expand Down Expand Up @@ -1911,4 +1912,302 @@ describe('auth', () => {

await authHandler.handleAuthorizationResponse(response, request, testOpts);
});

it('auth flow identity (profile) with circuits V3', async () => {
const profileDID = await idWallet.createProfile(userDID, 777, issuerDID.string());

const claimReq: CredentialRequest = {
credentialSchema:
'https://raw.githubusercontent.com/iden3/claim-schema-vocab/main/schemas/json/kyc-nonmerklized.json',
type: 'KYCAgeCredential',
credentialSubject: {
id: userDID.string(),
birthday: 19960424,
documentType: 99
},
expiration: 2793526400,
revocationOpts: {
type: CredentialStatusType.Iden3ReverseSparseMerkleTreeProof,
id: RHS_URL
}
};
const issuerCred = await idWallet.issueCredential(issuerDID, claimReq);
const employeeCredRequest: CredentialRequest = {
credentialSchema:
'https://raw.githubusercontent.com/iden3/claim-schema-vocab/main/schemas/json/KYCEmployee-v101.json',
type: 'KYCEmployee',
credentialSubject: {
id: profileDID.string(),
ZKPexperiance: true,
hireDate: '2023-12-11',
position: 'boss',
salary: 200,
documentType: 1
},
revocationOpts: {
type: CredentialStatusType.Iden3ReverseSparseMerkleTreeProof,
id: RHS_URL
}
};
const employeeCred = await idWallet.issueCredential(issuerDID, employeeCredRequest);

await credWallet.saveAll([employeeCred, issuerCred]);

const res = await idWallet.addCredentialsToMerkleTree([employeeCred], issuerDID);
await idWallet.publishStateToRHS(issuerDID, RHS_URL);

const ethSigner = new ethers.Wallet(
WALLET_KEY,
(dataStorage.states as EthStateStorage).provider
);

const txId = await proofService.transitState(
issuerDID,
res.oldTreeState,
true,
dataStorage.states,
ethSigner
);

const credsWithIden3MTPProof = await idWallet.generateIden3SparseMerkleTreeProof(
issuerDID,
res.credentials,
txId
);

await credWallet.saveAll(credsWithIden3MTPProof);

const proofReqs: ZeroKnowledgeProofRequest[] = [
{
id: 1,
circuitId: CircuitId.AtomicQueryV3,
optional: false,
query: {
proofType: ProofType.BJJSignature,
allowedIssuers: ['*'],
type: 'KYCAgeCredential',
context:
'https://raw.githubusercontent.com/iden3/claim-schema-vocab/main/schemas/json-ld/kyc-nonmerklized.jsonld',
credentialSubject: {
documentType: {
$eq: 99
}
}
}
},
{
id: 2,
circuitId: CircuitId.LinkedMultiQuery10,
optional: false,
query: {
groupId: 1,
proofType: ProofType.Iden3SparseMerkleTreeProof,
allowedIssuers: ['*'],
type: 'KYCEmployee',
context:
'https://raw.githubusercontent.com/iden3/claim-schema-vocab/main/schemas/json-ld/kyc-v101.json-ld',
credentialSubject: {
documentType: {
$eq: 1
},
position: {
$eq: 'boss',
$ne: 'employee'
}
}
}
},
{
id: 3,
circuitId: CircuitId.AtomicQueryV3,
optional: false,
query: {
groupId: 1,
proofType: ProofType.BJJSignature,
allowedIssuers: ['*'],
type: 'KYCEmployee',
context:
'https://raw.githubusercontent.com/iden3/claim-schema-vocab/main/schemas/json-ld/kyc-v101.json-ld',
credentialSubject: {
hireDate: {
$eq: '2023-12-11'
}
}
},
params: {
nullifierSessionId: '12345'
}
}
];

const authReqBody: AuthorizationRequestMessageBody = {
callbackUrl: 'http://localhost:8080/callback?id=1234442-123123-123123',
reason: 'reason',
message: 'mesage',
did_doc: {},
scope: proofReqs
};

const id = uuid.v4();
const authReq: AuthorizationRequestMessage = {
id,
typ: PROTOCOL_CONSTANTS.MediaType.PlainMessage,
type: PROTOCOL_CONSTANTS.PROTOCOL_MESSAGE_TYPE.AUTHORIZATION_REQUEST_MESSAGE_TYPE,
thid: id,
body: authReqBody,
from: issuerDID.string()
};

const msgBytes = byteEncoder.encode(JSON.stringify(authReq));
const authRes = await authHandler.handleAuthorizationRequest(userDID, msgBytes);
// console.log(JSON.stringify(authRes.authResponse));
const tokenStr = authRes.token;
// console.log(tokenStr);
expect(tokenStr).to.be.a('string');
const token = await Token.parse(tokenStr);
expect(token).to.be.a('object');
});

it.only('key rotation use case', async () => {
const claimReq: CredentialRequest = {
credentialSchema:
'https://raw.githubusercontent.com/iden3/claim-schema-vocab/main/schemas/json/kyc-nonmerklized.json',
type: 'KYCAgeCredential',
credentialSubject: {
id: userDID.string(),
birthday: 19960424,
documentType: 99
},
expiration: 2793526400,
revocationOpts: {
type: CredentialStatusType.Iden3ReverseSparseMerkleTreeProof,
id: RHS_URL
}
};
const issuerCred = await idWallet.issueCredential(issuerDID, claimReq);

await credWallet.save(issuerCred);

const proofReq: ZeroKnowledgeProofRequest = {
id: 1,
circuitId: CircuitId.AtomicQuerySigV2,
optional: false,
query: {
allowedIssuers: ['*'],
type: claimReq.type,
context:
'https://raw.githubusercontent.com/iden3/claim-schema-vocab/main/schemas/json-ld/kyc-nonmerklized.jsonld',
credentialSubject: {
documentType: {
$eq: 99
}
}
}
};

const authReqBody: AuthorizationRequestMessageBody = {
callbackUrl: 'http://localhost:8080/callback?id=1234442-123123-123123',
reason: 'reason',
message: 'mesage',
did_doc: {},
scope: [proofReq as ZeroKnowledgeProofRequest]
};

const handleAuthorizationRequest = async (
userDID: DID,
authReqBody: AuthorizationRequestMessageBody
) => {
const id = uuid.v4();
const authReq: AuthorizationRequestMessage = {
id,
typ: PROTOCOL_CONSTANTS.MediaType.PlainMessage,
type: PROTOCOL_CONSTANTS.PROTOCOL_MESSAGE_TYPE.AUTHORIZATION_REQUEST_MESSAGE_TYPE,
thid: id,
body: authReqBody,
from: issuerDID.string()
};

const msgBytes = byteEncoder.encode(JSON.stringify(authReq));
const authRes = await authHandler.handleAuthorizationRequest(userDID, msgBytes);
expect(authRes.token).to.be.a('string');
const token = await Token.parse(authRes.token);
expect(token).to.be.a('object');
};

await handleAuthorizationRequest(userDID, authReqBody);

// add second Bjj auth credential
const circuitStorage = new FSCircuitStorage({
dirname: path.join(__dirname, '../proofs/testdata')
});
const prover = new NativeProver(circuitStorage);

const ethSigner = new ethers.Wallet(
WALLET_KEY,
(dataStorage.states as EthStateStorage).provider
);
const opts = {
seed: SEED_USER,
revocationOpts: {
type: CredentialStatusType.Iden3ReverseSparseMerkleTreeProof,
id: RHS_URL
}
};

const treesModel = await idWallet.getDIDTreeModel(issuerDID);
const [ctrHex, rtrHex, rorTrHex] = await Promise.all([
treesModel.claimsTree.root(),
treesModel.revocationTree.root(),
treesModel.rootsTree.root()
]);

const oldTreeState = {
state: treesModel.state,
claimsRoot: ctrHex,
revocationRoot: rtrHex,
rootOfRoots: rorTrHex
};

// add k2 auth credential (we have k1 already)
const credential2 = await idWallet.addBJJAuthCredential(
issuerDID,
oldTreeState,
false,
ethSigner,
opts,
prover
);

expect(credential2?.proof).not.to.be.undefined;

// get actual auth credential (k1)
const { authCredential: issuerAuthCredential } = await idWallet.getActualAuthCredential(
issuerDID
);

// revoke k1 auth credential
const nonce = await idWallet.revokeCredential(issuerDID, issuerAuthCredential);
await idWallet.publishStateToRHS(issuerDID, RHS_URL, [nonce]);

await handleAuthorizationRequest(userDID, authReqBody);

// get actual auth credential (k2)
const { authCredential: issuerAuthCredential2 } = await idWallet.getActualAuthCredential(
issuerDID
);

expect(issuerAuthCredential2).to.be.deep.equal(credential2);

// revoke k2 auth credential
const nonce2 = await idWallet.revokeCredential(issuerDID, issuerAuthCredential2);
await idWallet.publishStateToRHS(issuerDID, RHS_URL, [nonce2]);

// check that we don't have auth credentials now
await expect(idWallet.getActualAuthCredential(issuerDID)).to.rejectedWith(
'no auth credentials found'
);

// should this work?
await handleAuthorizationRequest(userDID, authReqBody);
});
});
3 changes: 1 addition & 2 deletions tests/identity/id.test.ts
Original file line number Diff line number Diff line change
Expand Up @@ -318,8 +318,7 @@ describe('identity', () => {
const afterRevokeProofNRcredential = await idWallet.generateNonRevocationMtp(did, credential);
expect(afterRevokeProofNRcredential.proof.existence).to.equal(true);

// credential2 was generated with sigproof from credential, so it should be revoked as well
const afterRevokeProofNRcredential2 = await idWallet.generateNonRevocationMtp(did, credential2);
expect(afterRevokeProofNRcredential2.proof.existence).to.equal(true);
expect(afterRevokeProofNRcredential2.proof.existence).to.equal(false);
});
});

0 comments on commit a451485

Please sign in to comment.