Skip to content

Commit

Permalink
feat: support publicKeyMultibase (#200)
Browse files Browse the repository at this point in the history
  • Loading branch information
yhuard authored Sep 29, 2021
1 parent 6dc2803 commit 0f4a81c
Show file tree
Hide file tree
Showing 6 changed files with 42 additions and 7 deletions.
3 changes: 2 additions & 1 deletion package.json
Original file line number Diff line number Diff line change
Expand Up @@ -84,9 +84,10 @@
"@stablelib/x25519": "^1.0.1",
"@stablelib/xchacha20poly1305": "^1.0.1",
"canonicalize": "^1.0.5",
"did-resolver": "^3.1.0",
"did-resolver": "^3.1.1",
"elliptic": "^6.5.4",
"js-sha3": "^0.8.0",
"multiformats": "^9.4.8",
"uint8arrays": "^3.0.0"
},
"eslintIgnore": [
Expand Down
5 changes: 5 additions & 0 deletions src/VerifierAlgorithm.ts
Original file line number Diff line number Diff line change
Expand Up @@ -2,6 +2,7 @@ import { ec as EC, SignatureInput } from 'elliptic'
import { sha256, toEthereumAddress } from './Digest'
import { verify } from '@stablelib/ed25519'
import type { VerificationMethod } from 'did-resolver'
import { bases } from 'multiformats/basics'
import { hexToBytes, base58ToBytes, base64ToBytes, bytesToHex, EcdsaSignature, stringToBytes } from './util'

const secp256k1 = new EC('secp256k1')
Expand Down Expand Up @@ -41,6 +42,10 @@ function extractPublicKeyBytes(pk: VerificationMethod): Uint8Array {
})
.getPublic('hex')
)
} else if (pk.publicKeyMultibase) {
const { base16, base58btc, base64, base64url } = bases
const baseDecoder = base16.decoder.or(base58btc.decoder.or(base64.decoder.or(base64url.decoder)))
return baseDecoder.decode(pk.publicKeyMultibase)
}
return new Uint8Array()
}
Expand Down
21 changes: 20 additions & 1 deletion src/__tests__/VerifierAlgorithm.test.ts
Original file line number Diff line number Diff line change
Expand Up @@ -3,7 +3,7 @@ import { createJWT } from '../JWT'
import { toEthereumAddress } from '../Digest'
import nacl from 'tweetnacl'
import { ec as EC } from 'elliptic'
import { base64ToBytes, bytesToBase58, bytesToBase64, hexToBytes, bytesToBase64url } from '../util'
import { base64ToBytes, bytesToBase58, bytesToBase64, hexToBytes, bytesToBase64url, bytesToMultibase } from '../util'
import * as u8a from 'uint8arrays'
import { EdDSASigner } from '../signers/EdDSASigner'
import { ES256KSigner } from '../signers/ES256KSigner'
Expand Down Expand Up @@ -42,6 +42,7 @@ const publicKeyJwk = {
x: bytesToBase64url(hexToBytes(kp.getPublic().getX().toString('hex'))),
y: bytesToBase64url(hexToBytes(kp.getPublic().getY().toString('hex'))),
}
const publicKeyMultibase = bytesToMultibase(hexToBytes(publicKey), 'base58btc')
const address = toEthereumAddress(publicKey)
const signer = ES256KSigner(privateKey)
const recoverySigner = ES256KSigner(privateKey, true)
Expand Down Expand Up @@ -169,6 +170,15 @@ describe('ES256K', () => {
return expect(verifier(parts[1], parts[2], [pubkey])).toEqual(pubkey)
})

it('validates with publicKeyMultibase', async () => {
expect.assertions(1)
const jwt = await createJWT({ bla: 'bla' }, { issuer: did, signer })
const parts = jwt.match(/^([a-zA-Z0-9_-]+\.[a-zA-Z0-9_-]+)\.([a-zA-Z0-9_-]+)$/)
const pubkey = Object.assign({ publicKeyMultibase }, ecKey2)
delete pubkey.publicKeyHex
return expect(verifier(parts[1], parts[2], [pubkey])).toEqual(pubkey)
})

