Skip to content

Commit 5351f87

Browse files
authored
feat: constrain usage of Buffer in enr package (#286)
1 parent 50cee57 commit 5351f87

7 files changed

Lines changed: 68 additions & 40 deletions

File tree

packages/enr/package.json

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -58,10 +58,10 @@
5858
"@libp2p/interface": "^1.1.1",
5959
"@libp2p/peer-id": "^4.0.4",
6060
"@multiformats/multiaddr": "^12.1.10",
61-
"base64url": "^3.0.1",
6261
"bigint-buffer": "^1.1.5",
6362
"ethereum-cryptography": "^2.1.3",
6463
"rlp": "^2.2.6",
65-
"uint8-varint": "^2.0.2"
64+
"uint8-varint": "^2.0.2",
65+
"uint8arrays": "^5.0.1"
6666
}
6767
}

packages/enr/src/create.ts

Lines changed: 0 additions & 8 deletions
This file was deleted.

packages/enr/src/defaultCrypto.ts

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -1,7 +1,7 @@
11
import { keccak256 } from "ethereum-cryptography/keccak";
22
import { secp256k1 } from "ethereum-cryptography/secp256k1";
33

4-
import { createNodeId } from "./create.js";
4+
import { createNodeId } from "./util.js";
55
import { NodeId } from "./types.js";
66

