From 6c8cde35ec1d1d3a2d4c70bfd80eeab71e1d9ba1 Mon Sep 17 00:00:00 2001 From: Mark Wylde Date: Mon, 4 Dec 2023 17:36:18 +0000 Subject: [PATCH 1/5] fix: use custom base64 decoder to workaround deno bug --- src/auth/scram.ts | 13 ++++++++++++- 1 file changed, 12 insertions(+), 1 deletion(-) diff --git a/src/auth/scram.ts b/src/auth/scram.ts index 39b505a..405032d 100644 --- a/src/auth/scram.ts +++ b/src/auth/scram.ts @@ -114,6 +114,17 @@ export async function executeScram( return continueScramConversation(cryptoMethod, result, authContext); } +function decodeBase64(b64: string): Uint8Array { + const b64Web = b64.replace(/-/g, "+").replace(/_/g, "/"); + const binString = atob(b64Web); + const size = binString.length; + const bytes = new Uint8Array(size); + for (let i = 0; i < size; i++) { + bytes[i] = binString.charCodeAt(i); + } + return bytes; +} + export async function continueScramConversation( cryptoMethod: CryptoMethod, response: Document, @@ -193,7 +204,7 @@ export async function continueScramConversation( ); if ( !compareDigest( - b64.decode(parsedResponse.v), + decodeBase64(parsedResponse.v), new Uint8Array(serverSignature), ) ) { From e7e45bdf5be19312ccd180ae35dda7062b38123e Mon Sep 17 00:00:00 2001 From: Mark Wylde Date: Mon, 4 Dec 2023 20:57:30 +0000 Subject: [PATCH 2/5] fix: move decodeBase64 to utils and remove atob usage --- src/auth/scram.ts | 14 ++------------ src/utils/decodeBase64.ts | 24 ++++++++++++++++++++++++ 2 files changed, 26 insertions(+), 12 deletions(-) create mode 100644 src/utils/decodeBase64.ts diff --git a/src/auth/scram.ts b/src/auth/scram.ts index 405032d..44f9aae 100644 --- a/src/auth/scram.ts +++ b/src/auth/scram.ts @@ -6,6 +6,7 @@ import { MongoDriverError } from "../error.ts"; import { b64, Binary, crypto as stdCrypto, Document, hex } from "../../deps.ts"; import { driverMetadata } from "../protocol/mod.ts"; import { pbkdf2 } from "./pbkdf2.ts"; +import { decodeBase64 } from "../utils/decodeBase64.ts"; type CryptoMethod = "sha1" | "sha256"; @@ -114,17 +115,6 @@ export async function executeScram( return continueScramConversation(cryptoMethod, result, authContext); } -function decodeBase64(b64: string): Uint8Array { - const b64Web = b64.replace(/-/g, "+").replace(/_/g, "/"); - const binString = atob(b64Web); - const size = binString.length; - const bytes = new Uint8Array(size); - for (let i = 0; i < size; i++) { - bytes[i] = binString.charCodeAt(i); - } - return bytes; -} - export async function continueScramConversation( cryptoMethod: CryptoMethod, response: Document, @@ -171,7 +161,7 @@ export async function continueScramConversation( const withoutProof = `c=biws,r=${rnonce}`; const saltedPassword = await HI( processedPassword, - b64.decode(salt), + decodeBase64(salt), iterations, cryptoMethod, ); diff --git a/src/utils/decodeBase64.ts b/src/utils/decodeBase64.ts new file mode 100644 index 0000000..16732d4 --- /dev/null +++ b/src/utils/decodeBase64.ts @@ -0,0 +1,24 @@ +export function decodeBase64(b64: string): Uint8Array { + const chars = 'ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789+/'; + let base64 = b64.replace(/-/g, '+').replace(/_/g, '/'); + + while (base64.length % 4) { + base64 += '='; + } + + let bitString = ''; + for (let i = 0; i < base64.length; i++) { + const char = base64.charAt(i); + if (char !== '=') { + const charIndex = chars.indexOf(char); + bitString += charIndex.toString(2).padStart(6, '0'); + } + } + + const bytes = new Uint8Array(bitString.length / 8); + for (let i = 0; i < bytes.length; i++) { + bytes[i] = parseInt(bitString.substring(8 * i, 8 * (i + 1)), 2); + } + + return bytes; +} From 781df1a690fd02e3d1499931ce72782bd240d8dc Mon Sep 17 00:00:00 2001 From: Mark Wylde Date: Mon, 4 Dec 2023 21:02:41 +0000 Subject: [PATCH 3/5] fix: match style of existing codebase --- demo/main.ts | 12 ++++++ src/auth/scram.ts | 2 +- .../{decodeBase64.ts => decode_base64.ts} | 0 tests/cases/11_decode_base64.ts | 41 +++++++++++++++++++ 4 files changed, 54 insertions(+), 1 deletion(-) create mode 100644 demo/main.ts rename src/utils/{decodeBase64.ts => decode_base64.ts} (100%) create mode 100644 tests/cases/11_decode_base64.ts diff --git a/demo/main.ts b/demo/main.ts new file mode 100644 index 0000000..8239b86 --- /dev/null +++ b/demo/main.ts @@ -0,0 +1,12 @@ +import { MongoClient } from '../mod.ts'; + +for (let x = 0; x < 100000; x++) { + const client = new MongoClient(); + + const url = `mongodb://testadmin1:testpass1@localhost:27017?directConnection=true` + console.log('url:', url); + await client.connect(url); + + await client.close() + console.log(''); +} diff --git a/src/auth/scram.ts b/src/auth/scram.ts index 44f9aae..94eaca5 100644 --- a/src/auth/scram.ts +++ b/src/auth/scram.ts @@ -6,7 +6,7 @@ import { MongoDriverError } from "../error.ts"; import { b64, Binary, crypto as stdCrypto, Document, hex } from "../../deps.ts"; import { driverMetadata } from "../protocol/mod.ts"; import { pbkdf2 } from "./pbkdf2.ts"; -import { decodeBase64 } from "../utils/decodeBase64.ts"; +import { decodeBase64 } from "../utils/decode_base64.ts"; type CryptoMethod = "sha1" | "sha256"; diff --git a/src/utils/decodeBase64.ts b/src/utils/decode_base64.ts similarity index 100% rename from src/utils/decodeBase64.ts rename to src/utils/decode_base64.ts diff --git a/tests/cases/11_decode_base64.ts b/tests/cases/11_decode_base64.ts new file mode 100644 index 0000000..4d679e0 --- /dev/null +++ b/tests/cases/11_decode_base64.ts @@ -0,0 +1,41 @@ +import { decodeBase64 } from "../../src/utils/decode_base64.ts"; + +import { assertEquals, describe, it } from "./../test.deps.ts"; + +describe("decodeBase64", () => { + it({ + name: "should correctly decode a standard base64 encoded string", + fn() { + const encoded = "SGVsbG8gV29ybGQ="; // "Hello World" in base64 + const decoded = decodeBase64(encoded); + assertEquals(new TextDecoder().decode(decoded), "Hello World"); + }, + }); + + it({ + name: "should correctly decode a URL-safe base64 encoded string", + fn() { + const encoded = "SGVsbG8tV29ybGRf"; // URL-safe base64 variant + const decoded = decodeBase64(encoded); + assertEquals(new TextDecoder().decode(decoded), "Hello-World_"); + }, + }); + + it({ + name: "should handle base64 strings with missing padding", + fn() { + const encoded = "SGVsbG8gV29ybGQ"; // Missing '=' at the end + const decoded = decodeBase64(encoded); + assertEquals(new TextDecoder().decode(decoded), "Hello World"); + }, + }); + + it({ + name: "should return an empty array for an empty string", + fn() { + const encoded = ""; + const decoded = decodeBase64(encoded); + assertEquals(decoded.length, 0); + }, + }); +}); From 059cce80a93c38befd0b4347d52a0e3f1d3d89b1 Mon Sep 17 00:00:00 2001 From: Mark Wylde Date: Mon, 4 Dec 2023 21:03:46 +0000 Subject: [PATCH 4/5] fix: remove demo folder --- demo/main.ts | 12 ------------ 1 file changed, 12 deletions(-) delete mode 100644 demo/main.ts diff --git a/demo/main.ts b/demo/main.ts deleted file mode 100644 index 8239b86..0000000 --- a/demo/main.ts +++ /dev/null @@ -1,12 +0,0 @@ -import { MongoClient } from '../mod.ts'; - -for (let x = 0; x < 100000; x++) { - const client = new MongoClient(); - - const url = `mongodb://testadmin1:testpass1@localhost:27017?directConnection=true` - console.log('url:', url); - await client.connect(url); - - await client.close() - console.log(''); -} From 25b1b3c1ddd5f6ab0f355d86f3de39c9af11d89b Mon Sep 17 00:00:00 2001 From: Mark Wylde Date: Mon, 4 Dec 2023 21:04:57 +0000 Subject: [PATCH 5/5] fix: lint --- src/utils/decode_base64.ts | 21 +++++++++++---------- 1 file changed, 11 insertions(+), 10 deletions(-) diff --git a/src/utils/decode_base64.ts b/src/utils/decode_base64.ts index 16732d4..ca702fe 100644 --- a/src/utils/decode_base64.ts +++ b/src/utils/decode_base64.ts @@ -1,23 +1,24 @@ export function decodeBase64(b64: string): Uint8Array { - const chars = 'ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789+/'; - let base64 = b64.replace(/-/g, '+').replace(/_/g, '/'); + const chars = + "ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789+/"; + let base64 = b64.replace(/-/g, "+").replace(/_/g, "/"); while (base64.length % 4) { - base64 += '='; + base64 += "="; } - let bitString = ''; + let bitString = ""; for (let i = 0; i < base64.length; i++) { - const char = base64.charAt(i); - if (char !== '=') { - const charIndex = chars.indexOf(char); - bitString += charIndex.toString(2).padStart(6, '0'); - } + const char = base64.charAt(i); + if (char !== "=") { + const charIndex = chars.indexOf(char); + bitString += charIndex.toString(2).padStart(6, "0"); + } } const bytes = new Uint8Array(bitString.length / 8); for (let i = 0; i < bytes.length; i++) { - bytes[i] = parseInt(bitString.substring(8 * i, 8 * (i + 1)), 2); + bytes[i] = parseInt(bitString.substring(8 * i, 8 * (i + 1)), 2); } return bytes;