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
2596fc13e2
commit
a28c7e50bf
|
@ -35,7 +35,8 @@
|
|||
"dependencies": {
|
||||
"@bufbuild/protobuf": "^1.0.0",
|
||||
"ethereum-cryptography": "^1.0.3",
|
||||
"js-waku": "^0.30.0"
|
||||
"js-waku": "^0.30.0",
|
||||
"multiformats": "^11.0.1"
|
||||
},
|
||||
"devDependencies": {
|
||||
"@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"
|
||||
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:
|
||||
version "9.6.4"
|
||||
resolved "https://registry.yarnpkg.com/multiformats/-/multiformats-9.6.4.tgz#5dce1f11a407dbb69aa612cb7e5076069bb759ca"
|
||||
|
|
Loading…
Reference in New Issue