77
export function hash(input: Uint8Array): Uint8Array {
@@ -25,5 +25,5 @@ function uncompressPublicKey(pubKey: Uint8Array): Uint8Array {
2525
}
2626

2727
export function nodeId(pubKey: Uint8Array): NodeId {
28-
return createNodeId(Buffer.from(hash(uncompressPublicKey(pubKey).slice(1))));
28+
return createNodeId(hash(uncompressPublicKey(pubKey).slice(1)));
2929
}

packages/enr/src/enr.ts

Lines changed: 14 additions & 13 deletions
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,4 @@
11
import { Multiaddr, multiaddr, protocols } from "@multiformats/multiaddr";
2-
import base64url from "base64url";
3-
import { toBigIntBE } from "bigint-buffer";
42
import * as RLP from "rlp";
53
import { KeyType, PeerId } from "@libp2p/interface";
64
import { convertToString, convertToBytes } from "@multiformats/multiaddr/convert";
@@ -9,8 +7,9 @@ import { encode as varintEncode } from "uint8-varint";
97
import { ERR_INVALID_ID, MAX_RECORD_SIZE } from "./constants.js";
108
import { ENRKey, ENRValue, SequenceNumber, NodeId } from "./types.js";
119
import { createPeerIdFromPublicKey, createPrivateKeyFromPeerId } from "./peerId.js";
12-
import { toNewUint8Array } from "./util.js";
10+
import { fromBase64url, toBase64url, toBigInt, toNewUint8Array } from "./util.js";
1311
import { getV4Crypto } from "./crypto.js";
12+
import { compare, fromString, toString } from "uint8arrays";
1413

1514
/** ENR identity scheme */
1615
export enum IDScheme {
@@ -34,7 +33,7 @@ export type SignableENRData = {
3433
export function id(kvs: ReadonlyMap<ENRKey, ENRValue>): IDScheme {
3534
const idBuf = kvs.get("id");
3635
if (!idBuf) throw new Error("id not found");
37-
const id = Buffer.from(idBuf).toString("utf8") as IDScheme;
36+
const id = toString(idBuf, "utf8") as IDScheme;
3837
if (IDScheme[id] == null) {
3938
throw new Error("Unknown enr id scheme: " + id);
4039
}
@@ -143,7 +142,7 @@ export function decodeFromValues(decoded: Uint8Array[]): ENRData {
143142
}
144143
return {
145144
kvs,
146-
seq: toBigIntBE(Buffer.from(seq)),
145+
seq: toBigInt(seq),
147146
signature,
148147
};
149148
}
@@ -154,7 +153,7 @@ export function txtToBuf(encoded: string): Uint8Array {
154153
if (!encoded.startsWith("enr:")) {
155154
throw new Error("string encoded ENR must start with 'enr:'");
156155
}
157-
return base64url.toBuffer(encoded.slice(4));
156+
return fromBase64url(encoded.slice(4));
158157
}
159158
export function decodeTxt(encoded: string): ENRData {
160159
return decode(txtToBuf(encoded));
@@ -176,17 +175,19 @@ export function getIPValue(kvs: ReadonlyMap<ENRKey, ENRValue>, key: string, mult
176175
export function getProtocolValue(kvs: ReadonlyMap<ENRKey, ENRValue>, key: string): number | undefined {
177176
const raw = kvs.get(key);
178177
if (raw) {
179-
const view = new DataView(raw.buffer, raw.byteOffset, raw.byteLength);
180-
return view.getUint16(0);
178+
if (raw.length < 2) {
179+
throw new Error("Encoded protocol length should be 2");
180+
}
181+
return (raw[0] << 8) + raw[1];
181182
} else {
182183
return undefined;
183184
}
184185
}
185186

186187
export function portToBuf(port: number): Uint8Array {
187188
const buf = new Uint8Array(2);
188-
const view = new DataView(buf.buffer, buf.byteOffset, buf.byteLength);
189-
view.setUint16(0, port);
189+
buf[0] = port >> 8;
190+
buf[1] = port;
190191
return buf;
191192
}
192193

@@ -296,7 +297,7 @@ export abstract class BaseENR {
296297
abstract encodeToValues(): (ENRKey | ENRValue | number)[];
297298
abstract encode(): Uint8Array;
298299
encodeTxt(): string {
299-
return "enr:" + base64url.encode(Buffer.from(this.encode()));
300+
return "enr:" + toBase64url(this.encode());
300301
}
301302
}
302303
/**
@@ -397,7 +398,7 @@ export class SignableENR extends BaseENR {
397398
this._signature = signature;
398399

399400
if (this.id === IDScheme.v4) {
400-
if (Buffer.compare(getV4Crypto().publicKey(this.privateKey), this.publicKey) !== 0) {
401+
if (compare(getV4Crypto().publicKey(this.privateKey), this.publicKey) !== 0) {
401402
throw new Error("Provided keypair doesn't match kv pubkey");
402403
}
403404
}
@@ -411,7 +412,7 @@ export class SignableENR extends BaseENR {
411412
return new SignableENR(
412413
{
413414
...kvs,
414-
id: Buffer.from("v4"),
415+
id: fromString("v4"),
415416
secp256k1: getV4Crypto().publicKey(privateKey),
416417
},
417418
BigInt(1),

packages/enr/src/index.ts

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -3,5 +3,5 @@ export * from "./crypto.js";
33
export * as defaultCrypto from "./defaultCrypto.js";
44
export * from "./enr.js";
55
export * from "./types.js";
6-
export * from "./create.js";
76
export * from "./peerId.js";
7+
export { createNodeId } from "./util.js";

packages/enr/src/util.ts

Lines changed: 42 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,47 @@
1+
import { toBigIntBE } from "bigint-buffer";
2+
import { fromString, toString } from "uint8arrays";
3+
import { NodeId } from "./types.js";
4+
15
// multiaddr 8.0.0 expects an Uint8Array with internal buffer starting at 0 offset
26
export function toNewUint8Array(buf: Uint8Array): Uint8Array {
37
const arrayBuffer = buf.buffer.slice(buf.byteOffset, buf.byteOffset + buf.byteLength);
48
return new Uint8Array(arrayBuffer);
59
}
10+
11+
export function toBase64url(buf: Uint8Array): string {
12+
if (globalThis.Buffer != null) {
13+
return globalThis.Buffer.from(buf).toString("base64url");
14+
}
15+
return toString(buf, "base64url");
16+
}
17+
18+
export function fromBase64url(str: string): Uint8Array {
19+
if (globalThis.Buffer != null) {
20+
return globalThis.Buffer.from(str, "base64url");
21+
}
22+
return fromString(str, "base64url");
23+
}
24+
25+
export function toBigInt(buf: Uint8Array): bigint {
26+
if (globalThis.Buffer != null) {
27+
return toBigIntBE(globalThis.Buffer.from(buf));
28+
}
29+
30+
if (buf.length === 0) {
31+
return BigInt(0);
32+
}
33+
34+
return BigInt(`0x${toString(buf, "hex")}`);
35+
}
36+
37+
export function createNodeId(buf: Uint8Array): NodeId {
38+
if (buf.length !== 32) {
39+
throw new Error("NodeId must be 32 bytes in length");
40+
}
41+
42+
if (globalThis.Buffer != null) {
43+
return globalThis.Buffer.from(buf).toString("hex");
44+
}
45+
46+
return toString(buf, "hex");
47+
}

packages/enr/test/unit/enr.test.ts

Lines changed: 7 additions & 14 deletions
Original file line numberDiff line numberDiff line change
@@ -211,10 +211,7 @@ describe("ENR", function () {
211211

212212
it("should throw decoding error - no id", () => {
213213
try {
214-
const txt = Buffer.from(
215-
"656e723a2d435972595a62404b574342526c4179357a7a61445a584a42476b636e68344d486342465a6e75584e467264764a6a5830346a527a6a7a",
216-
"hex"
217-
).toString();
214+
const txt = "enr:-CYrYZbKWCBRlAy5zzaDZXJBGkcnh4MHcBFZnuXNFrdvJjX04jRzjw";
218215
ENR.decodeTxt(txt);
219216
expect.fail("Expect error here");
220217
} catch (err: any) {
@@ -237,23 +234,19 @@ describe("ENR", function () {
237234

238235
describe("ENR fuzzing testcases", () => {
239236
it("should throw error in invalid signature", () => {
240-
const buf = Buffer.from(
241-
"656e723a2d4b7634514147774f54385374716d7749354c486149796d494f346f6f464b664e6b456a576130663150384f73456c67426832496a622d4772445f2d623957346b6350466377796e354845516d526371584e716470566f3168656f42683246306447356c64484f494141414141414141414143455a58526f4d704141414141414141414141505f5f5f5f5f5f5f5f5f5f676d6c6b676e5930676d6c7768424c663232534a6332566a634449314e6d73786f514a78436e4536765f7832656b67595f756f45317274777a76477934306d7139654436365866485042576749494e315a48437f7f7f7f7f7f7f7f7f7f7f7f7f7f7f7f7f7f7f7f7f7f434436410d0a",
242-
"hex"
243-
).toString();
237+
const txt =
238+
"enr:-Kv4QAGwOT8StqmwI5LHaIymIO4ooFKfNkEjWa0f1P8OsElgBh2Ijb-GrD_-b9W4kcPFcwyn5HEQmRcqXNqdpVo1heoBh2F0dG5ldHOIAAAAAAAAAACEZXRoMpAAAAAAAAAAAP__________gmlkgnY0gmlwhBLf22SJc2VjcDI1NmsxoQJxCnE6v_x2ekgY_uoE1rtwzvGy40mq9eD66XfHPBWgIIN1ZHCCD6A";
244239
try {
245-
ENR.decodeTxt(buf);
240+
ENR.decodeTxt(txt);
246241
} catch (e: any) {
247242
expect(e.message).to.equal("Decoded ENR invalid signature: must be a byte array");
248243
}
249244
});
250245
it("should throw error in invalid sequence number", () => {
251-
const buf = Buffer.from(
252-
"656e723a2d495334514b6b33ff583945717841337838334162436979416e537550444d764b353264433530486d31584744643574457951684d3356634a4c2d5062446b44673541507a5f706f76763022d48dcf992d5379716b306e616e636f4e572d656e7263713042676d6c6b676e5930676d6c77684838414141474a6332566a634449314e6d73786f514d31453579557370397638516a397476335a575843766146427672504e647a384b5049314e68576651577a494e315a4843434239410a",
253-
"hex"
254-
).toString();
246+
const txt =
247+
"enr:-IS4QKk3X9EqxA3x83AbCiyAnSuPDMvK52dC50Hm1XGDd5tEyQhM3VcJL-PbDkDg5APz_povv0-Syqk0nancoNW-enrcq0BgmlkgnY0gmlwhH8AAAGJc2VjcDI1NmsxoQM1E5yUsp9v8Qj9tv3ZWXCvaFBvrPNdz8KPI1NhWfQWzIN1ZHCA";
255248
try {
256-
ENR.decodeTxt(buf);
249+
ENR.decodeTxt(txt);
257250
} catch (e: any) {
258251
expect(e.message).to.equal("Decoded ENR invalid sequence number: must be a byte array");
259252
}

0 commit comments

Comments
 (0)