-
Notifications
You must be signed in to change notification settings - Fork 0
/
index.js
152 lines (135 loc) · 4.15 KB
/
index.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
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
const deasync = require('deasync');
const aws = require('aws-sdk');
// [['a', 1], ['b', 'x']] -> { a: 1, b: 'x' }
const getObjFromEntries = (entries) => {
const obj = {};
for (const [k, v] of entries) {
obj[k] = v;
}
return obj;
};
class KmsEncryptObj {
constructor({
awsAccessKey,
awsSecretKey,
awsRegion,
}) {
this.kms = new aws.KMS({
accessKeyId: awsAccessKey,
secretAccessKey: awsSecretKey,
region: awsRegion,
});
}
/**
* 暗号化された文字列を復号する
* @param {string} text 暗号化され、base64エンコードされた文字列
* @returns {Promise<string>} 復号化された文字列
*/
_decryptString(text) {
return new Promise((resolve, reject) => {
this.kms.decrypt({
CiphertextBlob: Buffer.from(text, 'base64'),
}, (err, data) => {
if (err) {
reject(err);
} else {
resolve(data.Plaintext.toString());
}
});
});
}
/**
* 文字列を暗号化する
* @param {string} text 暗号化したい文字列
* @param {string} keyId 暗号化に用いるKMSのキーのID
* @returns {Promise<string>} 暗号化され、base64エンコードされた文字列
*/
_encryptString(text, keyId) {
return new Promise((resolve, reject) => {
this.kms.encrypt({
KeyId: keyId,
Plaintext: text,
}, (err, data) => {
if (err) {
reject(err);
} else {
resolve(data.CiphertextBlob.toString('base64'));
}
});
});
}
/**
* オブジェクト(のバリュー)を暗号化する
* @param {object} obj 暗号化したいオブジェクト
* @param {string} keyId 暗号化に用いるKMSのキーのID
* @returns {Promise<object>} 暗号化されたオブジェクト
* { key_a: 'value_a' } -> { key_a: 'value_a_encrypted' }
*/
_encryptObj(obj, keyId) {
return Promise.all(Object.entries(obj).map(async ([key, value]) => {
const valueEncrypted = await this._encryptString(JSON.stringify(value), keyId);
return [key, valueEncrypted];
}))
.then(getObjFromEntries);
}
/**
* オブジェクトの各キー・バリューについてバリューを復号化して返す
* @param {object} obj
* @returns {Promise<object>}
* { key_a: 'value_a_encrypted' } -> { key_a: 'value_a' }
*/
_decryptObj(obj) {
return Promise.all(Object.entries(obj).map(async ([key, value]) => {
const valueDecrypted = JSON.parse(await this._decryptString(value));
return [key, valueDecrypted];
}))
.then(getObjFromEntries);
}
/**
* @param {object} obj (特定のキーに対応するバリューを)暗号化したいオブジェクト
* @param {string} kmsKeyId 暗号化に用いるKMSのキーのID
* @param {Array<string>} keysToEncrypt 暗号化したいキーのリスト
* obj = { key_a: 'value_a', key_b: 'value_b' }, keysToEncrypt = ['key_b']
* -> { key_a: 'value_a', _encrypted: { key_b: 'value_b_encrypted' }}
*/
async encrypt(obj, kmsKeyId, keysToEncrypt) {
const objNotToEncrypt = {};
const objToEncrypt = {};
for (const key of Object.keys(obj)) {
if (keysToEncrypt.includes(key)) {
objToEncrypt[key] = obj[key];
} else {
objNotToEncrypt[key] = obj[key];
}
}
return {
...objNotToEncrypt,
_encrypted: await this._encryptObj(objToEncrypt, kmsKeyId),
};
}
/**
* @param {object} obj 復号化したいオブジェクト
* @return {Promise<object>} 復号化されたオブジェクト
* { key_a: 'value_a', _encrypted: { key_b: 'value_b_encrypted' }}
* -> { key_a: 'value_a', key_b: 'value_b' }
*/
async decrypt(obj) {
const ret = {};
for (const [k, v] of Object.entries(obj)) {
if (k !== '_encrypted') {
ret[k] = v;
}
}
for (const [k, v] of Object.entries(await this._decryptObj(obj._encrypted))) {
ret[k] = v;
}
return ret;
}
/** decryptの同期版 */
decryptSync(obj) {
return deasync(
(obj, cb) => this.decrypt(obj).then(i => cb(null, i)).catch(err => cb(err)),
)(obj);
}
}
module.exports = KmsEncryptObj;