/
crypto-common.ts
94 lines (80 loc) · 2.72 KB
/
crypto-common.ts
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
import { AesBlockSize, AESGCM, CounterIvChecker, createDecrypter, createEncrypter } from "@ndn/keychain";
import { type LLDecrypt, type LLEncrypt, SignedInterestPolicy } from "@ndn/packet";
import { crypto } from "@ndn/util";
const ECDH_PARAMS: EcKeyGenParams & EcKeyImportParams = {
name: "ECDH",
namedCurve: "P-256",
};
export async function generateEcdhKey(): Promise<[pvt: CryptoKey, pub: CryptoKey]> {
const { privateKey, publicKey } = await crypto.subtle.generateKey(ECDH_PARAMS, false, ["deriveBits"]);
return [privateKey, publicKey];
}
export async function importEcdhPub(raw: Uint8Array): Promise<CryptoKey> {
return crypto.subtle.importKey("raw", raw, ECDH_PARAMS, true, []);
}
export async function exportEcdhPub(key: CryptoKey): Promise<Uint8Array> {
const raw = await crypto.subtle.exportKey("raw", key);
return new Uint8Array(raw);
}
const SALT_LEN = 32;
export function makeSalt(): Uint8Array {
return crypto.getRandomValues(new Uint8Array(SALT_LEN));
}
export function checkSalt(input: Uint8Array): void {
if (input.byteLength !== SALT_LEN) {
throw new Error("bad Salt");
}
}
const REQUEST_ID_LEN = 8;
export function makeRequestId(): Uint8Array {
return crypto.getRandomValues(new Uint8Array(REQUEST_ID_LEN));
}
export function checkRequestId(input: Uint8Array): void {
if (input.byteLength !== REQUEST_ID_LEN) {
throw new Error("bad RequestId");
}
}
export interface SessionKey {
sessionEncrypter: LLEncrypt.Key;
sessionLocalDecrypter: LLDecrypt.Key;
sessionDecrypter: LLDecrypt.Key;
}
export async function makeSessionKey(
ecdhPvt: CryptoKey,
ecdhPub: CryptoKey,
salt: Uint8Array,
requestId: Uint8Array,
): Promise<SessionKey> {
const hkdfBits = await crypto.subtle.deriveBits({ name: "ECDH", public: ecdhPub }, ecdhPvt, 256);
const hkdfKey = await crypto.subtle.importKey("raw", hkdfBits, "HKDF", false, ["deriveKey"]);
const secretKey = await crypto.subtle.deriveKey(
{
name: "HKDF",
salt,
info: requestId,
hash: "SHA-256",
},
hkdfKey,
AESGCM.makeAesKeyGenParams({ length: 128 }),
false,
AESGCM.keyUsages.secret,
);
const key = { secretKey, info: {} };
const ivChk = new CounterIvChecker({
ivLength: AESGCM.ivLength,
counterBits: 32,
blockSize: AesBlockSize,
requireSameRandom: true,
});
const sessionEncrypter = createEncrypter(AESGCM, key);
const sessionLocalDecrypter = createDecrypter(AESGCM, key);
const sessionDecrypter = ivChk.wrap(sessionLocalDecrypter);
return {
sessionEncrypter,
sessionLocalDecrypter,
sessionDecrypter,
};
}
export function makeSignedInterestPolicy(): SignedInterestPolicy {
return new SignedInterestPolicy(SignedInterestPolicy.Nonce(), SignedInterestPolicy.Time());
}