Skip to content

Commit

Permalink
Merge pull request #2 from nealfennimore/proxy-support
Browse files Browse the repository at this point in the history
Proxy support
  • Loading branch information
nealfennimore committed May 30, 2023
2 parents 2927ab2 + 7fbcb35 commit 941b7bc
Show file tree
Hide file tree
Showing 49 changed files with 4,525 additions and 769 deletions.
7 changes: 4 additions & 3 deletions .github/workflows/test.yml
Expand Up @@ -9,11 +9,10 @@ on:
jobs:
build:
runs-on: ubuntu-latest

strategy:
fail-fast: false
matrix:
node-version: [16.x, 18.x]
# See supported Node.js release schedule at https://nodejs.org/en/about/releases/
node-version: [16.x, 18.x, 20.x]

steps:
- uses: actions/checkout@v3
Expand All @@ -35,6 +34,8 @@ jobs:
run: npm ci

- run: npm run test -- --coverage
env:
NODE_VERSION: ${{ matrix.node-version }}

- name: Upload coverage to Codecov
uses: codecov/codecov-action@v3
Expand Down
3 changes: 2 additions & 1 deletion .gitignore
Expand Up @@ -5,4 +5,5 @@ coverage
docs
shell.nix
.envrc
.direnv
.direnv
scratchpad.js
1 change: 1 addition & 0 deletions .prettierignore
@@ -0,0 +1 @@
*.yml
59 changes: 59 additions & 0 deletions README.md
Expand Up @@ -15,3 +15,62 @@ Algorithms are split into their own modules, which enforces consumption of crypt
```sh
npm i @nfen/webcrypto-ts
```

## Examples

