@scure/base
Advanced tools
Comparing version 1.1.1 to 1.1.2
/*! scure-base - MIT License (c) 2022 Paul Miller (paulmillr.com) */ | ||
// Utilities | ||
/** | ||
* @__NO_SIDE_EFFECTS__ | ||
*/ | ||
export function assertNumber(n) { | ||
@@ -6,10 +10,20 @@ if (!Number.isSafeInteger(n)) | ||
} | ||
/** | ||
* @__NO_SIDE_EFFECTS__ | ||
*/ | ||
function chain(...args) { | ||
// Wrap call in closure so JIT can inline calls | ||
const wrap = (a, b) => (c) => a(b(c)); | ||
// Construct chain of args[-1].encode(args[-2].encode([...])) | ||
const encode = Array.from(args) | ||
.reverse() | ||
.reduce((acc, i) => (acc ? wrap(acc, i.encode) : i.encode), undefined); | ||
// Construct chain of args[0].decode(args[1].decode(...)) | ||
const decode = args.reduce((acc, i) => (acc ? wrap(acc, i.decode) : i.decode), undefined); | ||
return { encode, decode }; | ||
} | ||
/** | ||
* Encodes integer radix representation to array of strings using alphabet and back | ||
* @__NO_SIDE_EFFECTS__ | ||
*/ | ||
function alphabet(alphabet) { | ||
@@ -41,2 +55,5 @@ return { | ||
} | ||
/** | ||
* @__NO_SIDE_EFFECTS__ | ||
*/ | ||
function join(separator = '') { | ||
@@ -61,2 +78,6 @@ if (typeof separator !== 'string') | ||
} | ||
/** | ||
* Pad strings array so it has integer number of bits | ||
* @__NO_SIDE_EFFECTS__ | ||
*/ | ||
function padding(bits, chr = '=') { | ||
@@ -94,2 +115,5 @@ assertNumber(bits); | ||
} | ||
/** | ||
* @__NO_SIDE_EFFECTS__ | ||
*/ | ||
function normalize(fn) { | ||
@@ -100,3 +124,8 @@ if (typeof fn !== 'function') | ||
} | ||
/** | ||
* Slow: O(n^2) time complexity | ||
* @__NO_SIDE_EFFECTS__ | ||
*/ | ||
function convertRadix(data, from, to) { | ||
// base 1 is impossible | ||
if (from < 2) | ||
@@ -130,8 +159,9 @@ throw new Error(`convertRadix: wrong from=${from}, base cannot be less than 2`); | ||
carry = digitBase % to; | ||
digits[i] = Math.floor(digitBase / to); | ||
if (!Number.isSafeInteger(digits[i]) || digits[i] * to + carry !== digitBase) | ||
const rounded = Math.floor(digitBase / to); | ||
digits[i] = rounded; | ||
if (!Number.isSafeInteger(rounded) || rounded * to + carry !== digitBase) | ||
throw new Error('convertRadix: carry overflow'); | ||
if (!done) | ||
continue; | ||
else if (!digits[i]) | ||
else if (!rounded) | ||
pos = i; | ||
@@ -149,4 +179,8 @@ else | ||
} | ||
const gcd = (a, b) => (!b ? a : gcd(b, a % b)); | ||
const radix2carry = (from, to) => from + (to - gcd(from, to)); | ||
const gcd = /* @__NO_SIDE_EFFECTS__ */ (a, b) => (!b ? a : gcd(b, a % b)); | ||
const radix2carry = /*@__NO_SIDE_EFFECTS__ */ (from, to) => from + (to - gcd(from, to)); | ||
/** | ||
* Implemented with numbers, because BigInt is 5x slower | ||
* @__NO_SIDE_EFFECTS__ | ||
*/ | ||
function convertRadix2(data, from, to, padding) { | ||
@@ -163,3 +197,3 @@ if (!Array.isArray(data)) | ||
let carry = 0; | ||
let pos = 0; | ||
let pos = 0; // bitwise position in current element | ||
const mask = 2 ** to - 1; | ||
@@ -177,3 +211,3 @@ const res = []; | ||
res.push(((carry >> (pos - to)) & mask) >>> 0); | ||
carry &= 2 ** pos - 1; | ||
carry &= 2 ** pos - 1; // clean carry, otherwise it will cause overflow | ||
} | ||
@@ -189,2 +223,5 @@ carry = (carry << (to - pos)) & mask; | ||
} | ||
/** | ||
* @__NO_SIDE_EFFECTS__ | ||
*/ | ||
function radix(num) { | ||
@@ -205,2 +242,7 @@ assertNumber(num); | ||
} | ||
/** | ||
* If both bases are power of same number (like `2**8 <-> 2**64`), | ||
* there is a linear algorithm. For now we have implementation for power-of-two bases only. | ||
* @__NO_SIDE_EFFECTS__ | ||
*/ | ||
function radix2(bits, revPadding = false) { | ||
@@ -225,2 +267,5 @@ assertNumber(bits); | ||
} | ||
/** | ||
* @__NO_SIDE_EFFECTS__ | ||
*/ | ||
function unsafeWrapper(fn) { | ||
@@ -236,2 +281,5 @@ if (typeof fn !== 'function') | ||
} | ||
/** | ||
* @__NO_SIDE_EFFECTS__ | ||
*/ | ||
function checksum(len, fn) { | ||
@@ -265,12 +313,20 @@ assertNumber(len); | ||
export const utils = { alphabet, chain, checksum, radix, radix2, join, padding }; | ||
export const base16 = chain(radix2(4), alphabet('0123456789ABCDEF'), join('')); | ||
export const base32 = chain(radix2(5), alphabet('ABCDEFGHIJKLMNOPQRSTUVWXYZ234567'), padding(5), join('')); | ||
export const base32hex = chain(radix2(5), alphabet('0123456789ABCDEFGHIJKLMNOPQRSTUV'), padding(5), join('')); | ||
export const base32crockford = chain(radix2(5), alphabet('0123456789ABCDEFGHJKMNPQRSTVWXYZ'), join(''), normalize((s) => s.toUpperCase().replace(/O/g, '0').replace(/[IL]/g, '1'))); | ||
export const base64 = chain(radix2(6), alphabet('ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789+/'), padding(6), join('')); | ||
export const base64url = chain(radix2(6), alphabet('ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789-_'), padding(6), join('')); | ||
// RFC 4648 aka RFC 3548 | ||
// --------------------- | ||
export const base16 = /* @__PURE__ */ chain(radix2(4), alphabet('0123456789ABCDEF'), join('')); | ||
export const base32 = /* @__PURE__ */ chain(radix2(5), alphabet('ABCDEFGHIJKLMNOPQRSTUVWXYZ234567'), padding(5), join('')); | ||
export const base32hex = /* @__PURE__ */ chain(radix2(5), alphabet('0123456789ABCDEFGHIJKLMNOPQRSTUV'), padding(5), join('')); | ||
export const base32crockford = /* @__PURE__ */ chain(radix2(5), alphabet('0123456789ABCDEFGHJKMNPQRSTVWXYZ'), join(''), normalize((s) => s.toUpperCase().replace(/O/g, '0').replace(/[IL]/g, '1'))); | ||
export const base64 = /* @__PURE__ */ chain(radix2(6), alphabet('ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789+/'), padding(6), join('')); | ||
export const base64url = /* @__PURE__ */ chain(radix2(6), alphabet('ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789-_'), padding(6), join('')); | ||
export const base64urlnopad = /* @__PURE__ */ chain(radix2(6), alphabet('ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789-_'), join('')); | ||
// base58 code | ||
// ----------- | ||
const genBase58 = (abc) => chain(radix(58), alphabet(abc), join('')); | ||
export const base58 = genBase58('123456789ABCDEFGHJKLMNPQRSTUVWXYZabcdefghijkmnopqrstuvwxyz'); | ||
export const base58flickr = genBase58('123456789abcdefghijkmnopqrstuvwxyzABCDEFGHJKLMNPQRSTUVWXYZ'); | ||
export const base58xrp = genBase58('rpshnaf39wBUDNEGHJKLM4PQRST7VWXYZ2bcdeCg65jkm8oFqi1tuvAxyz'); | ||
export const base58 = /* @__PURE__ */ genBase58('123456789ABCDEFGHJKLMNPQRSTUVWXYZabcdefghijkmnopqrstuvwxyz'); | ||
export const base58flickr = /* @__PURE__ */ genBase58('123456789abcdefghijkmnopqrstuvwxyzABCDEFGHJKLMNPQRSTUVWXYZ'); | ||
export const base58xrp = /* @__PURE__ */ genBase58('rpshnaf39wBUDNEGHJKLM4PQRST7VWXYZ2bcdeCg65jkm8oFqi1tuvAxyz'); | ||
// xmr ver is done in 8-byte blocks (which equals 11 chars in decoding). Last (non-full) block padded with '1' to size in XMR_BLOCK_LEN. | ||
// Block encoding significantly reduces quadratic complexity of base58. | ||
// Data len (index) -> encoded block len | ||
const XMR_BLOCK_LEN = [0, 2, 3, 5, 6, 7, 9, 10, 11]; | ||
@@ -301,5 +357,8 @@ export const base58xmr = { | ||
}; | ||
export const base58check = (sha256) => chain(checksum(4, (data) => sha256(sha256(data))), base58); | ||
const BECH_ALPHABET = chain(alphabet('qpzry9x8gf2tvdw0s3jn54khce6mua7l'), join('')); | ||
export const base58check = /* @__PURE__ */ (sha256) => chain(checksum(4, (data) => sha256(sha256(data))), base58); | ||
const BECH_ALPHABET = /* @__PURE__ */ chain(alphabet('qpzry9x8gf2tvdw0s3jn54khce6mua7l'), join('')); | ||
const POLYMOD_GENERATORS = [0x3b6a57b2, 0x26508e6d, 0x1ea119fa, 0x3d4233dd, 0x2a1462b3]; | ||
/** | ||
* @__NO_SIDE_EFFECTS__ | ||
*/ | ||
function bech32Polymod(pre) { | ||
@@ -314,2 +373,5 @@ const b = pre >> 25; | ||
} | ||
/** | ||
* @__NO_SIDE_EFFECTS__ | ||
*/ | ||
function bechChecksum(prefix, words, encodingConst = 1) { | ||
@@ -334,2 +396,5 @@ const len = prefix.length; | ||
} | ||
/** | ||
* @__NO_SIDE_EFFECTS__ | ||
*/ | ||
function genBech32(encoding) { | ||
@@ -349,4 +414,5 @@ const ENCODING_CONST = encoding === 'bech32' ? 1 : 0x2bc830a3; | ||
throw new TypeError(`Length ${actualLength} exceeds limit ${limit}`); | ||
prefix = prefix.toLowerCase(); | ||
return `${prefix}1${BECH_ALPHABET.encode(words)}${bechChecksum(prefix, words, ENCODING_CONST)}`; | ||
const lowered = prefix.toLowerCase(); | ||
const sum = bechChecksum(lowered, words, ENCODING_CONST); | ||
return `${lowered}1${BECH_ALPHABET.encode(words)}${sum}`; | ||
} | ||
@@ -358,2 +424,3 @@ function decode(str, limit = 90) { | ||
throw new TypeError(`Wrong string length: ${str.length} (${str}). Expected (8..${limit})`); | ||
// don't allow mixed case | ||
const lowered = str.toLowerCase(); | ||
@@ -383,4 +450,4 @@ if (str !== lowered && str !== str.toUpperCase()) | ||
} | ||
export const bech32 = genBech32('bech32'); | ||
export const bech32m = genBech32('bech32m'); | ||
export const bech32 = /* @__PURE__ */ genBech32('bech32'); | ||
export const bech32m = /* @__PURE__ */ genBech32('bech32m'); | ||
export const utf8 = { | ||
@@ -390,3 +457,3 @@ encode: (data) => new TextDecoder().decode(data), | ||
}; | ||
export const hex = chain(radix2(4), alphabet('0123456789abcdef'), join(''), normalize((s) => { | ||
export const hex = /* @__PURE__ */ chain(radix2(4), alphabet('0123456789abcdef'), join(''), normalize((s) => { | ||
if (typeof s !== 'string' || s.length % 2) | ||
@@ -396,6 +463,7 @@ throw new TypeError(`hex.decode: expected string, got ${typeof s} with length ${s.length}`); | ||
})); | ||
// prettier-ignore | ||
const CODERS = { | ||
utf8, hex, base16, base32, base64, base64url, base58, base58xmr | ||
}; | ||
const coderTypeError = `Invalid encoding type. Available types: ${Object.keys(CODERS).join(', ')}`; | ||
const coderTypeError = 'Invalid encoding type. Available types: utf8, hex, base16, base32, base64, base64url, base58, base58xmr'; | ||
export const bytesToString = (type, bytes) => { | ||
@@ -408,3 +476,3 @@ if (typeof type !== 'string' || !CODERS.hasOwnProperty(type)) | ||
}; | ||
export const str = bytesToString; | ||
export const str = bytesToString; // as in python, but for bytes only | ||
export const stringToBytes = (type, str) => { | ||
@@ -411,0 +479,0 @@ if (!CODERS.hasOwnProperty(type)) |
@@ -1,1 +0,1 @@ | ||
{ "type": "module" } | ||
{ "type": "module", "sideEffects": false } |
/*! scure-base - MIT License (c) 2022 Paul Miller (paulmillr.com) */ | ||
/** | ||
* @__NO_SIDE_EFFECTS__ | ||
*/ | ||
export declare function assertNumber(n: number): void; | ||
@@ -11,18 +14,43 @@ export interface Coder<F, T> { | ||
} | ||
declare type Chain = [Coder<any, any>, ...Coder<any, any>[]]; | ||
declare type Input<F> = F extends Coder<infer T, any> ? T : never; | ||
declare type Output<F> = F extends Coder<any, infer T> ? T : never; | ||
declare type First<T> = T extends [infer U, ...any[]] ? U : never; | ||
declare type Last<T> = T extends [...any[], infer U] ? U : never; | ||
declare type Tail<T> = T extends [any, ...infer U] ? U : never; | ||
declare type AsChain<C extends Chain, Rest = Tail<C>> = { | ||
type Chain = [Coder<any, any>, ...Coder<any, any>[]]; | ||
type Input<F> = F extends Coder<infer T, any> ? T : never; | ||
type Output<F> = F extends Coder<any, infer T> ? T : never; | ||
type First<T> = T extends [infer U, ...any[]] ? U : never; | ||
type Last<T> = T extends [...any[], infer U] ? U : never; | ||
type Tail<T> = T extends [any, ...infer U] ? U : never; | ||
type AsChain<C extends Chain, Rest = Tail<C>> = { | ||
[K in keyof C]: Coder<Input<C[K]>, Input<K extends keyof Rest ? Rest[K] : any>>; | ||
}; | ||
/** | ||
* @__NO_SIDE_EFFECTS__ | ||
*/ | ||
declare function chain<T extends Chain & AsChain<T>>(...args: T): Coder<Input<First<T>>, Output<Last<T>>>; | ||
declare type Alphabet = string[] | string; | ||
type Alphabet = string[] | string; | ||
/** | ||
* Encodes integer radix representation to array of strings using alphabet and back | ||
* @__NO_SIDE_EFFECTS__ | ||
*/ | ||
declare function alphabet(alphabet: Alphabet): Coder<number[], string[]>; | ||
/** | ||
* @__NO_SIDE_EFFECTS__ | ||
*/ | ||
declare function join(separator?: string): Coder<string[], string>; | ||
/** | ||
* Pad strings array so it has integer number of bits | ||
* @__NO_SIDE_EFFECTS__ | ||
*/ | ||
declare function padding(bits: number, chr?: string): Coder<string[], string[]>; | ||
/** | ||
* @__NO_SIDE_EFFECTS__ | ||
*/ | ||
declare function radix(num: number): Coder<Uint8Array, number[]>; | ||
/** | ||
* If both bases are power of same number (like `2**8 <-> 2**64`), | ||
* there is a linear algorithm. For now we have implementation for power-of-two bases only. | ||
* @__NO_SIDE_EFFECTS__ | ||
*/ | ||
declare function radix2(bits: number, revPadding?: boolean): Coder<Uint8Array, number[]>; | ||
/** | ||
* @__NO_SIDE_EFFECTS__ | ||
*/ | ||
declare function checksum(len: number, fn: (data: Uint8Array) => Uint8Array): Coder<Uint8Array, Uint8Array>; | ||
@@ -44,2 +72,3 @@ export declare const utils: { | ||
export declare const base64url: BytesCoder; | ||
export declare const base64urlnopad: BytesCoder; | ||
export declare const base58: BytesCoder; | ||
@@ -50,8 +79,8 @@ export declare const base58flickr: BytesCoder; | ||
export declare const base58check: (sha256: (data: Uint8Array) => Uint8Array) => BytesCoder; | ||
export interface Bech32Decoded { | ||
prefix: string; | ||
export interface Bech32Decoded<Prefix extends string = string> { | ||
prefix: Prefix; | ||
words: number[]; | ||
} | ||
export interface Bech32DecodedWithArray { | ||
prefix: string; | ||
export interface Bech32DecodedWithArray<Prefix extends string = string> { | ||
prefix: Prefix; | ||
words: number[]; | ||
@@ -61,17 +90,23 @@ bytes: Uint8Array; | ||
export declare const bech32: { | ||
encode: (prefix: string, words: number[] | Uint8Array, limit?: number | false) => string; | ||
decode: (str: string, limit?: number | false) => Bech32Decoded; | ||
encode: <Prefix extends string>(prefix: Prefix, words: number[] | Uint8Array, limit?: number | false) => `${Lowercase<Prefix>}1${string}`; | ||
decode: { | ||
<Prefix_1 extends string>(str: `${Prefix_1}1${string}`, limit: number | false): Bech32Decoded<Prefix_1>; | ||
(str: string, limit: number | false): Bech32Decoded; | ||
}; | ||
decodeToBytes: (str: string) => Bech32DecodedWithArray; | ||
decodeUnsafe: (str: string, limit?: number | false | undefined) => Bech32Decoded | undefined; | ||
decodeUnsafe: (str: string, limit: number | false) => void | Bech32Decoded<string>; | ||
fromWords: (to: number[]) => Uint8Array; | ||
fromWordsUnsafe: (to: number[]) => Uint8Array | undefined; | ||
fromWordsUnsafe: (to: number[]) => void | Uint8Array; | ||
toWords: (from: Uint8Array) => number[]; | ||
}; | ||
export declare const bech32m: { | ||
encode: (prefix: string, words: number[] | Uint8Array, limit?: number | false) => string; | ||
decode: (str: string, limit?: number | false) => Bech32Decoded; | ||
encode: <Prefix extends string>(prefix: Prefix, words: number[] | Uint8Array, limit?: number | false) => `${Lowercase<Prefix>}1${string}`; | ||
decode: { | ||
<Prefix_1 extends string>(str: `${Prefix_1}1${string}`, limit: number | false): Bech32Decoded<Prefix_1>; | ||
(str: string, limit: number | false): Bech32Decoded; | ||
}; | ||
decodeToBytes: (str: string) => Bech32DecodedWithArray; | ||
decodeUnsafe: (str: string, limit?: number | false | undefined) => Bech32Decoded | undefined; | ||
decodeUnsafe: (str: string, limit: number | false) => void | Bech32Decoded<string>; | ||
fromWords: (to: number[]) => Uint8Array; | ||
fromWordsUnsafe: (to: number[]) => Uint8Array | undefined; | ||
fromWordsUnsafe: (to: number[]) => void | Uint8Array; | ||
toWords: (from: Uint8Array) => number[]; | ||
@@ -91,3 +126,3 @@ }; | ||
}; | ||
declare type CoderType = keyof typeof CODERS; | ||
type CoderType = keyof typeof CODERS; | ||
export declare const bytesToString: (type: CoderType, bytes: Uint8Array) => string; | ||
@@ -98,1 +133,2 @@ export declare const str: (type: CoderType, bytes: Uint8Array) => string; | ||
export {}; | ||
//# sourceMappingURL=index.d.ts.map |
"use strict"; | ||
/*! scure-base - MIT License (c) 2022 Paul Miller (paulmillr.com) */ | ||
Object.defineProperty(exports, "__esModule", { value: true }); | ||
exports.bytes = exports.stringToBytes = exports.str = exports.bytesToString = exports.hex = exports.utf8 = exports.bech32m = exports.bech32 = exports.base58check = exports.base58xmr = exports.base58xrp = exports.base58flickr = exports.base58 = exports.base64url = exports.base64 = exports.base32crockford = exports.base32hex = exports.base32 = exports.base16 = exports.utils = exports.assertNumber = void 0; | ||
exports.bytes = exports.stringToBytes = exports.str = exports.bytesToString = exports.hex = exports.utf8 = exports.bech32m = exports.bech32 = exports.base58check = exports.base58xmr = exports.base58xrp = exports.base58flickr = exports.base58 = exports.base64urlnopad = exports.base64url = exports.base64 = exports.base32crockford = exports.base32hex = exports.base32 = exports.base16 = exports.utils = exports.assertNumber = void 0; | ||
// Utilities | ||
/** | ||
* @__NO_SIDE_EFFECTS__ | ||
*/ | ||
function assertNumber(n) { | ||
@@ -10,10 +14,20 @@ if (!Number.isSafeInteger(n)) | ||
exports.assertNumber = assertNumber; | ||
/** | ||
* @__NO_SIDE_EFFECTS__ | ||
*/ | ||
function chain(...args) { | ||
// Wrap call in closure so JIT can inline calls | ||
const wrap = (a, b) => (c) => a(b(c)); | ||
// Construct chain of args[-1].encode(args[-2].encode([...])) | ||
const encode = Array.from(args) | ||
.reverse() | ||
.reduce((acc, i) => (acc ? wrap(acc, i.encode) : i.encode), undefined); | ||
// Construct chain of args[0].decode(args[1].decode(...)) | ||
const decode = args.reduce((acc, i) => (acc ? wrap(acc, i.decode) : i.decode), undefined); | ||
return { encode, decode }; | ||
} | ||
/** | ||
* Encodes integer radix representation to array of strings using alphabet and back | ||
* @__NO_SIDE_EFFECTS__ | ||
*/ | ||
function alphabet(alphabet) { | ||
@@ -45,2 +59,5 @@ return { | ||
} | ||
/** | ||
* @__NO_SIDE_EFFECTS__ | ||
*/ | ||
function join(separator = '') { | ||
@@ -65,2 +82,6 @@ if (typeof separator !== 'string') | ||
} | ||
/** | ||
* Pad strings array so it has integer number of bits | ||
* @__NO_SIDE_EFFECTS__ | ||
*/ | ||
function padding(bits, chr = '=') { | ||
@@ -98,2 +119,5 @@ assertNumber(bits); | ||
} | ||
/** | ||
* @__NO_SIDE_EFFECTS__ | ||
*/ | ||
function normalize(fn) { | ||
@@ -104,3 +128,8 @@ if (typeof fn !== 'function') | ||
} | ||
/** | ||
* Slow: O(n^2) time complexity | ||
* @__NO_SIDE_EFFECTS__ | ||
*/ | ||
function convertRadix(data, from, to) { | ||
// base 1 is impossible | ||
if (from < 2) | ||
@@ -134,8 +163,9 @@ throw new Error(`convertRadix: wrong from=${from}, base cannot be less than 2`); | ||
carry = digitBase % to; | ||
digits[i] = Math.floor(digitBase / to); | ||
if (!Number.isSafeInteger(digits[i]) || digits[i] * to + carry !== digitBase) | ||
const rounded = Math.floor(digitBase / to); | ||
digits[i] = rounded; | ||
if (!Number.isSafeInteger(rounded) || rounded * to + carry !== digitBase) | ||
throw new Error('convertRadix: carry overflow'); | ||
if (!done) | ||
continue; | ||
else if (!digits[i]) | ||
else if (!rounded) | ||
pos = i; | ||
@@ -153,4 +183,8 @@ else | ||
} | ||
const gcd = (a, b) => (!b ? a : gcd(b, a % b)); | ||
const radix2carry = (from, to) => from + (to - gcd(from, to)); | ||
const gcd = /* @__NO_SIDE_EFFECTS__ */ (a, b) => (!b ? a : gcd(b, a % b)); | ||
const radix2carry = /*@__NO_SIDE_EFFECTS__ */ (from, to) => from + (to - gcd(from, to)); | ||
/** | ||
* Implemented with numbers, because BigInt is 5x slower | ||
* @__NO_SIDE_EFFECTS__ | ||
*/ | ||
function convertRadix2(data, from, to, padding) { | ||
@@ -167,3 +201,3 @@ if (!Array.isArray(data)) | ||
let carry = 0; | ||
let pos = 0; | ||
let pos = 0; // bitwise position in current element | ||
const mask = 2 ** to - 1; | ||
@@ -181,3 +215,3 @@ const res = []; | ||
res.push(((carry >> (pos - to)) & mask) >>> 0); | ||
carry &= 2 ** pos - 1; | ||
carry &= 2 ** pos - 1; // clean carry, otherwise it will cause overflow | ||
} | ||
@@ -193,2 +227,5 @@ carry = (carry << (to - pos)) & mask; | ||
} | ||
/** | ||
* @__NO_SIDE_EFFECTS__ | ||
*/ | ||
function radix(num) { | ||
@@ -209,2 +246,7 @@ assertNumber(num); | ||
} | ||
/** | ||
* If both bases are power of same number (like `2**8 <-> 2**64`), | ||
* there is a linear algorithm. For now we have implementation for power-of-two bases only. | ||
* @__NO_SIDE_EFFECTS__ | ||
*/ | ||
function radix2(bits, revPadding = false) { | ||
@@ -229,2 +271,5 @@ assertNumber(bits); | ||
} | ||
/** | ||
* @__NO_SIDE_EFFECTS__ | ||
*/ | ||
function unsafeWrapper(fn) { | ||
@@ -240,2 +285,5 @@ if (typeof fn !== 'function') | ||
} | ||
/** | ||
* @__NO_SIDE_EFFECTS__ | ||
*/ | ||
function checksum(len, fn) { | ||
@@ -269,2 +317,4 @@ assertNumber(len); | ||
exports.utils = { alphabet, chain, checksum, radix, radix2, join, padding }; | ||
// RFC 4648 aka RFC 3548 | ||
// --------------------- | ||
exports.base16 = chain(radix2(4), alphabet('0123456789ABCDEF'), join('')); | ||
@@ -276,2 +326,5 @@ exports.base32 = chain(radix2(5), alphabet('ABCDEFGHIJKLMNOPQRSTUVWXYZ234567'), padding(5), join('')); | ||
exports.base64url = chain(radix2(6), alphabet('ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789-_'), padding(6), join('')); | ||
exports.base64urlnopad = chain(radix2(6), alphabet('ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789-_'), join('')); | ||
// base58 code | ||
// ----------- | ||
const genBase58 = (abc) => chain(radix(58), alphabet(abc), join('')); | ||
@@ -281,2 +334,5 @@ exports.base58 = genBase58('123456789ABCDEFGHJKLMNPQRSTUVWXYZabcdefghijkmnopqrstuvwxyz'); | ||
exports.base58xrp = genBase58('rpshnaf39wBUDNEGHJKLM4PQRST7VWXYZ2bcdeCg65jkm8oFqi1tuvAxyz'); | ||
// xmr ver is done in 8-byte blocks (which equals 11 chars in decoding). Last (non-full) block padded with '1' to size in XMR_BLOCK_LEN. | ||
// Block encoding significantly reduces quadratic complexity of base58. | ||
// Data len (index) -> encoded block len | ||
const XMR_BLOCK_LEN = [0, 2, 3, 5, 6, 7, 9, 10, 11]; | ||
@@ -309,4 +365,7 @@ exports.base58xmr = { | ||
exports.base58check = base58check; | ||
const BECH_ALPHABET = chain(alphabet('qpzry9x8gf2tvdw0s3jn54khce6mua7l'), join('')); | ||
const BECH_ALPHABET = /* @__PURE__ */ chain(alphabet('qpzry9x8gf2tvdw0s3jn54khce6mua7l'), join('')); | ||
const POLYMOD_GENERATORS = [0x3b6a57b2, 0x26508e6d, 0x1ea119fa, 0x3d4233dd, 0x2a1462b3]; | ||
/** | ||
* @__NO_SIDE_EFFECTS__ | ||
*/ | ||
function bech32Polymod(pre) { | ||
@@ -321,2 +380,5 @@ const b = pre >> 25; | ||
} | ||
/** | ||
* @__NO_SIDE_EFFECTS__ | ||
*/ | ||
function bechChecksum(prefix, words, encodingConst = 1) { | ||
@@ -341,2 +403,5 @@ const len = prefix.length; | ||
} | ||
/** | ||
* @__NO_SIDE_EFFECTS__ | ||
*/ | ||
function genBech32(encoding) { | ||
@@ -356,4 +421,5 @@ const ENCODING_CONST = encoding === 'bech32' ? 1 : 0x2bc830a3; | ||
throw new TypeError(`Length ${actualLength} exceeds limit ${limit}`); | ||
prefix = prefix.toLowerCase(); | ||
return `${prefix}1${BECH_ALPHABET.encode(words)}${bechChecksum(prefix, words, ENCODING_CONST)}`; | ||
const lowered = prefix.toLowerCase(); | ||
const sum = bechChecksum(lowered, words, ENCODING_CONST); | ||
return `${lowered}1${BECH_ALPHABET.encode(words)}${sum}`; | ||
} | ||
@@ -365,2 +431,3 @@ function decode(str, limit = 90) { | ||
throw new TypeError(`Wrong string length: ${str.length} (${str}). Expected (8..${limit})`); | ||
// don't allow mixed case | ||
const lowered = str.toLowerCase(); | ||
@@ -401,6 +468,7 @@ if (str !== lowered && str !== str.toUpperCase()) | ||
})); | ||
// prettier-ignore | ||
const CODERS = { | ||
utf8: exports.utf8, hex: exports.hex, base16: exports.base16, base32: exports.base32, base64: exports.base64, base64url: exports.base64url, base58: exports.base58, base58xmr: exports.base58xmr | ||
}; | ||
const coderTypeError = `Invalid encoding type. Available types: ${Object.keys(CODERS).join(', ')}`; | ||
const coderTypeError = 'Invalid encoding type. Available types: utf8, hex, base16, base32, base64, base64url, base58, base58xmr'; | ||
const bytesToString = (type, bytes) => { | ||
@@ -414,3 +482,3 @@ if (typeof type !== 'string' || !CODERS.hasOwnProperty(type)) | ||
exports.bytesToString = bytesToString; | ||
exports.str = exports.bytesToString; | ||
exports.str = exports.bytesToString; // as in python, but for bytes only | ||
const stringToBytes = (type, str) => { | ||
@@ -417,0 +485,0 @@ if (!CODERS.hasOwnProperty(type)) |
{ | ||
"name": "@scure/base", | ||
"version": "1.1.1", | ||
"description": "Secure, audited & 0-dep implementation of bech32, base64, base58, base32 & base16", | ||
"version": "1.1.2", | ||
"description": "Secure, audited & 0-dep implementation of base64, bech32, base58, base32 & base16", | ||
"files": [ | ||
@@ -9,3 +9,5 @@ "lib/index.js", | ||
"lib/esm/package.json", | ||
"lib/index.d.ts" | ||
"lib/index.d.ts", | ||
"lib/index.d.ts.map", | ||
"index.ts" | ||
], | ||
@@ -17,16 +19,19 @@ "main": "lib/index.js", | ||
".": { | ||
"types": "./lib/index.d.ts", | ||
"import": "./lib/esm/index.js", | ||
"default": "./lib/index.js" | ||
}, | ||
"./index.d.ts": "./lib/index.d.ts" | ||
} | ||
}, | ||
"scripts": { | ||
"bench": "node test/benchmark/index.js", | ||
"build": "tsc -d && tsc -p tsconfig.esm.json", | ||
"build": "tsc && tsc -p tsconfig.esm.json", | ||
"lint": "prettier --check index.ts", | ||
"test": "node test/index.js" | ||
"format": "prettier --write index.ts", | ||
"test": "node test/index.js", | ||
"test:deno": "deno test test/deno.ts" | ||
}, | ||
"sideEffects": false, | ||
"author": "Paul Miller (https://paulmillr.com)", | ||
"license": "MIT", | ||
"homepage": "https://github.com/paulmillr/scure-base", | ||
"homepage": "https://paulmillr.com/noble/#scure", | ||
"repository": { | ||
@@ -37,5 +42,5 @@ "type": "git", | ||
"devDependencies": { | ||
"micro-should": "0.2.0", | ||
"prettier": "2.6.2", | ||
"typescript": "4.7.3" | ||
"micro-should": "0.4.0", | ||
"prettier": "2.8.4", | ||
"typescript": "5.0.2" | ||
}, | ||
@@ -57,8 +62,3 @@ "keywords": [ | ||
], | ||
"funding": [ | ||
{ | ||
"type": "individual", | ||
"url": "https://paulmillr.com/funding/" | ||
} | ||
] | ||
"funding": "https://paulmillr.com/funding/" | ||
} |
113
README.md
# scure-base | ||
Secure, [audited](#security) and 0-dep implementation of bech32, base64, base58, base32 & base16. | ||
Audited and 0-dep implementation of bech32, base64, base58, base32 & base16. | ||
Written in [functional style](#design-rationale), uses chaining, has unique tests which ensure correctness. | ||
- [🔒 Audited](#security) by an independent security firm | ||
- 🔻 Tree-shaking-friendly: use only what's necessary, other code won't be included | ||
- 🔍 Unique tests which ensure correctness | ||
- ✍️ Written in [functional style](#design-rationale), easily composable | ||
- 💼 Matches specs | ||
- [BIP173](https://en.bitcoin.it/wiki/BIP_0173), [BIP350](https://en.bitcoin.it/wiki/BIP_0350) for bech32 / bech32m | ||
- [RFC 4648](https://datatracker.ietf.org/doc/html/rfc4648) (aka RFC 3548) for Base16, Base32, Base32Hex, Base64, Base64Url | ||
- [Base58](https://www.ietf.org/archive/id/draft-msporny-base58-03.txt), | ||
[Base58check](https://en.bitcoin.it/wiki/Base58Check_encoding), | ||
[Base32 Crockford](https://www.crockford.com/base32.html) | ||
Matches following specs: | ||
Check out [Projects using scure-base](#projects-using-scure-base). | ||
- Bech32, Bech32m: [BIP173](https://en.bitcoin.it/wiki/BIP_0173), [BIP350](https://en.bitcoin.it/wiki/BIP_0350) | ||
- Base16, Base32, Base32Hex, Base64, Base64Url: [RFC 4648](https://datatracker.ietf.org/doc/html/rfc4648) (aka RFC 3548) | ||
- [Base58](https://www.ietf.org/archive/id/draft-msporny-base58-03.txt), [Base58check](https://en.bitcoin.it/wiki/Base58Check_encoding), [Base32 Crockford](https://www.crockford.com/base32.html) | ||
### This library belongs to _scure_ | ||
### This library belongs to *scure* | ||
> **scure** — secure, independently audited packages for every use case. | ||
> **scure** — secure audited packages for every use case. | ||
- Independent security audits | ||
- All releases are signed with PGP keys | ||
- Audited by a third-party | ||
- Releases are signed with PGP keys and built transparently with NPM provenance | ||
- Check out all libraries: | ||
[base](https://github.com/paulmillr/scure-base), | ||
[bip32](https://github.com/paulmillr/scure-bip32), | ||
[bip39](https://github.com/paulmillr/scure-bip39) | ||
[bip39](https://github.com/paulmillr/scure-bip39), | ||
[btc-signer](https://github.com/paulmillr/scure-btc-signer), | ||
[starknet](https://github.com/paulmillr/scure-starknet) | ||
@@ -28,10 +35,15 @@ ## Usage | ||
Or | ||
We support all major platforms and runtimes. The library is hybrid ESM / Common.js package. | ||
> yarn add @scure/base | ||
```js | ||
const { base16, base32, base64, base58 } = require('@scure/base'); | ||
import { base16, base32, base64, base58 } from '@scure/base'; | ||
// Flavors | ||
const { base58xmr, base58xrp, base32hex, base32crockford, base64url } = require('@scure/base'); | ||
import { | ||
base58xmr, | ||
base58xrp, | ||
base32hex, | ||
base32crockford, | ||
base64url, | ||
base64urlnopad, | ||
} from '@scure/base'; | ||
@@ -41,2 +53,6 @@ const data = Uint8Array.from([1, 2, 3]); | ||
// Convert utf8 string to Uint8Array | ||
const data2 = new TextEncoder().encode('hello'); | ||
base58.encode(data2); | ||
// Everything has the same API except for bech32 and base58check | ||
@@ -46,17 +62,15 @@ base32.encode(data); | ||
base32hex.encode(data); | ||
``` | ||
// bech32 | ||
const { bech32, bech32m } = require('@scure/base'); | ||
const words = bech32.toWords(data); | ||
const be = bech32.encode('prefix', words); | ||
const { prefix, words } = bech32.decode(be); | ||
bech32m.encode('prefix', words); | ||
base58check is a special case: you need to pass `sha256()` function: | ||
// base58check is special-case | ||
// you need to pass sha256() function that returns Uint8Array | ||
const { base58check } = require('@scure/base'); | ||
```js | ||
import { base58check } from '@scure/base'; | ||
base58check(sha256).encode(data); | ||
``` | ||
// Alternative API | ||
const { str, bytes } = require('@scure/base'); | ||
Alternative API: | ||
```js | ||
import { str, bytes } from '@scure/base'; | ||
const encoded = str('base64', data); | ||
@@ -66,2 +80,27 @@ const data = bytes('base64', encoded); | ||
## Bech32, Bech32m and Bitcoin | ||
We provide low-level bech32 operations. | ||
If you need high-level methods for BTC (addresses, and others), use | ||
[scure-btc-signer](https://github.com/paulmillr/scure-btc-signer) instead. | ||
Bitcoin addresses use both 5-bit words and bytes representations. | ||
They can't be parsed using `bech32.decodeToBytes`. Instead, do something this: | ||
```ts | ||
const decoded = bech32.decode(address); | ||
// NOTE: words in bitcoin addresses contain version as first element, | ||
// with actual witnes program words in rest | ||
// BIP-141: The value of the first push is called the "version byte". | ||
// The following byte vector pushed is called the "witness program". | ||
const [version, ...dataW] = decoded.words; | ||
const program = bech32.fromWords(dataW); // actual witness program | ||
``` | ||
Same applies to Lightning Invoice Protocol | ||
[BOLT-11](https://github.com/lightning/bolts/blob/master/11-payment-encoding.md). | ||
We have many tests in `./test/bip173.test.js` that serve as minimal examples of | ||
Bitcoin address and Lightning Invoice Protocol parsers. | ||
Keep in mind that you'll need to verify the examples before using them in your code. | ||
## Design rationale | ||
@@ -144,9 +183,19 @@ | ||
1. The library was initially developed for [js-ethereum-cryptography](https://github.com/ethereum/js-ethereum-cryptography) | ||
2. At commit [ae00e6d7](https://github.com/ethereum/js-ethereum-cryptography/commit/ae00e6d7d24fb3c76a1c7fe10039f6ecd120b77e), it | ||
was extracted to a separate package called `micro-base` | ||
3. After the audit we've decided to use NPM namespace for security. Since `@micro` namespace was taken, we've renamed the package to `@scure/base` | ||
The library was initially developed for [js-ethereum-cryptography](https://github.com/ethereum/js-ethereum-cryptography). | ||
At commit [ae00e6d7](https://github.com/ethereum/js-ethereum-cryptography/commit/ae00e6d7d24fb3c76a1c7fe10039f6ecd120b77e), | ||
it was extracted to a separate package called `micro-base`. | ||
After the audit we've decided to use `@scure` NPM namespace for security. | ||
## Resources | ||
### Projects using scure-base | ||
- [prefixed-api-key](https://github.com/truestamp/prefixed-api-key): | ||
A re-write of seamapi/prefixed-api-key that enhances the | ||
cryptographic security properties and safety when verifying a key. The keys and verifiers | ||
of these two libraries are not compatible. | ||
[Motivating post on the issues with using JWT from fly.io](https://fly.io/blog/api-tokens-a-tedious-survey/) | ||
## License | ||
MIT (c) Paul Miller [(https://paulmillr.com)](https://paulmillr.com), see LICENSE file. |
License Policy Violation
LicenseThis package is not allowed per your license policy. Review the package's license to ensure compliance.
Found 1 instance in 1 package
License Policy Violation
LicenseThis package is not allowed per your license policy. Review the package's license to ensure compliance.
Found 1 instance in 1 package
77563
9
1585
197