@ew-did-registry/claims
Version:
The package exposes functionality needed to create, inspect, approve, and verify Private and Public claims
210 lines (187 loc) • 6.74 kB
text/typescript
import { decrypt } from 'eciesjs';
import chai, { expect } from 'chai';
import chaiAsPromised from 'chai-as-promised';
import { Keys, KeyType } from '@ew-did-registry/keys';
import { Methods } from '@ew-did-registry/did';
import {
Operator,
EwSigner,
compressedSecp256k1KeyLength,
} from '@ew-did-registry/did-ethr-resolver';
import { DidStore } from '@ew-did-registry/did-ipfs-store';
import { DIDDocumentFull } from '@ew-did-registry/did-document';
import {
DIDAttribute,
Encoding,
IUpdateData,
KeyTags,
ProviderSettings,
ProviderTypes,
PubKeyType,
} from '@ew-did-registry/did-resolver-interface';
import { ChildProcess } from 'child_process';
import {
ClaimsUser,
IClaimsUser,
IPrivateClaim,
IProofClaim,
ProofVerifier,
} from '../src';
import {
deployRegistry,
shutdownIpfsCluster,
spawnIpfsCluster,
} from '../../../tests';
chai.use(chaiAsPromised);
chai.should();
const claimData = {
name: 'John',
};
const providerSettings: ProviderSettings = {
type: ProviderTypes.HTTP,
};
describe('[CLAIMS PACKAGE/USER CLAIMS]', function () {
this.timeout(0);
const userKeys = new Keys();
const userAddress = userKeys.getAddress();
const userDid = `did:${Methods.Erc1056}:${userAddress}`;
const user = EwSigner.fromPrivateKey(userKeys.privateKey, providerSettings);
let userDoc: DIDDocumentFull;
const issuerKeys = new Keys();
const issuerAddress = issuerKeys.getAddress();
const issuerDid = `did:${Methods.Erc1056}:${issuerAddress}`;
const issuer = EwSigner.fromPrivateKey(
issuerKeys.privateKey,
providerSettings
);
const hexPrefixKeys = new Keys();
const hexPrefixAddress = hexPrefixKeys.getAddress();
let userClaims: IClaimsUser;
let store: DidStore;
let registry: string;
let cluster: ChildProcess;
before(async () => {
registry = await deployRegistry([
issuerAddress,
userAddress,
hexPrefixAddress,
]);
console.log(`registry: ${registry}`);
cluster = await spawnIpfsCluster();
store = new DidStore('http://localhost:8080');
userDoc = new DIDDocumentFull(
userDid,
new Operator(user, { address: registry })
);
userClaims = new ClaimsUser(user, userDoc, store);
const issuerDoc = new DIDDocumentFull(
issuerDid,
new Operator(issuer, { address: registry })
);
await userDoc.create();
await issuerDoc.create();
});
after(async () => {
shutdownIpfsCluster(cluster);
});
it('createPublicClaim should create token with claim data', async () => {
const publicData = {
name: 'John',
};
const token = await userClaims.createPublicClaim(publicData);
const proofVerifier = new ProofVerifier(await userDoc.read());
expect(await proofVerifier.verifyAssertionProof(token)).not.null;
const claim = (await userClaims.jwt.verify(
token,
userClaims.keys.publicKey
)) as Record<string, unknown>;
claim.should.deep.include({
did: userDid,
signer: userDid,
claimData: publicData,
});
});
it('creates and verifies 100 claims', async () => {
const claims: { token: string; data: object }[] = [];
for (let i = 0; i < 100; i++) {
const data = { name: 'John', lastName: 'Doe', index: i };
// eslint-disable-next-line no-await-in-loop
claims.push({ token: await userClaims.createPublicClaim(data), data });
}
// eslint-disable-next-line no-restricted-syntax
for (const { token, data } of claims) {
// eslint-disable-next-line no-await-in-loop
userClaims.verifyClaimContent(token, data);
const proofVerifier = new ProofVerifier(await userDoc.read());
expect(await proofVerifier.verifyAssertionProof(token)).not.null;
}
});
it('createPrivateToken should create token with data decryptable by issuer', async () => {
const secret = '123';
const { token, saltedFields } = await userClaims.createPrivateClaim(
{ secret },
issuerDid
);
const claim = userClaims.jwt.decode(token) as IPrivateClaim;
const decrypted = decrypt(
issuerKeys.privateKey,
Buffer.from(claim.claimData.secret as string, 'hex')
);
decrypted.toString().should.equal(saltedFields.secret);
});
it('createProofClaim should return well formed proof claim', async () => {
const claimUrl = 'http://test.com';
const proofData = { secret: { value: '123abc', encrypted: true } };
const token = await userClaims.createProofClaim(claimUrl, proofData);
const claim = (await userClaims.jwt.verify(
token,
userClaims.keys.publicKey
)) as IProofClaim;
claim.should.include({ did: userDid, signer: userDid, claimUrl });
claim.should.have.nested
.property('proofData.secret.value.h')
.which.instanceOf(Array);
claim.should.have.nested
.property('proofData.secret.value.s')
.which.instanceOf(Array);
});
it('self signed claim should be verified', async () => {
const claim = await userClaims.createPublicClaim(claimData);
const url = await userClaims.publishPublicClaim(claim, claimData);
return userClaims.verify(url).should.be.fulfilled;
});
/**
* publicKeyHex property of W3C security vocab does not have 0x prefix
* However, historically, some keys were added to DID Documents with a 0x prefix
* Therefore, the verification should be able to handle both.
* https://w3c-ccg.github.io/security-vocab/#publicKeyHex
* https://w3c-ccg.github.io/lds-ecdsa-secp256k1-2019/
* https://github.com/decentralized-identity/ethr-did-resolver/issues/140
*/
it('verifies claim of user with secp256k1 key with 0x prefix', async () => {
const did = `did:${Methods.Erc1056}:${hexPrefixAddress}`;
const signer = EwSigner.fromPrivateKey(
hexPrefixKeys.privateKey,
providerSettings
);
const doc = new DIDDocumentFull(
did,
new Operator(signer, { address: registry })
);
// Manually doing "create" as need to add hex prefix
const pubKey = signer.publicKey;
pubKey.length.should.equal(compressedSecp256k1KeyLength);
const updateData: IUpdateData = {
algo: KeyType.Secp256k1,
type: PubKeyType.VerificationKey2018,
encoding: Encoding.HEX,
// Adding hex prefix to simulate an owner key with a hex prefix
value: { publicKey: `0x${signer.publicKey}`, tag: KeyTags.OWNER },
};
await doc.update(DIDAttribute.PublicKey, updateData);
const userWithHexPrefixKey = new ClaimsUser(signer, doc, store);
const claim = await userWithHexPrefixKey.createPublicClaim(claimData);
const url = await userWithHexPrefixKey.publishPublicClaim(claim, claimData);
return userWithHexPrefixKey.verify(url).should.be.fulfilled;
});
});