Many more examples in the [Documentation](https://neal.codes/webcrypto-ts/).

### ECDSA

```ts
import ECDSA from "@nfen/webcrypto-ts/lib/ec/ecdsa";
const keyPair = await ECDSA.generateKey();

const message = new TextEncoder().encode("a message");
const signature = await keyPair.privateKey.sign({ hash: "SHA-512" }, message);

const isVerified = await someOtherKeypair.publicKey.verify(
{ hash: "SHA-512" },
signature,
message
);
```

### RSA-OAEP

```ts
import * as RSA_OAEP from "@nfen/webcrypto-ts/lib/rsa/rsa_oaep";
import * as AES_CBC from "@nfen/webcrypto-ts/lib/aes/aes_cbc";
import * as Random from "@nfen/webcrypto-ts/random";

const kek = await RSA_OAEP.generateKey(
{
hash: "SHA-512",
modulusLength: 4096,
publicExponent: new Uint8Array([0x01, 0x00, 0x01]),
},
true,
["wrapKey", "unwrapKey"]
);
const dek = await AES_CBC.generateKey();
const label = await Random.getValues(8);
const wrappedCbcKey = await kek.wrapKey("raw", dek.self, { label });
```

### AES-GCM

```ts
import * as AES_GCM from "@nfen/webcrypto-ts/lib/aes/aes_gcm";
import { IV } from "@nfen/webcrypto-ts/lib/random";

const iv = await IV.generate();
const key = await AES_GCM.generateKey();
const message = "a message";
const cipherText = await key.encrypt(
{ iv },
new TextEncoder().encode("a message")
);
console.assert(
new TextDecoder().decode(await key.decrypt({ iv }, message)) === message
);
```
7 changes: 7 additions & 0 deletions global.d.ts
@@ -1,2 +1,9 @@
declare var encode: Function;
declare var decode: Function;

interface ProxyConstructor {
new <TSource extends object, TTarget extends object>(
target: TSource,
handler: ProxyHandler<TSource>
): TTarget;
}
2 changes: 1 addition & 1 deletion package-lock.json

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

2 changes: 1 addition & 1 deletion package.json
Expand Up @@ -29,7 +29,7 @@
"exports": "./lib/index.js",
"type": "module",
"engines": {
"node": ">=16"
"node": ">=18"
},
"scripts": {
"prepublishOnly": "npm run test && npm run build",
Expand Down
196 changes: 135 additions & 61 deletions src/aes/__tests__/aes_cbc.test.ts
Expand Up @@ -2,75 +2,149 @@ import * as Random from "../../random.js";
import * as AES from "../index.js";

describe("AES_CBC", () => {
let iv: Uint8Array, key: AES.AesCbcCryptoKey;
const text = "brown fox fox fox fox fox fox fox fox fox";
beforeEach(async () => {
iv = await Random.IV.generate();
key = await AES.AES_CBC.generateKey();
});
it("should encrypt and decrypt", async () => {
const ciphertextBytes = await AES.AES_CBC.encrypt(
{ iv },
key,
new TextEncoder().encode(text)
);
const plaintextBytes = await AES.AES_CBC.decrypt(
{ iv },
key,
ciphertextBytes
);
expect(new TextDecoder().decode(plaintextBytes)).toEqual(text);
});
it("should import and export keys", async () => {
const ciphertextBytes = await AES.AES_CBC.encrypt(
{ iv },
key,
new TextEncoder().encode(text)
);
describe("Original", () => {
let iv: Uint8Array,
proxiedKey: AES.AesCbcProxiedCryptoKey,
key: AES.AesCbcCryptoKey;
const text = "brown fox fox fox fox fox fox fox fox fox";
beforeEach(async () => {
iv = await Random.IV.generate();
proxiedKey = await AES.AES_CBC.generateKey();
key = proxiedKey.self;
});
it("should encrypt and decrypt", async () => {
const ciphertextBytes = await AES.AES_CBC.encrypt(
{ iv },
key,
new TextEncoder().encode(text)
);
const plaintextBytes = await AES.AES_CBC.decrypt(
{ iv },
key,
ciphertextBytes
);
expect(new TextDecoder().decode(plaintextBytes)).toEqual(text);
});
it("should import and export keys", async () => {
const ciphertextBytes = await AES.AES_CBC.encrypt(
{ iv },
key,
new TextEncoder().encode(text)
);

const jwk = await AES.AES_CBC.exportKey("jwk", key);
const importedKey = await AES.AES_CBC.importKey("jwk", jwk, {
length: 256,
});

const jwk = await AES.AES_CBC.exportKey("jwk", key);
const importedKey = await AES.AES_CBC.importKey("jwk", jwk, {
length: 256,
const plaintextBytes = await AES.AES_CBC.decrypt(
{ iv },
importedKey.self,
ciphertextBytes
);
expect(new TextDecoder().decode(plaintextBytes)).toEqual(text);
});
it("should wrap and unwrap keys", async () => {
const kek = await AES.AES_CBC.generateKey({ length: 256 }, true, [
"wrapKey",
"unwrapKey",
]);
const dek = await AES.AES_CBC.generateKey({
length: 256,
});

const ciphertextBytes = await AES.AES_CBC.encrypt(
{ iv },
dek.self,
new TextEncoder().encode(text)
);

const plaintextBytes = await AES.AES_CBC.decrypt(
{ iv },
importedKey,
ciphertextBytes
);
expect(new TextDecoder().decode(plaintextBytes)).toEqual(text);
const wrappedKey = await AES.AES_CBC.wrapKey(
"raw",
dek.self,
kek.self,
{
iv,
}
);
const unwrappedkey = (await AES.AES_CBC.unwrapKey(
"raw",
wrappedKey,
{ name: AES.Alg.Mode.AES_CBC },
kek.self,
{ iv }
)) as AES.AesCbcCryptoKey;

const plaintextBytes = await AES.AES_CBC.decrypt(
{ iv },
unwrappedkey,
ciphertextBytes
);
expect(new TextDecoder().decode(plaintextBytes)).toEqual(text);
});
});
it("should wrap and unwrap keys", async () => {
const kek = await AES.AES_CBC.generateKey({ length: 256 }, true, [
"wrapKey",
"unwrapKey",
]);
const dek: AES.AesCbcCryptoKey = await AES.AES_CBC.generateKey({
length: 256,
describe("Proxied", () => {
let iv: Uint8Array, key: AES.AesCbcProxiedCryptoKey;
const text = "brown fox fox fox fox fox fox fox fox fox";
beforeEach(async () => {
iv = await Random.IV.generate();
key = await AES.AES_CBC.generateKey();
});
it("should encrypt and decrypt", async () => {
const ciphertextBytes = await key.encrypt(
{ iv },
new TextEncoder().encode(text)
);
const plaintextBytes = await key.decrypt({ iv }, ciphertextBytes);
expect(new TextDecoder().decode(plaintextBytes)).toEqual(text);
});
it("should import and export keys", async () => {
const ciphertextBytes = await key.encrypt(
{ iv },
new TextEncoder().encode(text)
);

const ciphertextBytes = await AES.AES_CBC.encrypt(
{ iv },
dek,
new TextEncoder().encode(text)
);
const jwk = await key.exportKey("jwk");
const importedKey = await AES.AES_CBC.importKey("jwk", jwk, {
length: 256,
});

const wrappedKey = await AES.AES_CBC.wrapKey("raw", dek, kek, {
iv,
const plaintextBytes = await importedKey.decrypt(
{ iv },
ciphertextBytes
);
expect(new TextDecoder().decode(plaintextBytes)).toEqual(text);
});
const unwrappedkey = (await AES.AES_CBC.unwrapKey(
"raw",
wrappedKey,
{ name: AES.Alg.Mode.AES_CBC },
kek,
{ iv }
)) as AES.AesCbcCryptoKey;
it("should wrap and unwrap keys", async () => {
const kek = await AES.AES_CBC.generateKey({ length: 256 }, true, [
"wrapKey",
"unwrapKey",
]);
const dek = await AES.AES_CBC.generateKey({
length: 256,
});

const plaintextBytes = await AES.AES_CBC.decrypt(
{ iv },
unwrappedkey,
ciphertextBytes
);
expect(new TextDecoder().decode(plaintextBytes)).toEqual(text);
const ciphertextBytes = await dek.encrypt(
{ iv },
new TextEncoder().encode(text)
);

const wrappedKey = await kek.wrapKey("raw", dek.self, {
iv,
});
const unwrappedkey = (await kek.unwrapKey(
"raw",
wrappedKey,
{ name: AES.Alg.Mode.AES_CBC },
{ iv }
)) as AES.AesCbcCryptoKey;

const plaintextBytes = await AES.AES_CBC.decrypt(
{ iv },
unwrappedkey,
ciphertextBytes
);
expect(new TextDecoder().decode(plaintextBytes)).toEqual(text);
});
});
});

0 comments on commit 941b7bc

Please sign in to comment.