Add public key deserialization (#341)
* add `multiformats` dep * add `deserialize-public-key.ts` * compress by default * add tests * Update packages/status-js/src/utils/deserialize-public-key.ts Co-authored-by: Pavel <14926950+prichodko@users.noreply.github.com> * rename vars --------- Co-authored-by: Pavel <14926950+prichodko@users.noreply.github.com>
This commit is contained in:
parent
1faeb69ac1
commit
6b3ef713e9
|
@ -35,7 +35,8 @@
|
||||||
"dependencies": {
|
"dependencies": {
|
||||||
"@bufbuild/protobuf": "^1.0.0",
|
"@bufbuild/protobuf": "^1.0.0",
|
||||||
"ethereum-cryptography": "^1.0.3",
|
"ethereum-cryptography": "^1.0.3",
|
||||||
"js-waku": "^0.30.0"
|
"js-waku": "^0.30.0",
|
||||||
|
"multiformats": "^11.0.1"
|
||||||
},
|
},
|
||||||
"devDependencies": {
|
"devDependencies": {
|
||||||
"@bufbuild/protoc-gen-es": "^1.0.0"
|
"@bufbuild/protoc-gen-es": "^1.0.0"
|
||||||
|
|
|
@ -0,0 +1,45 @@
|
||||||
|
import { expect, test } from 'vitest'
|
||||||
|
|
||||||
|
import { deserializePublicKey } from './deserialize-public-key'
|
||||||
|
|
||||||
|
test('should deserialize public key from compressed base58btc encoding', () => {
|
||||||
|
expect(
|
||||||
|
deserializePublicKey('zQ3shY7r4cAdg4eUF5dfcuCqCFzWmdjHW4SX5hspM9ucAarfU')
|
||||||
|
).toEqual(
|
||||||
|
'0x029f196bbfef4fa6a5eb81dd802133a63498325445ca1af1d154b1bb4542955133'
|
||||||
|
)
|
||||||
|
})
|
||||||
|
|
||||||
|
test('should deserialize public key from compressed hexadecimal encoding', () => {
|
||||||
|
expect(
|
||||||
|
deserializePublicKey(
|
||||||
|
'0x029f196bbfef4fa6a5eb81dd802133a63498325445ca1af1d154b1bb4542955133'
|
||||||
|
)
|
||||||
|
).toEqual(
|
||||||
|
'0x029f196bbfef4fa6a5eb81dd802133a63498325445ca1af1d154b1bb4542955133'
|
||||||
|
)
|
||||||
|
})
|
||||||
|
|
||||||
|
test('should deserialize public key from uncompressed hexadecimal encoding', () => {
|
||||||
|
expect(
|
||||||
|
deserializePublicKey(
|
||||||
|
'0x049f196bbfef4fa6a5eb81dd802133a63498325445ca1af1d154b1bb454295513305b23fcf11d005ee622144fc402b713a8928f80d705781e2e78d701c6e01bfc4'
|
||||||
|
)
|
||||||
|
).toEqual(
|
||||||
|
'0x029f196bbfef4fa6a5eb81dd802133a63498325445ca1af1d154b1bb4542955133'
|
||||||
|
)
|
||||||
|
})
|
||||||
|
|
||||||
|
test('should throw when deserializing unsupported multibase encoding', () => {
|
||||||
|
expect(() =>
|
||||||
|
deserializePublicKey('ZQ3shY7r4cAdg4eUF5dfcuCqCFzWmdjHW4SX5hspM9ucAarfU')
|
||||||
|
).toThrowError()
|
||||||
|
})
|
||||||
|
|
||||||
|
test('should throw when deserializing invalid public key', () => {
|
||||||
|
expect(() =>
|
||||||
|
deserializePublicKey(
|
||||||
|
'0x019f196bbfef4fa6a5eb81dd802133a63498325445ca1af1d154b1bb4542955133'
|
||||||
|
)
|
||||||
|
).toThrowError()
|
||||||
|
})
|
|
@ -0,0 +1,73 @@
|
||||||
|
import { Point } from 'ethereum-cryptography/secp256k1'
|
||||||
|
import {
|
||||||
|
toHex,
|
||||||
|
// utf8ToBytes as toBytes, // see https://github.com/paulmillr/noble-hashes/blob/d76eb7c818931d290c4c27abb778e8e269895154/src/utils.ts#L91-L96
|
||||||
|
} from 'ethereum-cryptography/utils'
|
||||||
|
import { varint } from 'multiformats'
|
||||||
|
import { base58btc } from 'multiformats/bases/base58'
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @see https://github.com/multiformats/multibase/blob/af2d36bdfaeaca453d20b18542ca57bd56b51f6c/README.md#multibase-table
|
||||||
|
*/
|
||||||
|
const VALID_MULTIBASE_CODES = [
|
||||||
|
'f', // hexadecimal
|
||||||
|
'z', // base58btc
|
||||||
|
] as const
|
||||||
|
|
||||||
|
type MultibaseCode = typeof VALID_MULTIBASE_CODES[number]
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @see https://pkg.go.dev/github.com/multiformats/go-multicodec#pkg-types
|
||||||
|
*/
|
||||||
|
const VALID_MULTICODEC_CODES = [
|
||||||
|
231, // secp256k1-pub (compressed) (0xe7)
|
||||||
|
] as const
|
||||||
|
|
||||||
|
type MulticodecCode = typeof VALID_MULTICODEC_CODES[number]
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @see https://specs.status.im/spec/2#public-key-serialization for specification
|
||||||
|
*/
|
||||||
|
export function deserializePublicKey(
|
||||||
|
publicKey: string, // uncompressed, compressed, or compressed & encoded
|
||||||
|
options = { compress: true }
|
||||||
|
): string {
|
||||||
|
const multibasePublicKey = publicKey.replace(/^0[xX]/, 'f') // ensure multibase code for hexadecimal encoding
|
||||||
|
const multibaseCode = multibasePublicKey[0] as MultibaseCode
|
||||||
|
|
||||||
|
if (!VALID_MULTIBASE_CODES.includes(multibaseCode)) {
|
||||||
|
throw new Error('Invalid public key multibase code')
|
||||||
|
}
|
||||||
|
|
||||||
|
let hexadecimalPublicKey: string
|
||||||
|
switch (multibaseCode) {
|
||||||
|
case 'z': {
|
||||||
|
const base58btcPublicKey = base58btc.decode(multibasePublicKey)
|
||||||
|
const multicodec = varint.decode(base58btcPublicKey)
|
||||||
|
const multicodecCode = multicodec[0] as MulticodecCode
|
||||||
|
const multicodecCodeByteLength = multicodec[1]
|
||||||
|
|
||||||
|
if (!VALID_MULTICODEC_CODES.includes(multicodecCode)) {
|
||||||
|
throw new Error('Invalid public key multicodec code')
|
||||||
|
}
|
||||||
|
|
||||||
|
hexadecimalPublicKey = toHex(
|
||||||
|
base58btcPublicKey.slice(multicodecCodeByteLength)
|
||||||
|
)
|
||||||
|
|
||||||
|
break
|
||||||
|
}
|
||||||
|
|
||||||
|
case 'f': {
|
||||||
|
hexadecimalPublicKey = multibasePublicKey.slice(1)
|
||||||
|
|
||||||
|
break
|
||||||
|
}
|
||||||
|
|
||||||
|
default: {
|
||||||
|
throw new Error('Unsupported public key multicodec code')
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
return `0x${Point.fromHex(hexadecimalPublicKey).toHex(options.compress)}` // validates and sets compression
|
||||||
|
}
|
|
@ -5429,6 +5429,11 @@ ms@^2.1.1:
|
||||||
resolved "https://registry.yarnpkg.com/ms/-/ms-2.1.3.tgz#574c8138ce1d2b5861f0b44579dbadd60c6615b2"
|
resolved "https://registry.yarnpkg.com/ms/-/ms-2.1.3.tgz#574c8138ce1d2b5861f0b44579dbadd60c6615b2"
|
||||||
integrity sha512-6FlzubTLZG3J2a/NVCAleEhjzq5oxgHyaCU9yYXvcLsvoVaHJq/s5xXI6/XXP6tz7R9xAOtHnSO/tXtF3WRTlA==
|
integrity sha512-6FlzubTLZG3J2a/NVCAleEhjzq5oxgHyaCU9yYXvcLsvoVaHJq/s5xXI6/XXP6tz7R9xAOtHnSO/tXtF3WRTlA==
|
||||||
|
|
||||||
|
multiformats@^11.0.1:
|
||||||
|
version "11.0.1"
|
||||||
|
resolved "https://registry.yarnpkg.com/multiformats/-/multiformats-11.0.1.tgz#ba58c3f69f032ab67dab4b48cc70f01ac2ca07fe"
|
||||||
|
integrity sha512-atWruyH34YiknSdL5yeIir00EDlJRpHzELYQxG7Iy29eCyL+VrZHpPrX5yqlik3jnuqpLpRKVZ0SGVb9UzKaSA==
|
||||||
|
|
||||||
multiformats@^9.4.2, multiformats@^9.4.5:
|
multiformats@^9.4.2, multiformats@^9.4.5:
|
||||||
version "9.6.4"
|
version "9.6.4"
|
||||||
resolved "https://registry.yarnpkg.com/multiformats/-/multiformats-9.6.4.tgz#5dce1f11a407dbb69aa612cb7e5076069bb759ca"
|
resolved "https://registry.yarnpkg.com/multiformats/-/multiformats-9.6.4.tgz#5dce1f11a407dbb69aa612cb7e5076069bb759ca"
|
||||||
|
|
Loading…
Reference in New Issue