From b21997bf24d393775091c117ae1ff3da7c5cda3a Mon Sep 17 00:00:00 2001 From: James Broadberry Date: Mon, 11 May 2020 20:54:42 +0100 Subject: [PATCH] Moved base64 functions to it's own file and updated README --- README.md | 3 +- base64.ts | 212 +++++++++++++++++++++++++++++++++++++++++++++++++++ bcrypt.ts | 222 ++---------------------------------------------------- 3 files changed, 218 insertions(+), 219 deletions(-) create mode 100644 base64.ts diff --git a/README.md b/README.md index 591be29..3bef146 100644 --- a/README.md +++ b/README.md @@ -7,8 +7,7 @@ It has zero third-party dependencies and requires no permissions. ## Import ```ts -// Not published yet -import * as bcrypt from "https://denopkg.com/x/bcrypt/mod.ts"; +import * as bcrypt from "https://deno.land/x/bcrypt/mod.ts"; ``` ## Usage diff --git a/base64.ts b/base64.ts new file mode 100644 index 0000000..527dc5c --- /dev/null +++ b/base64.ts @@ -0,0 +1,212 @@ +const base64_code = "./ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789".split( + "" +); + +const index_64 = new Uint8Array([ + -1, + -1, + -1, + -1, + -1, + -1, + -1, + -1, + -1, + -1, + -1, + -1, + -1, + -1, + -1, + -1, + -1, + -1, + -1, + -1, + -1, + -1, + -1, + -1, + -1, + -1, + -1, + -1, + -1, + -1, + -1, + -1, + -1, + -1, + -1, + -1, + -1, + -1, + -1, + -1, + -1, + -1, + -1, + -1, + -1, + -1, + 0, + 1, + 54, + 55, + 56, + 57, + 58, + 59, + 60, + 61, + 62, + 63, + -1, + -1, + -1, + -1, + -1, + -1, + -1, + 2, + 3, + 4, + 5, + 6, + 7, + 8, + 9, + 10, + 11, + 12, + 13, + 14, + 15, + 16, + 17, + 18, + 19, + 20, + 21, + 22, + 23, + 24, + 25, + 26, + 27, + -1, + -1, + -1, + -1, + -1, + -1, + 28, + 29, + 30, + 31, + 32, + 33, + 34, + 35, + 36, + 37, + 38, + 39, + 40, + 41, + 42, + 43, + 44, + 45, + 46, + 47, + 48, + 49, + 50, + 51, + 52, + 53, + -1, + -1, + -1, + -1, + -1, +]); + +export function encode(d: Uint8Array, len: number): string { + let off = 0; + let rs: string[] = []; + let c1 = 0; + let c2 = 0; + + while (off < len) { + c1 = d[off++] & 0xff; + rs.push(base64_code[(c1 >> 2) & 0x3f]); + c1 = (c1 & 0x03) << 4; + if (off >= len) { + rs.push(base64_code[c1 & 0x3f]); + break; + } + c2 = d[off++] & 0xff; + c1 |= (c2 >> 4) & 0x0f; + rs.push(base64_code[c1 & 0x3f]); + c1 = (c2 & 0x0f) << 2; + if (off >= len) { + rs.push(base64_code[c1 & 0x3f]); + break; + } + c2 = d[off++] & 0xff; + c1 |= (c2 >> 6) & 0x03; + rs.push(base64_code[c1 & 0x3f]); + rs.push(base64_code[c2 & 0x3f]); + } + return rs.join(""); +} + +// x is a single character +function char64(x: string): number { + if (x.length > 1) { + throw new Error("Expected a single character"); + } + + let characterAsciiCode = x.charCodeAt(0); + + if (characterAsciiCode < 0 || characterAsciiCode > index_64.length) return -1; + return index_64[characterAsciiCode]; +} + +export function decode(s: string, maxolen: number): Uint8Array { + let rs: number[] = []; + let off = 0; + let slen = s.length; + let olen = 0; + let ret: Uint8Array; + let c1, c2, c3, c4, o; + + if (maxolen <= 0) throw new Error("Invalid maxolen"); + + while (off < slen - 1 && olen < maxolen) { + c1 = char64(s.charAt(off++)); + c2 = char64(s.charAt(off++)); + if (c1 === -1 || c2 === -1) break; + o = c1 << 2; + o |= (c2 & 0x30) >> 4; + rs.push(o); + if (++olen >= maxolen || off >= slen) break; + c3 = char64(s.charAt(off++)); + if (c3 === -1) break; + o = (c2 & 0x0f) << 4; + o |= (c3 & 0x3c) >> 2; + rs.push(o); + if (++olen >= maxolen || off >= slen) break; + c4 = char64(s.charAt(off++)); + o = (c3 & 0x03) << 6; + o |= c4; + rs.push(o); + ++olen; + } + + ret = new Uint8Array(olen); + for (off = 0; off < olen; off++) ret[off] = rs[off]; + return ret; +} diff --git a/bcrypt.ts b/bcrypt.ts index 031a324..728fc94 100644 --- a/bcrypt.ts +++ b/bcrypt.ts @@ -1,4 +1,5 @@ import { encode } from "https://deno.land/std/encoding/utf8.ts"; +import * as base64 from "./base64.ts"; // BCrypt parameters const GENSALT_DEFAULT_LOG2_ROUNDS = 10; @@ -1067,223 +1068,10 @@ const bf_crypt_ciphertext = new Int32Array([ 0x6f756274, ]); -const base64_code = "./ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789".split( - "" -); - -const index_64 = new Uint8Array([ - -1, - -1, - -1, - -1, - -1, - -1, - -1, - -1, - -1, - -1, - -1, - -1, - -1, - -1, - -1, - -1, - -1, - -1, - -1, - -1, - -1, - -1, - -1, - -1, - -1, - -1, - -1, - -1, - -1, - -1, - -1, - -1, - -1, - -1, - -1, - -1, - -1, - -1, - -1, - -1, - -1, - -1, - -1, - -1, - -1, - -1, - 0, - 1, - 54, - 55, - 56, - 57, - 58, - 59, - 60, - 61, - 62, - 63, - -1, - -1, - -1, - -1, - -1, - -1, - -1, - 2, - 3, - 4, - 5, - 6, - 7, - 8, - 9, - 10, - 11, - 12, - 13, - 14, - 15, - 16, - 17, - 18, - 19, - 20, - 21, - 22, - 23, - 24, - 25, - 26, - 27, - -1, - -1, - -1, - -1, - -1, - -1, - 28, - 29, - 30, - 31, - 32, - 33, - 34, - 35, - 36, - 37, - 38, - 39, - 40, - 41, - 42, - 43, - 44, - 45, - 46, - 47, - 48, - 49, - 50, - 51, - 52, - 53, - -1, - -1, - -1, - -1, - -1, -]); - // Expanded Bloefish key let P: Int32Array; let S: Int32Array; -function encode_base64(d: Uint8Array, len: number): string { - let off = 0; - let rs: string[] = []; - let c1 = 0; - let c2 = 0; - - while (off < len) { - c1 = d[off++] & 0xff; - rs.push(base64_code[(c1 >> 2) & 0x3f]); - c1 = (c1 & 0x03) << 4; - if (off >= len) { - rs.push(base64_code[c1 & 0x3f]); - break; - } - c2 = d[off++] & 0xff; - c1 |= (c2 >> 4) & 0x0f; - rs.push(base64_code[c1 & 0x3f]); - c1 = (c2 & 0x0f) << 2; - if (off >= len) { - rs.push(base64_code[c1 & 0x3f]); - break; - } - c2 = d[off++] & 0xff; - c1 |= (c2 >> 6) & 0x03; - rs.push(base64_code[c1 & 0x3f]); - rs.push(base64_code[c2 & 0x3f]); - } - return rs.join(""); -} - -// x is a single character -function char64(x: string): number { - if (x.length > 1) { - throw new Error("Expected a single character"); - } - - let characterAsciiCode = x.charCodeAt(0); - - if (characterAsciiCode < 0 || characterAsciiCode > index_64.length) return -1; - return index_64[characterAsciiCode]; -} - -function decode_base64(s: string, maxolen: number): Uint8Array { - let rs: number[] = []; - let off = 0; - let slen = s.length; - let olen = 0; - let ret: Uint8Array; - let c1, c2, c3, c4, o; - - if (maxolen <= 0) throw new Error("Invalid maxolen"); - - while (off < slen - 1 && olen < maxolen) { - c1 = char64(s.charAt(off++)); - c2 = char64(s.charAt(off++)); - if (c1 === -1 || c2 === -1) break; - o = c1 << 2; - o |= (c2 & 0x30) >> 4; - rs.push(o); - if (++olen >= maxolen || off >= slen) break; - c3 = char64(s.charAt(off++)); - if (c3 === -1) break; - o = (c2 & 0x0f) << 4; - o |= (c3 & 0x3c) >> 2; - rs.push(o); - if (++olen >= maxolen || off >= slen) break; - c4 = char64(s.charAt(off++)); - o = (c3 & 0x03) << 6; - o |= c4; - rs.push(o); - ++olen; - } - - ret = new Uint8Array(olen); - for (off = 0; off < olen; off++) ret[off] = rs[off]; - return ret; -} - function encipher(lr: Int32Array, off: number): void { let i = 0; let n = 0; @@ -1449,7 +1237,7 @@ export function hashpw(password: string, salt: string = gensalt()): string { password + (minor.charCodeAt(0) >= "a".charCodeAt(0) ? "\u0000" : "") ); - saltb = decode_base64(real_salt, BCRYPT_SALT_LEN); + saltb = base64.decode(real_salt, BCRYPT_SALT_LEN); hashed = crypt_raw(passwordb, saltb, rounds, bf_crypt_ciphertext.slice()); @@ -1462,8 +1250,8 @@ export function hashpw(password: string, salt: string = gensalt()): string { } rs.push(rounds.toString()); rs.push("$"); - rs.push(encode_base64(saltb, saltb.length)); - rs.push(encode_base64(hashed, bf_crypt_ciphertext.length * 4 - 1)); + rs.push(base64.encode(saltb, saltb.length)); + rs.push(base64.encode(hashed, bf_crypt_ciphertext.length * 4 - 1)); return rs.join(""); } @@ -1481,7 +1269,7 @@ export function gensalt( } rs.push(log_rounds.toString()); rs.push("$"); - rs.push(encode_base64(rnd, rnd.length)); + rs.push(base64.encode(rnd, rnd.length)); return rs.join(""); }