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

fix: remove urlsafe-base64 dependency #218

Merged
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
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
Loading