make recoverPublicKey universal
This commit is contained in:
parent
e8a8cc5e12
commit
025787a7ec
|
@ -1,4 +1,4 @@
|
||||||
import { recoverPublicKeyFromMetadata } from '~/src/utils/recover-public-key-from-metadata'
|
import { recoverPublicKey } from '~/src/utils/recover-public-key'
|
||||||
|
|
||||||
import { ApplicationMetadataMessage } from '../../../protos/application-metadata-message'
|
import { ApplicationMetadataMessage } from '../../../protos/application-metadata-message'
|
||||||
import { ChatMessage } from '../../../protos/chat-message'
|
import { ChatMessage } from '../../../protos/chat-message'
|
||||||
|
@ -48,7 +48,10 @@ export function handleChannelChatMessage(
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
|
|
||||||
const publicKey = recoverPublicKeyFromMetadata(decodedMetadata)
|
const publicKey = recoverPublicKey(
|
||||||
|
decodedMetadata.signature,
|
||||||
|
decodedMetadata.payload
|
||||||
|
)
|
||||||
|
|
||||||
const decodedPayload = ChatMessage.decode(messageToDecode)
|
const decodedPayload = ChatMessage.decode(messageToDecode)
|
||||||
|
|
||||||
|
|
|
@ -2,7 +2,7 @@
|
||||||
// todo?: rename to handle-message
|
// todo?: rename to handle-message
|
||||||
import { bytesToHex } from 'ethereum-cryptography/utils'
|
import { bytesToHex } from 'ethereum-cryptography/utils'
|
||||||
|
|
||||||
import { recoverPublicKeyFromMetadata } from '~/src/utils/recover-public-key-from-metadata'
|
import { recoverPublicKey } from '~/src/utils/recover-public-key'
|
||||||
|
|
||||||
import { ApplicationMetadataMessage } from '../../../protos/application-metadata-message'
|
import { ApplicationMetadataMessage } from '../../../protos/application-metadata-message'
|
||||||
import {
|
import {
|
||||||
|
@ -63,7 +63,10 @@ export function handleChannelChatMessage(
|
||||||
// break
|
// break
|
||||||
// }
|
// }
|
||||||
|
|
||||||
const publicKey = recoverPublicKeyFromMetadata(decodedMetadata)
|
const publicKey = recoverPublicKey(
|
||||||
|
decodedMetadata.signature,
|
||||||
|
decodedMetadata.payload
|
||||||
|
)
|
||||||
|
|
||||||
// todo: merge and process other types of messages
|
// todo: merge and process other types of messages
|
||||||
// TODO?: ignore messages which are messageType !== COMMUNITY_CHAT
|
// TODO?: ignore messages which are messageType !== COMMUNITY_CHAT
|
||||||
|
|
|
@ -1,24 +0,0 @@
|
||||||
import { keccak256 } from 'ethereum-cryptography/keccak'
|
|
||||||
import { recoverPublicKey } from 'ethereum-cryptography/secp256k1'
|
|
||||||
|
|
||||||
import type { ApplicationMetadataMessage } from '../../protos/application-metadata-message'
|
|
||||||
|
|
||||||
/**
|
|
||||||
* returns the public key of the signer
|
|
||||||
* msg must be the 32-byte keccak hash of the message to be signed.
|
|
||||||
* sig must be a 65-byte compact ECDSA signature containing the recovery id as the last element.
|
|
||||||
*/
|
|
||||||
export function recoverPublicKeyFromMetadata(
|
|
||||||
metadata: ApplicationMetadataMessage
|
|
||||||
): Uint8Array {
|
|
||||||
const signature = metadata.signature.slice(0, 64)
|
|
||||||
const recoveryId = metadata.signature.slice(-1)
|
|
||||||
|
|
||||||
const pk = recoverPublicKey(
|
|
||||||
keccak256(metadata.payload),
|
|
||||||
signature,
|
|
||||||
Number(recoveryId)
|
|
||||||
)
|
|
||||||
|
|
||||||
return pk
|
|
||||||
}
|
|
|
@ -1,10 +1,25 @@
|
||||||
import { recoverPublicKeyFromMetadata } from './recover-public-key-from-metadata'
|
import { bytesToHex, utf8ToBytes } from 'ethereum-cryptography/utils'
|
||||||
|
|
||||||
import type { ApplicationMetadataMessage } from '../../protos/application-metadata-message'
|
import { Account } from '../account'
|
||||||
|
import { recoverPublicKey } from './recover-public-key'
|
||||||
|
|
||||||
describe('TODO: recoverPublicKeyFromMetadata', () => {
|
import type { ApplicationMetadataMessage } from '~/protos/application-metadata-message'
|
||||||
|
|
||||||
|
describe('recoverPublicKey', () => {
|
||||||
it('should recover public key', async () => {
|
it('should recover public key', async () => {
|
||||||
const metadataFixture = {
|
const payload = utf8ToBytes('hello')
|
||||||
|
|
||||||
|
const account = new Account()
|
||||||
|
const signature = await account.sign(payload)
|
||||||
|
|
||||||
|
expect(bytesToHex(recoverPublicKey(signature, payload))).toEqual(
|
||||||
|
account.publicKey
|
||||||
|
)
|
||||||
|
})
|
||||||
|
|
||||||
|
it('should recover public key from fixture', async () => {
|
||||||
|
const metadataFixture: ApplicationMetadataMessage = {
|
||||||
|
type: 'TYPE_EMOJI_REACTION' as ApplicationMetadataMessage.Type,
|
||||||
signature: new Uint8Array([
|
signature: new Uint8Array([
|
||||||
250, 132, 234, 119, 159, 124, 98, 93, 197, 108, 99, 52, 186, 234, 142,
|
250, 132, 234, 119, 159, 124, 98, 93, 197, 108, 99, 52, 186, 234, 142,
|
||||||
101, 147, 180, 50, 190, 102, 61, 219, 189, 95, 124, 29, 74, 43, 46, 106,
|
101, 147, 180, 50, 190, 102, 61, 219, 189, 95, 124, 29, 74, 43, 46, 106,
|
||||||
|
@ -25,8 +40,7 @@ describe('TODO: recoverPublicKeyFromMetadata', () => {
|
||||||
102, 55, 99, 48, 98, 55, 55, 97, 55, 99, 48, 97, 53, 101, 98, 97, 53,
|
102, 55, 99, 48, 98, 55, 55, 97, 55, 99, 48, 97, 53, 101, 98, 97, 53,
|
||||||
102, 97, 57, 100, 52, 100, 57, 49, 98, 97, 56, 32, 5, 40, 2,
|
102, 97, 57, 100, 52, 100, 57, 49, 98, 97, 56, 32, 5, 40, 2,
|
||||||
]),
|
]),
|
||||||
type: 'TYPE_EMOJI_REACTION',
|
}
|
||||||
} as unknown as ApplicationMetadataMessage
|
|
||||||
|
|
||||||
const publicKeySnapshot = new Uint8Array([
|
const publicKeySnapshot = new Uint8Array([
|
||||||
4, 172, 65, 157, 172, 154, 139, 187, 88, 130, 90, 60, 222, 96, 238, 240,
|
4, 172, 65, 157, 172, 154, 139, 187, 88, 130, 90, 60, 222, 96, 238, 240,
|
||||||
|
@ -36,8 +50,33 @@ describe('TODO: recoverPublicKeyFromMetadata', () => {
|
||||||
99, 24, 17,
|
99, 24, 17,
|
||||||
])
|
])
|
||||||
|
|
||||||
const result = recoverPublicKeyFromMetadata(metadataFixture)
|
const result = recoverPublicKey(
|
||||||
|
metadataFixture.signature,
|
||||||
|
metadataFixture.payload
|
||||||
|
)
|
||||||
|
|
||||||
expect(result).toEqual(publicKeySnapshot)
|
expect(result).toEqual(publicKeySnapshot)
|
||||||
})
|
})
|
||||||
|
|
||||||
|
it('should not recover public key with different payload', async () => {
|
||||||
|
const payload = utf8ToBytes('1')
|
||||||
|
|
||||||
|
const account = new Account()
|
||||||
|
const signature = await account.sign(payload)
|
||||||
|
|
||||||
|
const payload2 = utf8ToBytes('2')
|
||||||
|
expect(recoverPublicKey(signature, payload2)).not.toEqual(account.publicKey)
|
||||||
|
})
|
||||||
|
|
||||||
|
it('should throw error when signature length is not 65 bytes', async () => {
|
||||||
|
const payload = utf8ToBytes('hello')
|
||||||
|
const signature = new Uint8Array([
|
||||||
|
1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 15, 16,
|
||||||
|
])
|
||||||
|
|
||||||
|
// TODO: use toThrowErrorMatchingInlineSnapshot
|
||||||
|
expect(recoverPublicKey(signature, payload)).toThrow(
|
||||||
|
'Signature must be 65 bytes long'
|
||||||
|
)
|
||||||
|
})
|
||||||
})
|
})
|
|
@ -0,0 +1,25 @@
|
||||||
|
import { keccak256 } from 'ethereum-cryptography/keccak'
|
||||||
|
import { recoverPublicKey as secpRecoverPublicKey } from 'ethereum-cryptography/secp256k1'
|
||||||
|
|
||||||
|
/**
|
||||||
|
* returns the public key of the signer
|
||||||
|
* msg must be the 32-byte keccak hash of the message to be signed.
|
||||||
|
* sig must be a 65-byte compact ECDSA signature containing the recovery id as the last element.
|
||||||
|
*/
|
||||||
|
export function recoverPublicKey(
|
||||||
|
sig: Uint8Array,
|
||||||
|
payload: Uint8Array
|
||||||
|
): Uint8Array {
|
||||||
|
if (sig.length !== 65) {
|
||||||
|
throw new Error('Signature must be 65 bytes long')
|
||||||
|
}
|
||||||
|
|
||||||
|
if (sig[64] >= 4) {
|
||||||
|
throw new Error('Recovery id must be less than 4')
|
||||||
|
}
|
||||||
|
|
||||||
|
const signature = sig.slice(0, 64)
|
||||||
|
const recoveryId = sig.slice(-1)
|
||||||
|
|
||||||
|
return secpRecoverPublicKey(keccak256(payload), signature, Number(recoveryId))
|
||||||
|
}
|
Loading…
Reference in New Issue