Skip to content

Commit

Permalink
Add ability to generate() a did from a secret key seed.
Browse files Browse the repository at this point in the history
  • Loading branch information
dmitrizagidulin committed Jan 26, 2022
1 parent 016374f commit da6e726
Show file tree
Hide file tree
Showing 5 changed files with 102 additions and 7 deletions.
5 changes: 5 additions & 0 deletions CHANGELOG.md
Original file line number Diff line number Diff line change
@@ -1,5 +1,10 @@
# did-web-driver ChangeLog

## 2.2.0 -

### Added
- Add ability to `.generate()` from a secret key seed.

## 2.1.1 - 2022-01-19

### Changed
Expand Down
33 changes: 32 additions & 1 deletion README.md
Original file line number Diff line number Diff line change
Expand Up @@ -58,9 +58,40 @@ resolver.use(didWebDriver)

### Generating a new DID

#### Generating from seed

If you have a deterministic secret seed (created with [`did-cli`](https://github.com/digitalcredentials/did-cli),
for example) and would like to generate a `did:web` document from it:

```js
const { didDocument, keyPairs, methodFor } = await didWebDriver.generate()
const url = 'https://example.com'
const seed = 'z1AhV1bADy7RepJ64mvH7Kk7htFNGc7EA1WA5nGzLSTWc6o'

const { didDocument, keyPairs, methodFor } = await didWebDriver.generate({ url, seed })
// didDocument
{
'@context': [
'https://www.w3.org/ns/did/v1',
'https://w3id.org/security/suites/ed25519-2020/v1',
'https://w3id.org/security/suites/x25519-2020/v1'
],
id: 'did:web:example.com',
assertionMethod: [{
id: 'did:web:example.com#z6MkmDMjfkjs9XPCN1LfoQQRHz1mJ8PEdiVYC66XKhj3wGyB',
type: 'Ed25519VerificationKey2020',
controller: 'did:web:example.com',
publicKeyMultibase: 'z6MkmDMjfkjs9XPCN1LfoQQRHz1mJ8PEdiVYC66XKhj3wGyB'
}]
}
```

#### Generating new random keys

Invoking `generate()` by itself will create new keypairs for each proof purpose.

```js
const { didDocument, keyPairs, methodFor } = await didWebDriver.generate()
// didDocument
{
'@context': [
'https://www.w3.org/ns/did/v1',
Expand Down
7 changes: 4 additions & 3 deletions package.json
Original file line number Diff line number Diff line change
Expand Up @@ -43,6 +43,7 @@
"./package.json": "./package.json"
},
"dependencies": {
"@digitalcredentials/bnid": "^2.1.1",
"@digitalcredentials/did-io": "^1.0.2",
"@digitalcredentials/http-client": "^1.2.2",
"did-context": "^3.1.1",
Expand All @@ -61,8 +62,8 @@
"chai": "^4.3.4",
"cross-env": "^7.0.3",
"crypto-ld": "^6.0.0",
"esm": "^3.2.25",
"dirty-chai": "^2.0.1",
"esm": "^3.2.25",
"karma": "^6.3.9",
"karma-babel-preprocessor": "^8.0.1",
"karma-chai": "^0.1.0",
Expand All @@ -73,10 +74,10 @@
"karma-webpack": "^5.0.0",
"mocha": "^8.4.0",
"nyc": "^15.1.0",
"sinon": "^12.0.1",
"standard": "^16.0.4",
"rimraf": "^3.0.2",
"rollup": "^2.62.0",
"sinon": "^12.0.1",
"standard": "^16.0.4",
"webpack": "^5.65.0"
},
"nyc": {
Expand Down
49 changes: 46 additions & 3 deletions src/DidWebResolver.js
Original file line number Diff line number Diff line change
Expand Up @@ -3,6 +3,7 @@ import * as didIo from '@digitalcredentials/did-io'
import ed25519Context from 'ed25519-signature-2020-context'
import x25519Context from 'x25519-key-agreement-2020-context'
import didContext from 'did-context'
import { decodeSecretKeySeed } from '@digitalcredentials/bnid'

const { VERIFICATION_RELATIONSHIPS } = didIo

Expand Down Expand Up @@ -103,7 +104,7 @@ export function urlFromDid ({ did } = {}) {
* DID Document initialized with keys, as well as the map of the corresponding
* key pairs (by key id).
*/
export async function initKeys ({ didDocument, cryptoLd, keyMap = {} } = {}) {
export async function initKeys ({ didDocument, cryptoLd, keyMap } = {}) {
const doc = { ...didDocument }
if (!doc.id) {
throw new TypeError(
Expand Down Expand Up @@ -167,7 +168,7 @@ export class DidWebResolver {
* @param [id] {string} - A did:web DID. If absent, will be converted from url
* @param [url] {string}
*
* @param [keyMap=DEFAULT_KEY_MAP] {object} A hashmap of key types by purpose.
* @param [keyMap] {object} A hashmap of key types by purpose.
*
* @parma [cryptoLd] {object} CryptoLD instance with support for supported
* crypto suites installed.
Expand All @@ -177,9 +178,27 @@ export class DidWebResolver {
* with the corresponding key pairs used to generate it (for storage in a
* KMS).
*/
async generate ({ id, url, keyMap = this.keyMap, cryptoLd = this.cryptoLd } = {}) {
async generate ({ id, url, seed, keyMap, cryptoLd = this.cryptoLd } = {}) {
if (!id && !url) {
throw new TypeError('A "url" or an "id" parameter is required.')
}
if (seed && keyMap) {
throw new TypeError(
'Either a "seed" or a "keyMap" param must be provided, but not both.'
)
}

const did = id || didFromUrl({ url })

if (seed) {
const keyPair = await _keyPairFromSecretSeed({
seed, controller: did, cryptoLd
})
keyMap = { assertionMethod: keyPair }
} else {
keyMap = keyMap || this.keyMap
}

// Compose the DID Document
let didDocument = {
'@context': [
Expand Down Expand Up @@ -300,3 +319,27 @@ export class DidWebResolver {
return method
}
}

/**
* @param options {object}
* @param options.seed {string|Uint8Array}
* @param controller {string}
* @param cryptoLd {object}
*
* @return {Promise<LDKeyPair>}
*/
async function _keyPairFromSecretSeed ({ seed, controller, cryptoLd } = {}) {
let seedBytes
if (typeof seed === 'string') {
// Currently only supports base58 multibase / identity multihash encoding.
if (!seed.startsWith('z1A')) {
throw new TypeError('"seed" parameter must be a multibase/multihash encoded string, or a Uint8Array.')
}
seedBytes = decodeSecretKeySeed({ secretKeySeed: seed })
} else {
seedBytes = new Uint8Array(seed)
}
return cryptoLd.generate({
controller, seed: seedBytes, type: 'Ed25519VerificationKey2020'
})
}
15 changes: 15 additions & 0 deletions test/unit/DidWebResolver.spec.js
Original file line number Diff line number Diff line change
Expand Up @@ -73,6 +73,21 @@ describe('DidWebDriver', () => {
expect(keyAgreementKey).to.have.property('publicKeyMultibase')
expect(keyAgreementKey).to.have.property('privateKeyMultibase')
})

it('should generate from seed', async () => {
const seed = 'z1AhV1bADy7RepJ64mvH7Kk7htFNGc7EA1WA5nGzLSTWc6o'
const url = 'https://example.com'
const { didDocument, methodFor } = await didWeb.generate({ url, seed })

expect(didDocument).to.have.property('id', 'did:web:example.com')

const assertionKey = methodFor({ purpose: 'assertionMethod' })
expect(assertionKey).to.have.property('id', 'did:web:example.com#z6MkmDMjfkjs9XPCN1LfoQQRHz1mJ8PEdiVYC66XKhj3wGyB')
expect(assertionKey).to.have.property('type', 'Ed25519VerificationKey2020')
expect(assertionKey).to.have.property('controller', 'did:web:example.com')
expect(assertionKey).to.have.property('publicKeyMultibase', 'z6MkmDMjfkjs9XPCN1LfoQQRHz1mJ8PEdiVYC66XKhj3wGyB')
expect(assertionKey).to.have.property('privateKeyMultibase')
})
})

describe('urlFromDid()', () => {
Expand Down

0 comments on commit da6e726

Please sign in to comment.