Skip to content

Commit

Permalink
fix: remove urlsafe-base64 dependency
Browse files Browse the repository at this point in the history
  • Loading branch information
lenkan committed Mar 11, 2024
1 parent e2b2f6e commit d534ee3
Show file tree
Hide file tree
Showing 9 changed files with 83 additions and 27 deletions.
8 changes: 1 addition & 7 deletions package-lock.json

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

3 changes: 1 addition & 2 deletions package.json
Original file line number Diff line number Diff line change
Expand Up @@ -59,8 +59,7 @@
"ecdsa-secp256r1": "^1.3.3",
"libsodium-wrappers-sumo": "^0.7.9",
"mathjs": "^12.4.0",
"structured-headers": "^0.5.0",
"urlsafe-base64": "^1.0.0"
"structured-headers": "^0.5.0"
},
"workspaces": [
"examples/*"
Expand Down
28 changes: 28 additions & 0 deletions src/keri/core/base64.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,28 @@
import { Buffer } from 'buffer';
// base64url is supported by node Buffer, but not in buffer package for browser compatibility
// https://github.com/feross/buffer/issues/309

// Instead of using a node.js-only module and forcing us to polyfill the Buffer global,
// we insert code from https://gitlab.com/seangenabe/safe-base64 here

export function encodeBase64Url(buffer: Buffer) {
if (!Buffer.isBuffer(buffer)) {
throw new TypeError('`buffer` must be a buffer.');
}
return buffer
.toString('base64')
.replace(/\+/g, '-')
.replace(/\//g, '_')
.replace(/=+/, '');
}

export function decodeBase64Url(input: string) {
if (!(typeof input === 'string')) {
throw new TypeError('`input` must be a string.');
}

const n = input.length % 4;
const padded = input + '='.repeat(n > 0 ? 4 - n : n);
const base64String = padded.replace(/-/g, '+').replace(/_/g, '/');
return Buffer.from(base64String, 'base64');
}
6 changes: 3 additions & 3 deletions src/keri/core/bexter.ts
Original file line number Diff line number Diff line change
@@ -1,7 +1,7 @@
import { BexDex, Matter, MatterArgs, MtrDex } from './matter';
import { EmptyMaterialError } from './kering';
import Base64 from 'urlsafe-base64';
import { Buffer } from 'buffer';
import { decodeBase64Url, encodeBase64Url } from './base64';

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

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

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

let ws = 0;
if (sizage?.ls === 0 && bext !== undefined) {
Expand Down
4 changes: 2 additions & 2 deletions src/keri/core/httping.ts
Original file line number Diff line number Diff line change
Expand Up @@ -10,8 +10,8 @@ import { b } from './core';
import { Cigar } from './cigar';
import { nowUTC } from './utils';
import { Siger } from './siger';
import Base64 from 'urlsafe-base64';
import { Buffer } from 'buffer';
import { encodeBase64Url } from './base64';

export function normalize(header: string) {
return header.trim();
Expand Down Expand Up @@ -121,7 +121,7 @@ export class Unqualified {
}

get qb64(): string {
return Base64.encode(Buffer.from(this._raw));
return encodeBase64Url(Buffer.from(this._raw));
}

get qb64b(): Uint8Array {
Expand Down
8 changes: 4 additions & 4 deletions src/keri/core/indexer.ts
Original file line number Diff line number Diff line change
@@ -1,7 +1,7 @@
import { EmptyMaterialError } from './kering';
import { b, b64ToInt, d, intToB64, readInt } from './core';
import Base64 from 'urlsafe-base64';
import { Buffer } from 'buffer';
import { decodeBase64Url, encodeBase64Url } from './base64';

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

const full =
both + Base64.encode(Buffer.from(bytes)).slice(ps - xizage.ls);
both + encodeBase64Url(Buffer.from(bytes)).slice(ps - xizage.ls);
if (full.length != xizage.fs) {
throw new Error(`Invalid code=${both} for raw size=${raw.length}.`);
}
Expand Down Expand Up @@ -474,7 +474,7 @@ export class Indexer {
let raw;
if (ps != 0) {
const base = new Array(ps + 1).join('A') + qb64.slice(cs);
const paw = Base64.decode(base); // decode base to leave prepadded raw
const paw = decodeBase64Url(base); // decode base to leave prepadded raw
const pi = readInt(paw.slice(0, ps)); // prepad as int
if (pi & (2 ** pbs - 1)) {
// masked pad bits non-zero
Expand All @@ -485,7 +485,7 @@ export class Indexer {
raw = paw.slice(ps); // strip off ps prepad paw bytes
} else {
const base = qb64.slice(cs);
const paw = Base64.decode(base);
const paw = decodeBase64Url(base);
const li = readInt(paw.slice(0, xizage!.ls));
if (li != 0) {
if (li == 1) {
Expand Down
10 changes: 5 additions & 5 deletions src/keri/core/matter.ts
Original file line number Diff line number Diff line change
@@ -1,9 +1,9 @@
import { EmptyMaterialError } from './kering';

import { intToB64, readInt } from './core';
import Base64 from 'urlsafe-base64';
import { b, d } from './core';
import { Buffer } from 'buffer';
import { decodeBase64Url, encodeBase64Url } from './base64';

export class Codex {
has(prop: string): boolean {
Expand Down Expand Up @@ -421,7 +421,7 @@ export class Matter {
bytes[odx] = raw[i];
}

return both + Base64.encode(Buffer.from(bytes));
return both + encodeBase64Url(Buffer.from(bytes));
} else {
const both = code;
const cs = both.length;
Expand All @@ -443,7 +443,7 @@ export class Matter {
bytes[odx] = raw[i];
}

return both + Base64.encode(Buffer.from(bytes)).slice(cs % 4);
return both + encodeBase64Url(Buffer.from(bytes)).slice(cs % 4);
}
}

Expand Down Expand Up @@ -487,7 +487,7 @@ export class Matter {
let raw;
if (ps != 0) {
const base = new Array(ps + 1).join('A') + qb64.slice(cs);
const paw = Base64.decode(base); // decode base to leave prepadded raw
const paw = decodeBase64Url(base); // decode base to leave prepadded raw
const pi = readInt(paw.subarray(0, ps)); // prepad as int
if (pi & (2 ** pbs - 1)) {
// masked pad bits non-zero
Expand All @@ -498,7 +498,7 @@ export class Matter {
raw = paw.subarray(ps); // strip off ps prepad paw bytes
} else {
const base = qb64.slice(cs);
const paw = Base64.decode(base);
const paw = decodeBase64Url(base);
const li = readInt(paw.subarray(0, sizage!.ls));
if (li != 0) {
if (li == 1) {
Expand Down
35 changes: 35 additions & 0 deletions test/core/base64.test.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,35 @@
import assert from 'node:assert';
import { decodeBase64Url, encodeBase64Url } from '../../src/keri/core/base64';

test('encode', () => {
assert.equal(encodeBase64Url(Buffer.from('f')), 'Zg');
assert.equal(encodeBase64Url(Buffer.from('fi')), 'Zmk');
assert.equal(encodeBase64Url(Buffer.from('fis')), 'Zmlz');
assert.equal(encodeBase64Url(Buffer.from('fish')), 'ZmlzaA');
assert.equal(encodeBase64Url(Buffer.from([248])), '-A');
assert.equal(encodeBase64Url(Buffer.from([252])), '_A');
});

test('decode', () => {
assert.equal(decodeBase64Url('Zg').toString(), 'f');
assert.equal(decodeBase64Url('Zmk').toString(), 'fi');
assert.equal(decodeBase64Url('Zmlz').toString(), 'fis');
assert.equal(decodeBase64Url('ZmlzaA').toString(), 'fish');
assert.equal(Buffer.from([248]).buffer, decodeBase64Url('-A').buffer);
assert.equal(Buffer.from([252]).buffer, decodeBase64Url('_A').buffer);
});

test('Test encode / decode compare with built in node Buffer', () => {
const text = '🏳️🏳️';
const b64url = '8J-Ps--4j_Cfj7PvuI8';

assert.equal(
Buffer.from(text).toString('base64url'),
encodeBase64Url(Buffer.from(text))
);

assert.equal(
Buffer.from(b64url, 'base64url').buffer,
decodeBase64Url(b64url).buffer
);
});
8 changes: 4 additions & 4 deletions test/core/indexer.test.ts
Original file line number Diff line number Diff line change
Expand Up @@ -2,8 +2,8 @@ import libsodium from 'libsodium-wrappers-sumo';
import { strict as assert } from 'assert';
import { IdrDex, Indexer } from '../../src/keri/core/indexer';
import { b, intToB64 } from '../../src/keri/core/core';
import Base64 from 'urlsafe-base64';
import { Buffer } from 'buffer';
import { decodeBase64Url, encodeBase64Url } from '../../src/keri/core/base64';

describe('Indexer', () => {
it('should encode and decode dual indexed signatures', async () => {
Expand Down Expand Up @@ -68,7 +68,7 @@ describe('Indexer', () => {
const odx = i + ps;
bytes[odx] = sig[i];
}
const sig64 = Base64.encode(Buffer.from(bytes));
const sig64 = encodeBase64Url(Buffer.from(bytes));
assert.equal(sig64.length, 88);
assert.equal(
sig64,
Expand All @@ -85,7 +85,7 @@ describe('Indexer', () => {
assert.equal(qsig64.length, 88);
let qsig64b = b(qsig64);

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

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

0 comments on commit d534ee3

Please sign in to comment.