Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

fix(credential-eip712): add support for all did methods that use secp256k #1011

Merged
merged 6 commits into from
Oct 3, 2022
Merged
Show file tree
Hide file tree
Changes from 1 commit
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
6 changes: 3 additions & 3 deletions packages/core/plugin.schema.json
Original file line number Diff line number Diff line change
Expand Up @@ -3224,7 +3224,7 @@
"save": {
"type": "boolean",
"description": "Optional. If set to `true`, the message will be saved using\n {@link @veramo/core#IDataStore.dataStoreSaveMessage | dataStoreSaveMessage } \n<p/><p/>",
"deprecated": "Please call {@link @veramo/core#IDataStore.dataStoreSaveMessage | dataStoreSaveMessage()} after\nhandling the message and determining that it must be saved."
"deprecated": "Please call {@link @veramo/core#IDataStore.dataStoreSaveMessage | dataStoreSaveMessage()} after\r\nhandling the message and determining that it must be saved."
}
},
"required": [
Expand Down Expand Up @@ -3560,7 +3560,7 @@
"save": {
"type": "boolean",
"description": "If this parameter is true, the resulting VerifiablePresentation is sent to the\n {@link @veramo/core#IDataStore | storage plugin } to be saved.",
"deprecated": "Please call\n{@link @veramo/core#IDataStore.dataStoreSaveVerifiableCredential | dataStoreSaveVerifiableCredential()} to save\nthe credential after creating it."
"deprecated": "Please call\r\n{@link @veramo/core#IDataStore.dataStoreSaveVerifiableCredential | dataStoreSaveVerifiableCredential()} to save\r\nthe credential after creating it."
},
"proofFormat": {
"$ref": "#/components/schemas/ProofFormat",
Expand Down Expand Up @@ -3763,7 +3763,7 @@
"save": {
"type": "boolean",
"description": "If this parameter is true, the resulting VerifiablePresentation is sent to the\n {@link @veramo/core#IDataStore | storage plugin } to be saved. <p/><p/>",
"deprecated": "Please call\n{@link @veramo/core#IDataStore.dataStoreSaveVerifiablePresentation | dataStoreSaveVerifiablePresentation()} to\nsave the credential after creating it."
"deprecated": "Please call\r\n{@link @veramo/core#IDataStore.dataStoreSaveVerifiablePresentation | dataStoreSaveVerifiablePresentation()} to\r\nsave the credential after creating it."
},
"challenge": {
"type": "string",
Expand Down
4 changes: 2 additions & 2 deletions packages/credential-eip712/package.json
Original file line number Diff line number Diff line change
Expand Up @@ -16,9 +16,9 @@
"dependencies": {
"@metamask/eth-sig-util": "^4.0.1",
"@veramo/core": "^4.0.0",
"@veramo/utils": "^4.0.0",
"debug": "^4.3.3",
"eip-712-types-generation": "^0.1.6"
"eip-712-types-generation": "^0.1.6",
"@veramo/utils": "^4.0.0"
},
"devDependencies": {
"@types/debug": "4.1.7",
Expand Down
9 changes: 6 additions & 3 deletions packages/credential-eip712/src/agent/CredentialEIP712.ts
Original file line number Diff line number Diff line change
Expand Up @@ -85,7 +85,9 @@ export class CredentialIssuerEIP712 implements IAgentPlugin {
if (!extendedKey)
throw Error('key_not_found: The signing key is not available in the issuer DID document')

const chainId = getChainIdForDidEthr(extendedKey.meta.verificationMethod)
let chainId = 1
if (identifier.did.split(':')[1] === 'ethr')
chainId = getChainIdForDidEthr(extendedKey.meta.verificationMethod)

const credential: CredentialPayload = {
...args?.credential,
Expand Down Expand Up @@ -235,8 +237,9 @@ export class CredentialIssuerEIP712 implements IAgentPlugin {
const extendedKey = extendedKeys.find((key) => key.kid === keyRef)
if (!extendedKey)
throw Error('key_not_found: The signing key is not available in the issuer DID document')

const chainId = getChainIdForDidEthr(extendedKey.meta.verificationMethod)
let chainId = 1
if (identifier.did.split(':')[1] === 'ethr')
chainId = getChainIdForDidEthr(extendedKey.meta.verificationMethod)
presentation['proof'] = {
verificationMethod: extendedKey.meta.verificationMethod.id,
created: issuanceDate,
Expand Down
2 changes: 1 addition & 1 deletion packages/did-comm/plugin.schema.json
Original file line number Diff line number Diff line change
Expand Up @@ -188,7 +188,7 @@
"required": [
"data"
],
"deprecated": "Please use {@link IDIDComm.sendDIDCommMessage } instead. This will be removed in Veramo 4.0.\nInput arguments for {@link IDIDComm.sendMessageDIDCommAlpha1 }"
"deprecated": "Please use {@link IDIDComm.sendDIDCommMessage } instead. This will be removed in Veramo 4.0.\r\nInput arguments for {@link IDIDComm.sendMessageDIDCommAlpha1 }"
},
"IMessage": {
"type": "object",
Expand Down
6 changes: 4 additions & 2 deletions packages/utils/package.json
Original file line number Diff line number Diff line change
Expand Up @@ -19,12 +19,14 @@
"did-jwt-vc": "^3.1.0",
"did-resolver": "^4.0.0",
"uint8arrays": "^3.0.0",
"uuid": "^8.3.0"
"uuid": "^8.3.0",
"elliptic": "^6.5.4"
},
"devDependencies": {
"@types/debug": "4.1.7",
"@types/uuid": "8.3.4",
"typescript": "4.7.3"
"typescript": "4.7.3",
"@types/elliptic": "6.4.14"
},
"files": [
"build/**/*",
Expand Down
95 changes: 89 additions & 6 deletions packages/utils/src/did-utils.ts
Original file line number Diff line number Diff line change
Expand Up @@ -10,7 +10,10 @@ import {
} from './types/utility-types'
import { isDefined } from './type-utils'
import * as u8a from 'uint8arrays'
import elliptic from 'elliptic'
import Debug from 'debug'
import sha3 from 'js-sha3'
import { publicKeyConvert } from 'secp256k1'
mirceanis marked this conversation as resolved.
Show resolved Hide resolved

const debug = Debug('veramo:utils')

Expand Down Expand Up @@ -80,11 +83,12 @@ export function compressIdentifierSecp256k1Keys(identifier: IIdentifier): IKey[]
*
* @beta This API may change without a BREAKING CHANGE notice.
*/
function compareBlockchainAccountId(
localKey: IKey,
verificationMethod: _NormalizedVerificationMethod,
): boolean {
if (verificationMethod.type !== 'EcdsaSecp256k1RecoveryMethod2020' || localKey.type !== 'Secp256k1') {
function compareBlockchainAccountId(localKey: IKey, verificationMethod: VerificationMethod): boolean {
if (
(verificationMethod.type !== 'EcdsaSecp256k1RecoveryMethod2020' &&
verificationMethod.type !== 'EcdsaSecp256k1RecoveryMethod2019') ||
localKey.type !== 'Secp256k1'
) {
return false
}
let vmEthAddr = getEthereumAddress(verificationMethod)
Expand All @@ -105,17 +109,96 @@ function compareBlockchainAccountId(
*
* @beta This API may change without a BREAKING CHANGE notice.
*/
export function getEthereumAddress(verificationMethod: _NormalizedVerificationMethod): string | undefined {
export function getEthereumAddress(verificationMethod: VerificationMethod): string | undefined {
let vmEthAddr = verificationMethod.ethereumAddress?.toLowerCase()
if (!vmEthAddr) {
if (verificationMethod.blockchainAccountId?.includes('@eip155')) {
vmEthAddr = verificationMethod.blockchainAccountId?.split('@eip155')[0].toLowerCase()
} else if (verificationMethod.blockchainAccountId?.startsWith('eip155')) {
vmEthAddr = verificationMethod.blockchainAccountId.split(':')[2]?.toLowerCase()
} else if (
verificationMethod.publicKeyHex ||
verificationMethod.publicKeyBase58 ||
verificationMethod.publicKeyBase64
) {
const pbBytes = extractPublicKeyBytes(verificationMethod)
let pbHex = bytesToHex(pbBytes)
if (pbHex.substring(0, 2) === '02' || pbHex.substring(0, 2) === '03') {
pbHex = getUncompressedPublicKey(pbHex as string)
}
vmEthAddr = toEthereumAddress(pbHex).toLowerCase()
}
}
return vmEthAddr
}
interface LegacyVerificationMethod extends VerificationMethod {
publicKeyBase64: string
}

function extractPublicKeyBytes(pk: VerificationMethod): Uint8Array {
if (pk.publicKeyBase58) {
return base58ToBytes(pk.publicKeyBase58)
} else if ((<LegacyVerificationMethod>pk).publicKeyBase64) {
return base64ToBytes((<LegacyVerificationMethod>pk).publicKeyBase64)
} else if (pk.publicKeyHex) {
return hexToBytes(pk.publicKeyHex)
} else if (
pk.publicKeyJwk &&
pk.publicKeyJwk.crv === 'secp256k1' &&
pk.publicKeyJwk.x &&
pk.publicKeyJwk.y
) {
const secp256k1 = new elliptic.ec('secp256k1')
return hexToBytes(
secp256k1
.keyFromPublic({
x: bytesToHex(base64ToBytes(pk.publicKeyJwk.x)),
y: bytesToHex(base64ToBytes(pk.publicKeyJwk.y)),
})
.getPublic('hex'),
)
}
return new Uint8Array()
}

function base64ToBytes(s: string): Uint8Array {
const inputBase64Url = s.replace(/\+/g, '-').replace(/\//g, '_').replace(/=/g, '')
return u8a.fromString(inputBase64Url, 'base64url')
}

function base58ToBytes(s: string): Uint8Array {
return u8a.fromString(s, 'base58btc')
}

function hexToBytes(s: string): Uint8Array {
const input = s.startsWith('0x') ? s.substring(2) : s
return u8a.fromString(input.toLowerCase(), 'base16')
}

function bytesToHex(b: Uint8Array): string {
return u8a.toString(b, 'base16')
}
mirceanis marked this conversation as resolved.
Show resolved Hide resolved

function toEthereumAddress(hexPublicKey: string): string {
mirceanis marked this conversation as resolved.
Show resolved Hide resolved
const hashInput = u8a.fromString(hexPublicKey.slice(2), 'base16')
return `0x${u8a.toString(keccak(hashInput).slice(-20), 'base16')}`
}

function keccak(data: Uint8Array): Uint8Array {
return new Uint8Array(sha3.keccak_256.arrayBuffer(data))
}

export function getUncompressedPublicKey(publicKey: string): string {
console.log('publicKey', publicKey)
return _uint8ArrayToHex(publicKeyConvert(_hexToUnit8Array(publicKey), false))
}
mirceanis marked this conversation as resolved.
Show resolved Hide resolved
export function _uint8ArrayToHex(arr: any) {
return Buffer.from(arr).toString('hex')
}

export function _hexToUnit8Array(str: any) {
return new Uint8Array(Buffer.from(str, 'hex'))
}
mirceanis marked this conversation as resolved.
Show resolved Hide resolved

/**
* Extracts the chain ID from a {@link did-resolver#VerificationMethod | verification method} supporting legacy
Expand Down
14 changes: 14 additions & 0 deletions yarn.lock
Original file line number Diff line number Diff line change
Expand Up @@ -4594,6 +4594,13 @@
dependencies:
"@types/node" "*"

"@types/bn.js@*":
version "5.1.1"
resolved "https://registry.yarnpkg.com/@types/bn.js/-/bn.js-5.1.1.tgz#b51e1b55920a4ca26e9285ff79936bbdec910682"
integrity sha512-qNrYbZqMx0uJAfKnKclPh+dTwK33KfLHYqtyODwd5HnXOjnkhc4qgn3BrK6RWyGZm5+sIFE7Q7Vz6QQtJB7w7g==
dependencies:
"@types/node" "*"

"@types/bn.js@^4.11.3":
version "4.11.6"
resolved "https://registry.yarnpkg.com/@types/bn.js/-/bn.js-4.11.6.tgz#c306c70d9358aaea33cd4eda092a742b9505967c"
Expand Down Expand Up @@ -4660,6 +4667,13 @@
dependencies:
"@types/ms" "*"

"@types/elliptic@6.4.14":
version "6.4.14"
resolved "https://registry.yarnpkg.com/@types/elliptic/-/elliptic-6.4.14.tgz#7bbaad60567a588c1f08b10893453e6b9b4de48e"
integrity sha512-z4OBcDAU0GVwDTuwJzQCiL6188QvZMkvoERgcVjq0/mPM8jCfdwZ3x5zQEVoL9WCAru3aG5wl3Z5Ww5wBWn7ZQ==
dependencies:
"@types/bn.js" "*"

"@types/eslint-scope@^3.7.3":
version "3.7.3"
resolved "https://registry.yarnpkg.com/@types/eslint-scope/-/eslint-scope-3.7.3.tgz#125b88504b61e3c8bc6f870882003253005c3224"
Expand Down