Skip to content

Commit e8b66e9

Browse files
authored
fix: remove urlsafe-base64 dependency (#218)
1 parent e2b2f6e commit e8b66e9

File tree

9 files changed

+83
-27
lines changed

9 files changed

+83
-27
lines changed

package-lock.json

Lines changed: 1 addition & 7 deletions
Some generated files are not rendered by default. Learn more about customizing how changed files appear on GitHub.

package.json

Lines changed: 1 addition & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -59,8 +59,7 @@
5959
"ecdsa-secp256r1": "^1.3.3",
6060
"libsodium-wrappers-sumo": "^0.7.9",
6161
"mathjs": "^12.4.0",
62-
"structured-headers": "^0.5.0",
63-
"urlsafe-base64": "^1.0.0"
62+
"structured-headers": "^0.5.0"
6463
},
6564
"workspaces": [
6665
"examples/*"

src/keri/core/base64.ts

Lines changed: 28 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,28 @@
1+
import { Buffer } from 'buffer';
2+
// base64url is supported by node Buffer, but not in buffer package for browser compatibility
3+
// https://github.com/feross/buffer/issues/309
4+
5+
// Instead of using a node.js-only module and forcing us to polyfill the Buffer global,
6+
// we insert code from https://gitlab.com/seangenabe/safe-base64 here
7+
8+
export function encodeBase64Url(buffer: Buffer) {
9+
if (!Buffer.isBuffer(buffer)) {
10+
throw new TypeError('`buffer` must be a buffer.');
11+
}
12+
return buffer
13+
.toString('base64')
14+
.replace(/\+/g, '-')
15+
.replace(/\//g, '_')
16+
.replace(/=+/, '');
17+
}
18+
19+
export function decodeBase64Url(input: string) {
20+
if (!(typeof input === 'string')) {
21+
throw new TypeError('`input` must be a string.');
22+
}
23+
24+
const n = input.length % 4;
25+
const padded = input + '='.repeat(n > 0 ? 4 - n : n);
26+
const base64String = padded.replace(/-/g, '+').replace(/_/g, '/');
27+
return Buffer.from(base64String, 'base64');
28+
}

src/keri/core/bexter.ts

Lines changed: 3 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -1,7 +1,7 @@
11
import { BexDex, Matter, MatterArgs, MtrDex } from './matter';
22
import { EmptyMaterialError } from './kering';
3-
import Base64 from 'urlsafe-base64';
43
import { Buffer } from 'buffer';
4+
import { decodeBase64Url, encodeBase64Url } from './base64';
55

66
const B64REX = '^[A-Za-z0-9\\-_]*$';
77
export const Reb64 = new RegExp(B64REX);
@@ -115,15 +115,15 @@ export class Bexter extends Matter {
115115
const wad = new Array(ws);
116116
wad.fill('A');
117117
const base = wad.join('') + bext; // pre pad with wad of zeros in Base64 == 'A'
118-
const raw = Base64.decode(base); // [ls:] // convert and remove leader
118+
const raw = decodeBase64Url(base); // [ls:] // convert and remove leader
119119

120120
return Uint8Array.from(raw).subarray(ls); // raw binary equivalent of text
121121
}
122122

123123
get bext(): string {
124124
const sizage = Matter.Sizes.get(this.code);
125125
const wad = Uint8Array.from(new Array(sizage?.ls).fill(0));
126-
const bext = Base64.encode(Buffer.from([...wad, ...this.raw]));
126+
const bext = encodeBase64Url(Buffer.from([...wad, ...this.raw]));
127127

128128
let ws = 0;
129129
if (sizage?.ls === 0 && bext !== undefined) {

src/keri/core/httping.ts

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -10,8 +10,8 @@ import { b } from './core';
1010
import { Cigar } from './cigar';
1111
import { nowUTC } from './utils';
1212
import { Siger } from './siger';
13-
import Base64 from 'urlsafe-base64';
1413
import { Buffer } from 'buffer';
14+
import { encodeBase64Url } from './base64';
1515

1616
export function normalize(header: string) {
1717
return header.trim();
@@ -121,7 +121,7 @@ export class Unqualified {
121121
}
122122

123123
get qb64(): string {
124-
return Base64.encode(Buffer.from(this._raw));
124+
return encodeBase64Url(Buffer.from(this._raw));
125125
}
126126

127127
get qb64b(): Uint8Array {

src/keri/core/indexer.ts

Lines changed: 4 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -1,7 +1,7 @@
11
import { EmptyMaterialError } from './kering';
22
import { b, b64ToInt, d, intToB64, readInt } from './core';
3-
import Base64 from 'urlsafe-base64';
43
import { Buffer } from 'buffer';
4+
import { decodeBase64Url, encodeBase64Url } from './base64';
55

66
export class IndexerCodex {
77
Ed25519_Sig: string = 'A'; // Ed25519 sig appears same in both lists if any.
@@ -399,7 +399,7 @@ export class Indexer {
399399
}
400400

401401
const full =
402-
both + Base64.encode(Buffer.from(bytes)).slice(ps - xizage.ls);
402+
both + encodeBase64Url(Buffer.from(bytes)).slice(ps - xizage.ls);
403403
if (full.length != xizage.fs) {
404404
throw new Error(`Invalid code=${both} for raw size=${raw.length}.`);
405405
}
@@ -474,7 +474,7 @@ export class Indexer {
474474
let raw;
475475
if (ps != 0) {
476476
const base = new Array(ps + 1).join('A') + qb64.slice(cs);
477-
const paw = Base64.decode(base); // decode base to leave prepadded raw
477+
const paw = decodeBase64Url(base); // decode base to leave prepadded raw
478478
const pi = readInt(paw.slice(0, ps)); // prepad as int
479479
if (pi & (2 ** pbs - 1)) {
480480
// masked pad bits non-zero
@@ -485,7 +485,7 @@ export class Indexer {
485485
raw = paw.slice(ps); // strip off ps prepad paw bytes
486486
} else {
487487
const base = qb64.slice(cs);
488-
const paw = Base64.decode(base);
488+
const paw = decodeBase64Url(base);
489489
const li = readInt(paw.slice(0, xizage!.ls));
490490
if (li != 0) {
491491
if (li == 1) {

src/keri/core/matter.ts

Lines changed: 5 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -1,9 +1,9 @@
11
import { EmptyMaterialError } from './kering';
22

33
import { intToB64, readInt } from './core';
4-
import Base64 from 'urlsafe-base64';
54
import { b, d } from './core';
65
import { Buffer } from 'buffer';
6+
import { decodeBase64Url, encodeBase64Url } from './base64';
77

88
export class Codex {
99
has(prop: string): boolean {
@@ -421,7 +421,7 @@ export class Matter {
421421
bytes[odx] = raw[i];
422422
}
423423

424-
return both + Base64.encode(Buffer.from(bytes));
424+
return both + encodeBase64Url(Buffer.from(bytes));
425425
} else {
426426
const both = code;
427427
const cs = both.length;
@@ -443,7 +443,7 @@ export class Matter {
443443
bytes[odx] = raw[i];
444444
}
445445

446-
return both + Base64.encode(Buffer.from(bytes)).slice(cs % 4);
446+
return both + encodeBase64Url(Buffer.from(bytes)).slice(cs % 4);
447447
}
448448
}
449449

@@ -487,7 +487,7 @@ export class Matter {
487487
let raw;
488488
if (ps != 0) {
489489
const base = new Array(ps + 1).join('A') + qb64.slice(cs);
490-
const paw = Base64.decode(base); // decode base to leave prepadded raw
490+
const paw = decodeBase64Url(base); // decode base to leave prepadded raw
491491
const pi = readInt(paw.subarray(0, ps)); // prepad as int
492492
if (pi & (2 ** pbs - 1)) {
493493
// masked pad bits non-zero
@@ -498,7 +498,7 @@ export class Matter {
498498
raw = paw.subarray(ps); // strip off ps prepad paw bytes
499499
} else {
500500
const base = qb64.slice(cs);
501-
const paw = Base64.decode(base);
501+
const paw = decodeBase64Url(base);
502502
const li = readInt(paw.subarray(0, sizage!.ls));
503503
if (li != 0) {
504504
if (li == 1) {

test/core/base64.test.ts

Lines changed: 35 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,35 @@
1+
import assert from 'node:assert';
2+
import { decodeBase64Url, encodeBase64Url } from '../../src/keri/core/base64';
3+
4+
test('encode', () => {
5+
assert.equal(encodeBase64Url(Buffer.from('f')), 'Zg');
6+
assert.equal(encodeBase64Url(Buffer.from('fi')), 'Zmk');
7+
assert.equal(encodeBase64Url(Buffer.from('fis')), 'Zmlz');
8+
assert.equal(encodeBase64Url(Buffer.from('fish')), 'ZmlzaA');
9+
assert.equal(encodeBase64Url(Buffer.from([248])), '-A');
10+
assert.equal(encodeBase64Url(Buffer.from([252])), '_A');
11+
});
12+
13+
test('decode', () => {
14+
assert.equal(decodeBase64Url('Zg').toString(), 'f');
15+
assert.equal(decodeBase64Url('Zmk').toString(), 'fi');
16+
assert.equal(decodeBase64Url('Zmlz').toString(), 'fis');
17+
assert.equal(decodeBase64Url('ZmlzaA').toString(), 'fish');
18+
assert.equal(Buffer.from([248]).buffer, decodeBase64Url('-A').buffer);
19+
assert.equal(Buffer.from([252]).buffer, decodeBase64Url('_A').buffer);
20+
});
21+
22+
test('Test encode / decode compare with built in node Buffer', () => {
23+
const text = '🏳️🏳️';
24+
const b64url = '8J-Ps--4j_Cfj7PvuI8';
25+
26+
assert.equal(
27+
Buffer.from(text).toString('base64url'),
28+
encodeBase64Url(Buffer.from(text))
29+
);
30+
31+
assert.equal(
32+
Buffer.from(b64url, 'base64url').buffer,
33+
decodeBase64Url(b64url).buffer
34+
);
35+
});

test/core/indexer.test.ts

Lines changed: 4 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -2,8 +2,8 @@ import libsodium from 'libsodium-wrappers-sumo';
22
import { strict as assert } from 'assert';
33
import { IdrDex, Indexer } from '../../src/keri/core/indexer';
44
import { b, intToB64 } from '../../src/keri/core/core';
5-
import Base64 from 'urlsafe-base64';
65
import { Buffer } from 'buffer';
6+
import { decodeBase64Url, encodeBase64Url } from '../../src/keri/core/base64';
77

88
describe('Indexer', () => {
99
it('should encode and decode dual indexed signatures', async () => {
@@ -68,7 +68,7 @@ describe('Indexer', () => {
6868
const odx = i + ps;
6969
bytes[odx] = sig[i];
7070
}
71-
const sig64 = Base64.encode(Buffer.from(bytes));
71+
const sig64 = encodeBase64Url(Buffer.from(bytes));
7272
assert.equal(sig64.length, 88);
7373
assert.equal(
7474
sig64,
@@ -85,7 +85,7 @@ describe('Indexer', () => {
8585
assert.equal(qsig64.length, 88);
8686
let qsig64b = b(qsig64);
8787

88-
let qsig2b = Base64.decode(qsig64);
88+
let qsig2b = decodeBase64Url(qsig64);
8989
assert.equal(qsig2b.length, 66);
9090
// assert qsig2b == (b"\x00\x00\x99\xd2<9$$0\x9fk\xfb\x18\xa0\x8c@r\x122.k\xb2\xc7\x1fp\x0e'm"
9191
// b'\x8f@\xaa\xa5\x8c\xc8n\x85\xc8!\xf6q\x91p\xa9\xec\xcf\x92\xaf)'
@@ -166,7 +166,7 @@ describe('Indexer', () => {
166166
qsig64 =
167167
'AFCZ0jw5JCQwn2v7GKCMQHISMi5rsscfcA4nbY9AqqWMyG6FyCH2cZFwqezPkq8p3sr8f37Xb3wXgh3UPG8igSYJ';
168168
qsig64b = b(qsig64);
169-
qsig2b = Base64.decode(qsig64);
169+
qsig2b = decodeBase64Url(qsig64);
170170
assert.equal(qsig2b.length, 66);
171171

172172
indexer = new Indexer({ raw: sig, code: IdrDex.Ed25519_Sig, index: 5 });

0 commit comments

Comments
 (0)