-
-
Notifications
You must be signed in to change notification settings - Fork 2
Commit
This commit does not belong to any branch on this repository, and may belong to a fork outside of the repository.
- Loading branch information
Showing
23 changed files
with
1,688 additions
and
1,694 deletions.
There are no files selected for viewing
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -1,4 +1,10 @@ | ||
# iron-webcrypto [](https://www.jsdocs.io/package/iron-webcrypto) [](https://www.npmjs.com/package/iron-webcrypto) [](https://www.npmjs.com/package/iron-webcrypto) [](https://deno.land/x/[email protected]/mod.ts) | ||
# iron-webcrypto | ||
|
||
[](https://www.jsdocs.io/package/iron-webcrypto) | ||
[](https://www.npmjs.com/package/iron-webcrypto) | ||
[](https://www.npmjs.com/package/iron-webcrypto) | ||
[](https://deno.land/x/[email protected]/mod.ts) | ||
[](https://jsr.io/@brc-dd/iron) | ||
|
||
This module is a replacement for `@hapi/iron`, written using standard APIs like | ||
Web Crypto and Uint8Array, which make this compatible with a variety of runtimes | ||
|
@@ -13,25 +19,50 @@ like Node.js, Deno, Bun, browsers, workers, and edge environments. Refer | |
|
||
## Installation | ||
|
||
Simply run: | ||
For Node.js and Bun, run any of the following commands depending on your package | ||
manager / runtime: | ||
|
||
```sh | ||
npm add iron-webcrypto | ||
yarn add iron-webcrypto | ||
pnpm add iron-webcrypto | ||
bun add iron-webcrypto | ||
``` | ||
|
||
Change the package manager to whatever you like. On Deno and browsers, you can | ||
use [esm.sh](https://esm.sh/) for importing this: | ||
You can then import it using: | ||
|
||
```ts | ||
import * as Iron from 'https://esm.sh/[email protected]' | ||
import * as Iron from 'iron-webcrypto' | ||
``` | ||
|
||
If using JSR, run any of the following commands depending on your package | ||
manager / runtime: | ||
|
||
```sh | ||
npx jsr add @brc-dd/iron | ||
yarn dlx jsr add @brc-dd/iron | ||
pnpm dlx jsr add @brc-dd/iron | ||
bunx jsr add @brc-dd/iron | ||
deno add @brc-dd/iron | ||
``` | ||
|
||
You can then import it using: | ||
|
||
```ts | ||
import * as Iron from '@brc-dd/iron' | ||
``` | ||
|
||
This module is also published on `deno.land/x` as `iron`: | ||
On Deno, you can also any of the following imports: | ||
|
||
```ts | ||
import * as Iron from 'https://deno.land/x/[email protected]/mod.ts' | ||
import * as Iron from 'https://esm.sh/[email protected]' | ||
import * as Iron from 'npm:[email protected]' | ||
``` | ||
|
||
Don't use this module directly in the browser. While it will work, it's not | ||
recommended to use it in client-side code because of the security implications. | ||
|
||
## Usage | ||
|
||
Refer [`@hapi/iron`'s docs](https://hapi.dev/module/iron/). There are certain | ||
|
@@ -95,13 +126,17 @@ thoroughly review the code. | |
@smithy/util-base64 | ||
Copyright 2018-2020 Amazon.com, Inc. or its affiliates. All Rights Reserved. | ||
https://cdn.jsdelivr.net/npm/@smithy/[email protected]/LICENSE | ||
https://cdn.jsdelivr.net/npm/@smithy/[email protected]/LICENSE | ||
@smithy/util-utf8 | ||
Copyright 2018-2020 Amazon.com, Inc. or its affiliates. All Rights Reserved. | ||
https://cdn.jsdelivr.net/npm/@smithy/[email protected]/LICENSE | ||
``` | ||
|
||
## Sponsors | ||
|
||
<p align="center"> | ||
<a href="https://cdn.jsdelivr.net/gh/brc-dd/static/sponsors.svg"> | ||
<img src='https://cdn.jsdelivr.net/gh/brc-dd/static/sponsors.svg'/> | ||
<img alt="brc-dd's sponsors" src='https://cdn.jsdelivr.net/gh/brc-dd/static/sponsors.svg'/> | ||
</a> | ||
</p> |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -1,8 +1,8 @@ | ||
'use strict'; | ||
|
||
// node_modules/.pnpm/@smithy[email protected]/node_modules/@smithy/util-base64/dist-es/constants.browser.js | ||
// src/utils.ts | ||
var alphabetByEncoding = {}; | ||
var alphabetByValue = new Array(64); | ||
var alphabetByValue = Array.from({ length: 64 }); | ||
for (let i = 0, start = "A".charCodeAt(0), limit = "Z".charCodeAt(0); i + start <= limit; i++) { | ||
const char = String.fromCharCode(i + start); | ||
alphabetByEncoding[char] = i; | ||
|
@@ -21,20 +21,25 @@ for (let i = 0; i < 10; i++) { | |
alphabetByEncoding[char] = index; | ||
alphabetByValue[index] = char; | ||
} | ||
alphabetByEncoding["+"] = 62; | ||
alphabetByValue[62] = "+"; | ||
alphabetByEncoding["/"] = 63; | ||
alphabetByValue[63] = "/"; | ||
alphabetByEncoding["-"] = 62; | ||
alphabetByValue[62] = "-"; | ||
alphabetByEncoding["_"] = 63; | ||
alphabetByValue[63] = "_"; | ||
var bitsPerLetter = 6; | ||
var bitsPerByte = 8; | ||
var maxLetterValue = 63; | ||
|
||
// node_modules/.pnpm/@smithy[email protected]/node_modules/@smithy/util-base64/dist-es/fromBase64.browser.js | ||
var fromBase64 = (input) => { | ||
var stringToBuffer = (value) => { | ||
return new TextEncoder().encode(value); | ||
}; | ||
var bufferToString = (value) => { | ||
return new TextDecoder().decode(value); | ||
}; | ||
var base64urlDecode = (_input) => { | ||
const input = _input + "=".repeat((4 - _input.length % 4) % 4); | ||
let totalByteLength = input.length / 4 * 3; | ||
if (input.slice(-2) === "==") { | ||
if (input.endsWith("==")) { | ||
totalByteLength -= 2; | ||
} else if (input.slice(-1) === "=") { | ||
} else if (input.endsWith("=")) { | ||
totalByteLength--; | ||
} | ||
const out = new ArrayBuffer(totalByteLength); | ||
|
@@ -43,14 +48,14 @@ var fromBase64 = (input) => { | |
let bits = 0; | ||
let bitLength = 0; | ||
for (let j = i, limit = i + 3; j <= limit; j++) { | ||
if (input[j] !== "=") { | ||
if (input[j] === "=") { | ||
bits >>= bitsPerLetter; | ||
} else { | ||
if (!(input[j] in alphabetByEncoding)) { | ||
throw new TypeError(`Invalid character ${input[j]} in base64 string.`); | ||
} | ||
bits |= alphabetByEncoding[input[j]] << (limit - j) * bitsPerLetter; | ||
bitLength += bitsPerLetter; | ||
} else { | ||
bits >>= bitsPerLetter; | ||
} | ||
} | ||
const chunkOffset = i / 4 * 3; | ||
|
@@ -63,9 +68,8 @@ var fromBase64 = (input) => { | |
} | ||
return new Uint8Array(out); | ||
}; | ||
|
||
// node_modules/.pnpm/@smithy[email protected]/node_modules/@smithy/util-base64/dist-es/toBase64.browser.js | ||
function toBase64(input) { | ||
var base64urlEncode = (_input) => { | ||
const input = typeof _input === "string" ? stringToBuffer(_input) : _input; | ||
let str = ""; | ||
for (let i = 0; i < input.length; i += 3) { | ||
let bits = 0; | ||
|
@@ -80,22 +84,11 @@ function toBase64(input) { | |
const offset = (bitClusterCount - k) * bitsPerLetter; | ||
str += alphabetByValue[(bits & maxLetterValue << offset) >> offset]; | ||
} | ||
str += "==".slice(0, 4 - bitClusterCount); | ||
} | ||
return str; | ||
} | ||
}; | ||
|
||
// src/index.ts | ||
var stringToBuffer = (value) => { | ||
return new TextEncoder().encode(value); | ||
}; | ||
var bufferToString = (value) => { | ||
return new TextDecoder().decode(value); | ||
}; | ||
var base64urlEncode = (value) => toBase64(typeof value === "string" ? stringToBuffer(value) : value).replace(/\+/g, "-").replace(/\//g, "_").replace(/=/g, ""); | ||
var base64urlDecode = (value) => fromBase64( | ||
value.replace(/-/g, "+").replace(/_/g, "/") + Array((4 - value.length % 4) % 4 + 1).join("=") | ||
); | ||
var defaults = { | ||
encryption: { saltBits: 256, algorithm: "aes-256-cbc", iterations: 1, minPasswordlength: 32 }, | ||
integrity: { saltBits: 256, algorithm: "sha256", iterations: 1, minPasswordlength: 32 }, | ||
|
@@ -114,15 +107,15 @@ var algorithms = { | |
sha256: { keyBits: 256, name: "SHA-256" } | ||
}; | ||
var macFormatVersion = "2"; | ||
var macPrefix = `Fe26.${macFormatVersion}`; | ||
var macPrefix = "Fe26.2"; | ||
var randomBytes = (_crypto, size) => { | ||
const bytes = new Uint8Array(size); | ||
_crypto.getRandomValues(bytes); | ||
return bytes; | ||
}; | ||
var randomBits = (_crypto, bits) => { | ||
if (bits < 1) | ||
throw Error("Invalid random bits count"); | ||
throw new Error("Invalid random bits count"); | ||
const bytes = Math.ceil(bits / 8); | ||
return randomBytes(_crypto, bytes); | ||
}; | ||
|
@@ -226,20 +219,20 @@ var normalizePassword = (password) => { | |
}; | ||
var seal = async (_crypto, object, password, options) => { | ||
if (!password) | ||
throw Error("Empty password"); | ||
throw new Error("Empty password"); | ||
const opts = clone(options); | ||
const now = Date.now() + (opts.localtimeOffsetMsec || 0); | ||
const objectString = JSON.stringify(object); | ||
const pass = normalizePassword(password); | ||
const { id = "" } = pass; | ||
const { id = "", encryption, integrity } = pass; | ||
if (id && !/^\w+$/.test(id)) | ||
throw new Error("Invalid password id"); | ||
const { encrypted, key } = await encrypt(_crypto, pass.encryption, opts.encryption, objectString); | ||
const { encrypted, key } = await encrypt(_crypto, encryption, opts.encryption, objectString); | ||
const encryptedB64 = base64urlEncode(new Uint8Array(encrypted)); | ||
const iv = base64urlEncode(key.iv); | ||
const expiration = opts.ttl ? now + opts.ttl : ""; | ||
const macBaseString = `${macPrefix}*${id}*${key.salt}*${iv}*${encryptedB64}*${expiration}`; | ||
const mac = await hmacWithPassword(_crypto, pass.integrity, opts.integrity, macBaseString); | ||
const mac = await hmacWithPassword(_crypto, integrity, opts.integrity, macBaseString); | ||
const sealed = `${macBaseString}*${mac.salt}*${mac.digest}`; | ||
return sealed; | ||
}; | ||
|
@@ -253,7 +246,7 @@ var fixedTimeComparison = (a, b) => { | |
}; | ||
var unseal = async (_crypto, sealed, password, options) => { | ||
if (!password) | ||
throw Error("Empty password"); | ||
throw new Error("Empty password"); | ||
const opts = clone(options); | ||
const now = Date.now() + (opts.localtimeOffsetMsec || 0); | ||
const parts = sealed.split("*"); | ||
|
@@ -271,22 +264,21 @@ var unseal = async (_crypto, sealed, password, options) => { | |
if (macPrefix !== prefix) | ||
throw new Error("Wrong mac prefix"); | ||
if (expiration) { | ||
if (!/^\d+$/.exec(expiration)) | ||
if (!/^\d+$/.test(expiration)) | ||
throw new Error("Invalid expiration"); | ||
const exp = parseInt(expiration, 10); | ||
const exp = Number.parseInt(expiration, 10); | ||
if (exp <= now - opts.timestampSkewSec * 1e3) | ||
throw new Error("Expired seal"); | ||
} | ||
if (typeof password === "undefined" || typeof password === "string" && password.length === 0) | ||
throw new Error("Empty password"); | ||
let pass = ""; | ||
passwordId = passwordId || "default"; | ||
if (typeof password === "string" || password instanceof Uint8Array) | ||
pass = password; | ||
else if (!(passwordId in password)) | ||
throw new Error(`Cannot find password: ${passwordId}`); | ||
else | ||
else if (passwordId in password) { | ||
pass = password[passwordId]; | ||
} else { | ||
throw new Error(`Cannot find password: ${passwordId}`); | ||
} | ||
pass = normalizePassword(pass); | ||
const macOptions = opts.integrity; | ||
macOptions.salt = hmacSalt; | ||
|
Oops, something went wrong.