Skip to content

Commit

Permalink
Add public key deserialization (status-im#341)
Browse files Browse the repository at this point in the history
* 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 <[email protected]>

* rename vars

---------

Co-authored-by: Pavel <[email protected]>
  • Loading branch information
felicio and prichodko authored Feb 23, 2023
1 parent 1faeb69 commit 6b3ef71
Show file tree
Hide file tree
Showing 4 changed files with 125 additions and 1 deletion.
3 changes: 2 additions & 1 deletion packages/status-js/package.json
Original file line number Diff line number Diff line change
Expand Up @@ -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"
Expand Down
45 changes: 45 additions & 0 deletions packages/status-js/src/utils/deserialize-public-key.test.ts
Original file line number Diff line number Diff line change
@@ -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()
})
73 changes: 73 additions & 0 deletions packages/status-js/src/utils/deserialize-public-key.ts
Original file line number Diff line number Diff line change
@@ -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
}
5 changes: 5 additions & 0 deletions yarn.lock
Original file line number Diff line number Diff line change
Expand Up @@ -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"
Expand Down

0 comments on commit 6b3ef71

Please sign in to comment.