forked from openpgpjs/openpgpjs
/
gcm.js
123 lines (107 loc) · 4.51 KB
/
gcm.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
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
// OpenPGP.js - An OpenPGP implementation in javascript
// Copyright (C) 2016 Tankred Hase
//
// This library is free software; you can redistribute it and/or
// modify it under the terms of the GNU Lesser General Public
// License as published by the Free Software Foundation; either
// version 3.0 of the License, or (at your option) any later version.
//
// This library is distributed in the hope that it will be useful,
// but WITHOUT ANY WARRANTY; without even the implied warranty of
// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
// Lesser General Public License for more details.
//
// You should have received a copy of the GNU Lesser General Public
// License along with this library; if not, write to the Free Software
// Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA
/**
* @fileoverview This module wraps native AES-GCM en/decryption for both
* the WebCrypto api as well as node.js' crypto api.
* @module crypto/mode/gcm
* @private
*/
import { AES_GCM } from '@openpgp/asmcrypto.js/dist_es8/aes/gcm';
import util from '../../util';
import enums from '../../enums';
const webCrypto = util.getWebCrypto();
const nodeCrypto = util.getNodeCrypto();
const Buffer = util.getNodeBuffer();
const blockLength = 16;
const ivLength = 12; // size of the IV in bytes
const tagLength = 16; // size of the tag in bytes
const ALGO = 'AES-GCM';
/**
* Class to en/decrypt using GCM mode.
* @param {enums.symmetric} cipher - The symmetric cipher algorithm to use
* @param {Uint8Array} key - The encryption key
*/
async function GCM(cipher, key) {
if (cipher !== enums.symmetric.aes128 &&
cipher !== enums.symmetric.aes192 &&
cipher !== enums.symmetric.aes256) {
throw new Error('GCM mode supports only AES cipher');
}
if (util.getWebCrypto() && key.length !== 24) { // WebCrypto (no 192 bit support) see: https://www.chromium.org/blink/webcrypto#TOC-AES-support
const _key = await webCrypto.importKey('raw', key, { name: ALGO }, false, ['encrypt', 'decrypt']);
return {
encrypt: async function(pt, iv, adata = new Uint8Array()) {
if (!pt.length) { // iOS does not support GCM-en/decrypting empty messages
return AES_GCM.encrypt(pt, key, iv, adata);
}
const ct = await webCrypto.encrypt({ name: ALGO, iv, additionalData: adata, tagLength: tagLength * 8 }, _key, pt);
return new Uint8Array(ct);
},
decrypt: async function(ct, iv, adata = new Uint8Array()) {
if (ct.length === tagLength) { // iOS does not support GCM-en/decrypting empty messages
return AES_GCM.decrypt(ct, key, iv, adata);
}
const pt = await webCrypto.decrypt({ name: ALGO, iv, additionalData: adata, tagLength: tagLength * 8 }, _key, ct);
return new Uint8Array(pt);
}
};
}
if (util.getNodeCrypto()) { // Node crypto library
return {
encrypt: async function(pt, iv, adata = new Uint8Array()) {
const en = new nodeCrypto.createCipheriv('aes-' + (key.length * 8) + '-gcm', key, iv);
en.setAAD(adata);
const ct = Buffer.concat([en.update(pt), en.final(), en.getAuthTag()]); // append auth tag to ciphertext
return new Uint8Array(ct);
},
decrypt: async function(ct, iv, adata = new Uint8Array()) {
const de = new nodeCrypto.createDecipheriv('aes-' + (key.length * 8) + '-gcm', key, iv);
de.setAAD(adata);
de.setAuthTag(ct.slice(ct.length - tagLength, ct.length)); // read auth tag at end of ciphertext
const pt = Buffer.concat([de.update(ct.slice(0, ct.length - tagLength)), de.final()]);
return new Uint8Array(pt);
}
};
}
return {
encrypt: async function(pt, iv, adata) {
return AES_GCM.encrypt(pt, key, iv, adata);
},
decrypt: async function(ct, iv, adata) {
return AES_GCM.decrypt(ct, key, iv, adata);
}
};
}
/**
* Get GCM nonce. Note: this operation is not defined by the standard.
* A future version of the standard may define GCM mode differently,
* hopefully under a different ID (we use Private/Experimental algorithm
* ID 100) so that we can maintain backwards compatibility.
* @param {Uint8Array} iv - The initialization vector (12 bytes)
* @param {Uint8Array} chunkIndex - The chunk index (8 bytes)
*/
GCM.getNonce = function(iv, chunkIndex) {
const nonce = iv.slice();
for (let i = 0; i < chunkIndex.length; i++) {
nonce[4 + i] ^= chunkIndex[i];
}
return nonce;
};
GCM.blockLength = blockLength;
GCM.ivLength = ivLength;
GCM.tagLength = tagLength;
export default GCM;