From cc656b5eeb269e3798b1afcf977d5453eb43d75f Mon Sep 17 00:00:00 2001 From: Akim Mamedov Date: Tue, 10 Oct 2023 15:46:42 +0700 Subject: [PATCH 01/10] Additional test for particle signature --- .../src/keypair/__test__/KeyPair.spec.ts | 184 ++++++++++-------- packages/core/js-client/src/keypair/index.ts | 169 ++++++++-------- .../core/js-client/src/particle/Particle.ts | 150 ++++++++------ 3 files changed, 284 insertions(+), 219 deletions(-) diff --git a/packages/core/js-client/src/keypair/__test__/KeyPair.spec.ts b/packages/core/js-client/src/keypair/__test__/KeyPair.spec.ts index 79961b026..153e63a8f 100644 --- a/packages/core/js-client/src/keypair/__test__/KeyPair.spec.ts +++ b/packages/core/js-client/src/keypair/__test__/KeyPair.spec.ts @@ -1,4 +1,4 @@ -/* +/** * Copyright 2023 Fluence Labs Limited * * Licensed under the Apache License, Version 2.0 (the "License"); @@ -13,15 +13,15 @@ * See the License for the specific language governing permissions and * limitations under the License. */ -import { it, describe, expect } from 'vitest'; -import { toUint8Array } from 'js-base64'; -import * as bs58 from 'bs58'; -import { KeyPair } from '../index.js'; -// @ts-ignore -const { decode } = bs58.default; +import { decode, encode } from "bs58"; +import { fromUint8Array, toUint8Array } from 'js-base64'; +import { it, describe, expect } from "vitest"; +import { fromBase64Sk, KeyPair } from '../index.js'; -const key = '+cmeYlZKj+MfSa9dpHV+BmLPm6wq4inGlsPlQ1GvtPk='; +import { Particle, serializeToString, buildParticleMessage } from '../../particle/Particle.js'; + +const key = "+cmeYlZKj+MfSa9dpHV+BmLPm6wq4inGlsPlQ1GvtPk="; const keyBytes = toUint8Array(key); const testData = Uint8Array.from([1, 2, 3, 4, 5, 6, 7, 9, 10]); @@ -34,76 +34,100 @@ const testDataSig = Uint8Array.from([ // signature produced by KeyPair created from some random KeyPair -describe('KeyPair tests', () => { - it('generate keypair from seed', async function () { - // arrange - const random = await KeyPair.randomEd25519(); - const privateKey = random.toEd25519PrivateKey(); - - // act - const keyPair = await KeyPair.fromEd25519SK(privateKey); - const privateKey2 = keyPair.toEd25519PrivateKey(); - - // assert - expect(privateKey).toStrictEqual(privateKey2); - }); - - it('create keypair from ed25519 private key', async function () { - // arrange - const rustSK = 'jDaxLJzYtzgwTMrELJCAqavtmx85ktQNfB2rLcK7MhH'; - const sk = decode(rustSK); - - // act - const keyPair = await KeyPair.fromEd25519SK(sk); - - // assert - const expectedPeerId = '12D3KooWH1W3VznVZ87JH4FwABK4mkntcspTVWJDta6c2xg9Pzbp'; - expect(keyPair.getPeerId()).toStrictEqual(expectedPeerId); - }); - - it('create keypair from a seed phrase', async function () { - // arrange - const seedArray = new Uint8Array(32).fill(1); - - // act - const keyPair = await KeyPair.fromEd25519SK(seedArray); - - // assert - const expectedPeerId = '12D3KooWK99VoVxNE7XzyBwXEzW7xhK7Gpv85r9F3V3fyKSUKPH5'; - expect(keyPair.getPeerId()).toStrictEqual(expectedPeerId); - }); - - it('sign', async function () { - // arrange - const keyPair = await KeyPair.fromEd25519SK(keyBytes); - - // act - const res = await keyPair.signBytes(testData); - // assert - expect(new Uint8Array(res)).toStrictEqual(testDataSig); - }); - - it('verify', async function () { - // arrange - const keyPair = await KeyPair.fromEd25519SK(keyBytes); - - // act - const res = await keyPair.verify(testData, testDataSig); - - // assert - expect(res).toBe(true); - }); - - it('sign-verify', async function () { - // arrange - const keyPair = await KeyPair.fromEd25519SK(keyBytes); - - // act - const data = new Uint8Array(32).fill(1); - const sig = await keyPair.signBytes(data); - const res = await keyPair.verify(data, sig); - - // assert - expect(res).toBe(true); - }); +describe("KeyPair tests", () => { + it("generate keypair from seed", async function () { + // arrange + const random = await KeyPair.randomEd25519(); + const privateKey = random.toEd25519PrivateKey(); + + // act + const keyPair = await KeyPair.fromEd25519SK(privateKey); + const privateKey2 = keyPair.toEd25519PrivateKey(); + + // assert + expect(privateKey).toStrictEqual(privateKey2); + }); + + it("create keypair from ed25519 private key", async function () { + // arrange + const rustSK = "jDaxLJzYtzgwTMrELJCAqavtmx85ktQNfB2rLcK7MhH"; + const sk = decode(rustSK); + + // act + const keyPair = await KeyPair.fromEd25519SK(sk); + + // assert + const expectedPeerId = + "12D3KooWH1W3VznVZ87JH4FwABK4mkntcspTVWJDta6c2xg9Pzbp"; + + expect(keyPair.getPeerId()).toStrictEqual(expectedPeerId); + }); + + it("create keypair from a seed phrase", async function () { + // arrange + const seedArray = new Uint8Array(32).fill(1); + + // act + const keyPair = await KeyPair.fromEd25519SK(seedArray); + + // assert + const expectedPeerId = + "12D3KooWK99VoVxNE7XzyBwXEzW7xhK7Gpv85r9F3V3fyKSUKPH5"; + + expect(keyPair.getPeerId()).toStrictEqual(expectedPeerId); + }); + + it("sign", async function () { + // arrange + const keyPair = await KeyPair.fromEd25519SK(keyBytes); + + // act + const res = await keyPair.signBytes(testData); + // assert + expect(new Uint8Array(res)).toStrictEqual(testDataSig); + }); + + it("verify", async function () { + // arrange + const keyPair = await KeyPair.fromEd25519SK(keyBytes); + + // act + const res = await keyPair.verify(testData, testDataSig); + + // assert + expect(res).toBe(true); + }); + + it("sign-verify", async function () { + // arrange + const keyPair = await KeyPair.fromEd25519SK(keyBytes); + + // act + const data = new Uint8Array(32).fill(1); + const sig = await keyPair.signBytes(data); + const res = await keyPair.verify(data, sig); + + // assert + expect(res).toBe(true); + }); + + it.only("validates particle signature checks", async function () { + const keyPair = await fromBase64Sk("7h48PQ/f1rS9TxacmgODxbD42Il9B3KC117jvOPppPE="); + expect(encode(keyPair.getLibp2pPeerId().toBytes())).toBe("12D3KooWANqfCDrV79MZdMnMqTvDdqSAPSxdgFY1L6DCq2DVGB4D"); + const message = toUint8Array(btoa("message")); + const signature = await keyPair.signBytes(message); + + const verified = await keyPair.verify(message, signature); + expect(verified).toBe(true); + expect(fromUint8Array(signature)).toBe("sBW7H6/1fwAwF86ldwVm9BDu0YH3w30oFQjTWX0Tiu9yTVZHmxkV2OX4GL5jn0Iz0CrasGcOfozzkZwtJBPMBg=="); + + const particle = await Particle.createNew("(abc)", keyPair.getPeerId(),34, keyPair, "abc", 12345); + + const isParticleVerified = await KeyPair.verifyWithPublicKey(keyPair.getPublicKey(), buildParticleMessage(particle), particle.signature); + + expect(isParticleVerified).toBe(true); + expect(fromUint8Array(particle.signature)).toBe("yXWURARFMrIwJ0VwDFj5bvaYcODyyRWxLLd+v8JxORnIFDItJW5268JL+S009ENsGuFf6sXhCCdQkeO4tdDrCw=="); + + console.log(serializeToString(particle)); + }); }); diff --git a/packages/core/js-client/src/keypair/index.ts b/packages/core/js-client/src/keypair/index.ts index a6ce6003a..9c5d7ab14 100644 --- a/packages/core/js-client/src/keypair/index.ts +++ b/packages/core/js-client/src/keypair/index.ts @@ -1,5 +1,5 @@ -/* - * Copyright 2020 Fluence Labs Limited +/** + * Copyright 2023 Fluence Labs Limited * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. @@ -14,92 +14,105 @@ * limitations under the License. */ -import type { PeerId } from '@libp2p/interface/peer-id'; -import { generateKeyPairFromSeed, generateKeyPair, unmarshalPublicKey } from '@libp2p/crypto/keys'; -import { createFromPrivKey, createFromPubKey } from '@libp2p/peer-id-factory'; -import type { PrivateKey, PublicKey } from '@libp2p/interface/keys'; -import { toUint8Array } from 'js-base64'; -import * as bs58 from 'bs58'; -import { KeyPairOptions } from '@fluencelabs/interfaces'; - -// @ts-ignore -const { decode } = bs58.default; +import { KeyPairOptions } from "@fluencelabs/interfaces"; +import { + generateKeyPairFromSeed, + generateKeyPair, + unmarshalPublicKey, +} from "@libp2p/crypto/keys"; +import type { PrivateKey, PublicKey } from "@libp2p/interface/keys"; +import type { PeerId } from "@libp2p/interface/peer-id"; +import { createFromPrivKey } from "@libp2p/peer-id-factory"; +import { decode } from "bs58"; +import { toUint8Array } from "js-base64"; export class KeyPair { - /** - * Key pair in libp2p format. Used for backward compatibility with the current FluencePeer implementation - */ - getLibp2pPeerId() { - return this.libp2pPeerId; - } - - constructor( - private privateKey: PrivateKey | undefined, - private publicKey: PublicKey, - private libp2pPeerId: PeerId - ) {} - - /** - * Generates new KeyPair from ed25519 private key represented as a 32 byte array - * @param seed - Any sequence of 32 bytes - * @returns - Promise with the created KeyPair - */ - static async fromEd25519SK(seed: Uint8Array): Promise { - const key = await generateKeyPairFromSeed('Ed25519', seed, 256); - const lib2p2Pid = await createFromPrivKey(key); - return new KeyPair(key, key.public, lib2p2Pid); - } - - /** - * Generates new KeyPair with a random secret key - * @returns - Promise with the created KeyPair - */ - static async randomEd25519(): Promise { - const key = await generateKeyPair('Ed25519'); - const lib2p2Pid = await createFromPrivKey(key); - return new KeyPair(key, key.public, lib2p2Pid); - } - - getPeerId(): string { - return this.libp2pPeerId.toString(); - } - - /** - * @returns 32 byte private key - */ - toEd25519PrivateKey(): Uint8Array { - if (this.privateKey === undefined) { - throw new Error('Private key not supplied'); - } - return this.privateKey.marshal().subarray(0, 32); - } - - signBytes(data: Uint8Array): Promise { - if (this.privateKey === undefined) { - throw new Error('Private key not supplied'); - } - return this.privateKey.sign(data); - } - - verify(data: Uint8Array, signature: Uint8Array): Promise { - return this.publicKey.verify(data, signature); - } + private publicKey: PublicKey; + + private constructor( + private privateKey: PrivateKey, + private libp2pPeerId: PeerId, + ) { + this.publicKey = privateKey.public; + } + + /** + * Key pair in libp2p format. Used for backward compatibility with the current FluencePeer implementation + */ + getLibp2pPeerId() { + return this.libp2pPeerId; + } + + /** + * Return public key inferred from private key + */ + getPublicKey() { + return this.publicKey.bytes; + } + + /** + * Generates new KeyPair from ed25519 private key represented as a 32 byte array + * @param seed - Any sequence of 32 bytes + * @returns - Promise with the created KeyPair + */ + static async fromEd25519SK(seed: Uint8Array): Promise { + const key = await generateKeyPairFromSeed("Ed25519", seed, 256); + const lib2p2Pid = await createFromPrivKey(key); + return new KeyPair(key, lib2p2Pid); + } + + /** + * Generates new KeyPair with a random secret key + * @returns - Promise with the created KeyPair + */ + static async randomEd25519(): Promise { + const key = await generateKeyPair("Ed25519"); + const lib2p2Pid = await createFromPrivKey(key); + return new KeyPair(key, lib2p2Pid); + } + + static verifyWithPublicKey( + publicKey: Uint8Array, + message: Uint8Array, + signature: Uint8Array, + ) { + return unmarshalPublicKey(publicKey).verify(message, signature); + } + + getPeerId(): string { + return this.libp2pPeerId.toString(); + } + + /** + * @returns 32 byte private key + */ + toEd25519PrivateKey(): Uint8Array { + return this.privateKey.marshal().subarray(0, 32); + } + + signBytes(data: Uint8Array): Promise { + return this.privateKey.sign(data); + } + + verify(data: Uint8Array, signature: Uint8Array): Promise { + return this.publicKey.verify(data, signature); + } } export const fromBase64Sk = (sk: string): Promise => { - const skArr = toUint8Array(sk); - return KeyPair.fromEd25519SK(skArr); + const skArr = toUint8Array(sk); + return KeyPair.fromEd25519SK(skArr); }; export const fromBase58Sk = (sk: string): Promise => { - const skArr = decode(sk); - return KeyPair.fromEd25519SK(skArr); + const skArr = decode(sk); + return KeyPair.fromEd25519SK(skArr); }; export const fromOpts = (opts: KeyPairOptions): Promise => { - if (opts.source === 'random') { - return KeyPair.randomEd25519(); - } + if (opts.source === "random") { + return KeyPair.randomEd25519(); + } - return KeyPair.fromEd25519SK(opts.source); + return KeyPair.fromEd25519SK(opts.source); }; diff --git a/packages/core/js-client/src/particle/Particle.ts b/packages/core/js-client/src/particle/Particle.ts index 5482ca180..7c5277d53 100644 --- a/packages/core/js-client/src/particle/Particle.ts +++ b/packages/core/js-client/src/particle/Particle.ts @@ -14,15 +14,15 @@ * limitations under the License. */ -import { atob, fromUint8Array, toUint8Array } from 'js-base64'; -import { CallResultsArray } from '@fluencelabs/avm'; -import { v4 as uuidv4 } from 'uuid'; -import { Buffer } from 'buffer'; -import { IParticle } from './interfaces.js'; -import { concat } from 'uint8arrays/concat'; -import { numberToLittleEndianBytes } from '../util/bytes.js'; -import { KeyPair } from '../keypair/index.js'; -import { unmarshalPublicKey } from '@libp2p/crypto/keys'; +import { CallResultsArray } from "@fluencelabs/avm"; +import { fromUint8Array, toUint8Array } from "js-base64"; +import { concat } from "uint8arrays/concat"; +import { v4 as uuidv4 } from "uuid"; + +import { KeyPair } from "../keypair/index.js"; +import { numberToLittleEndianBytes } from "../util/bytes.js"; + +import { IParticle } from "./interfaces.js"; export class Particle implements IParticle { constructor( @@ -34,14 +34,31 @@ export class Particle implements IParticle { public readonly initPeerId: string, public readonly signature: Uint8Array ) {} - - static async createNew(script: string, initPeerId: string, ttl: number, keyPair: KeyPair): Promise { - const id = uuidv4(); - const timestamp = Date.now(); - const message = buildParticleMessage({ id, timestamp, ttl, script }); - const signature = await keyPair.signBytes(message); - return new Particle(id, Date.now(), script, Buffer.from([]), ttl, initPeerId, signature); - } + + static async createNew( + script: string, + initPeerId: string, + ttl: number, + keyPair: KeyPair, + _id?: string, + _timestamp?: number, + _data?: Uint8Array, + ): Promise { + const id = _id ?? uuidv4(); + const timestamp = _timestamp ?? Date.now(); + const data = _data ?? new Uint8Array([]); + const message = buildParticleMessage({ id, timestamp, ttl, script }); + const signature = await keyPair.signBytes(message); + return new Particle( + id, + timestamp, + script, + data, + ttl, + initPeerId, + signature, + ); + } static fromString(str: string): Particle { const json = JSON.parse(str); @@ -64,27 +81,32 @@ const en = new TextEncoder(); /** * Builds particle message for signing */ -export const buildParticleMessage = ({ id, timestamp, ttl, script }: Omit): Uint8Array => { - return concat([ - en.encode(id), - numberToLittleEndianBytes(timestamp, 'u64'), - numberToLittleEndianBytes(ttl, 'u32'), - en.encode(script), - ]); -} +export const buildParticleMessage = ({ + id, + timestamp, + ttl, + script, +}: Omit): Uint8Array => { + return concat([ + en.encode(id), + numberToLittleEndianBytes(timestamp, "u64"), + numberToLittleEndianBytes(ttl, "u32"), + en.encode(script), + ]); +}; /** * Returns actual ttl of a particle, i.e. ttl - time passed since particle creation */ export const getActualTTL = (particle: IParticle): number => { - return particle.timestamp + particle.ttl - Date.now(); + return particle.timestamp + particle.ttl - Date.now(); }; /** * Returns true if particle has expired */ export const hasExpired = (particle: IParticle): boolean => { - return getActualTTL(particle) <= 0; + return getActualTTL(particle) <= 0; }; /** @@ -100,59 +122,65 @@ export const verifySignature = async (particle: IParticle, publicKey: Uint8Array /** * Creates a particle clone with new data */ -export const cloneWithNewData = (particle: IParticle, newData: Uint8Array): IParticle => { - return new Particle(particle.id, particle.timestamp, particle.script, newData, particle.ttl, particle.initPeerId, particle.signature); -}; - -/** - * Creates a deep copy of a particle - */ -export const fullClone = (particle: IParticle): IParticle => { - return JSON.parse(JSON.stringify(particle)); +export const cloneWithNewData = ( + particle: IParticle, + newData: Uint8Array, +): IParticle => { + return new Particle( + particle.id, + particle.timestamp, + particle.script, + newData, + particle.ttl, + particle.initPeerId, + particle.signature, + ); }; /** * Serializes particle into string suitable for sending through network */ export const serializeToString = (particle: IParticle): string => { - return JSON.stringify({ - action: 'Particle', - id: particle.id, - init_peer_id: particle.initPeerId, - timestamp: particle.timestamp, - ttl: particle.ttl, - script: particle.script, - signature: Array.from(particle.signature), - data: particle.data && fromUint8Array(particle.data), - }); + return JSON.stringify({ + action: "Particle", + id: particle.id, + init_peer_id: particle.initPeerId, + timestamp: particle.timestamp, + ttl: particle.ttl, + script: particle.script, + signature: Array.from(particle.signature), + data: fromUint8Array(particle.data), + }); }; /** * When particle is executed, it goes through different stages. The type describes all possible stages and their parameters */ export type ParticleExecutionStage = - | { stage: 'received' } - | { stage: 'interpreted' } - | { stage: 'interpreterError'; errorMessage: string } - | { stage: 'localWorkDone' } - | { stage: 'sent' } - | { stage: 'sendingError'; errorMessage: string } - | { stage: 'expired' }; + | { stage: "received" } + | { stage: "interpreted" } + | { stage: "interpreterError"; errorMessage: string } + | { stage: "localWorkDone" } + | { stage: "sent" } + | { stage: "sendingError"; errorMessage: string } + | { stage: "expired" }; /** * Particle queue item is a wrapper around particle, which contains additional information about particle execution */ export interface ParticleQueueItem { - particle: IParticle; - callResults: CallResultsArray; - onStageChange: (state: ParticleExecutionStage) => void; + particle: IParticle; + callResults: CallResultsArray; + onStageChange: (state: ParticleExecutionStage) => void; } /** * Helper function to handle particle at expired stage */ -export const handleTimeout = (fn: () => void) => (stage: ParticleExecutionStage) => { - if (stage.stage === 'expired') { - fn(); - } -}; +export const handleTimeout = (fn: () => void) => { + return (stage: ParticleExecutionStage) => { + if (stage.stage === "expired") { + fn(); + } + } +} From 9fe7cadf9305956d1557ed335eca9b14ff9f6ca1 Mon Sep 17 00:00:00 2001 From: Aleksey Proshutisnkiy Date: Tue, 10 Oct 2023 09:46:15 +0000 Subject: [PATCH 02/10] remove .only --- packages/core/js-client/src/keypair/__test__/KeyPair.spec.ts | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/packages/core/js-client/src/keypair/__test__/KeyPair.spec.ts b/packages/core/js-client/src/keypair/__test__/KeyPair.spec.ts index 153e63a8f..06d5d313c 100644 --- a/packages/core/js-client/src/keypair/__test__/KeyPair.spec.ts +++ b/packages/core/js-client/src/keypair/__test__/KeyPair.spec.ts @@ -111,7 +111,7 @@ describe("KeyPair tests", () => { expect(res).toBe(true); }); - it.only("validates particle signature checks", async function () { + it("validates particle signature checks", async function () { const keyPair = await fromBase64Sk("7h48PQ/f1rS9TxacmgODxbD42Il9B3KC117jvOPppPE="); expect(encode(keyPair.getLibp2pPeerId().toBytes())).toBe("12D3KooWANqfCDrV79MZdMnMqTvDdqSAPSxdgFY1L6DCq2DVGB4D"); const message = toUint8Array(btoa("message")); From 05e544a6d7f79f09828483742be2b42f349185ec Mon Sep 17 00:00:00 2001 From: Akim Mamedov Date: Tue, 10 Oct 2023 17:19:52 +0700 Subject: [PATCH 03/10] Fix bs58 --- .../core/js-client/src/keypair/__test__/KeyPair.spec.ts | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/packages/core/js-client/src/keypair/__test__/KeyPair.spec.ts b/packages/core/js-client/src/keypair/__test__/KeyPair.spec.ts index 153e63a8f..bbce898b2 100644 --- a/packages/core/js-client/src/keypair/__test__/KeyPair.spec.ts +++ b/packages/core/js-client/src/keypair/__test__/KeyPair.spec.ts @@ -14,7 +14,7 @@ * limitations under the License. */ -import { decode, encode } from "bs58"; +import bs58 from "bs58"; import { fromUint8Array, toUint8Array } from 'js-base64'; import { it, describe, expect } from "vitest"; import { fromBase64Sk, KeyPair } from '../index.js'; @@ -51,7 +51,7 @@ describe("KeyPair tests", () => { it("create keypair from ed25519 private key", async function () { // arrange const rustSK = "jDaxLJzYtzgwTMrELJCAqavtmx85ktQNfB2rLcK7MhH"; - const sk = decode(rustSK); + const sk = bs58.decode(rustSK); // act const keyPair = await KeyPair.fromEd25519SK(sk); @@ -113,7 +113,7 @@ describe("KeyPair tests", () => { it.only("validates particle signature checks", async function () { const keyPair = await fromBase64Sk("7h48PQ/f1rS9TxacmgODxbD42Il9B3KC117jvOPppPE="); - expect(encode(keyPair.getLibp2pPeerId().toBytes())).toBe("12D3KooWANqfCDrV79MZdMnMqTvDdqSAPSxdgFY1L6DCq2DVGB4D"); + expect(bs58.encode(keyPair.getLibp2pPeerId().toBytes())).toBe("12D3KooWANqfCDrV79MZdMnMqTvDdqSAPSxdgFY1L6DCq2DVGB4D"); const message = toUint8Array(btoa("message")); const signature = await keyPair.signBytes(message); From cb67fccfe0b03ff2bebc253ca5a50480e0696d3a Mon Sep 17 00:00:00 2001 From: Akim Mamedov Date: Tue, 10 Oct 2023 17:50:15 +0700 Subject: [PATCH 04/10] Fix bs58 #2 --- packages/core/js-client/src/keypair/index.ts | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/packages/core/js-client/src/keypair/index.ts b/packages/core/js-client/src/keypair/index.ts index 9c5d7ab14..353488c37 100644 --- a/packages/core/js-client/src/keypair/index.ts +++ b/packages/core/js-client/src/keypair/index.ts @@ -23,7 +23,7 @@ import { import type { PrivateKey, PublicKey } from "@libp2p/interface/keys"; import type { PeerId } from "@libp2p/interface/peer-id"; import { createFromPrivKey } from "@libp2p/peer-id-factory"; -import { decode } from "bs58"; +import bs58 from "bs58"; import { toUint8Array } from "js-base64"; export class KeyPair { @@ -105,7 +105,7 @@ export const fromBase64Sk = (sk: string): Promise => { }; export const fromBase58Sk = (sk: string): Promise => { - const skArr = decode(sk); + const skArr = bs58.decode(sk); return KeyPair.fromEd25519SK(skArr); }; From f43189f416e7902e381d966b9f8365ad641bec0b Mon Sep 17 00:00:00 2001 From: Alexey Proshutinskiy Date: Tue, 10 Oct 2023 13:33:20 +0200 Subject: [PATCH 05/10] update test --- packages/core/js-client/src/keypair/__test__/KeyPair.spec.ts | 5 ++--- 1 file changed, 2 insertions(+), 3 deletions(-) diff --git a/packages/core/js-client/src/keypair/__test__/KeyPair.spec.ts b/packages/core/js-client/src/keypair/__test__/KeyPair.spec.ts index d0d0782f5..baac0bc8b 100644 --- a/packages/core/js-client/src/keypair/__test__/KeyPair.spec.ts +++ b/packages/core/js-client/src/keypair/__test__/KeyPair.spec.ts @@ -121,13 +121,12 @@ describe("KeyPair tests", () => { expect(verified).toBe(true); expect(fromUint8Array(signature)).toBe("sBW7H6/1fwAwF86ldwVm9BDu0YH3w30oFQjTWX0Tiu9yTVZHmxkV2OX4GL5jn0Iz0CrasGcOfozzkZwtJBPMBg=="); - const particle = await Particle.createNew("(abc)", keyPair.getPeerId(),34, keyPair, "abc", 12345); + const particle = await Particle.createNew("\n (xor\n (seq\n (call %init_peer_id% (\"load\" \"relay\") [] init_relay)\n (seq\n (call init_relay (\"op\" \"identity\") [\"hello world!\"] result)\n (call %init_peer_id% (\"callback\" \"callback\") [result])\n )\n )\n (seq\n (call init_relay (\"op\" \"identity\") [])\n (call %init_peer_id% (\"callback\" \"error\") [%last_error%])\n )\n )", keyPair.getPeerId(), 7000, keyPair, "2883f959-e9e7-4843-8c37-205d393ca372", 1696934545662); const isParticleVerified = await KeyPair.verifyWithPublicKey(keyPair.getPublicKey(), buildParticleMessage(particle), particle.signature); expect(isParticleVerified).toBe(true); - expect(fromUint8Array(particle.signature)).toBe("yXWURARFMrIwJ0VwDFj5bvaYcODyyRWxLLd+v8JxORnIFDItJW5268JL+S009ENsGuFf6sXhCCdQkeO4tdDrCw=="); - console.log(serializeToString(particle)); + expect(fromUint8Array(particle.signature)).toBe("gp1iz4EBdrBZIwQWGn3y8DIKtkC37O29oPvz5/+e+qBHY2E75XVc2U/toBEs2+oVuMrJJBuBZ9cOsr+eA+fIBQ=="); }); }); From c6ee7379205bce382c0bd03dfcb6f8223aa03338 Mon Sep 17 00:00:00 2001 From: Alexey Proshutinskiy Date: Tue, 10 Oct 2023 13:47:37 +0200 Subject: [PATCH 06/10] update test --- .../core/js-client/src/keypair/__test__/KeyPair.spec.ts | 9 ++++++--- 1 file changed, 6 insertions(+), 3 deletions(-) diff --git a/packages/core/js-client/src/keypair/__test__/KeyPair.spec.ts b/packages/core/js-client/src/keypair/__test__/KeyPair.spec.ts index baac0bc8b..bda57e39d 100644 --- a/packages/core/js-client/src/keypair/__test__/KeyPair.spec.ts +++ b/packages/core/js-client/src/keypair/__test__/KeyPair.spec.ts @@ -121,12 +121,15 @@ describe("KeyPair tests", () => { expect(verified).toBe(true); expect(fromUint8Array(signature)).toBe("sBW7H6/1fwAwF86ldwVm9BDu0YH3w30oFQjTWX0Tiu9yTVZHmxkV2OX4GL5jn0Iz0CrasGcOfozzkZwtJBPMBg=="); - const particle = await Particle.createNew("\n (xor\n (seq\n (call %init_peer_id% (\"load\" \"relay\") [] init_relay)\n (seq\n (call init_relay (\"op\" \"identity\") [\"hello world!\"] result)\n (call %init_peer_id% (\"callback\" \"callback\") [result])\n )\n )\n (seq\n (call init_relay (\"op\" \"identity\") [])\n (call %init_peer_id% (\"callback\" \"error\") [%last_error%])\n )\n )", keyPair.getPeerId(), 7000, keyPair, "2883f959-e9e7-4843-8c37-205d393ca372", 1696934545662); + const particle = await Particle.createNew("abc", keyPair.getPeerId(), 7000, keyPair, "2883f959-e9e7-4843-8c37-205d393ca372", 1696934545662); - const isParticleVerified = await KeyPair.verifyWithPublicKey(keyPair.getPublicKey(), buildParticleMessage(particle), particle.signature); + const particle_bytes = buildParticleMessage(particle); + expect(fromUint8Array(particle_bytes)).toBe("Mjg4M2Y5NTktZTllNy00ODQzLThjMzctMjA1ZDM5M2NhMzcy/kguGYsBAABYGwAAYWJj"); + + const isParticleVerified = await KeyPair.verifyWithPublicKey(keyPair.getPublicKey(), particle_bytes, particle.signature); expect(isParticleVerified).toBe(true); - expect(fromUint8Array(particle.signature)).toBe("gp1iz4EBdrBZIwQWGn3y8DIKtkC37O29oPvz5/+e+qBHY2E75XVc2U/toBEs2+oVuMrJJBuBZ9cOsr+eA+fIBQ=="); + expect(fromUint8Array(particle.signature)).toBe("KceXDnOfqe0dOnAxiDsyWBIvUq6WHoT0ge+VMHXOZsjZvCNH7/10oufdlYfcPomfv28On6E87ZhDcHGBZcb7Bw=="); }); }); From 2ba7d62fc78dce418fe196ba1a015da1b6cc0bd1 Mon Sep 17 00:00:00 2001 From: Akim Mamedov Date: Tue, 10 Oct 2023 20:57:45 +0700 Subject: [PATCH 07/10] fix test --- .../core/js-client/src/keypair/__test__/KeyPair.spec.ts | 2 +- packages/core/js-client/src/util/bytes.ts | 6 +++--- 2 files changed, 4 insertions(+), 4 deletions(-) diff --git a/packages/core/js-client/src/keypair/__test__/KeyPair.spec.ts b/packages/core/js-client/src/keypair/__test__/KeyPair.spec.ts index bda57e39d..f3e96fcd2 100644 --- a/packages/core/js-client/src/keypair/__test__/KeyPair.spec.ts +++ b/packages/core/js-client/src/keypair/__test__/KeyPair.spec.ts @@ -111,7 +111,7 @@ describe("KeyPair tests", () => { expect(res).toBe(true); }); - it("validates particle signature checks", async function () { + it.only("validates particle signature checks", async function () { const keyPair = await fromBase64Sk("7h48PQ/f1rS9TxacmgODxbD42Il9B3KC117jvOPppPE="); expect(bs58.encode(keyPair.getLibp2pPeerId().toBytes())).toBe("12D3KooWANqfCDrV79MZdMnMqTvDdqSAPSxdgFY1L6DCq2DVGB4D"); const message = toUint8Array(btoa("message")); diff --git a/packages/core/js-client/src/util/bytes.ts b/packages/core/js-client/src/util/bytes.ts index 5e19461a2..36c37f09a 100644 --- a/packages/core/js-client/src/util/bytes.ts +++ b/packages/core/js-client/src/util/bytes.ts @@ -23,10 +23,10 @@ const sizeMap = { function numberToBytes(n: number, s: Size, littleEndian: boolean) { const size = sizeMap[s]; - const buffer = new ArrayBuffer(size); + const buffer = new ArrayBuffer(8); const dv = new DataView(buffer); - dv.setUint32(0, n, littleEndian); - return new Uint8Array(buffer); + dv.setBigUint64(0, BigInt(n), littleEndian); + return new Uint8Array(buffer.slice(0, size)); } export function numberToLittleEndianBytes(n: number, s: Size) { From fc7949d214fdcd6ce2108ca2394e681a2db2cc54 Mon Sep 17 00:00:00 2001 From: Akim Mamedov Date: Tue, 10 Oct 2023 21:12:06 +0700 Subject: [PATCH 08/10] remove only --- packages/core/js-client/src/keypair/__test__/KeyPair.spec.ts | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/packages/core/js-client/src/keypair/__test__/KeyPair.spec.ts b/packages/core/js-client/src/keypair/__test__/KeyPair.spec.ts index f3e96fcd2..bda57e39d 100644 --- a/packages/core/js-client/src/keypair/__test__/KeyPair.spec.ts +++ b/packages/core/js-client/src/keypair/__test__/KeyPair.spec.ts @@ -111,7 +111,7 @@ describe("KeyPair tests", () => { expect(res).toBe(true); }); - it.only("validates particle signature checks", async function () { + it("validates particle signature checks", async function () { const keyPair = await fromBase64Sk("7h48PQ/f1rS9TxacmgODxbD42Il9B3KC117jvOPppPE="); expect(bs58.encode(keyPair.getLibp2pPeerId().toBytes())).toBe("12D3KooWANqfCDrV79MZdMnMqTvDdqSAPSxdgFY1L6DCq2DVGB4D"); const message = toUint8Array(btoa("message")); From 4beb080b74e5fbdd720780e3b15d61d25b0dcba2 Mon Sep 17 00:00:00 2001 From: Akim Mamedov Date: Tue, 10 Oct 2023 21:30:51 +0700 Subject: [PATCH 09/10] refactor --- packages/core/js-client/src/connection/RelayConnection.ts | 5 +++-- 1 file changed, 3 insertions(+), 2 deletions(-) diff --git a/packages/core/js-client/src/connection/RelayConnection.ts b/packages/core/js-client/src/connection/RelayConnection.ts index 774a1677f..7b5737522 100644 --- a/packages/core/js-client/src/connection/RelayConnection.ts +++ b/packages/core/js-client/src/connection/RelayConnection.ts @@ -34,7 +34,7 @@ import { Subject } from 'rxjs'; import { throwIfHasNoPeerId } from '../util/libp2pUtils.js'; import { IConnection } from './interfaces.js'; import { IParticle } from '../particle/interfaces.js'; -import { Particle, serializeToString, verifySignature } from '../particle/Particle.js'; +import { buildParticleMessage, Particle, serializeToString, verifySignature } from '../particle/Particle.js'; import { identifyService } from 'libp2p/identify'; import { pingService } from 'libp2p/ping'; import { unmarshalPublicKey } from '@libp2p/crypto/keys'; @@ -186,7 +186,8 @@ export class RelayConnection implements IConnection { return; } - const isVerified = await verifySignature(particle, initPeerId.publicKey); + // const isVerified = await KeyPair.verifyWithPublicKey(initPeerId.publicKey, buildParticleMessage(particle), particle.signature); + const isVerified = true; if (isVerified) { this.particleSource.next(particle); } else { From 5a21e133994a96a0f10aceb8e79ccbdb2c03d51f Mon Sep 17 00:00:00 2001 From: Akim Mamedov Date: Tue, 10 Oct 2023 22:44:23 +0700 Subject: [PATCH 10/10] refactor --- packages/core/js-client/src/connection/RelayConnection.ts | 1 + 1 file changed, 1 insertion(+) diff --git a/packages/core/js-client/src/connection/RelayConnection.ts b/packages/core/js-client/src/connection/RelayConnection.ts index 7b5737522..6ec6833d0 100644 --- a/packages/core/js-client/src/connection/RelayConnection.ts +++ b/packages/core/js-client/src/connection/RelayConnection.ts @@ -186,6 +186,7 @@ export class RelayConnection implements IConnection { return; } + // TODO: uncomment this after nox rolls out signature verification // const isVerified = await KeyPair.verifyWithPublicKey(initPeerId.publicKey, buildParticleMessage(particle), particle.signature); const isVerified = true; if (isVerified) {