diff --git a/README.md b/README.md index c6482e28..1f370659 100644 --- a/README.md +++ b/README.md @@ -36,12 +36,13 @@ Options: -h, --help display help for command Commands: - new-wallet Create new wallet from a recovery phrase + create-wallet Create new wallet (or show existing wallet) + import-wallet Create new wallet from a recovery phrase show-wallet Show wallet show-mnemonic Show recovery phrase for wallet backup-wallet Backup wallet to encrypted DID recover-wallet Recover wallet from encrypted DID - create-id Create a new decentralized ID + create-id [registry] Create a new decentralized ID resolve-id Resolves the current ID backup-id Backup the current ID to its registry recover-id Recovers the ID from the DID @@ -57,11 +58,10 @@ Commands: sign-file Sign a JSON file verify-file Verify the signature in a JSON file create-credential [name] Create credential from schema file - create-challenge [name] Create challenge from a file + create-challenge [file] [name] Create challenge (optionally from a file) create-challenge-cc [name] Create challenge from a credential DID - issue-challenge Issue a challenge to a user bind-credential Create bound credential for a user - attest-credential [name] Sign and encrypt a bound credential file + attest-credential [registry] [name] Sign and encrypt a bound credential file revoke-credential Revokes a verifiable credential accept-credential Save verifiable credential for current ID publish-credential Publish the existence of a credential to the current user manifest diff --git a/keychain-cli.js b/keychain-cli.js index 1aa6ce86..aee84070 100644 --- a/keychain-cli.js +++ b/keychain-cli.js @@ -285,11 +285,12 @@ program }); program - .command('create-challenge [name]') - .description('Create challenge from a file') + .command('create-challenge [file] [name]') + .description('Create challenge (optionally from a file)') .action(async (file, name) => { try { - const challenge = JSON.parse(fs.readFileSync(file).toString()); + const defaultChallenge = { credentials: [] }; + const challenge = file ? JSON.parse(fs.readFileSync(file).toString()) : defaultChallenge; const did = await keymaster.createChallenge(challenge); if (name) { @@ -323,20 +324,6 @@ program } }); -program - .command('issue-challenge ') - .description('Issue a challenge to a user') - .action(async (challenge, user) => { - try { - const did = await keymaster.issueChallenge(challenge, user); - console.log(did); - } - catch (error) { - console.error(error); - } - }); - - program .command('bind-credential ') .description('Create bound credential for a user') diff --git a/keymaster.js b/keymaster.js index 36a6714c..f3c5e14b 100644 --- a/keymaster.js +++ b/keymaster.js @@ -657,25 +657,6 @@ export async function createChallenge(challenge) { return createData(challenge); } -export async function issueChallenge(challenge, holder, expiresIn = 24) { - const id = getCurrentId(); - const now = new Date(); - const expires = new Date(); - expires.setHours(now.getHours() + expiresIn); - const challengeDID = lookupDID(challenge); - const holderDID = lookupDID(holder); - const issue = { - challenge: challengeDID, - from: id.did, - to: holderDID, - validFrom: now.toISOString(), - validUntil: expires.toISOString(), - }; - const signed = await addSignature(issue); - const cipherDid = await encryptJSON(signed, holderDID); - return cipherDid; -} - async function findMatchingCredential(credential) { const id = getCurrentId(); @@ -723,15 +704,20 @@ async function findMatchingCredential(credential) { } export async function createResponse(did) { - const id = getCurrentId(); const challenge = lookupDID(did); - const boundChallenge = await decryptJSON(challenge); - if (!boundChallenge.challenge || boundChallenge.to !== id.did) { + if (!challenge) { + throw "Invalid challenge"; + } + + const doc = await resolveDID(challenge); + const requestor = doc.didDocument.controller; + const { credentials } = await resolveAsset(challenge); + + if (!credentials) { throw "Invalid challenge"; } - const { credentials } = await resolveAsset(boundChallenge.challenge); const matches = []; for (let credential of credentials) { @@ -750,24 +736,37 @@ export async function createResponse(did) { for (let vcDid of matches) { const plaintext = await decrypt(vcDid); - const vpDid = await encrypt(plaintext, boundChallenge.from); + const vpDid = await encrypt(plaintext, requestor); pairs.push({ vc: vcDid, vp: vpDid }); } - const responseDid = await createData({ - challenge: did, + const requested = credentials.length; + const fulfilled = matches.length; + const match = (requested === fulfilled); + const response = { + challenge: challenge, credentials: pairs, - }); + requested: requested, + fulfilled: fulfilled, + match: match, + }; + + const responseDid = await encryptJSON(response, requestor); return responseDid; } export async function verifyResponse(did) { - const response = lookupDID(did); - const { credentials } = await resolveAsset(response); + const responseDID = lookupDID(did); + + if (!responseDID) { + throw "Invalid response"; + } + + const response = await decryptJSON(responseDID); const vps = []; - for (let credential of credentials) { + for (let credential of response.credentials) { const vcData = await resolveAsset(credential.vc); const vpData = await resolveAsset(credential.vp); diff --git a/keymaster.test.js b/keymaster.test.js index 188450d0..f9e4afb8 100644 --- a/keymaster.test.js +++ b/keymaster.test.js @@ -1185,49 +1185,6 @@ describe('createChallenge', () => { }); }); -describe('issueChallenge', () => { - - afterEach(() => { - mockFs.restore(); - }); - - it('should create a challenge bound to a user', async () => { - mockFs({}); - - const alice = await keymaster.createId('Alice'); - const bob = await keymaster.createId('Bob'); - - keymaster.useId('Alice'); - - const credentialDid = await keymaster.createCredential(mockSchema); - const challenge = { - credentials: [ - { - schema: credentialDid, - attestors: [alice, bob] - } - ] - }; - const challengeDid = await keymaster.createChallenge(challenge); - const challengeForBob = await keymaster.issueChallenge(challengeDid, bob); - const boundChallenge = await keymaster.decryptJSON(challengeForBob); - - expect(boundChallenge.challenge).toBe(challengeDid); - expect(boundChallenge.from).toBe(alice); - expect(boundChallenge.to).toBe(bob); - - const isValid = await keymaster.verifySignature(boundChallenge); - expect(isValid).toBe(true); - - const validFrom = new Date(boundChallenge.validFrom); - const validUntil = new Date(boundChallenge.validUntil); - const now = new Date(); - - expect(validFrom < now).toBe(true); - expect(validUntil > now).toBe(true); - }); -}); - describe('createResponse', () => { afterEach(() => { @@ -1267,14 +1224,12 @@ describe('createResponse', () => { ] }; const challengeDid = await keymaster.createChallenge(challenge); - const challengeForBob = await keymaster.issueChallenge(challengeDid, bob); keymaster.useId('Bob'); - const vpDid = await keymaster.createResponse(challengeForBob); - const vpDoc = await keymaster.resolveDID(vpDid); - const data = vpDoc.didDocumentMetadata.data; + const vpDid = await keymaster.createResponse(challengeDid); + const data = await keymaster.decryptJSON(vpDid); - expect(data.challenge).toBe(challengeForBob); + expect(data.challenge).toBe(challengeDid); expect(data.credentials.length).toBe(1); expect(data.credentials[0].vc).toBe(vcDid); }); @@ -1346,13 +1301,12 @@ describe('verifyResponse', () => { ] }; const challengeDid = await keymaster.createChallenge(challenge); - const challengeForCarol = await keymaster.issueChallenge(challengeDid, carol); keymaster.useId('Carol'); - const vpDid = await keymaster.createResponse(challengeForCarol); - const data = await keymaster.resolveAsset(vpDid); + const vpDid = await keymaster.createResponse(challengeDid); + const data = await keymaster.decryptJSON(vpDid); - expect(data.challenge).toBe(challengeForCarol); + expect(data.challenge).toBe(challengeDid); expect(data.credentials.length).toBe(4); keymaster.useId('Victor');