Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

feat: constrain usage of Buffer in enr package #286

Merged
merged 1 commit into from
Jan 25, 2024
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
4 changes: 2 additions & 2 deletions packages/enr/package.json
Original file line number Diff line number Diff line change
Expand Up @@ -58,10 +58,10 @@
"@libp2p/interface": "^1.1.1",
"@libp2p/peer-id": "^4.0.4",
"@multiformats/multiaddr": "^12.1.10",
"base64url": "^3.0.1",
"bigint-buffer": "^1.1.5",
"ethereum-cryptography": "^2.1.3",
"rlp": "^2.2.6",
"uint8-varint": "^2.0.2"
"uint8-varint": "^2.0.2",
"uint8arrays": "^5.0.1"
}
}
8 changes: 0 additions & 8 deletions packages/enr/src/create.ts

This file was deleted.

4 changes: 2 additions & 2 deletions packages/enr/src/defaultCrypto.ts
Original file line number Diff line number Diff line change
@@ -1,7 +1,7 @@
import { keccak256 } from "ethereum-cryptography/keccak";
import { secp256k1 } from "ethereum-cryptography/secp256k1";

import { createNodeId } from "./create.js";
import { createNodeId } from "./util.js";
import { NodeId } from "./types.js";

export function hash(input: Uint8Array): Uint8Array {
Expand All @@ -25,5 +25,5 @@ function uncompressPublicKey(pubKey: Uint8Array): Uint8Array {
}

export function nodeId(pubKey: Uint8Array): NodeId {
return createNodeId(Buffer.from(hash(uncompressPublicKey(pubKey).slice(1))));
return createNodeId(hash(uncompressPublicKey(pubKey).slice(1)));
}
27 changes: 14 additions & 13 deletions packages/enr/src/enr.ts
Original file line number Diff line number Diff line change
@@ -1,6 +1,4 @@
import { Multiaddr, multiaddr, protocols } from "@multiformats/multiaddr";
import base64url from "base64url";
import { toBigIntBE } from "bigint-buffer";
import * as RLP from "rlp";
import { KeyType, PeerId } from "@libp2p/interface";
import { convertToString, convertToBytes } from "@multiformats/multiaddr/convert";
Expand All @@ -9,8 +7,9 @@ import { encode as varintEncode } from "uint8-varint";
import { ERR_INVALID_ID, MAX_RECORD_SIZE } from "./constants.js";
import { ENRKey, ENRValue, SequenceNumber, NodeId } from "./types.js";
import { createPeerIdFromPublicKey, createPrivateKeyFromPeerId } from "./peerId.js";
import { toNewUint8Array } from "./util.js";
import { fromBase64url, toBase64url, toBigInt, toNewUint8Array } from "./util.js";
import { getV4Crypto } from "./crypto.js";
import { compare, fromString, toString } from "uint8arrays";

/** ENR identity scheme */
export enum IDScheme {
Expand All @@ -34,7 +33,7 @@ export type SignableENRData = {
export function id(kvs: ReadonlyMap<ENRKey, ENRValue>): IDScheme {
const idBuf = kvs.get("id");
if (!idBuf) throw new Error("id not found");
const id = Buffer.from(idBuf).toString("utf8") as IDScheme;
const id = toString(idBuf, "utf8") as IDScheme;
if (IDScheme[id] == null) {
throw new Error("Unknown enr id scheme: " + id);
}
Expand Down Expand Up @@ -143,7 +142,7 @@ export function decodeFromValues(decoded: Uint8Array[]): ENRData {
}
return {
kvs,
seq: toBigIntBE(Buffer.from(seq)),
seq: toBigInt(seq),
signature,
};
}
Expand All @@ -154,7 +153,7 @@ export function txtToBuf(encoded: string): Uint8Array {
if (!encoded.startsWith("enr:")) {
throw new Error("string encoded ENR must start with 'enr:'");
}
return base64url.toBuffer(encoded.slice(4));
return fromBase64url(encoded.slice(4));
}
export function decodeTxt(encoded: string): ENRData {
return decode(txtToBuf(encoded));
Expand All @@ -176,17 +175,19 @@ export function getIPValue(kvs: ReadonlyMap<ENRKey, ENRValue>, key: string, mult
export function getProtocolValue(kvs: ReadonlyMap<ENRKey, ENRValue>, key: string): number | undefined {
const raw = kvs.get(key);
if (raw) {
const view = new DataView(raw.buffer, raw.byteOffset, raw.byteLength);
return view.getUint16(0);
if (raw.length < 2) {
throw new Error("Encoded protocol length should be 2");
}
return (raw[0] << 8) + raw[1];
} else {
return undefined;
}
}

export function portToBuf(port: number): Uint8Array {
const buf = new Uint8Array(2);
const view = new DataView(buf.buffer, buf.byteOffset, buf.byteLength);
view.setUint16(0, port);
buf[0] = port >> 8;
buf[1] = port;
return buf;
}

Expand Down Expand Up @@ -296,7 +297,7 @@ export abstract class BaseENR {
abstract encodeToValues(): (ENRKey | ENRValue | number)[];
abstract encode(): Uint8Array;
encodeTxt(): string {
return "enr:" + base64url.encode(Buffer.from(this.encode()));
return "enr:" + toBase64url(this.encode());
}
}
/**
Expand Down Expand Up @@ -397,7 +398,7 @@ export class SignableENR extends BaseENR {
this._signature = signature;

if (this.id === IDScheme.v4) {
if (Buffer.compare(getV4Crypto().publicKey(this.privateKey), this.publicKey) !== 0) {
if (compare(getV4Crypto().publicKey(this.privateKey), this.publicKey) !== 0) {
throw new Error("Provided keypair doesn't match kv pubkey");
}
}
Expand All @@ -411,7 +412,7 @@ export class SignableENR extends BaseENR {
return new SignableENR(
{
...kvs,
id: Buffer.from("v4"),
id: fromString("v4"),
secp256k1: getV4Crypto().publicKey(privateKey),
},
BigInt(1),
Expand Down
2 changes: 1 addition & 1 deletion packages/enr/src/index.ts
Original file line number Diff line number Diff line change
Expand Up @@ -3,5 +3,5 @@ export * from "./crypto.js";
export * as defaultCrypto from "./defaultCrypto.js";
export * from "./enr.js";
export * from "./types.js";
export * from "./create.js";
export * from "./peerId.js";
export { createNodeId } from "./util.js";
42 changes: 42 additions & 0 deletions packages/enr/src/util.ts
Original file line number Diff line number Diff line change
@@ -1,5 +1,47 @@
import { toBigIntBE } from "bigint-buffer";
import { fromString, toString } from "uint8arrays";
import { NodeId } from "./types.js";

// multiaddr 8.0.0 expects an Uint8Array with internal buffer starting at 0 offset
export function toNewUint8Array(buf: Uint8Array): Uint8Array {
const arrayBuffer = buf.buffer.slice(buf.byteOffset, buf.byteOffset + buf.byteLength);
return new Uint8Array(arrayBuffer);
}

export function toBase64url(buf: Uint8Array): string {
if (globalThis.Buffer != null) {
return globalThis.Buffer.from(buf).toString("base64url");
}
return toString(buf, "base64url");
}

export function fromBase64url(str: string): Uint8Array {
if (globalThis.Buffer != null) {
return globalThis.Buffer.from(str, "base64url");
}
return fromString(str, "base64url");
}

export function toBigInt(buf: Uint8Array): bigint {
if (globalThis.Buffer != null) {
return toBigIntBE(globalThis.Buffer.from(buf));
}

if (buf.length === 0) {
return BigInt(0);
}

return BigInt(`0x${toString(buf, "hex")}`);
}

export function createNodeId(buf: Uint8Array): NodeId {
if (buf.length !== 32) {
throw new Error("NodeId must be 32 bytes in length");
}

if (globalThis.Buffer != null) {
return globalThis.Buffer.from(buf).toString("hex");
}

return toString(buf, "hex");
}
21 changes: 7 additions & 14 deletions packages/enr/test/unit/enr.test.ts
Original file line number Diff line number Diff line change
Expand Up @@ -211,10 +211,7 @@ describe("ENR", function () {

it("should throw decoding error - no id", () => {
try {
const txt = Buffer.from(
"656e723a2d435972595a62404b574342526c4179357a7a61445a584a42476b636e68344d486342465a6e75584e467264764a6a5830346a527a6a7a",
"hex"
).toString();
const txt = "enr:-CYrYZbKWCBRlAy5zzaDZXJBGkcnh4MHcBFZnuXNFrdvJjX04jRzjw";
ENR.decodeTxt(txt);
expect.fail("Expect error here");
} catch (err: any) {
Expand All @@ -237,23 +234,19 @@ describe("ENR", function () {

describe("ENR fuzzing testcases", () => {
it("should throw error in invalid signature", () => {
const buf = Buffer.from(
"656e723a2d4b7634514147774f54385374716d7749354c486149796d494f346f6f464b664e6b456a576130663150384f73456c67426832496a622d4772445f2d623957346b6350466377796e354845516d526371584e716470566f3168656f42683246306447356c64484f494141414141414141414143455a58526f4d704141414141414141414141505f5f5f5f5f5f5f5f5f5f676d6c6b676e5930676d6c7768424c663232534a6332566a634449314e6d73786f514a78436e4536765f7832656b67595f756f45317274777a76477934306d7139654436365866485042576749494e315a48437f7f7f7f7f7f7f7f7f7f7f7f7f7f7f7f7f7f7f7f7f7f434436410d0a",
"hex"
).toString();
const txt =
"enr:-Kv4QAGwOT8StqmwI5LHaIymIO4ooFKfNkEjWa0f1P8OsElgBh2Ijb-GrD_-b9W4kcPFcwyn5HEQmRcqXNqdpVo1heoBh2F0dG5ldHOIAAAAAAAAAACEZXRoMpAAAAAAAAAAAP__________gmlkgnY0gmlwhBLf22SJc2VjcDI1NmsxoQJxCnE6v_x2ekgY_uoE1rtwzvGy40mq9eD66XfHPBWgIIN1ZHCCD6A";
try {
ENR.decodeTxt(buf);
ENR.decodeTxt(txt);
} catch (e: any) {
expect(e.message).to.equal("Decoded ENR invalid signature: must be a byte array");
}
});
it("should throw error in invalid sequence number", () => {
const buf = Buffer.from(
"656e723a2d495334514b6b33ff583945717841337838334162436979416e537550444d764b353264433530486d31584744643574457951684d3356634a4c2d5062446b44673541507a5f706f76763022d48dcf992d5379716b306e616e636f4e572d656e7263713042676d6c6b676e5930676d6c77684838414141474a6332566a634449314e6d73786f514d31453579557370397638516a397476335a575843766146427672504e647a384b5049314e68576651577a494e315a4843434239410a",
"hex"
).toString();
const txt =
"enr:-IS4QKk3X9EqxA3x83AbCiyAnSuPDMvK52dC50Hm1XGDd5tEyQhM3VcJL-PbDkDg5APz_povv0-Syqk0nancoNW-enrcq0BgmlkgnY0gmlwhH8AAAGJc2VjcDI1NmsxoQM1E5yUsp9v8Qj9tv3ZWXCvaFBvrPNdz8KPI1NhWfQWzIN1ZHCA";
try {
ENR.decodeTxt(buf);
ENR.decodeTxt(txt);
} catch (e: any) {
expect(e.message).to.equal("Decoded ENR invalid sequence number: must be a byte array");
}
Expand Down
Loading