Skip to content

Commit

Permalink
feat: Node.js compatible
Browse files Browse the repository at this point in the history
- Added package.json so Deno runs in Node.js compatibility mode.
- Use `jsr` registry when available
- Use `npm` registry when available
- "vendor" the rest of dependencies.
- Use `node` built-in modules that Deno polyfills.
- Splits `main.js` into `mod.js` (lib) and `main.js` (CLI)
  • Loading branch information
sntran committed Jun 27, 2024
1 parent 165c4af commit f75e227
Show file tree
Hide file tree
Showing 33 changed files with 1,072 additions and 596 deletions.
2 changes: 0 additions & 2 deletions .gitignore
Original file line number Diff line number Diff line change
@@ -1,5 +1,3 @@
.env
node_modules
package.json
package-lock.json
.DS_Store
1 change: 1 addition & 0 deletions .npmrc
Original file line number Diff line number Diff line change
@@ -0,0 +1 @@
@jsr:registry=https://npm.jsr.io
2 changes: 1 addition & 1 deletion backend/alias/main.js
Original file line number Diff line number Diff line change
Expand Up @@ -20,7 +20,7 @@
*/

import { join } from "../../deps.js";
import { fetch } from "../../main.js";
import { fetch } from "../../mod.js";

/**
* @param {Request} request
Expand Down
2 changes: 1 addition & 1 deletion backend/chunker/main.js
Original file line number Diff line number Diff line change
Expand Up @@ -103,7 +103,7 @@
*/
import { crypto, encodeHex } from "../../deps.js";
import { Chunker } from "../../lib/streams/chunker.js";
import { fetch } from "../../main.js";
import { fetch } from "../../mod.js";

const METADATA_VERSION = 1;

Expand Down
10 changes: 2 additions & 8 deletions backend/crypt/PathCipher.js
Original file line number Diff line number Diff line change
@@ -1,12 +1,6 @@
import { AES } from "https://esm.sh/aes-js@3.1.2";
import {
pad,
Padding,
unpad,
} from "https://deno.land/x/crypto/src/utils/padding.ts";
import { default as decodeBase32 } from "https://esm.sh/base32-decode@1.0.0";
import { default as encodeBase32 } from "https://esm.sh/base32-encode@2.0.0";
import { AES, decodeBase32, encodeBase32 } from "../../deps.js";
import { Decrypt, Encrypt } from "./eme.js";
import { pad, Padding, unpad } from "./padding.js";

const encoder = new TextEncoder();
const decoder = new TextDecoder();
Expand Down
24 changes: 11 additions & 13 deletions backend/crypt/main.js
Original file line number Diff line number Diff line change
Expand Up @@ -95,9 +95,8 @@
* - 1049120 bytes total (a 0.05% overhead). This is the overhead for big files.
*/

import { scrypt } from "https://deno.land/x/scrypto@v1.0.0/scrypt.ts";
import { contentType, extname } from "../../deps.js";
import { fetch } from "../../main.js";
import { contentType, extname, scrypt } from "../../deps.js";
import { fetch } from "../../mod.js";
import { reveal } from "../../cmd/obscure/main.js";
import PathCipher from "./PathCipher.js";
import { DecryptionStream, EncryptionStream } from "./secretbox.js";
Expand All @@ -124,10 +123,10 @@ const DEFAULT_SALT = new Uint8Array([
]);

// Params for scrypt
const N = 16384;
const N = 2 ** 14; // 16384
const r = 8;
const p = 1;
const keySize = 32 + 32 + 16;
const dkLen = 32 + 32 + 16;