it('validates signature with compressed public key and picks correct public key', async () => {
expect.assertions(1)
const jwt = await createJWT({ bla: 'bla' }, { issuer: did, signer })
Expand Down Expand Up @@ -287,6 +297,15 @@ describe('ES256K-R', () => {
return expect(verifier(parts[1], parts[2], [pubkey])).toEqual(pubkey)
})

it('validates with publicKeyMultibase', async () => {
expect.assertions(1)
const jwt = await createJWT({ bla: 'bla' }, { issuer: did, signer: recoverySigner, alg: 'ES256K-R' })
const parts = jwt.match(/^([a-zA-Z0-9_-]+\.[a-zA-Z0-9_-]+)\.([a-zA-Z0-9_-]+)$/)
const pubkey = Object.assign({ publicKeyMultibase }, ecKey2)
delete pubkey.publicKeyHex
return expect(verifier(parts[1], parts[2], [pubkey])).toEqual(pubkey)
})

it('throws error if invalid signature', async () => {
expect.assertions(1)
const jwt = await createJWT({ bla: 'bla' }, { issuer: did, signer: recoverySigner, alg: 'ES256K-R' })
Expand Down
5 changes: 5 additions & 0 deletions src/util.ts
Original file line number Diff line number Diff line change
@@ -1,4 +1,5 @@
import * as u8a from 'uint8arrays'
import { bases } from 'multiformats/basics'

/**
* @deprecated Signers will be expected to return base64url `string` signatures.
Expand Down Expand Up @@ -30,6 +31,10 @@ export function bytesToBase58(b: Uint8Array): string {
return u8a.toString(b, 'base58btc')
}

export function bytesToMultibase(b: Uint8Array, base: keyof typeof bases): string {
return bases[base].encode(b)
}

export function hexToBytes(s: string): Uint8Array {
const input = s.startsWith('0x') ? s.substring(2) : s
return u8a.fromString(input.toLowerCase(), 'base16')
Expand Down
2 changes: 1 addition & 1 deletion src/xc20pEncryption.ts
Original file line number Diff line number Diff line change
Expand Up @@ -3,7 +3,7 @@ import { generateKeyPair, sharedKey } from '@stablelib/x25519'
import { randomBytes } from '@stablelib/random'
import { concatKDF } from './Digest'
import { bytesToBase64url, base58ToBytes, encodeBase64url, toSealed, base64ToBytes } from './util'
import { Recipient, EncryptionResult, Encrypter, Decrypter, RecipientHeader, ProtectedHeader } from './JWE'
import { Recipient, EncryptionResult, Encrypter, Decrypter, ProtectedHeader } from './JWE'
import type { VerificationMethod, Resolvable } from 'did-resolver'
import { ECDH } from './ECDH'

Expand Down
13 changes: 9 additions & 4 deletions yarn.lock
Original file line number Diff line number Diff line change
Expand Up @@ -3468,10 +3468,10 @@ dezalgo@^1.0.0:
asap "^2.0.0"
wrappy "1"

did-resolver@^3.1.0:
version "3.1.0"
resolved "https://registry.yarnpkg.com/did-resolver/-/did-resolver-3.1.0.tgz#84f0e3d16abe9711dc04c34a5a0e2f63868c9611"
integrity sha512-uf3/4LfHoDn3Ek8bFegO72OemHMytBhAkud6CA1HlB5I0iVwrCpG4oh+DA6DXUuBdhrA0cY4cGz1D0VNDTlgnw==
did-resolver@^3.1.1:
version "3.1.1"
resolved "https://registry.yarnpkg.com/did-resolver/-/did-resolver-3.1.1.tgz#1a4fb3b0ea7fe083a6af1be8e60a73d3db0db38b"
integrity sha512-6K7Uh/Ewfob3J8Zkprq89InYY0tDu3qu/Nv3TTvL66h9ipRGJB3D5arWIJUwrczFzyuZwu/DXwgKl2PjRnYEUw==

diff-sequences@^27.0.1:
version "27.0.1"
Expand Down Expand Up @@ -6211,6 +6211,11 @@ multiformats@^9.4.2:
resolved "https://registry.yarnpkg.com/multiformats/-/multiformats-9.4.5.tgz#9ac47bbc87aadb09d4bd05e9cd3da6f4436414f6"
integrity sha512-zQxukxsHM34EJi3yT3MkUlycY9wEouyrAz0PSN+CyCj6cYchJZ4LrTH74YtlsxVyAK6waz/gnVLmJwi3P0knKg==

multiformats@^9.4.8:
version "9.4.8"
resolved "https://registry.yarnpkg.com/multiformats/-/multiformats-9.4.8.tgz#46f74ec116f7871f2a334cc6d4ad3d810dbac341"
integrity sha512-EOJL02/kv+FD5hoItMhKgkYUUruJYMYFq4NQ6YkCh3jVQ5CuHo+OKdHeR50hAxEQmXQ9yvrM9BxLIk42xtfwnQ==

mute-stream@~0.0.4:
version "0.0.8"
resolved "https://registry.yarnpkg.com/mute-stream/-/mute-stream-0.0.8.tgz#1630c42b2251ff81e2a283de96a5497ea92e5e0d"
Expand Down

0 comments on commit 0f4a81c

Please sign in to comment.