forked from UnitedID/YubiHSM-java-api
/
YubiHSM.java
483 lines (439 loc) · 23.1 KB
/
YubiHSM.java
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
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
256
257
258
259
260
261
262
263
264
265
266
267
268
269
270
271
272
273
274
275
276
277
278
279
280
281
282
283
284
285
286
287
288
289
290
291
292
293
294
295
296
297
298
299
300
301
302
303
304
305
306
307
308
309
310
311
312
313
314
315
316
317
318
319
320
321
322
323
324
325
326
327
328
329
330
331
332
333
334
335
336
337
338
339
340
341
342
343
344
345
346
347
348
349
350
351
352
353
354
355
356
357
358
359
360
361
362
363
364
365
366
367
368
369
370
371
372
373
374
375
376
377
378
379
380
381
382
383
384
385
386
387
388
389
390
391
392
393
394
395
396
397
398
399
400
401
402
403
404
405
406
407
408
409
410
411
412
413
414
415
416
417
418
419
420
421
422
423
424
425
426
427
428
429
430
431
432
433
434
435
436
437
438
439
440
441
442
443
444
445
446
447
448
449
450
451
452
453
454
455
456
457
458
459
460
461
462
463
464
465
466
467
468
469
470
471
472
473
474
475
476
477
478
479
480
481
482
483
/*
* Copyright (c) 2011 United ID. All rights reserved.
*
* 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.
*
* @author Stefan Wold <stefan.wold@unitedid.org>
*/
package org.unitedid.yhsm;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.unitedid.yhsm.internal.*;
import static org.unitedid.yhsm.utility.Utils.*;
import java.util.Map;
/** <code>YubiHSM</code> the main class to use for YubiHSM commands */
public class YubiHSM {
/** Logger */
private final Logger log = LoggerFactory.getLogger(YubiHSM.class);
/** The device handler */
private DeviceHandler deviceHandler;
/** The hash length, default is 20 */
public static int minHashLength = 20;
/**
* Constructor
*
* @param device the YubiHSM device name ie /dev/ttyACM0
* @param timeout the command read timeout
* @throws YubiHSMErrorException if the YubiHSM reset command fail
*/
public YubiHSM(String device, float timeout) throws YubiHSMErrorException {
deviceHandler = DeviceHandlerFactory.get(device, timeout);
CommandHandler.reset(deviceHandler);
}
/**
* Test the YubiHSM by sending a string that the YubiHSM will echo back.
*
* @param str the string that the YubiHSM should return
* @return the the same string sent to the YubiHSM
* @throws YubiHSMErrorException if the YubiHSM echo command fail
*/
public String echo(String str) throws YubiHSMErrorException {
return EchoCmd.execute(deviceHandler, str);
}
/**
* Get the firmware version and unique ID from the YubiHSM.
*
* @return a map with version, protocol and unique ID
* @throws YubiHSMErrorException if the YubiHSM info command fail
*/
public Map<String, String> info() throws YubiHSMErrorException {
return SystemInfoCmd.execute(deviceHandler);
}
/**
* Get the firmware verseion and unique ID from the YubiHSM (string representation).
*
* @return a string with version, protocol and unique ID
* @throws YubiHSMErrorException if the YubiHSM info command fail
*/
public String infoToString() throws YubiHSMErrorException {
Map<String, String> info = SystemInfoCmd.execute(deviceHandler);
return String.format("Version %s.%s.%s Protocol=%s SysId: %s", info.get("major"), info.get("minor"),
info.get("build"), info.get("protocol"),
info.get("sysid"));
}
/**
* Generate AEAD block from the data for a specific key handle and nonce.
*
* @param nonce the nonce
* @param keyHandle the key to use
* @param data is either a string or a YubiHSM YubiKey secret
* @return a hash map with the AEAD and nonce
* @throws YubiHSMCommandFailedException if the YubiHSM fail to execute the command
* @throws YubiHSMErrorException if validation fail for some values returned by the YubiHSM
* @throws YubiHSMInputException if an argument does not validate
*/
public Map<String, String> generateAEAD(String nonce, int keyHandle, String data) throws YubiHSMCommandFailedException, YubiHSMErrorException, YubiHSMInputException {
return AEADCmd.generateAEAD(deviceHandler, nonce, keyHandle, data);
}
/**
* Generate a random AEAD block using the YubiHSM internal TRNG.
* To generate a secret for a YubiKey use public_id as nonce.
*
* @param nonce the nonce or public_id
* @param keyHandle the key to use
* @param length the resulting byte length of the AEAD
* @return a hash map with the AEAD and nonce
* @throws YubiHSMCommandFailedException if the YubiHSM fail to execute the command
* @throws YubiHSMErrorException if validation fail for some values returned by the YubiHSM
* @throws YubiHSMInputException if an argument does not validate
*/
public Map<String, String> generateRandomAEAD(String nonce, int keyHandle, int length) throws YubiHSMCommandFailedException, YubiHSMErrorException, YubiHSMInputException {
return AEADCmd.generateRandomAEAD(deviceHandler, nonce, keyHandle, length);
}
/**
* Generate AEAD block of data buffer for a specific key.
* After a key has been loaded into the internal data buffer, this command can be
* used a number of times to get AEADs of the data buffer for different key handles.
* For example, to encrypt a YubiKey secrets to one or more Yubico KSM's that
* all have a YubiHSM attached to them.
*
* @param nonce the nonce
* @param keyHandle the key to use
* @return a hash map with the AEAD and nonce
* @throws YubiHSMCommandFailedException if the YubiHSM fail to execute the command
* @throws YubiHSMErrorException if validation fail for some values returned by the YubiHSM
* @throws YubiHSMInputException if an argument does not validate
*/
public Map<String, String> generateBufferAEAD(String nonce, int keyHandle) throws YubiHSMCommandFailedException, YubiHSMErrorException, YubiHSMInputException {
return AEADCmd.generateBufferAEAD(deviceHandler, nonce, keyHandle);
}
/**
* Generate AEAD block which can be used for OATH-HOTP OTP validation, see <code>validateOathHOTP</code>.
*
* @param nonce the nonce
* @param keyHandle the key handle with permission to generateBufferAEAD
* @param tokenSeed the OATH HOTP token seed
* @return returns an AEAD
* @throws YubiHSMInputException thrown if an argument fail to validate
* @throws YubiHSMErrorException thrown if an error have occurred
* @throws YubiHSMCommandFailedException thrown if the YubiHSM fail to execute a command
*/
public String generateOathHotpAEAD(String nonce, int keyHandle, String tokenSeed) throws YubiHSMInputException, YubiHSMErrorException, YubiHSMCommandFailedException {
if (tokenSeed.length() != 40)
throw new YubiHSMInputException("Seed is not of required length, got " + tokenSeed.length() + " but expected 40");
byte[] seed = hexToByteArray(tokenSeed);
byte[] flag = leIntToBA(0x10000); // Generate HMAC SHA1 Flag
loadBufferData(concatAllArrays(seed, flag), 0);
return generateBufferAEAD(nonce, keyHandle).get("aead");
}
/**
* Validate an AEAD using the YubiHSM, matching it against some known plain text.
* Matching is done inside the YubiHSM so the decrypted AEAD is never exposed.
*
* @param nonce the nonce or public_id
* @param keyHandle the key to use
* @param aead the AEAD
* @param plaintext the plain text string
* @return returns true if validation was a success, false if the validation failed
* @throws YubiHSMCommandFailedException if the YubiHSM fail to execute the command
* @throws YubiHSMErrorException if validation fail for some values returned by the YubiHSM
* @throws YubiHSMInputException if an argument does not validate
*/
public boolean validateAEAD(String nonce, int keyHandle, String aead, String plaintext) throws YubiHSMInputException, YubiHSMCommandFailedException, YubiHSMErrorException {
return AEADCmd.validateAEAD(deviceHandler, nonce, keyHandle, aead, plaintext);
}
/**
* Load data into the YubiHSMs internal buffer.
*
* @param data the data to load into the internal buffer
* @param offset the offset where to load the data, if set to 0 the buffer will reset before loading the data
* @return the length of the loaded buffer
* @throws YubiHSMErrorException if validation fail for some values returned by the YubiHSM
*/
public int loadBufferData(String data, int offset) throws YubiHSMErrorException {
return BufferCmd.loadData(deviceHandler, data, offset);
}
/**
* Load data into the YubiHSMs internal buffer.
*
* @param data the data to load into the internal buffer
* @param offset the offset where to load the data, if set to 0 the buffer will reset before loading the data
* @return the length of the loaded buffer
* @throws YubiHSMErrorException if validation fail for some values returned by the YubiHSM
*/
public int loadBufferData(byte[] data, int offset) throws YubiHSMErrorException {
return BufferCmd.loadData(deviceHandler, data, offset);
}
/**
* Load random data into the YubiHSMs internal buffer.
*
* @param length the length of the generated data
* @param offset the offset where to load the data, if set to 0 the buffer will reset before loading the data
* @return the length of the loaded buffer
* @throws YubiHSMErrorException if validation fail for some values returned by the YubiHSM
*/
public int loadRandomBufferData(int length, int offset) throws YubiHSMErrorException {
return BufferCmd.loadRandomData(deviceHandler, length, offset);
}
/**
* Tell the YubiHSM to exit to configuration mode (requires 'debug' mode enabled).
*
* @throws YubiHSMErrorException if the YubiHSM exit monitor command fail
*/
public void exitMonitorDebugMode() throws YubiHSMErrorException {
MonitorExitCmd.execute(deviceHandler);
}
/**
* Load the content of an AEAD into the phantom key handle 0xffffffff.
*
* @param nonce the nonce
* @param keyHandle the key handle with permission to use YSM_TEMP_KEY_LOAD
* @param aead the AEAD to load into the phantom key handle
* @return returns true if the AEAD was successfully loaded
* @throws YubiHSMCommandFailedException command fail exception
* @throws YubiHSMErrorException error exception
* @throws YubiHSMInputException argument exceptions
*/
public boolean loadTemporaryKey(String nonce, int keyHandle, String aead) throws YubiHSMCommandFailedException, YubiHSMErrorException, YubiHSMInputException {
return LoadTemporaryKeyCmd.execute(deviceHandler, nonce, keyHandle, aead);
}
/**
* Generate HMAC SHA1 using a key handle in the YubiHSM.
*
* @param data the data used to generate the SHA1
* @param keyHandle the key handle to use in the YubiHSM
* @param last set to false to not get a hash generated for the initial request
* @param toBuffer set to true to get the SHA1 stored into the internal buffer, for use in some other cryptographic operations.
* @return a map containing status and SHA1 hash
* @throws YubiHSMCommandFailedException if the YubiHSM fail to execute the command
* @throws YubiHSMErrorException if validation fail for some values returned by the YubiHSM
* @throws YubiHSMInputException if an argument does not validate
*/
public Map<String, String> generateHMACSHA1(String data, int keyHandle, boolean last, boolean toBuffer) throws YubiHSMInputException, YubiHSMCommandFailedException, YubiHSMErrorException {
return HMACCmd.generateHMACSHA1(deviceHandler, data.getBytes(), keyHandle, (byte) 0, last, toBuffer);
}
/**
* Generate HMAC SHA1 using a key handle in the YubiHSM.
*
* @param data the data used to generate the SHA1
* @param keyHandle the key handle to use in the YubiHSM
* @param last set to false to not get a hash generated for the initial request
* @param toBuffer set to true to get the SHA1 stored into the internal buffer, for use in some other cryptographic operations.
* @return a map containing status and SHA1 hash
* @throws YubiHSMCommandFailedException if the YubiHSM fail to execute the command
* @throws YubiHSMErrorException if validation fail for some values returned by the YubiHSM
* @throws YubiHSMInputException if an argument does not validate
*/
public Map<String, String> generateHMACSHA1(byte[] data, int keyHandle, boolean last, boolean toBuffer) throws YubiHSMInputException, YubiHSMCommandFailedException, YubiHSMErrorException {
return HMACCmd.generateHMACSHA1(deviceHandler, data, keyHandle, (byte) 0, last, toBuffer);
}
/**
* Generate HMAC SHA1 using a key handle in the YubiHSM.
*
* @param data the data used to generate the SHA1
* @param keyHandle the key handle to use in the YubiHSM
* @param flags set custom flags to be used when generating a SHA1, if set to (byte) 0 defaults will be used.
* @param last set to false to not get a hash generated for the initial request
* @param toBuffer set to true to get the SHA1 stored into the internal buffer, for use in some other cryptographic operations.
* @return a map containing status and SHA1 hash
* @throws YubiHSMCommandFailedException if the YubiHSM fail to execute the command
* @throws YubiHSMErrorException if validation fail for some values returned by the YubiHSM
* @throws YubiHSMInputException if an argument does not validate
*/
public Map<String, String> generateHMACSHA1(String data, int keyHandle, byte flags, boolean last, boolean toBuffer) throws YubiHSMInputException, YubiHSMCommandFailedException, YubiHSMErrorException {
return HMACCmd.generateHMACSHA1(deviceHandler, data.getBytes(), keyHandle, flags, last, toBuffer);
}
/**
* Generate HMAC SHA1 using a key handle in the YubiHSM.
*
* @param data the data used to generate the SHA1
* @param keyHandle the key handle to use in the YubiHSM
* @param flags set custom flags to be used when generating a SHA1, if set to (byte) 0 defaults will be used.
* @param last set to false to not get a hash generated for the initial request
* @param toBuffer set to true to get the SHA1 stored into the internal buffer, for use in some other cryptographic operations.
* @return a map containing status and SHA1 hash
* @throws YubiHSMCommandFailedException if the YubiHSM fail to execute the command
* @throws YubiHSMErrorException if validation fail for some values returned by the YubiHSM
* @throws YubiHSMInputException if an argument does not validate
*/
public Map<String, String> generateHMACSHA1(byte[] data, int keyHandle, byte flags, boolean last, boolean toBuffer) throws YubiHSMInputException, YubiHSMCommandFailedException, YubiHSMErrorException {
return HMACCmd.generateHMACSHA1(deviceHandler, data, keyHandle, flags, last, toBuffer);
}
/**
* Add more input to the HMAC SHA1, used after calling {@link #generateHMACSHA1} with last set to false.
*
* @param data the data to add before generating SHA1
* @param keyHandle the key handle to use in the YubiHSM
* @param last set to false to not get a hash generated after this call
* @param toBuffer set to true to get the SHA1 stored into the internal buffer, for use in some other cryptographic operations.
* @return a map containing status and SHA1 hash
* @throws YubiHSMCommandFailedException if the YubiHSM fail to execute the command
* @throws YubiHSMErrorException if validation fail for some values returned by the YubiHSM
* @throws YubiHSMInputException if an argument does not validate
*/
public Map<String, String> generateHMACSHA1Next(String data, int keyHandle, boolean last, boolean toBuffer) throws YubiHSMInputException, YubiHSMCommandFailedException, YubiHSMErrorException {
return HMACCmd.next(deviceHandler, data.getBytes(), keyHandle, last, toBuffer);
}
/**
* Add more input to the HMAC SHA1, used after calling {@link #generateHMACSHA1} with last set to false.
*
* @param data the data to add before generating SHA1
* @param keyHandle the key handle to use in the YubiHSM
* @param last set to false to not get a hash generated after this call
* @param toBuffer set to true to get the SHA1 stored into the internal buffer, for use in some other cryptographic operations.
* @return a map containing status and SHA1 hash
* @throws YubiHSMCommandFailedException if the YubiHSM fail to execute the command
* @throws YubiHSMErrorException if validation fail for some values returned by the YubiHSM
* @throws YubiHSMInputException if an argument does not validate
*/
public Map<String, String> generateHMACSHA1Next(byte[] data, int keyHandle, boolean last, boolean toBuffer) throws YubiHSMInputException, YubiHSMCommandFailedException, YubiHSMErrorException {
return HMACCmd.next(deviceHandler, data, keyHandle, last, toBuffer);
}
/**
* AES ECB encrypt a plaintext string using a specific key handle.
*
* @param keyHandle the key handle to use when encrypting AES ECB
* @param plaintext the plaintext string
* @return a hash string in hex format
* @throws YubiHSMInputException if an argument does not validate
* @throws YubiHSMErrorException if validation fail for some values returned by the YubiHSM
* @throws YubiHSMCommandFailedException if the YubiHSM fail to execute the command
*/
public String encryptAES_ECB(String plaintext, int keyHandle) throws YubiHSMErrorException, YubiHSMInputException, YubiHSMCommandFailedException {
return AESECBCmd.encrypt(deviceHandler, keyHandle, plaintext);
}
/**
* AES ECB decrypt a cipher text using a specific key handle.
*
* @param keyHandle the key handle to use when decrypting AES ECB
* @param cipherText the cipher string
* @return a plaintext string
* @throws YubiHSMInputException if an argument does not validate
* @throws YubiHSMErrorException if validation fail for some values returned by the YubiHSM
* @throws YubiHSMCommandFailedException if the YubiHSM fail to execute the command
*/
public String decryptAES_ECB(String cipherText, int keyHandle) throws YubiHSMErrorException, YubiHSMInputException, YubiHSMCommandFailedException {
return AESECBCmd.decrypt(deviceHandler, keyHandle, cipherText);
}
/**
* AES ECB decrypt a cipher text using a specific key handle, and then compare it with the supplied plaintext.
*
* @param keyHandle the key handle to use when comparing AES ECB cipher with plaintext
* @param cipherText the cipher string
* @param plaintext the plaintext string
* @return true if successful, false if not successful
* @throws YubiHSMInputException if an argument does not validate
* @throws YubiHSMErrorException if validation fail for some values returned by the YubiHSM
* @throws YubiHSMCommandFailedException if the YubiHSM fail to execute the command
*/
public boolean compareAES_ECB(int keyHandle, String cipherText, String plaintext) throws YubiHSMCommandFailedException, YubiHSMErrorException, YubiHSMInputException {
return AESECBCmd.compare(deviceHandler, keyHandle, cipherText, plaintext);
}
/**
* Unlock the YubiHSM key storage using the HSM password.
*
* @param password the password in hex format (see output of automatic password generation during HSM configuration)
* @return true if unlock was successful, otherwise an YubiHSMCommandFailedException is thrown
* @throws YubiHSMCommandFailedException command failed exception
* @throws YubiHSMErrorException error exception
* @throws YubiHSMInputException argument exception
*/
public boolean keyStorageUnlock(String password) throws YubiHSMCommandFailedException, YubiHSMErrorException, YubiHSMInputException {
return KeyStorageUnlockCmd.execute(deviceHandler, password);
}
/**
* Validate OATH-HOTP OTP by a token whose seed is available to the YubiHSM through an AEAD.
*
* @param hsm the current hsm object
* @param keyHandle a keyHandle with the permission YSM_TEMP_KEY_LOAD enabled
* @param nonce the nonce used to generate the AEAD
* @param aead the AEAD based on the token seed
* @param counter the current OTP counter
* @param otp the token OTP
* @param lookAhead the number of iterations to run to find the current users OTP
* @return return next counter value on success, 0 if the OTP couldn't be validated
* @throws YubiHSMInputException argument exceptions
* @throws YubiHSMCommandFailedException command failed exception
* @throws YubiHSMErrorException error exception
*/
public int validateOathHOTP(YubiHSM hsm, int keyHandle, String nonce, String aead, int counter, String otp, int lookAhead) throws YubiHSMCommandFailedException, YubiHSMErrorException, YubiHSMInputException {
return OathHOTPCmd.validateOTP(hsm, keyHandle, nonce, aead, counter, otp, lookAhead);
}
/**
* Get a nonce from the YubiHSM. Increment the nonce by the number supplied as increment.
* To get the current nonce send 0 as increment.
*
* @param increment the increment (short)
* @return returns a Nonce class
* @throws YubiHSMErrorException error exception
* @throws YubiHSMCommandFailedException command failed exception
*/
public Nonce getNonce(short increment) throws YubiHSMErrorException, YubiHSMCommandFailedException {
return NonceGetCmd.execute(deviceHandler, increment);
}
/**
* Tell the YubiHSM to generate a number of random bytes.
*
* @param bytes the number of bytes to generate
* @return returns a byte array of random bytes
* @throws YubiHSMErrorException error exception
* @throws YubiHSMInputException invalid argument exception
*/
public byte[] getRandom(int bytes) throws YubiHSMErrorException, YubiHSMInputException {
return RandomCmd.execute(deviceHandler, bytes);
}
/**
* Provide YubiHSM DRBG_CTR with a new seed.
* The seed is a string of a length 32.
*
* @param seed the seed string with a length of 32
* @return return true on success, otherwise a YubiHSMCommandFailedException is thrown
* @throws YubiHSMInputException argument exception
* @throws YubiHSMErrorException error exception
* @throws YubiHSMCommandFailedException command failed exception
*/
public boolean randomReseed(String seed) throws YubiHSMCommandFailedException, YubiHSMErrorException, YubiHSMInputException {
return RandomReseedCmd.execute(deviceHandler, seed);
}
/**
* Drain all remaining output from the YubiHSM, used for debugging.
*
* @return true if successful, false otherwise.
*/
public boolean drainData() {
return deviceHandler.drain();
}
/**
* Get the raw device, used for debugging.
*
* @return the device handler
*/
public DeviceHandler getRawDevice() {
return deviceHandler;
}
/**
* Get the minimum hash length.
*
* @return the minimum hash length
*/
public int getMinHashLength() {
return minHashLength;
}
/**
* Set the minimum hash length.
* @param value the minimum hash length
*/
public void setMinHashLength(int value) {
minHashLength = value;
}
}