diff --git a/src/main/java/com/amazonaws/encryptionsdk/CryptoAlgorithm.java b/src/main/java/com/amazonaws/encryptionsdk/CryptoAlgorithm.java
index 6d5ea166a..5fbd4a3c6 100644
--- a/src/main/java/com/amazonaws/encryptionsdk/CryptoAlgorithm.java
+++ b/src/main/java/com/amazonaws/encryptionsdk/CryptoAlgorithm.java
@@ -16,7 +16,7 @@
import java.nio.ByteBuffer;
import java.nio.ByteOrder;
import java.security.InvalidKeyException;
-import java.security.Security;
+import java.security.NoSuchAlgorithmException;
import java.util.EnumSet;
import java.util.HashMap;
import java.util.Map;
@@ -25,11 +25,7 @@
import javax.crypto.spec.SecretKeySpec;
import com.amazonaws.encryptionsdk.internal.BouncyCastleConfiguration;
-import org.bouncycastle.crypto.Digest;
-import org.bouncycastle.crypto.digests.SHA256Digest;
-import org.bouncycastle.crypto.digests.SHA384Digest;
-import org.bouncycastle.crypto.generators.HKDFBytesGenerator;
-import org.bouncycastle.crypto.params.HKDFParameters;
+import com.amazonaws.encryptionsdk.internal.HmacKeyDerivationFunction;
import com.amazonaws.encryptionsdk.internal.Constants;
import com.amazonaws.encryptionsdk.model.CiphertextHeaders;
@@ -267,7 +263,7 @@ public SecretKey getEncryptionKeyFromDataKey(final SecretKey dataKey, final Ciph
+ dataKey.getAlgorithm());
}
- final Digest dgst;
+ final String macAlgorithm;
switch (this) {
case ALG_AES_128_GCM_IV12_TAG16_NO_KDF:
@@ -278,11 +274,11 @@ public SecretKey getEncryptionKeyFromDataKey(final SecretKey dataKey, final Ciph
case ALG_AES_192_GCM_IV12_TAG16_HKDF_SHA256:
case ALG_AES_256_GCM_IV12_TAG16_HKDF_SHA256:
case ALG_AES_128_GCM_IV12_TAG16_HKDF_SHA256_ECDSA_P256:
- dgst = new SHA256Digest();
+ macAlgorithm = "HmacSHA256";
break;
case ALG_AES_192_GCM_IV12_TAG16_HKDF_SHA384_ECDSA_P384:
case ALG_AES_256_GCM_IV12_TAG16_HKDF_SHA384_ECDSA_P384:
- dgst = new SHA384Digest();
+ macAlgorithm = "HmacSHA384";
break;
default:
throw new UnsupportedOperationException("Support for " + this + " not yet built.");
@@ -304,10 +300,14 @@ public SecretKey getEncryptionKeyFromDataKey(final SecretKey dataKey, final Ciph
+ rawDataKey.length);
}
- final byte[] rawEncKey = new byte[getKeyLength()];
- final HKDFBytesGenerator hkdf = new HKDFBytesGenerator(dgst);
- hkdf.init(new HKDFParameters(rawDataKey, null, info.array()));
- hkdf.generateBytes(rawEncKey, 0, getKeyLength());
- return new SecretKeySpec(rawEncKey, getKeyAlgo());
+ final HmacKeyDerivationFunction hkdf;
+ try {
+ hkdf = HmacKeyDerivationFunction.getInstance(macAlgorithm);
+ } catch (NoSuchAlgorithmException e) {
+ throw new IllegalStateException(e);
+ }
+
+ hkdf.init(rawDataKey);
+ return new SecretKeySpec(hkdf.deriveKey(info.array(), getKeyLength()), getKeyAlgo());
}
}
diff --git a/src/main/java/com/amazonaws/encryptionsdk/internal/HmacKeyDerivationFunction.java b/src/main/java/com/amazonaws/encryptionsdk/internal/HmacKeyDerivationFunction.java
new file mode 100644
index 000000000..ede4d8c46
--- /dev/null
+++ b/src/main/java/com/amazonaws/encryptionsdk/internal/HmacKeyDerivationFunction.java
@@ -0,0 +1,171 @@
+/*
+ * Copyright 2019 Amazon.com, Inc. or its affiliates. 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.
+ * A copy of the License is located at
+ *
+ * http://aws.amazon.com/apache2.0
+ *
+ * or in the "license" file accompanying this file. This file 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.
+ */
+package com.amazonaws.encryptionsdk.internal;
+
+import java.security.GeneralSecurityException;
+import java.security.InvalidKeyException;
+import java.security.NoSuchAlgorithmException;
+import java.security.Provider;
+import java.util.Arrays;
+
+import javax.crypto.Mac;
+import javax.crypto.SecretKey;
+import javax.crypto.spec.SecretKeySpec;
+
+import static org.apache.commons.lang3.Validate.isTrue;
+
+/**
+ * HMAC-based Key Derivation Function.
+ * Adapted from Hkdf.java in aws-dynamodb-encryption-java
+ *
+ * @see RFC 5869
+ */
+public final class HmacKeyDerivationFunction {
+ private static final byte[] EMPTY_ARRAY = new byte[0];
+ private final String algorithm;
+ private final Provider provider;
+ private SecretKey prk = null;
+
+ /**
+ * Returns an HmacKeyDerivationFunction
object using the specified algorithm.
+ *
+ * @param algorithm the standard name of the requested MAC algorithm. See the Mac
+ * section in the Java Cryptography Architecture Standard Algorithm Name
+ * Documentation for information about standard algorithm
+ * names.
+ * @return the new Hkdf
object
+ * @throws NoSuchAlgorithmException if no Provider supports a MacSpi implementation for the
+ * specified algorithm.
+ */
+ public static HmacKeyDerivationFunction getInstance(final String algorithm)
+ throws NoSuchAlgorithmException {
+ // Constructed specifically to sanity-test arguments.
+ Mac mac = Mac.getInstance(algorithm);
+ return new HmacKeyDerivationFunction(algorithm, mac.getProvider());
+ }
+
+ /**
+ * Initializes this Hkdf with input keying material. A default salt of
+ * HashLen zeros will be used (where HashLen is the length of the return
+ * value of the supplied algorithm).
+ *
+ * @param ikm the Input Keying Material
+ */
+ public void init(final byte[] ikm) {
+ init(ikm, null);
+ }
+
+ /**
+ * Initializes this Hkdf with input keying material and a salt. If
+ * salt
is null
or of length 0, then a default salt of
+ * HashLen zeros will be used (where HashLen is the length of the return
+ * value of the supplied algorithm).
+ *
+ * @param salt the salt used for key extraction (optional)
+ * @param ikm the Input Keying Material
+ */
+ public void init(final byte[] ikm, final byte[] salt) {
+ byte[] realSalt = (salt == null) ? EMPTY_ARRAY : salt.clone();
+ byte[] rawKeyMaterial = EMPTY_ARRAY;
+ try {
+ Mac extractionMac = Mac.getInstance(algorithm, provider);
+ if (realSalt.length == 0) {
+ realSalt = new byte[extractionMac.getMacLength()];
+ Arrays.fill(realSalt, (byte) 0);
+ }
+ extractionMac.init(new SecretKeySpec(realSalt, algorithm));
+ rawKeyMaterial = extractionMac.doFinal(ikm);
+ this.prk = new SecretKeySpec(rawKeyMaterial, algorithm);
+ } catch (GeneralSecurityException e) {
+ // We've already checked all of the parameters so no exceptions
+ // should be possible here.
+ throw new RuntimeException("Unexpected exception", e);
+ } finally {
+ Arrays.fill(rawKeyMaterial, (byte) 0); // Zeroize temporary array
+ }
+ }
+
+ private HmacKeyDerivationFunction(final String algorithm, final Provider provider) {
+ isTrue(algorithm.startsWith("Hmac"), "Invalid algorithm " + algorithm
+ + ". Hkdf may only be used with Hmac algorithms.");
+ this.algorithm = algorithm;
+ this.provider = provider;
+ }
+
+ /**
+ * Returns a pseudorandom key of length
bytes.
+ *
+ * @param info optional context and application specific information (can be
+ * a zero-length array).
+ * @param length the length of the output key in bytes
+ * @return a pseudorandom key of length
bytes.
+ * @throws IllegalStateException if this object has not been initialized
+ */
+ public byte[] deriveKey(final byte[] info, final int length) throws IllegalStateException {
+ isTrue(length >= 0, "Length must be a non-negative value.");
+ assertInitialized();
+ final byte[] result = new byte[length];
+ Mac mac = createMac();
+
+ isTrue(length <= 255 * mac.getMacLength(),
+ "Requested keys may not be longer than 255 times the underlying HMAC length.");
+
+ byte[] t = EMPTY_ARRAY;
+ try {
+ int loc = 0;
+ byte i = 1;
+ while (loc < length) {
+ mac.update(t);
+ mac.update(info);
+ mac.update(i);
+ t = mac.doFinal();
+
+ for (int x = 0; x < t.length && loc < length; x++, loc++) {
+ result[loc] = t[x];
+ }
+
+ i++;
+ }
+ } finally {
+ Arrays.fill(t, (byte) 0); // Zeroize temporary array
+ }
+ return result;
+ }
+
+ private Mac createMac() {
+ try {
+ Mac mac = Mac.getInstance(algorithm, provider);
+ mac.init(prk);
+ return mac;
+ } catch (NoSuchAlgorithmException | InvalidKeyException ex) {
+ // We've already validated that this algorithm/key is correct.
+ throw new RuntimeException(ex);
+ }
+ }
+
+ /**
+ * Throws an IllegalStateException
if this object has not been
+ * initialized.
+ *
+ * @throws IllegalStateException if this object has not been initialized
+ */
+ private void assertInitialized() throws IllegalStateException {
+ if (prk == null) {
+ throw new IllegalStateException("Hkdf has not been initialized");
+ }
+ }
+}
\ No newline at end of file
diff --git a/src/test/java/com/amazonaws/encryptionsdk/internal/HmacKeyDerivationFunctionTest.java b/src/test/java/com/amazonaws/encryptionsdk/internal/HmacKeyDerivationFunctionTest.java
new file mode 100644
index 000000000..5481cd442
--- /dev/null
+++ b/src/test/java/com/amazonaws/encryptionsdk/internal/HmacKeyDerivationFunctionTest.java
@@ -0,0 +1,200 @@
+/*
+ * Copyright 2019 Amazon.com, Inc. or its affiliates. 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. A copy of the License is located at
+ *
+ * http://aws.amazon.com/apache2.0
+ *
+ * or in the "license" file accompanying this file. This file 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.
+ */
+package com.amazonaws.encryptionsdk.internal;
+
+import com.amazonaws.util.StringUtils;
+import org.junit.Test;
+
+import static org.junit.Assert.assertArrayEquals;
+
+public class HmacKeyDerivationFunctionTest {
+ private static final testCase[] testCases = new testCase[]{
+ new testCase(
+ "HmacSHA256",
+ fromCHex("\\x0b\\x0b\\x0b\\x0b\\x0b\\x0b\\x0b\\x0b\\x0b\\x0b\\x0b\\x0b\\x0b\\x0b"
+ + "\\x0b\\x0b\\x0b\\x0b\\x0b\\x0b\\x0b\\x0b"),
+ fromCHex("\\x00\\x01\\x02\\x03\\x04\\x05\\x06\\x07\\x08\\x09\\x0a\\x0b\\x0c"),
+ fromCHex("\\xf0\\xf1\\xf2\\xf3\\xf4\\xf5\\xf6\\xf7\\xf8\\xf9"),
+ fromHex("3CB25F25FAACD57A90434F64D0362F2A2D2D0A90CF1A5A4C5DB02D56ECC4C5BF34007208D5B887185865")),
+ new testCase(
+ "HmacSHA256",
+ fromCHex("\\x00\\x01\\x02\\x03\\x04\\x05\\x06\\x07\\x08\\x09\\x0a\\x0b\\x0c\\x0d"
+ + "\\x0e\\x0f\\x10\\x11\\x12\\x13\\x14\\x15\\x16\\x17\\x18\\x19\\x1a\\x1b"
+ + "\\x1c\\x1d\\x1e\\x1f\\x20\\x21\\x22\\x23\\x24\\x25\\x26\\x27\\x28\\x29"
+ + "\\x2a\\x2b\\x2c\\x2d\\x2e\\x2f\\x30\\x31\\x32\\x33\\x34\\x35\\x36\\x37"
+ + "\\x38\\x39\\x3a\\x3b\\x3c\\x3d\\x3e\\x3f\\x40\\x41\\x42\\x43\\x44\\x45"
+ + "\\x46\\x47\\x48\\x49\\x4a\\x4b\\x4c\\x4d\\x4e\\x4f"),
+ fromCHex("\\x60\\x61\\x62\\x63\\x64\\x65\\x66\\x67\\x68\\x69\\x6a\\x6b\\x6c\\x6d"
+ + "\\x6e\\x6f\\x70\\x71\\x72\\x73\\x74\\x75\\x76\\x77\\x78\\x79\\x7a\\x7b"
+ + "\\x7c\\x7d\\x7e\\x7f\\x80\\x81\\x82\\x83\\x84\\x85\\x86\\x87\\x88\\x89"
+ + "\\x8a\\x8b\\x8c\\x8d\\x8e\\x8f\\x90\\x91\\x92\\x93\\x94\\x95\\x96\\x97"
+ + "\\x98\\x99\\x9a\\x9b\\x9c\\x9d\\x9e\\x9f\\xa0\\xa1\\xa2\\xa3\\xa4\\xa5"
+ + "\\xa6\\xa7\\xa8\\xa9\\xaa\\xab\\xac\\xad\\xae\\xaf"),
+ fromCHex("\\xb0\\xb1\\xb2\\xb3\\xb4\\xb5\\xb6\\xb7\\xb8\\xb9\\xba\\xbb\\xbc\\xbd"
+ + "\\xbe\\xbf\\xc0\\xc1\\xc2\\xc3\\xc4\\xc5\\xc6\\xc7\\xc8\\xc9\\xca\\xcb"
+ + "\\xcc\\xcd\\xce\\xcf\\xd0\\xd1\\xd2\\xd3\\xd4\\xd5\\xd6\\xd7\\xd8\\xd9"
+ + "\\xda\\xdb\\xdc\\xdd\\xde\\xdf\\xe0\\xe1\\xe2\\xe3\\xe4\\xe5\\xe6\\xe7"
+ + "\\xe8\\xe9\\xea\\xeb\\xec\\xed\\xee\\xef\\xf0\\xf1\\xf2\\xf3\\xf4\\xf5"
+ + "\\xf6\\xf7\\xf8\\xf9\\xfa\\xfb\\xfc\\xfd\\xfe\\xff"),
+ fromHex("B11E398DC80327A1C8E7F78C596A4934"
+ + "4F012EDA2D4EFAD8A050CC4C19AFA97C"
+ + "59045A99CAC7827271CB41C65E590E09"
+ + "DA3275600C2F09B8367793A9ACA3DB71"
+ + "CC30C58179EC3E87C14C01D5C1F3434F" + "1D87")),
+ new testCase(
+ "HmacSHA256",
+ fromCHex("\\x0b\\x0b\\x0b\\x0b\\x0b\\x0b\\x0b\\x0b\\x0b\\x0b\\x0b\\x0b\\x0b\\x0b"
+ + "\\x0b\\x0b\\x0b\\x0b\\x0b\\x0b\\x0b\\x0b"),
+ new byte[0], new byte[0],
+ fromHex("8DA4E775A563C18F715F802A063C5A31"
+ + "B8A11F5C5EE1879EC3454E5F3C738D2D"
+ + "9D201395FAA4B61A96C8")),
+ new testCase(
+ "HmacSHA1",
+ fromCHex("\\x0b\\x0b\\x0b\\x0b\\x0b\\x0b\\x0b\\x0b\\x0b\\x0b\\x0b"),
+ fromCHex("\\x00\\x01\\x02\\x03\\x04\\x05\\x06\\x07\\x08\\x09\\x0a\\x0b\\x0c"),
+ fromCHex("\\xf0\\xf1\\xf2\\xf3\\xf4\\xf5\\xf6\\xf7\\xf8\\xf9"),
+ fromHex("085A01EA1B10F36933068B56EFA5AD81"
+ + "A4F14B822F5B091568A9CDD4F155FDA2"
+ + "C22E422478D305F3F896")),
+ new testCase(
+ "HmacSHA1",
+ fromCHex("\\x00\\x01\\x02\\x03\\x04\\x05\\x06\\x07\\x08\\x09\\x0a\\x0b\\x0c\\x0d"
+ + "\\x0e\\x0f\\x10\\x11\\x12\\x13\\x14\\x15\\x16\\x17\\x18\\x19\\x1a\\x1b"
+ + "\\x1c\\x1d\\x1e\\x1f\\x20\\x21\\x22\\x23\\x24\\x25\\x26\\x27\\x28\\x29"
+ + "\\x2a\\x2b\\x2c\\x2d\\x2e\\x2f\\x30\\x31\\x32\\x33\\x34\\x35\\x36\\x37"
+ + "\\x38\\x39\\x3a\\x3b\\x3c\\x3d\\x3e\\x3f\\x40\\x41\\x42\\x43\\x44\\x45"
+ + "\\x46\\x47\\x48\\x49\\x4a\\x4b\\x4c\\x4d\\x4e\\x4f"),
+ fromCHex("\\x60\\x61\\x62\\x63\\x64\\x65\\x66\\x67\\x68\\x69\\x6A\\x6B\\x6C\\x6D"
+ + "\\x6E\\x6F\\x70\\x71\\x72\\x73\\x74\\x75\\x76\\x77\\x78\\x79\\x7A\\x7B"
+ + "\\x7C\\x7D\\x7E\\x7F\\x80\\x81\\x82\\x83\\x84\\x85\\x86\\x87\\x88\\x89"
+ + "\\x8A\\x8B\\x8C\\x8D\\x8E\\x8F\\x90\\x91\\x92\\x93\\x94\\x95\\x96\\x97"
+ + "\\x98\\x99\\x9A\\x9B\\x9C\\x9D\\x9E\\x9F\\xA0\\xA1\\xA2\\xA3\\xA4\\xA5"
+ + "\\xA6\\xA7\\xA8\\xA9\\xAA\\xAB\\xAC\\xAD\\xAE\\xAF"),
+ fromCHex("\\xB0\\xB1\\xB2\\xB3\\xB4\\xB5\\xB6\\xB7\\xB8\\xB9\\xBA\\xBB\\xBC\\xBD"
+ + "\\xBE\\xBF\\xC0\\xC1\\xC2\\xC3\\xC4\\xC5\\xC6\\xC7\\xC8\\xC9\\xCA\\xCB"
+ + "\\xCC\\xCD\\xCE\\xCF\\xD0\\xD1\\xD2\\xD3\\xD4\\xD5\\xD6\\xD7\\xD8\\xD9"
+ + "\\xDA\\xDB\\xDC\\xDD\\xDE\\xDF\\xE0\\xE1\\xE2\\xE3\\xE4\\xE5\\xE6\\xE7"
+ + "\\xE8\\xE9\\xEA\\xEB\\xEC\\xED\\xEE\\xEF\\xF0\\xF1\\xF2\\xF3\\xF4\\xF5"
+ + "\\xF6\\xF7\\xF8\\xF9\\xFA\\xFB\\xFC\\xFD\\xFE\\xFF"),
+ fromHex("0BD770A74D1160F7C9F12CD5912A06EB"
+ + "FF6ADCAE899D92191FE4305673BA2FFE"
+ + "8FA3F1A4E5AD79F3F334B3B202B2173C"
+ + "486EA37CE3D397ED034C7F9DFEB15C5E"
+ + "927336D0441F4C4300E2CFF0D0900B52D3B4")),
+ new testCase(
+ "HmacSHA1",
+ fromCHex("\\x0b\\x0b\\x0b\\x0b\\x0b\\x0b\\x0b\\x0b\\x0b\\x0b\\x0b\\x0b\\x0b\\x0b"
+ + "\\x0b\\x0b\\x0b\\x0b\\x0b\\x0b\\x0b\\x0b"),
+ new byte[0], new byte[0],
+ fromHex("0AC1AF7002B3D761D1E55298DA9D0506"
+ + "B9AE52057220A306E07B6B87E8DF21D0")),
+ new testCase(
+ "HmacSHA1",
+ fromCHex("\\x0c\\x0c\\x0c\\x0c\\x0c\\x0c\\x0c\\x0c\\x0c\\x0c\\x0c\\x0c\\x0c\\x0c"
+ + "\\x0c\\x0c\\x0c\\x0c\\x0c\\x0c\\x0c\\x0c"),
+ null, new byte[0],
+ fromHex("2C91117204D745F3500D636A62F64F0A"
+ + "B3BAE548AA53D423B0D1F27EBBA6F5E5"
+ + "673A081D70CCE7ACFC48"))};
+
+ @Test
+ public void rfc5869Tests() throws Exception {
+ for (int x = 0; x < testCases.length; x++) {
+ testCase trial = testCases[x];
+ System.out.println("Test case A." + (x + 1));
+ HmacKeyDerivationFunction kdf = HmacKeyDerivationFunction.getInstance(trial.algo);
+ kdf.init(trial.ikm, trial.salt);
+ byte[] result = kdf.deriveKey(trial.info, trial.expected.length);
+ assertArrayEquals("Trial A." + x, trial.expected, result);
+ }
+ }
+
+ @Test
+ public void nullTests() throws Exception {
+ testCase trial = testCases[0];
+ HmacKeyDerivationFunction kdf = HmacKeyDerivationFunction.getInstance(trial.algo);
+ kdf.init(trial.ikm, trial.salt);
+ // Just ensuring no exceptions are thrown
+ kdf.deriveKey(null, 16);
+ }
+
+ @Test(expected = IllegalArgumentException.class)
+ public void invalidLength() throws Exception {
+ testCase trial = testCases[0];
+ HmacKeyDerivationFunction kdf = HmacKeyDerivationFunction.getInstance(trial.algo);
+ kdf.init(trial.ikm, trial.salt);
+ kdf.deriveKey(trial.info, -1);
+ }
+
+ @Test
+ public void defaultSalt() throws Exception {
+ // Tests all the different ways to get the default salt
+
+ testCase trial = testCases[0];
+ HmacKeyDerivationFunction kdf1 = HmacKeyDerivationFunction.getInstance(trial.algo);
+ kdf1.init(trial.ikm, null);
+ HmacKeyDerivationFunction kdf2 = HmacKeyDerivationFunction.getInstance(trial.algo);
+ kdf2.init(trial.ikm, new byte[0]);
+ HmacKeyDerivationFunction kdf3 = HmacKeyDerivationFunction.getInstance(trial.algo);
+ kdf3.init(trial.ikm);
+ HmacKeyDerivationFunction kdf4 = HmacKeyDerivationFunction.getInstance(trial.algo);
+ kdf4.init(trial.ikm, new byte[32]);
+
+ byte[] testBytes = "Test".getBytes(StringUtils.UTF8);
+ byte[] key1 = kdf1.deriveKey(testBytes, 16);
+ byte[] key2 = kdf2.deriveKey(testBytes, 16);
+ byte[] key3 = kdf3.deriveKey(testBytes, 16);
+ byte[] key4 = kdf4.deriveKey(testBytes, 16);
+
+ assertArrayEquals(key1, key2);
+ assertArrayEquals(key1, key3);
+ assertArrayEquals(key1, key4);
+ }
+
+ private static byte[] fromHex(String data) {
+ byte[] result = new byte[data.length() / 2];
+ for (int x = 0; x < result.length; x++) {
+ result[x] = (byte) Integer.parseInt(
+ data.substring(2 * x, 2 * x + 2), 16);
+ }
+ return result;
+ }
+
+ private static byte[] fromCHex(String data) {
+ byte[] result = new byte[data.length() / 4];
+ for (int x = 0; x < result.length; x++) {
+ result[x] = (byte) Integer.parseInt(
+ data.substring(4 * x + 2, 4 * x + 4), 16);
+ }
+ return result;
+ }
+
+ private static class testCase {
+ public final String algo;
+ public final byte[] ikm;
+ public final byte[] salt;
+ public final byte[] info;
+ public final byte[] expected;
+
+ testCase(String algo, byte[] ikm, byte[] salt, byte[] info,
+ byte[] expected) {
+ super();
+ this.algo = algo;
+ this.ikm = ikm;
+ this.salt = salt;
+ this.info = info;
+ this.expected = expected;
+ }
+ }
+}