Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
8 changes: 4 additions & 4 deletions .circleci/config.yml
Original file line number Diff line number Diff line change
Expand Up @@ -2,22 +2,22 @@ version: 2.1
default: &default
working_directory: ~/repo
docker:
- image: circleci/node:14.17
- image: circleci/node:16.13
commands:
prepare:
description: "Prepare working directory"
steps:
- checkout
- restore_cache:
keys:
- v2-dependencies-{{ checksum "package.json" }}
- v3-dependencies-{{ checksum "package.json" }}
# fallback to using the latest cache if no exact match is found
- v2-dependencies-
- v3-dependencies-
- run: npm install
- save_cache:
paths:
- node_modules
key: v2-dependencies-{{ checksum "package.json" }}
key: v3-dependencies-{{ checksum "package.json" }}
jobs:
build:
<<: *default
Expand Down
1 change: 0 additions & 1 deletion .eslintignore
Original file line number Diff line number Diff line change
@@ -1,3 +1,2 @@
node_modules
dist
Tuple.ts
3 changes: 1 addition & 2 deletions .eslintrc
Original file line number Diff line number Diff line change
Expand Up @@ -10,8 +10,7 @@
"eslint:recommended",
"plugin:@typescript-eslint/eslint-recommended",
"plugin:@typescript-eslint/recommended",
"plugin:prettierx/default",
"plugin:prettierx/@typescript-eslint"
"plugin:prettierx/default"
],
"rules": {
"prettierx/options": [
Expand Down
6 changes: 3 additions & 3 deletions Algorithm.ts
Original file line number Diff line number Diff line change
Expand Up @@ -41,11 +41,11 @@ export class Algorithm {
}
async export(): Promise<string>
async export(parts: number): Promise<string[]>
async export(parts = 1): Promise<string | string[]> {
async export(parts?: number): Promise<string | string[]> {
const key = new Uint8Array(await crypto.subtle.exportKey("raw", await this.key))
let result: Uint8Array[] = Algorithm.generateRandomKeys(key.length, parts - 1)
let result: Uint8Array[] = Algorithm.generateRandomKeys(key.length, (parts ?? 1) - 1)
result = [Algorithm.reduceKeys([key, ...result]), ...result]
return result.length == 1 ? Base64.encode(result[0], "url") : result.map(r => Base64.encode(r, "url"))
return parts == undefined ? Base64.encode(result[0], "url") : result.map(r => Base64.encode(r, "url"))
}
static aesCbc(key: 256 | string | string[]): Algorithm {
return Algorithm.generate("AES-CBC", key)
Expand Down
27 changes: 22 additions & 5 deletions Algorithms.spec.ts
Original file line number Diff line number Diff line change
Expand Up @@ -20,19 +20,36 @@ describe("Context.PrimarySecrets", () => {
`2020a: ${algorithm["2020a"][1]}, 2021a: ${algorithm["2021a"][1]}`,
`2020a: ${algorithm["2020a"][2]}`
)
expect(Object.keys(secrets)).toEqual(["current", "2020a", "2020b", "2021a"])
expect(Object.keys(secrets)).toEqual(["current", "2020a", "2020b", "2021a", "export"])
expect(await exportAll(secrets)).toEqual({ current: expected["2020a"], ...expected })
})
it("generate", async () => {
const generated = cryptly.Algorithms.generate(cryptly.Algorithm.aesGcm, 256, "2020a", "2020b", "2021a")
expect(generated.current.name).toEqual("2020a")
expect(await generated.export()).toEqual([
`2020a: ${await generated["2020a"].export()}, 2020b: ${await generated[
"2020b"
].export()}, 2021a: ${await generated["2021a"].export()}`,
])
})
it("generate + create", async () => {
const generated = cryptly.Algorithms.generate(cryptly.Algorithm.aesGcm, 256, "2020a", "2020b", "2021a")
const exported = await generated.export(2)
const imported = cryptly.Algorithms.create(cryptly.Algorithm.aesGcm, "2020a", ...exported)
expect(await imported.export()).toEqual(await generated.export())
})
})

async function exportAll<T>(
secrets: T
): Promise<
async function exportAll<T>(secrets: T): Promise<
{
[P in keyof T]: string
}
> {
return Object.fromEntries(
await Promise.all(Object.entries(secrets).map(async ([name, algorithm]) => [name, await algorithm.export()]))
await Promise.all(
Object.entries(secrets)
.filter(([name, _]) => name != "export")
.map(async ([name, algorithm]) => [name, await algorithm.export()])
)
)
}
31 changes: 27 additions & 4 deletions Algorithms.ts
Original file line number Diff line number Diff line change
@@ -1,17 +1,40 @@
import { Algorithm } from "./Algorithm"
import { Tuple2 } from "./Tuple"

export type Algorithms = Record<string, Algorithm> & { current: Algorithm }
export type Algorithms = Record<string, Algorithm> & {
current: Algorithm
export: (parts?: number) => Promise<string[]>
}

function createExport(algorithms: Record<string, Algorithm>): (parts?: number) => Promise<string[]> {
return async (parts = 1) => {
const exported = await Promise.all(
Object.entries(algorithms)
.filter(([name, _]) => name != "export" && name != "current")
.map(async ([name, algorithm]) => [name, await algorithm.export(parts)] as const)
)
return Array(parts)
.fill(undefined)
.map((_, index) => exported.map(([name, secrets]) => name + ": " + secrets[index]).join(", "))
}
}
export namespace Algorithms {
export function generate(
generate: (length: 256) => Algorithm,
length: 256,
current: string,
...keys: string[]
): Algorithms {
const result = Object.fromEntries([current, ...keys].map(name => [name, Object.assign(generate(length), { name })]))
return Object.assign(result, { current: result[current], export: createExport(result) })
}
export function create(create: (keys: string[]) => Algorithm, current: string, ...secrets: string[]): Algorithms {
const [first, ...remainder] = secrets.map(part =>
Object.fromEntries(part.split(",").map(secret => secret.split(":", 2).map(item => item.trim())))
)
const result = Object.assign(
{},
...Object.entries(first)
.map<Tuple2<string, string[]>>(([name, secret]) => [
.map<[string, string[]]>(([name, secret]) => [
name,
[secret, ...remainder.map(part => part[name]).filter(part => part)],
])
Expand All @@ -21,6 +44,6 @@ export namespace Algorithms {
},
}))
)
return { current: result[current], ...result }
return { current: result[current], ...result, export: createExport(result) }
}
}
50 changes: 2 additions & 48 deletions Identifier.spec.ts
Original file line number Diff line number Diff line change
Expand Up @@ -22,54 +22,8 @@ describe("Identifier", () => {
it("is not .", () => expect(cryptly.Identifier.is("Hello.0123")).toBeFalsy())

const binary = [
0,
16,
131,
16,
81,
135,
32,
146,
139,
48,
211,
143,
65,
20,
147,
81,
85,
151,
97,
150,
155,
113,
215,
159,
130,
24,
163,
146,
89,
167,
162,
154,
171,
178,
219,
175,
195,
28,
179,
211,
93,
183,
227,
158,
187,
243,
223,
191,
0, 16, 131, 16, 81, 135, 32, 146, 139, 48, 211, 143, 65, 20, 147, 81, 85, 151, 97, 150, 155, 113, 215, 159, 130, 24,
163, 146, 89, 167, 162, 154, 171, 178, 219, 175, 195, 28, 179, 211, 93, 183, 227, 158, 187, 243, 223, 191,
]
const all = "ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789-_"
it("fromBinary", () => expect(cryptly.Identifier.fromBinary(Uint8Array.from(binary))).toEqual(all))
Expand Down
34 changes: 2 additions & 32 deletions Identifier.ts
Original file line number Diff line number Diff line change
Expand Up @@ -52,38 +52,8 @@ export namespace Identifier {
return result.join("")
}
export const length = [
4,
8,
12,
16,
20,
24,
28,
32,
36,
40,
44,
48,
52,
56,
60,
64,
68,
72,
76,
80,
84,
88,
92,
96,
100,
104,
108,
112,
116,
120,
124,
128,
4, 8, 12, 16, 20, 24, 28, 32, 36, 40, 44, 48, 52, 56, 60, 64, 68, 72, 76, 80, 84, 88, 92, 96, 100, 104, 108, 112,
116, 120, 124, 128,
] as Length[]
export type Length =
| 4
Expand Down
2 changes: 0 additions & 2 deletions Tuple.ts

This file was deleted.

4 changes: 2 additions & 2 deletions crypto.browser.ts
Original file line number Diff line number Diff line change
@@ -1,4 +1,4 @@
export const crypto =
typeof window != "undefined"
? window.crypto || ((window as unknown) as { msCrypto: any }).msCrypto
: self.crypto || ((self as unknown) as { msCrypto: any }).msCrypto
? window.crypto || (window as unknown as { msCrypto: any }).msCrypto
: self.crypto || (self as unknown as { msCrypto: any }).msCrypto
Loading