Skip to content

Commit

Permalink
Merge pull request #103 from blockchain-certificates/feat/support-mul…
Browse files Browse the repository at this point in the history
…tikey-format

feat(Multikey): support multikey format for pub key representation
  • Loading branch information
lemoustachiste authored Sep 2, 2024
2 parents 8ffcf83 + ab3a198 commit eb73ab2
Show file tree
Hide file tree
Showing 6 changed files with 169 additions and 25 deletions.
121 changes: 120 additions & 1 deletion package-lock.json

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

1 change: 1 addition & 0 deletions package.json
Original file line number Diff line number Diff line change
Expand Up @@ -10,6 +10,7 @@
"dependencies": {
"@blockcerts/explorer-lookup": "^1.5.2",
"@blockcerts/schemas": "^3.6.4",
"@digitalbazaar/ecdsa-multikey": "github:blockchain-certificates/ecdsa-multikey#feat/secp256k1",
"@trust/keyto": "^1.0.1",
"@vaultie/lds-merkle-proof-2019": "^0.0.12",
"bitcoinjs-lib": "^6.0.2",
Expand Down
2 changes: 1 addition & 1 deletion src/index.ts
Original file line number Diff line number Diff line change
Expand Up @@ -288,7 +288,7 @@ export class LDMerkleProof2019 extends LinkedDataProof {
private async deriveIssuingAddressFromPublicKey (): Promise<void> {
this.derivedIssuingAddress = await this.executeStep(
'deriveIssuingAddressFromPublicKey',
() => deriveIssuingAddressFromPublicKey(this.verificationMethod, this.chain),
async () => await deriveIssuingAddressFromPublicKey(this.verificationMethod, this.chain),
this.type
);
}
Expand Down
12 changes: 9 additions & 3 deletions src/inspectors/deriveIssuingAddressFromPublicKey.ts
Original file line number Diff line number Diff line change
@@ -1,14 +1,20 @@
import type { IDidDocumentPublicKey } from '@decentralized-identity/did-common-typescript';
import { publicKeyUInt8ArrayFromJwk } from '../utils/keyUtils';
import { publicKeyUInt8ArrayFromJwk, publicKeyUInt8ArrayFromMultibase } from '../utils/keyUtils';
import { computeBitcoinAddressFromPublicKey, computeEthereumAddressFromPublicKey } from '../utils/issuingAddress';
import getText from '../helpers/getText';
import VerifierError from '../models/VerifierError';
import { SupportedChains } from '@blockcerts/explorer-lookup';
import type { IBlockchainObject } from '@blockcerts/explorer-lookup';
import type { ISecp256k1PublicKeyJwk } from '../utils/keyUtils';

export default function deriveIssuingAddressFromPublicKey (verificationMethodPublicKey: IDidDocumentPublicKey, chain: IBlockchainObject): string {
const publicKey = publicKeyUInt8ArrayFromJwk(verificationMethodPublicKey.publicKeyJwk as ISecp256k1PublicKeyJwk);
export default async function deriveIssuingAddressFromPublicKey (verificationMethodPublicKey: IDidDocumentPublicKey, chain: IBlockchainObject): Promise<string> {
let publicKey;
if ('publicKeyJwk' in verificationMethodPublicKey) {
publicKey = publicKeyUInt8ArrayFromJwk(verificationMethodPublicKey.publicKeyJwk as ISecp256k1PublicKeyJwk);
} else if ('publicKeyMultibase' in verificationMethodPublicKey) {
publicKey = await publicKeyUInt8ArrayFromMultibase(verificationMethodPublicKey);
}

const baseError = getText('errors', 'identityErrorBaseMessage');
let address: string = '';
switch (chain.code) {
Expand Down
7 changes: 7 additions & 0 deletions src/utils/keyUtils.ts
Original file line number Diff line number Diff line change
Expand Up @@ -5,6 +5,8 @@ import bs58 from 'bs58';
import secp256k1 from 'secp256k1';
import { Buffer as BufferPolyfill } from 'buffer';
import canonicalize from 'canonicalize';
// @ts-expect-error not a TS package
import * as multikey from '@digitalbazaar/ecdsa-multikey';

const buffer = typeof Buffer === 'undefined' ? BufferPolyfill : Buffer;

Expand Down Expand Up @@ -197,6 +199,11 @@ export const publicKeyUInt8ArrayFromJwk = (jwk: ISecp256k1PublicKeyJwk): Buffer
return asBuffer;
};

export const publicKeyUInt8ArrayFromMultibase = async (publicKeyMultibase): Promise<Buffer> => {
const pubKeyJwk = await multikey.toJwk({ keyPair: publicKeyMultibase });
return publicKeyUInt8ArrayFromJwk(pubKeyJwk);
};

/** convert publicKeyHex to base58 */
export const publicKeyBase58FromPublicKeyHex = (publicKeyHex: string): string => {
return bs58.encode(buffer.from(publicKeyHex, 'hex'));
Expand Down
51 changes: 31 additions & 20 deletions tests/inspectors/deriveIssuingAddressFromPublicKey.test.ts
Original file line number Diff line number Diff line change
Expand Up @@ -16,66 +16,77 @@ describe('deriveIssuingAddressFromPublicKey test suite', function () {
});

describe('given the argument chain was Bitcoin', function () {
it('should return the address of Bitcoin Mainnet', function () {
const address = deriveIssuingAddressFromPublicKey(publicKey, BLOCKCHAINS.bitcoin);
it('should return the address of Bitcoin Mainnet', async function () {
const address = await deriveIssuingAddressFromPublicKey(publicKey, BLOCKCHAINS.bitcoin);
expect(address).toBe('127ZSsk5cWiubyDBkocJdW9dFYLN5N1jHF');
});
});

describe('given the argument chain was Mocknet', function () {
it('should return the address of Bitcoin Mocknet', function () {
const address = deriveIssuingAddressFromPublicKey(publicKey, BLOCKCHAINS.mocknet);
it('should return the address of Bitcoin Mocknet', async function () {
const address = await deriveIssuingAddressFromPublicKey(publicKey, BLOCKCHAINS.mocknet);
expect(address).toBe('127ZSsk5cWiubyDBkocJdW9dFYLN5N1jHF');
});
});

describe('given the argument chain was Testnet', function () {
it('should return the address of Bitcoin Testnet', function () {
const address = deriveIssuingAddressFromPublicKey(publicKey, BLOCKCHAINS.testnet);
it('should return the address of Bitcoin Testnet', async function () {
const address = await deriveIssuingAddressFromPublicKey(publicKey, BLOCKCHAINS.testnet);
expect(address).toBe('mgdWjvq4RYAAP5goUNagTRMx7Xw534S5am');
});
});

describe('given the argument chain was Ethmain', function () {
it('should return the address of Ethereum', function () {
const address = deriveIssuingAddressFromPublicKey(publicKey, BLOCKCHAINS.ethmain);
it('should return the address of Ethereum', async function () {
const address = await deriveIssuingAddressFromPublicKey(publicKey, BLOCKCHAINS.ethmain);
expect(address).toBe('0x40cf9b7db6fcc742ad0a76b8588c7f8de2b54a60');
});
});

describe('given the argument chain was Ethropst', function () {
it('should return the address of Ethereum', function () {
const address = deriveIssuingAddressFromPublicKey(publicKey, BLOCKCHAINS.ethropst);
it('should return the address of Ethereum', async function () {
const address = await deriveIssuingAddressFromPublicKey(publicKey, BLOCKCHAINS.ethropst);
expect(address).toBe('0x40cf9b7db6fcc742ad0a76b8588c7f8de2b54a60');
});
});

describe('given the argument chain was Ethrinkeby', function () {
it('should return the address of Ethereum', function () {
const address = deriveIssuingAddressFromPublicKey(publicKey, BLOCKCHAINS.ethrinkeby);
it('should return the address of Ethereum', async function () {
const address = await deriveIssuingAddressFromPublicKey(publicKey, BLOCKCHAINS.ethrinkeby);
expect(address).toBe('0x40cf9b7db6fcc742ad0a76b8588c7f8de2b54a60');
});
});

describe('given the argument chain was Ethgoerli', function () {
it('should return the address of Ethereum', function () {
const address = deriveIssuingAddressFromPublicKey(publicKey, BLOCKCHAINS.ethgoerli);
it('should return the address of Ethereum', async function () {
const address = await deriveIssuingAddressFromPublicKey(publicKey, BLOCKCHAINS.ethgoerli);
expect(address).toBe('0x40cf9b7db6fcc742ad0a76b8588c7f8de2b54a60');
});
});

describe('given the argument chain was Ethsepolia', function () {
it('should return the address of Ethereum', function () {
const address = deriveIssuingAddressFromPublicKey(publicKey, BLOCKCHAINS.ethsepolia);
it('should return the address of Ethereum', async function () {
const address = await deriveIssuingAddressFromPublicKey(publicKey, BLOCKCHAINS.ethsepolia);
expect(address).toBe('0x40cf9b7db6fcc742ad0a76b8588c7f8de2b54a60');
});
});

describe('given the argument chain was Regtest (Unsupported)', function () {
it('should throw', function () {
expect(() => {
deriveIssuingAddressFromPublicKey(publicKey, BLOCKCHAINS.regtest);
}).toThrow('Issuer identity mismatch - Unsupported blockchain for DID verification');
it('should throw', async function () {
await expect(async () => {
await deriveIssuingAddressFromPublicKey(publicKey, BLOCKCHAINS.regtest);
}).rejects.toThrow('Issuer identity mismatch - Unsupported blockchain for DID verification');
});
});

describe('given the key format was Multikey', function () {
it('should return the address of Bitcoin Mainnet', async function () {
const publicKey = {
publicKeyMultibase: 'zQ3shw8MAkueKou9VhRyX1v2hDQ2WENWVQNkg6ifhn8DG1gQW'
};

const address = await deriveIssuingAddressFromPublicKey(publicKey, BLOCKCHAINS.bitcoin);
expect(address).toBe('1862cjGVHodmYyvfumSgrgfnWcpCMHK9sq');
});
});
});

0 comments on commit eb73ab2

Please sign in to comment.