Skip to content

Commit

Permalink
chore: upgrade to @noble/curves (#947)
Browse files Browse the repository at this point in the history
* chore: update @farcaster/core to use @noble/curves

* chore: update hubble to use @noble/curves

* add changeset

	new file:   .changeset/eleven-lies-wait.md

* preserve async interface

* update changeset
  • Loading branch information
deodad authored May 10, 2023
1 parent c489175 commit 2ca66b1
Show file tree
Hide file tree
Showing 11 changed files with 46 additions and 41 deletions.
8 changes: 8 additions & 0 deletions .changeset/eleven-lies-wait.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,8 @@
---
'@farcaster/core': minor
'@farcaster/hubble': patch
'@farcaster/hub-nodejs': patch
'@farcaster/hub-web': patch
---

replace @noble/ed25519 with faster and more secure @noble/curves
1 change: 1 addition & 0 deletions apps/hubble/package.json
Original file line number Diff line number Diff line change
Expand Up @@ -59,6 +59,7 @@
"@libp2p/tcp": "^6.0.0",
"@libp2p/utils": "^3.0.2",
"@multiformats/multiaddr": "^11.0.0",
"@noble/curves": "^1.0.0",
"async-lock": "^1.4.0",
"commander": "~10.0.0",
"ethers": "~6.2.1",
Expand Down
2 changes: 1 addition & 1 deletion apps/hubble/src/utils/periodicTestDataJob.ts
Original file line number Diff line number Diff line change
Expand Up @@ -15,7 +15,7 @@ import {
CastAddBody,
} from '@farcaster/hub-nodejs';
import { logger } from '~/utils/logger';
import * as ed from '@noble/ed25519';
import { ed25519 as ed } from '@noble/curves/ed25519';
import { faker } from '@faker-js/faker';
import Server from '~/rpc/server';
import { Result } from 'neverthrow';
Expand Down
2 changes: 1 addition & 1 deletion packages/core/package.json
Original file line number Diff line number Diff line change
Expand Up @@ -18,7 +18,7 @@
"dependencies": {
"@ethersproject/abstract-signer": "^5.7.0",
"@faker-js/faker": "^7.6.0",
"@noble/ed25519": "^1.7.3",
"@noble/curves": "^1.0.0",
"@noble/hashes": "^1.3.0",
"ethers": "~6.2.1",
"neverthrow": "^6.0.0",
Expand Down
6 changes: 3 additions & 3 deletions packages/core/src/bytes.ts
Original file line number Diff line number Diff line change
@@ -1,6 +1,6 @@
/* eslint-disable security/detect-object-injection */
import { utils } from '@noble/ed25519';
import { err, ok, Result } from 'neverthrow';
import { bytesToHex, hexToBytes } from '@noble/curves/abstract/utils';
import { HubError, HubResult } from './errors';

export const bytesCompare = (a: Uint8Array, b: Uint8Array): number => {
Expand Down Expand Up @@ -66,14 +66,14 @@ export const bytesDecrement = (inputBytes: Uint8Array): HubResult<Uint8Array> =>

export const bytesToHexString = (bytes: Uint8Array): HubResult<string> => {
return Result.fromThrowable(
(bytes: Uint8Array) => '0x' + utils.bytesToHex(bytes),
(bytes: Uint8Array) => '0x' + bytesToHex(bytes),
(e) => new HubError('unknown', e as Error)
)(bytes);
};

export const hexStringToBytes = (hex: string): HubResult<Uint8Array> => {
return Result.fromThrowable(
(hex: string) => utils.hexToBytes(hex.substring(0, 2) === '0x' ? hex.substring(2) : hex),
(hex: string) => hexToBytes(hex.substring(0, 2) === '0x' ? hex.substring(2) : hex),
(e) => new HubError('unknown', e as Error)
)(hex);
};
Expand Down
23 changes: 8 additions & 15 deletions packages/core/src/crypto/ed25519.ts
Original file line number Diff line number Diff line change
@@ -1,30 +1,23 @@
import * as ed from '@noble/ed25519';
import { sha512 } from '@noble/hashes/sha512';
import { ResultAsync } from 'neverthrow';
import { ed25519 } from '@noble/curves/ed25519';
import { Result } from 'neverthrow';
import { HubAsyncResult, HubError } from '../errors';

/** Setup ed to hash synchronously */
ed.utils.sha512Sync = (...m) => sha512(ed.utils.concatBytes(...m));

export const getPublicKeySync = (privateKey: Uint8Array): Uint8Array => {
return ed.sync.getPublicKey(privateKey);
};
const safeGetPublicKey = Result.fromThrowable(ed25519.getPublicKey, (err) => new HubError('bad_request', err as Error));
const safeSign = Result.fromThrowable(ed25519.sign, (err) => new HubError('bad_request', err as Error));
const safeVerify = Result.fromThrowable(ed25519.verify, (err) => new HubError('bad_request', err as Error));

export const getPublicKey = async (privateKey: Uint8Array): HubAsyncResult<Uint8Array> => {
return ResultAsync.fromPromise(ed.getPublicKey(privateKey), (err) => new HubError('bad_request', err as Error));
return safeGetPublicKey(privateKey);
};

export const signMessageHash = async (hash: Uint8Array, privateKey: Uint8Array): HubAsyncResult<Uint8Array> => {
return ResultAsync.fromPromise(ed.sign(hash, privateKey), (err) => new HubError('bad_request', err as Error));
return safeSign(hash, privateKey);
};

export const verifyMessageHashSignature = async (
signature: Uint8Array,
hash: Uint8Array,
publicKey: Uint8Array
): HubAsyncResult<boolean> => {
return ResultAsync.fromPromise(
ed.verify(signature, hash, publicKey),
(err) => new HubError('bad_request', err as Error)
);
return safeVerify(signature, hash, publicKey);
};
12 changes: 6 additions & 6 deletions packages/core/src/factories.ts
Original file line number Diff line number Diff line change
@@ -1,14 +1,14 @@
import { faker } from '@faker-js/faker';
import { Factory } from '@farcaster/fishery';
import * as protobufs from './protobufs';
import { utils } from '@noble/ed25519';
import { ed25519 } from '@noble/curves/ed25519';
import { blake3 } from '@noble/hashes/blake3';
import { Wallet } from 'ethers';
import * as protobufs from './protobufs';
import { bytesToHexString } from './bytes';
import * as ed25519 from './crypto/ed25519';
import { Ed25519Signer, Eip712Signer, EthersEip712Signer, NobleEd25519Signer, Signer } from './signers';
import { getFarcasterTime } from './time';
import { VerificationEthAddressClaim } from './verifications';
import { randomBytes } from '@noble/hashes/utils';

/** Scalars */

Expand All @@ -18,7 +18,7 @@ const FidFactory = Factory.define<number>(() => {

const BytesFactory = Factory.define<Uint8Array, { length?: number }>(({ transientParams }) => {
const length = transientParams.length ?? faker.datatype.number({ max: 64, min: 1 });
return utils.randomBytes(length);
return randomBytes(length);
});

const MessageHashFactory = Factory.define<Uint8Array>(() => {
Expand Down Expand Up @@ -74,12 +74,12 @@ const TransactionHashFactory = Factory.define<Uint8Array>(() => {
/** Signers */

const Ed25519PrivateKeyFactory = Factory.define<Uint8Array>(() => {
return utils.randomPrivateKey();
return ed25519.utils.randomPrivateKey();
});

const Ed25519PPublicKeyFactory = Factory.define<Uint8Array>(() => {
const privateKey = Ed25519PrivateKeyFactory.build();
return ed25519.getPublicKeySync(privateKey);
return ed25519.getPublicKey(privateKey);
});

const Ed25519SignerFactory = Factory.define<Ed25519Signer>(() => {
Expand Down
7 changes: 3 additions & 4 deletions packages/core/src/signers/nobleEd25519Signer.test.ts
Original file line number Diff line number Diff line change
@@ -1,9 +1,8 @@
import { blake3 } from '@noble/hashes/blake3';
import { randomBytes } from 'ethers';
import { ok } from 'neverthrow';
import { ed25519 } from '../crypto';
import { Factories } from '../factories';
import { NobleEd25519Signer } from './nobleEd25519Signer';
import { ed25519 } from '@noble/curves/ed25519';

describe('NobleEd25519Signer', () => {
const privateKey = Factories.Ed25519PrivateKey.build();
Expand All @@ -20,8 +19,8 @@ describe('NobleEd25519Signer', () => {
const hash = blake3(bytes, { dkLen: 20 });
const signature = await signer.signMessageHash(hash);
expect(signature.isOk()).toBeTruthy();
const isValid = await ed25519.verifyMessageHashSignature(signature._unsafeUnwrap(), hash, signerKey);
expect(isValid).toEqual(ok(true));
const isValid = ed25519.verify(signature._unsafeUnwrap(), hash, signerKey);
expect(isValid).toEqual(true);
});
});
});
7 changes: 4 additions & 3 deletions packages/core/src/signers/nobleEd25519Signer.ts
Original file line number Diff line number Diff line change
@@ -1,4 +1,5 @@
import { ed25519 } from '../crypto';
import { ok } from 'neverthrow';
import { ed25519 } from '@noble/curves/ed25519';
import { HubAsyncResult } from '../errors';
import { Ed25519Signer } from './ed25519Signer';

Expand All @@ -11,10 +12,10 @@ export class NobleEd25519Signer extends Ed25519Signer {
}

public async getSignerKey(): HubAsyncResult<Uint8Array> {
return ed25519.getPublicKey(this._privateKey);
return ok(ed25519.getPublicKey(this._privateKey));
}

public async signMessageHash(hash: Uint8Array): HubAsyncResult<Uint8Array> {
return ed25519.signMessageHash(hash, this._privateKey);
return ok(ed25519.sign(hash, this._privateKey));
}
}
7 changes: 4 additions & 3 deletions packages/core/src/validations.ts
Original file line number Diff line number Diff line change
@@ -1,8 +1,9 @@
import * as protobufs from './protobufs';
import { blake3 } from '@noble/hashes/blake3';
import { ed25519 } from '@noble/curves/ed25519';
import { err, ok, Result } from 'neverthrow';
import { bytesCompare, bytesToUtf8String, utf8StringToBytes } from './bytes';
import { ed25519, eip712 } from './crypto';
import { eip712 } from './crypto';
import { HubAsyncResult, HubError, HubResult } from './errors';
import { getFarcasterTime } from './time';
import { makeVerificationEthAddressClaim } from './verifications';
Expand Down Expand Up @@ -142,8 +143,8 @@ export const validateMessage = async (message: protobufs.Message): HubAsyncResul
return err(new HubError('bad_request.validation_failure', 'signature does not match signer'));
}
} else if (message.signatureScheme === protobufs.SignatureScheme.ED25519 && !eip712SignerRequired) {
const signatureIsValid = await ed25519.verifyMessageHashSignature(signature, hash, signer);
if (signatureIsValid.isErr() || (signatureIsValid.isOk() && !signatureIsValid.value)) {
const signatureIsValid = ed25519.verify(signature, hash, signer);
if (!signatureIsValid) {
return err(new HubError('bad_request.validation_failure', 'invalid signature'));
}
} else {
Expand Down
12 changes: 7 additions & 5 deletions yarn.lock
Original file line number Diff line number Diff line change
Expand Up @@ -2002,6 +2002,13 @@
dependencies:
"@noble/hashes" "1.3.0"

"@noble/curves@^1.0.0":
version "1.0.0"
resolved "https://registry.npmjs.org/@noble/curves/-/curves-1.0.0.tgz#e40be8c7daf088aaf291887cbc73f43464a92932"
integrity sha512-2upgEu0iLiDVDZkNLeFV2+ht0BAVgQnEmCk6JsOch9Rp8xfkMCbvbAZlA2pBHQc73dbl+vFOXfqkf4uemdn0bw==
dependencies:
"@noble/hashes" "1.3.0"

"@noble/curves@~0.8.3":
version "0.8.3"
resolved "https://registry.npmjs.org/@noble/curves/-/curves-0.8.3.tgz#ad6d48baf2599cf1d58dcb734c14d5225c8996e0"
Expand All @@ -2014,11 +2021,6 @@
resolved "https://registry.npmjs.org/@noble/ed25519/-/ed25519-1.7.2.tgz#7f8f395096a6417a65b8953d2a77fa48183c4c0a"
integrity sha512-G0MeBXnCWDoFHrpytNcp32XQWlTzeplrTXKtZt24BjVehIh/D87Hs/ddj2+0vvvmxi/uvq+wz7oLgz9NnoW/1Q==

"@noble/ed25519@^1.7.3":
version "1.7.3"
resolved "https://registry.npmjs.org/@noble/ed25519/-/ed25519-1.7.3.tgz#57e1677bf6885354b466c38e2b620c62f45a7123"
integrity sha512-iR8GBkDt0Q3GyaVcIu7mSsVIqnFbkbRzGLWlvhwunacoLwt4J3swfKhfaM6rN6WY+TBGoYT1GtT1mIh2/jGbRQ==

"@noble/hashes@1.1.2":
version "1.1.2"
resolved "https://registry.npmjs.org/@noble/hashes/-/hashes-1.1.2.tgz#e9e035b9b166ca0af657a7848eb2718f0f22f183"
Expand Down

0 comments on commit 2ca66b1

Please sign in to comment.