-
Notifications
You must be signed in to change notification settings - Fork 0
/
Copy pathjwe.js
98 lines (88 loc) · 3.15 KB
/
jwe.js
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
95
96
97
98
import { decodeBase64UrlAsArray, decodeBase64UrlAsString, encodeBase64UrlAsString } from '../utils/base64.js';
import { octetFromUtf8 } from '../utils/utf8.js';
import { encodeProtectedHeader } from './jose.js';
/**
* @typedef {Object} CreateEncryptionRecipientOptions
* @prop {JWEUnprotectedHeader} [header] unprotected header
* @prop {JWEProtectedHeader|string|ArrayBuffer|Uint8Array} [protected] protected header
* @prop {string} [encodedProtected]
* @prop {JWSPayload} [payload]
* @prop {string} [encodedPayload]
* @prop {JWK} [jwk] omit if unsecured
*/
/**
* https://datatracker.ietf.org/doc/html/rfc7515#section-5.1
* @param {CreateEncryptionRecipientOptions} options
* @return {Promise<JWERecipient>}
*/
export async function createEncryptionRecipient({
header,
protected: protectedVar,
encodedProtected = (protectedVar == null ? null : encodeProtectedHeader(protectedVar)),
payload,
encodedPayload = encodePayload(payload),
jwk,
}) {
const keyManagementMode;
/** @type {JWSSigningInput} */
const signingInput = `${encodedProtected}.${encodedPayload}`;
const decodedProtected = decodeProtectedHeader(protectedVar ?? encodedProtected);
const joseHeader = {
...header,
...decodedProtected,
};
let encodedSignature;
if (joseHeader.alg === 'none') {
if (jwk != null) throw new Error('Invalid algorithm.');
encodedSignature = '';
} else {
if (jwk == null) throw new Error('Missing signing key.');
/** @type {JWSSignature} */
const jwsSignature = await KeyStore.default.sign(jwk, signingInput);
encodedSignature = encodeBase64UrlAsString(jwsSignature);
}
return {
signature: encodedSignature,
...(header ? { header } : null),
...(encodedProtected ? { protected: encodedProtected } : null),
};
}
/**
* @param {string|BufferSource} plaintext
* @return {Uint8Array}
*/
export function plaintextAsUint8Array(plaintext) {
if (typeof plaintext === 'string') return Uint8Array.from(octetFromUtf8(plaintext));
if (plaintext instanceof Uint8Array) return plaintext;
if (plaintext instanceof ArrayBuffer) return new Uint8Array(plaintext);
return new Uint8Array(plaintext.buffer);
}
/**
* @param {string|JWECompactSerialization} jwe
* @return {JWEFlattened}
*/
export function uncompactJWE(jwe) {
const [protectedVar, encryptedKey, iv, ciphertext, tag] = jwe.split('.');
/** @type {JWEFlattened} */
const flattened = {
protected: protectedVar,
ciphertext,
};
if (iv) flattened.iv = iv;
if (encryptedKey) flattened.encrypted_key = encryptedKey;
if (tag) flattened.tag = tag;
return flattened;
}
/**
* @param {Object} options
* @param {JWEProtectedHeader|string|ArrayBuffer|Uint8Array} [options.protected]
* @param {string|BufferSource} options.plaintext
* @param {JWK} options.jwk
* @return {Promise<JWSCompactSerialization>}
*/
export async function encryptCompact({ protected: protectedVar, plaintext, jwk }) {
const encodedProtected = encodeProtectedHeader(protectedVar);
const plainTextArray = plaintextAsUint8Array(plaintext);
const ciphertext = await createSignature({ encodedProtected, encodedPayload, jwk });
return `${encodedProtected}.${encodedPayload}.${signature}`;
}