forked from tink-crypto/tink
-
Notifications
You must be signed in to change notification settings - Fork 0
/
Copy pathkeyset_handle.js
232 lines (216 loc) · 7.46 KB
/
keyset_handle.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
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
// Licensed under the Apache License, Version 2.0 (the "License");
// you may not use this file except in compliance with the License.
// You may obtain a copy of the License at
//
// http://www.apache.org/licenses/LICENSE-2.0
//
// Unless required by applicable law or agreed to in writing, software
// distributed under the License is distributed on an "AS IS" BASIS,
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
// See the License for the specific language governing permissions and
// limitations under the License.
//
////////////////////////////////////////////////////////////////////////////////
goog.module('tink.KeysetHandle');
const Aead = goog.require('tink.Aead');
const InvalidArgumentsException = goog.require('tink.exception.InvalidArgumentsException');
const KeyManager = goog.require('tink.KeyManager');
const KeysetReader = goog.require('tink.KeysetReader');
const KeysetWriter = goog.require('tink.KeysetWriter');
const PbKeyMaterialType = goog.require('proto.google.crypto.tink.KeyData.KeyMaterialType');
const PbKeyStatusType = goog.require('proto.google.crypto.tink.KeyStatusType');
const PbKeyTemplate = goog.require('proto.google.crypto.tink.KeyTemplate');
const PbKeyset = goog.require('proto.google.crypto.tink.Keyset');
const PrimitiveSet = goog.require('tink.PrimitiveSet');
const Random = goog.require('tink.subtle.Random');
const Registry = goog.require('tink.Registry');
const SecurityException = goog.require('tink.exception.SecurityException');
const Util = goog.require('tink.Util');
/**
* Keyset handle provide abstracted access to Keysets, to limit the exposure of
* actual protocol buffers that hold sensitive key material.
*
* @final
*/
class KeysetHandle {
/**
* @param {!PbKeyset} keyset
*/
constructor(keyset) {
Util.validateKeyset(keyset);
/** @const @private {!PbKeyset} */
this.keyset_ = keyset;
}
/**
* Creates a KeysetHandle from an encrypted keyset obtained via reader, using
* masterKeyAead to decrypt the keyset.
*
* @param {!KeysetReader} reader
* @param {!Aead} masterKeyAead
*
* @return {!Promise<!KeysetHandle>}
*/
static async read(reader, masterKeyAead) {
// TODO implement
throw new SecurityException('KeysetHandle -- read: Not implemented yet.');
}
/**
* Creates a KeysetHandle from a keyset, obtained via reader, which
* must contain no secret key material.
*
* This can be used to load public keysets or envelope encryption keysets.
* Users that need to load cleartext keysets can use CleartextKeysetHandle.
*
* @param {!KeysetReader} reader
* @return {!KeysetHandle}
*/
static readNoSecret(reader) {
if (reader === null) {
throw new SecurityException('Reader has to be non-null.');
}
const keyset = reader.read();
const keyList = keyset.getKeyList();
for (let key of keyList) {
switch (key.getKeyData().getKeyMaterialType()) {
case PbKeyMaterialType.ASYMMETRIC_PUBLIC: // fall through
case PbKeyMaterialType.REMOTE:
continue;
}
throw new SecurityException('Keyset contains secret key material.');
}
return new KeysetHandle(keyset);
}
/**
* Returns a new KeysetHandle that contains a single new key generated
* according to keyTemplate.
*
* @param {!PbKeyTemplate} keyTemplate
*
* @return {!Promise<!KeysetHandle>}
*/
static async generateNew(keyTemplate) {
// TODO(thaidn): move this to a key manager.
const keyset = await KeysetHandle.generateNewKeyset_(keyTemplate);
return new KeysetHandle(keyset);
}
/**
* Generates a new Keyset that contains a single new key generated
* according to keyTemplate.
*
* @param {!PbKeyTemplate} keyTemplate
* @private
* @return {!Promise<!PbKeyset>}
*/
static async generateNewKeyset_(keyTemplate) {
const key = new PbKeyset.Key()
.setStatus(PbKeyStatusType.ENABLED)
.setOutputPrefixType(keyTemplate.getOutputPrefixType());
const keyId = KeysetHandle.generateNewKeyId_();
key.setKeyId(keyId);
const keyData = await Registry.newKeyData(keyTemplate);
key.setKeyData(keyData);
const keyset = new PbKeyset();
keyset.addKey(key);
keyset.setPrimaryKeyId(keyId);
return keyset;
}
/**
* Generates a new random key ID.
*
* @private
* @return {number} The key ID.
*/
static generateNewKeyId_() {
const bytes = Random.randBytes(4);
let value = 0;
for (let i = 0; i < bytes.length; i++) {
value += (bytes[i] & 0xFF) << (i * 8);
}
// Make sure the key ID is a positive integer smaller than 2^32.
return Math.abs(value) % 2 ** 32;
};
/**
* Returns a primitive that uses key material from this keyset handle. If
* opt_customKeyManager is defined then the provided key manager is used to
* instantiate primitives. Otherwise key manager from Registry is used.
*
* @template P
*
* @param {!Object} primitiveType
* @param {?KeyManager.KeyManager<P>=} opt_customKeyManager
*
* @return {!Promise<!P>}
*/
async getPrimitive(primitiveType, opt_customKeyManager) {
if (!primitiveType) {
throw new InvalidArgumentsException('primitive type must be non-null');
}
const primitiveSet =
await this.getPrimitiveSet_(primitiveType, opt_customKeyManager);
return Registry.wrap(primitiveSet);
}
/**
* Creates a set of primitives corresponding to the keys with status Enabled
* in the given keysetHandle, assuming all the correspoding key managers are
* present (keys with status different from Enabled are skipped). If provided
* uses customKeyManager instead of registered key managers for keys supported
* by the customKeyManager.
*
* @template P
* @private
*
* @param {!Object} primitiveType
* @param {?KeyManager.KeyManager<P>=} opt_customKeyManager
*
* @return {!Promise.<!PrimitiveSet.PrimitiveSet<P>>}
*/
async getPrimitiveSet_(primitiveType, opt_customKeyManager) {
const primitiveSet = new PrimitiveSet.PrimitiveSet(primitiveType);
const keys = this.keyset_.getKeyList();
const keysLength = keys.length;
for (let i = 0; i < keysLength; i++) {
const key = keys[i];
if (key.getStatus() === PbKeyStatusType.ENABLED) {
const keyData = key.getKeyData();
if (!keyData) {
throw new SecurityException('Key data has to be non null.');
}
let primitive;
if (opt_customKeyManager &&
opt_customKeyManager.getKeyType() === keyData.getTypeUrl()) {
primitive =
await opt_customKeyManager.getPrimitive(primitiveType, keyData);
} else {
primitive = await Registry.getPrimitive(primitiveType, keyData);
}
const entry = primitiveSet.addPrimitive(primitive, key);
if (key.getKeyId() === this.keyset_.getPrimaryKeyId()) {
primitiveSet.setPrimary(entry);
}
}
}
return primitiveSet;
}
/**
* Encrypts the underlying keyset with the provided masterKeyAead wnd writes
* the resulting encryptedKeyset to the given writer which must be non-null.
*
* @param {!KeysetWriter} writer
* @param {!Aead} masterKeyAead
*
*/
async write(writer, masterKeyAead) {
// TODO implement
throw new SecurityException('KeysetHandle -- write: Not implemented yet.');
}
/**
* Returns the keyset held by this KeysetHandle.
*
* @package
* @return {!PbKeyset}
*/
getKeyset() {
return this.keyset_;
}
}
exports = KeysetHandle;