Skip to content

Commit

Permalink
refactor: always use hexToBytes from viem
Browse files Browse the repository at this point in the history
  • Loading branch information
rygine committed Feb 26, 2024
1 parent d140e8b commit 37005ca
Show file tree
Hide file tree
Showing 9 changed files with 24 additions and 38 deletions.
2 changes: 1 addition & 1 deletion package.json
Original file line number Diff line number Diff line change
Expand Up @@ -110,7 +110,7 @@
"elliptic": "^6.5.4",
"ethers": "^5.7.2",
"long": "^5.2.3",
"viem": "^2.7.6"
"viem": "^2.7.8"
},
"devDependencies": {
"@commitlint/cli": "17.8.1",
Expand Down
3 changes: 1 addition & 2 deletions src/authn/LocalAuthenticator.ts
Original file line number Diff line number Diff line change
@@ -1,9 +1,8 @@
import { authn, signature, publicKey } from '@xmtp/proto'
import AuthData from './AuthData'
import { PrivateKey } from '../crypto'
import { hexToBytes } from '../crypto/utils'
import Token from './Token'
import { keccak256 } from 'viem'
import { hexToBytes, keccak256 } from 'viem'

This comment has been minimized.

Copy link
@neekolas

neekolas Feb 27, 2024

Contributor

Are we sure these are equivalent? With some of the Frames work I ran into cases where this was different from the secp hexToBytes.

Do you mind, just out of an abundance of caution, running a SDK using this code and sending some messages back and forth to a client using a regular SDK?


export default class LocalAuthenticator {
private identityKey: PrivateKey
Expand Down
4 changes: 2 additions & 2 deletions src/crypto/Signature.ts
Original file line number Diff line number Diff line change
Expand Up @@ -4,8 +4,8 @@ import * as secp from '@noble/secp256k1'
import { PublicKey, UnsignedPublicKey, SignedPublicKey } from './PublicKey'
import { SignedPrivateKey } from './PrivateKey'
import { Signer } from '../types/Signer'
import { bytesToHex, equalBytes, hexToBytes, splitSignature } from './utils'
import { Hex, hashMessage } from 'viem'
import { bytesToHex, equalBytes, splitSignature } from './utils'
import { Hex, hashMessage, hexToBytes } from 'viem'

// ECDSA signature with recovery bit.
export type ECDSACompactWithRecovery = {
Expand Down
20 changes: 4 additions & 16 deletions src/crypto/utils.ts
Original file line number Diff line number Diff line change
Expand Up @@ -4,24 +4,12 @@ import {
getAddress,
hexToSignature,
keccak256,
hexToBytes as viemHexToBytes,
hexToBytes,
bytesToHex as viemBytesToHex,
} from 'viem'

export const bytesToHex = secp.utils.bytesToHex

export function hexToBytes(s: string): Uint8Array {
if (s.startsWith('0x')) {
s = s.slice(2)
}
const bytes = new Uint8Array(s.length / 2)
for (let i = 0; i < bytes.length; i++) {
const j = i * 2
bytes[i] = Number.parseInt(s.slice(j, j + 2), 16)
}
return bytes
}

export function bytesToBase64(bytes: Uint8Array): string {
return Buffer.from(bytes).toString('base64')
}
Expand All @@ -42,7 +30,7 @@ export function equalBytes(b1: Uint8Array, b2: Uint8Array): boolean {
* Compute the Ethereum address from uncompressed PublicKey bytes
*/
export function computeAddress(bytes: Uint8Array) {
const publicKey = viemBytesToHex(bytes.slice(1)) as `0x${string}`
const publicKey = viemBytesToHex(bytes.slice(1)) as Hex
const hash = keccak256(publicKey)
const address = hash.substring(hash.length - 40)
return getAddress(`0x${address}`)
Expand All @@ -53,8 +41,8 @@ export function computeAddress(bytes: Uint8Array) {
*/
export function splitSignature(signature: Hex) {
const eSig = hexToSignature(signature)
const r = viemHexToBytes(eSig.r)
const s = viemHexToBytes(eSig.s)
const r = hexToBytes(eSig.r)
const s = hexToBytes(eSig.s)
let v = Number(eSig.v)
if (v === 0 || v === 1) {
v += 27
Expand Down
10 changes: 5 additions & 5 deletions src/keystore/providers/NetworkKeyManager.ts
Original file line number Diff line number Diff line change
Expand Up @@ -9,11 +9,11 @@ import {
} from '../../crypto'
import type { PreEventCallback } from '../../Client'
import { LocalAuthenticator } from '../../authn'
import { bytesToHex, hexToBytes } from '../../crypto/utils'
import { bytesToHex } from '../../crypto/utils'
import Ciphertext from '../../crypto/Ciphertext'
import { privateKey as proto } from '@xmtp/proto'
import TopicPersistence from '../persistence/TopicPersistence'
import { getAddress, verifyMessage } from 'viem'
import { Hex, getAddress, hexToBytes, verifyMessage } from 'viem'

const KEY_BUNDLE_NAME = 'key_bundle'
/**
Expand Down Expand Up @@ -100,14 +100,14 @@ export default class NetworkKeyManager {
const valid = verifyMessage({
address: walletAddr as `0x${string}`,
message: input,
signature: sig as `0x${string}`,
signature: sig as Hex,
})

if (!valid) {
throw new Error('invalid signature')
}

const secret = hexToBytes(sig as `0x${string}`)
const secret = hexToBytes(sig as Hex)
const ciphertext = await encrypt(bytes, secret)
return proto.EncryptedPrivateKeyBundle.encode({
v1: {
Expand Down Expand Up @@ -137,7 +137,7 @@ export default class NetworkKeyManager {
const secret = hexToBytes(
(await wallet.signMessage(
storageSigRequestText(eBundle.walletPreKey)
)) as `0x${string}`
)) as Hex
)

// Ledger uses the last byte = v=[0,1,...] but Metamask and other wallets generate with
Expand Down
3 changes: 1 addition & 2 deletions test/authn/Authn.test.ts
Original file line number Diff line number Diff line change
Expand Up @@ -2,11 +2,10 @@ import Long from 'long'
import { PrivateKey, PrivateKeyBundleV1, Signature } from '../../src/crypto'
import Authenticator from '../../src/authn/LocalAuthenticator'
import Token from '../../src/authn/Token'
import { hexToBytes } from '../../src/crypto/utils'
import { newWallet, sleep } from '../helpers'
import { Wallet } from 'ethers'
import AuthCache from '../../src/authn/AuthCache'
import { keccak256 } from 'viem'
import { hexToBytes, keccak256 } from 'viem'

describe('authn', () => {
let authenticator: Authenticator
Expand Down
6 changes: 3 additions & 3 deletions test/crypto/PrivateKeyBundle.test.ts
Original file line number Diff line number Diff line change
Expand Up @@ -5,9 +5,9 @@ import {
PrivateKeyBundleV2,
SignedPublicKeyBundle,
} from '../../src/crypto'
import { hexToBytes } from '../../src/crypto/utils'
import { newWallet } from '../helpers'
import { storageSigRequestText } from '../../src/keystore/providers/NetworkKeyManager'
import { hexToBytes } from 'viem'

describe('Crypto', function () {
describe('PrivateKeyBundle', function () {
Expand All @@ -30,14 +30,14 @@ describe('Crypto', function () {
it('human-friendly storage signature request text', async function () {
const pri = PrivateKey.fromBytes(
hexToBytes(
'08aaa9dad3ed2f12220a206fd789a6ee2376bb6595b4ebace57c7a79e6e4f1f12c8416d611399eda6c74cb1a4c08aaa9dad3ed2f1a430a4104e208133ea0973a9968fe5362e5ac0a8bbbe2aa16d796add31f3d027a1b894389873d7f282163bceb1fc3ca60d589d1e667956c40fed4cdaa7edc1392d2100b8a'
'0x08aaa9dad3ed2f12220a206fd789a6ee2376bb6595b4ebace57c7a79e6e4f1f12c8416d611399eda6c74cb1a4c08aaa9dad3ed2f1a430a4104e208133ea0973a9968fe5362e5ac0a8bbbe2aa16d796add31f3d027a1b894389873d7f282163bceb1fc3ca60d589d1e667956c40fed4cdaa7edc1392d2100b8a'
)
)
expect(pri.secp256k1).toBeTruthy()
const wallet = newWallet()
const bundle = await PrivateKeyBundleV1.generate(wallet)
const preKey = hexToBytes(
'f51bd1da9ec2239723ae2cf6a9f8d0ac37546b27e634002c653d23bacfcc67ad'
'0xf51bd1da9ec2239723ae2cf6a9f8d0ac37546b27e634002c653d23bacfcc67ad'
)
const actual = storageSigRequestText(preKey)
const expected =
Expand Down
10 changes: 5 additions & 5 deletions test/crypto/PublicKey.test.ts
Original file line number Diff line number Diff line change
Expand Up @@ -5,12 +5,12 @@ import {
SignedPublicKey,
SignedPrivateKey,
WalletSigner,
utils,
Signature,
} from '../../src/crypto'
import { Wallet } from 'ethers'
import { hexToBytes, equalBytes } from '../../src/crypto/utils'
import { equalBytes } from '../../src/crypto/utils'
import { newWallet } from '../helpers'
import { hexToBytes } from 'viem'

describe('Crypto', function () {
describe('Signed Keys', function () {
Expand Down Expand Up @@ -67,8 +67,8 @@ describe('Crypto', function () {
describe('PublicKey', function () {
it('derives address from public key', function () {
// using the sample from https://kobl.one/blog/create-full-ethereum-keypair-and-address/
const bytes = utils.hexToBytes(
'04836b35a026743e823a90a0ee3b91bf615c6a757e2b60b9e1dc1826fd0dd16106f7bc1e8179f665015f43c6c81f39062fc2086ed849625c06e04697698b21855e'
const bytes = hexToBytes(
'0x04836b35a026743e823a90a0ee3b91bf615c6a757e2b60b9e1dc1826fd0dd16106f7bc1e8179f665015f43c6c81f39062fc2086ed849625c06e04697698b21855e'
)
const pub = new PublicKey({
secp256k1Uncompressed: { bytes },
Expand All @@ -81,7 +81,7 @@ describe('Crypto', function () {
it('human-friendly identity key signature request', async function () {
const alice = PrivateKey.fromBytes(
hexToBytes(
'08aaa9dad3ed2f12220a206fd789a6ee2376bb6595b4ebace57c7a79e6e4f1f12c8416d611399eda6c74cb1a4c08aaa9dad3ed2f1a430a4104e208133ea0973a9968fe5362e5ac0a8bbbe2aa16d796add31f3d027a1b894389873d7f282163bceb1fc3ca60d589d1e667956c40fed4cdaa7edc1392d2100b8a'
'0x08aaa9dad3ed2f12220a206fd789a6ee2376bb6595b4ebace57c7a79e6e4f1f12c8416d611399eda6c74cb1a4c08aaa9dad3ed2f1a430a4104e208133ea0973a9968fe5362e5ac0a8bbbe2aa16d796add31f3d027a1b894389873d7f282163bceb1fc3ca60d589d1e667956c40fed4cdaa7edc1392d2100b8a'
)
)
const actual = WalletSigner.identitySigRequestText(
Expand Down
4 changes: 2 additions & 2 deletions test/keystore/providers/NetworkKeystoreProvider.test.ts
Original file line number Diff line number Diff line change
Expand Up @@ -10,10 +10,10 @@ import NetworkKeyManager, {
storageSigRequestText,
} from '../../../src/keystore/providers/NetworkKeyManager'
import TopicPersistence from '../../../src/keystore/persistence/TopicPersistence'
import { hexToBytes } from '../../../src/crypto/utils'
import { LocalAuthenticator } from '../../../src/authn'
import crypto from '../../../src/crypto/crypto'
import { vi } from 'vitest'
import { Hex, hexToBytes } from 'viem'

describe('NetworkKeystoreProvider', () => {
let apiClient: ApiClient
Expand Down Expand Up @@ -59,7 +59,7 @@ describe('NetworkKeystoreProvider', () => {
const walletAddr = await wallet.getAddress()

let sig = await wallet.signMessage(input)
const secret = hexToBytes(sig)
const secret = hexToBytes(sig as Hex)
const ciphertext = await encrypt(bytes, secret)
const bytesToStore = privateKey.EncryptedPrivateKeyBundleV1.encode({
ciphertext,
Expand Down

0 comments on commit 37005ca

Please sign in to comment.