const SECRETBOX_OPTIONS = {
blockSize: 64 * 1024, // 64 KiB
Expand Down Expand Up @@ -330,16 +329,15 @@ export async function decode(options, ...args) {
* @returns {Promise<Uint8Array>}
*/
async function deriveKey(encPass, encSalt) {
const password = await reveal(encPass).then((r) => r.arrayBuffer()).then(
(buf) => new Uint8Array(buf),
);
const decryptedSalt = await reveal(encSalt).then((r) => r.arrayBuffer()).then(
(buf) => new Uint8Array(buf),
);
const password = await reveal(encPass)
.then((response) => response.arrayBuffer())
.then((buf) => new Uint8Array(buf));
const decryptedSalt = await reveal(encSalt)
.then((response) => response.arrayBuffer())
.then((buf) => new Uint8Array(buf));
const salt = decryptedSalt.length ? decryptedSalt : DEFAULT_SALT;

const derivedKey = await scrypt(password, salt, N, r, p, keySize);
return new Uint8Array(derivedKey);
return scrypt(password, salt, { N, r, p, dkLen });
}

export default {
Expand Down
2 changes: 1 addition & 1 deletion backend/crypt/main_test.js
Original file line number Diff line number Diff line change
@@ -1,7 +1,7 @@
import { contentType, extname, join } from "../../deps.js";
import { assert, assertEquals, equalBytes, fc } from "../../dev_deps.js";

import { decode, encode, default as crypt } from "./main.js";
import { decode, default as crypt, encode } from "./main.js";

const PASSWORD = "UmyLSdRHfew6aual28-ggx78qHqSfQ";
const SALT = "Cj3gLa5PVwc2aot0QpKiOZ3YEzs3Sw";
Expand Down
119 changes: 119 additions & 0 deletions backend/crypt/padding.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,119 @@
/**
* @enum { number }
*/
export const Padding = {
NONE: 1,
PKCS7: 2,
ONE_AND_ZEROS: 3,
LAST_BYTE: 4,
NULL: 5,
SPACES: 6,
};

/**
* Pads the input bytes to the specified block size.
* @param {Uint8Array} bytes
* @param {Padding} padding
* @param {number} blockSize
* @returns {Uint8Array}
*/
export function pad(bytes, padding, blockSize) {
if (padding === Padding.NONE) {
if (bytes.length % blockSize === 0) return bytes;
else {
throw new Error(
`Invalid data size (must be multiple of ${blockSize} bytes)`,
);
}
}

const count = blockSize - bytes.length % blockSize;

if (count === blockSize && bytes.length > 0 && padding !== Padding.PKCS7) {
return bytes;
}

const writer = new Uint8Array(bytes.length + count);
const newBytes = [];
let remaining = count;
let padChar = 0;

switch (padding) {
case Padding.PKCS7: {
padChar = count;
break;
}
case Padding.ONE_AND_ZEROS: {
newBytes.push(0x80);
remaining--;
break;
}
case Padding.SPACES: {
padChar = 0x20;
break;
}
}

while (remaining > 0) {
if (padding === Padding.LAST_BYTE && remaining === 1) {
newBytes.push(count);
break;
}
newBytes.push(padChar);
remaining--;
}

writer.set(bytes);
writer.set(newBytes, bytes.length);
return writer;
}

/**
* Unpads the input bytes to the specified block size.
* @param {Uint8Array} bytes
* @param {Padding} padding
* @param {number} blockSize
* @returns {Uint8Array}
*/
export function unpad(bytes, padding, blockSize) {
let cutLength = 0;
switch (padding) {
case Padding.NONE: {
return bytes;
}
case Padding.LAST_BYTE:
case Padding.PKCS7: {
const lastChar = bytes[bytes.length - 1];
if (lastChar <= blockSize) {
cutLength = lastChar;
}
break;
}
case Padding.ONE_AND_ZEROS: {
for (let i = 1; i <= blockSize; i++) {
const char = bytes[bytes.length - i];
if (char === 0x80) {
cutLength = i;
break;
}
if (char !== 0) {
break;
}
}
break;
}
case Padding.NULL:
case Padding.SPACES: {
const padChar = (padding === Padding.SPACES) ? 0x20 : 0;
for (let i = 1; i <= blockSize; i++) {
const char = bytes[bytes.length - i];
if (char !== padChar) {
cutLength = i - 1;
break;
}
}
break;
}
}
return bytes.subarray(0, bytes.length - cutLength);
}
2 changes: 1 addition & 1 deletion backend/crypt/secretbox.js
Original file line number Diff line number Diff line change
@@ -1,7 +1,7 @@
/**
* @module crypt/secretbox
*/
import { xsalsa20poly1305 } from "https://esm.sh/@noble/ciphers@0.3.0/salsa";
import { xsalsa20poly1305 } from "../../deps.js";

/**
* Options for secretbox
Expand Down
4 changes: 2 additions & 2 deletions backend/drive/auth.js
Original file line number Diff line number Diff line change
@@ -1,4 +1,4 @@
import { base64url } from "../../deps.js";
import { encodeBase64Url } from "../../deps.js";

import { reveal } from "../../cmd/obscure/main.js";

Expand Down Expand Up @@ -168,7 +168,7 @@ async function createJWT(serviceAccount, scopes) {
key,
encoder.encode(jwt),
);
const signatureEncoded = base64url.encode(new Uint8Array(signature));
const signatureEncoded = encodeBase64Url(new Uint8Array(signature));

return `${jwt}.${signatureEncoded}`;
}
Expand Down
18 changes: 16 additions & 2 deletions backend/drive/main.js
Original file line number Diff line number Diff line change
Expand Up @@ -64,7 +64,14 @@ async function router(request) {
/**
* @type {import("./file.js").File[]}
*/
const files = await list(request).then((r) => r.json());
let files = [];
try {
files = await list(request).then((r) => r.json());
} catch (_error) {
return new Response(null, {
status: 404,
});
}

if (method === "HEAD" || method === "GET") {
// For request to folder, displays the folder content in HTML
Expand Down Expand Up @@ -137,13 +144,20 @@ async function router(request) {

headers.set("Content-Type", "text/html;charset=utf-8");
} else {
const file = files[0];
if (!file) {
return new Response(null, {
status: 404,
});
}

const {
id,
name,
mimeType,
size,
modifiedTime,
} = files[0];
} = file;

// Sets initial headers based on file metadata.
headers.set("Content-Length", String(size));
Expand Down
2 changes: 1 addition & 1 deletion backend/http/main.js
Original file line number Diff line number Diff line change
Expand Up @@ -105,7 +105,7 @@ async function router(request) {
`);

/**
* @type {import("../../main.js").File}
* @type {import("../../mod.js").File}
*/
let item;

Expand Down
Loading

0 comments on commit f75e227

Please sign in to comment.