UNPKG

3.42 kBJavaScriptView Raw
1/**
2 * AES-GCM is a symmetric key for encryption
3 */
4
5import * as encoding from '../encoding.js'
6import * as decoding from '../decoding.js'
7import * as webcrypto from 'lib0/webcrypto'
8import * as string from '../string.js'
9export { exportKeyJwk, exportKeyRaw } from './common.js'
10
11/**
12 * @typedef {Array<'encrypt'|'decrypt'>} Usages
13 */
14
15/**
16 * @type {Usages}
17 */
18const defaultUsages = ['encrypt', 'decrypt']
19
20/**
21 * @param {CryptoKey} key
22 * @param {Uint8Array} data
23 */
24export const encrypt = (key, data) => {
25 const iv = webcrypto.getRandomValues(new Uint8Array(16)) // 92bit is enough. 128bit is recommended if space is not an issue.
26 return webcrypto.subtle.encrypt(
27 {
28 name: 'AES-GCM',
29 iv
30 },
31 key,
32 data
33 ).then(cipher => {
34 const encryptedDataEncoder = encoding.createEncoder()
35 // iv may be sent in the clear to the other peers
36 encoding.writeUint8Array(encryptedDataEncoder, iv)
37 encoding.writeVarUint8Array(encryptedDataEncoder, new Uint8Array(cipher))
38 return encoding.toUint8Array(encryptedDataEncoder)
39 })
40}
41
42/**
43 * @experimental The API is not final!
44 *
45 * Decrypt some data using AES-GCM method.
46 *
47 * @param {CryptoKey} key
48 * @param {Uint8Array} data
49 * @return {PromiseLike<Uint8Array>} decrypted buffer
50 */
51export const decrypt = (key, data) => {
52 const dataDecoder = decoding.createDecoder(data)
53 const iv = decoding.readUint8Array(dataDecoder, 16)
54 const cipher = decoding.readVarUint8Array(dataDecoder)
55 return webcrypto.subtle.decrypt(
56 {
57 name: 'AES-GCM',
58 iv
59 },
60 key,
61 cipher
62 ).then(data => new Uint8Array(data))
63}
64
65const aesAlgDef = {
66 name: 'AES-GCM',
67 length: 256
68}
69
70/**
71 * @param {any} jwk
72 * @param {Object} opts
73 * @param {Usages} [opts.usages]
74 * @param {boolean} [opts.extractable]
75 */
76export const importKeyJwk = (jwk, { usages, extractable = false } = {}) => {
77 if (usages == null) {
78 /* c8 ignore next */
79 usages = jwk.key_ops || defaultUsages
80 }
81 return webcrypto.subtle.importKey('jwk', jwk, 'AES-GCM', extractable, /** @type {Usages} */ (usages))
82}
83
84/**
85 * Only suited for importing public keys.
86 *
87 * @param {Uint8Array} raw
88 * @param {Object} opts
89 * @param {Usages} [opts.usages]
90 * @param {boolean} [opts.extractable]
91 */
92export const importKeyRaw = (raw, { usages = defaultUsages, extractable = false } = {}) =>
93 webcrypto.subtle.importKey('raw', raw, aesAlgDef, extractable, /** @type {Usages} */ (usages))
94
95/**
96 * @param {Uint8Array | string} data
97 */
98/* c8 ignore next */
99const toBinary = data => typeof data === 'string' ? string.encodeUtf8(data) : data
100
101/**
102 * @experimental The API is not final!
103 *
104 * Derive an symmetric key using the Password-Based-Key-Derivation-Function-2.
105 *
106 * @param {Uint8Array|string} secret
107 * @param {Uint8Array|string} salt
108 * @param {Object} opts
109 * @param {boolean} [opts.extractable]
110 * @param {Usages} [opts.usages]
111 */
112export const deriveKey = (secret, salt, { extractable = false, usages = defaultUsages } = {}) =>
113 webcrypto.subtle.importKey(
114 'raw',
115 toBinary(secret),
116 'PBKDF2',
117 false,
118 ['deriveKey']
119 ).then(keyMaterial =>
120 webcrypto.subtle.deriveKey(
121 {
122 name: 'PBKDF2',
123 salt: toBinary(salt), // NIST recommends at least 64 bits
124 iterations: 600000, // OWASP recommends 600k iterations
125 hash: 'SHA-256'
126 },
127 keyMaterial,
128 aesAlgDef,
129 extractable,
130 usages
131 )
132 )