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:
Felicio Mununga 2023-02-23 16:48:31 +01:00 committed by GitHub
parent 2596fc13e2
commit a28c7e50bf
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
4 changed files with 125 additions and 1 deletions

View File

@ -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"

View File

@ -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()
})

View File

@ -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
}

View File

@ -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"