blob: c41dcdaa04c711afd49101556d9124a89017d830 [file] [log] [blame]
/*
* Copyright 2015 The Android Open Source Project
*
* 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.
*/
package android.keystore.cts;
import android.security.keystore.KeyProperties;
import android.security.keystore.KeyProtection;
import junit.framework.TestCase;
import java.security.InvalidKeyException;
import java.security.Provider;
import java.security.Provider.Service;
import java.security.Security;
import java.util.Arrays;
import java.util.Collection;
import java.util.Date;
import java.util.HashSet;
import java.util.Locale;
import java.util.Map;
import java.util.Set;
import java.util.TreeMap;
import javax.crypto.Mac;
import javax.crypto.SecretKey;
import javax.crypto.spec.SecretKeySpec;
/**
* Tests for algorithm-agnostic functionality of MAC implementations backed by Android Keystore.
*/
public class MacTest extends TestCase {
private static final String EXPECTED_PROVIDER_NAME = TestUtils.EXPECTED_CRYPTO_OP_PROVIDER_NAME;
private static final String[] EXPECTED_ALGORITHMS = {
"HmacSHA1",
"HmacSHA224",
"HmacSHA256",
"HmacSHA384",
"HmacSHA512",
};
private static final byte[] KAT_KEY = HexEncoding.decode(
"227b212bebd775493929ef626729a587d3f81b8e18a3ed482d403910e184479b448cfa79b62bd90595efdd"
+ "15f87bd7b2d2dac480c61e969ba90a7b8ceadd3284");
private static final byte[] SHORT_MSG_KAT_MESSAGE = HexEncoding.decode("a16037e3c901c9a1ab");
private static final Map<String, Collection<KatVector>> SHORT_MSG_KAT_MACS =
new TreeMap<>(String.CASE_INSENSITIVE_ORDER);
static {
// From RI
SHORT_MSG_KAT_MACS.put("HmacSHA1", Arrays.asList(
new KatVector(HexEncoding.decode("4818c5466a1d05bc8f6ad4"),
HexEncoding.decode("aa178e4d174dc90d1ac3d22386c32d1af82396e7")),
new KatVector(HexEncoding.decode("8d07f59f63fc493711a2217d8603cbc4d9874c58"),
HexEncoding.decode("002215de36f8836e966ff459af80c4b31fe0ca27")),
new KatVector(HexEncoding.decode("f85f1c94023968729e3b4a7f495bf31c283b710662"),
HexEncoding.decode("f518db3015203606ea15145ad16b3d981db32a77"))));
SHORT_MSG_KAT_MACS.put("HmacSHA224", Arrays.asList(
new KatVector(
HexEncoding.decode(
"ed959815cc03f01e19ce93c1a3318ae87a905b7c27351d571ee858"),
HexEncoding.decode(
"abece4641458461f8b6a46f7daa61fc6119344c5c4bb5e7967da0e3e")),
new KatVector(
HexEncoding.decode(
"9150e1ad6a9d12370a2423a5d95e9bc2dd73b4ee9bc96b03c9cc2fba"),
HexEncoding.decode(
"523aa06730d1abe7da2ba94a966cd20db56c771f1899e2850c31158c")),
new KatVector(
HexEncoding.decode(
"424d5e7375c2040543f76b97451b1c074ee93b81ad24cef23800ebfe529a74ee2b"
),
HexEncoding.decode(
"7627a86d829f45e3295a25813219ed5291f80029b972192d32a845c3"))));
SHORT_MSG_KAT_MACS.put("HmacSHA256", Arrays.asList(
new KatVector(
HexEncoding.decode(
"74a7ec4c79419d76fa5d3bdbedc17e5bebf0ee011c609b9f4c9126091613"),
HexEncoding.decode(
"c17b62519155b0d7f005f465becf9a1610635ae46a2c4d2b255851f201689ba5"
)),
new KatVector(
HexEncoding.decode(
"42b44e6a1600bed10ca6c6dc24df2871790f948e73f9457fa4889c340cf69496"),
HexEncoding.decode(
"e9082a5db98c8086ad306ac23a1da9478eb5733757af6b1148d25fa1459290de")
),
new KatVector(
HexEncoding.decode(
"20bfc407c62022fea95f046f8ade6ee4b232665a9e97f75d3e35f1a9447991651a"
),
HexEncoding.decode(
"dbf10ca8c362aa665562065e76e42beb19444f61ab0828438714c82779b71a0d"
))));
SHORT_MSG_KAT_MACS.put("HmacSHA384", Arrays.asList(
new KatVector(
HexEncoding.decode(
"7d277b2ec95fca68fe6ce0b665b28b48e128762714c66ca2c3405b432f6ab835e3"
),
HexEncoding.decode(
"bf8555816d8fa058e1d0ed4be23abda522adfae629b6a8819dcc2416d00507782a"
+ "c714fdbfc7a340da4e6cf646a619f1")),
new KatVector(
HexEncoding.decode(
"7b30abe948ceab9d94965f274fd2a21c966aa9bdf06476f94a0bcc6c20fd5d2bdc"
+ "e21af7c6fdf6017bce342a701f55c3"),
HexEncoding.decode(
"0fe51798528407119a5884f65ad76409983e978e25ab8f82aa412c08e76c8065d2"
+ "6dfdb1935de49036fb24262a532a29")),
new KatVector(
HexEncoding.decode(
"26f232a40ada35850a14edf8f23c9ca97898ac0fa18640e5a7835230fa68f630b1"
+ "c4579bc059c318bb2c5da609db13f1567fa175a6439e1d729713d1fa"
+ "1039a3db"),
HexEncoding.decode(
"a086c610a382c24bb05e8bdd12cffc01055ec98a8f071239360cf8135205ffee33"
+ "2da2134bed2ec3efde8bf145d1d257"))));
SHORT_MSG_KAT_MACS.put("HmacSHA512", Arrays.asList(
new KatVector(
HexEncoding.decode(
"46fb12ef48d4e8162f0828a66c9f7124de"),
HexEncoding.decode(
"036320b51376f5840b03fdababac53c189d4d6b35f26f562a909f8ecac4a02c244"
+ "bfddc8f4eb89e0d0909fd2d8a46b796175e619cff215a675ce309540"
+ "42b1c9")),
new KatVector(
HexEncoding.decode(
"45b3f16b1a247dd76f72ab2d5019f87b94efeb9a2fc01da3ca347050302dbda9c1"
+ "19cf991aaa30b747c808ec6bc19be7b5ae5e66176e38f222347a1659"
+ "15d007"),
HexEncoding.decode(
"92817ce36858ccad20a903e15952565d241ebaa87e07655754470090f1c6b9252a"
+ "cff9b873f36840fa8fdaaf91c6f9de3b82f46de0b1fdfa584eaf27de"
+ "f52c65")),
new KatVector(
HexEncoding.decode(
"e91630c69c8c294755e27e5ccf01fe09e06de6c4e423c1c4ef0ac9b67f9af3cc6b"
+ "bc6292d18cf6e76738888a948b49f9509b44eb3af6974ca7e61f5208"
+ "b9f7dca3"),
HexEncoding.decode(
"6ff5616e9c38cef3d20076841c65b8747193eb8033ea61e8693715109e0e448966"
+ "3d8abcb2b7cf0911e461202112819fb8650ba02bdce08aa0d24b3873"
+ "30f18f"))));
}
private static final byte[] LONG_MSG_KAT_SEED = SHORT_MSG_KAT_MESSAGE;
private static final int LONG_MSG_KAT_SIZE_BYTES = 3 * 1024 * 1024 + 149;
private static final Map<String, byte[]> LONG_MSG_KAT_MACS =
new TreeMap<String, byte[]>(String.CASE_INSENSITIVE_ORDER);
static {
// From RI
LONG_MSG_KAT_MACS.put("HmacSHA1", HexEncoding.decode(
"2a89d12da79f541512db9c35c0a1e76750e01d48"));
LONG_MSG_KAT_MACS.put("HmacSHA224", HexEncoding.decode(
"5fef55c822f9b931c1b4ad7142e0a74ceaddf03f0a6533155cc06871"));
LONG_MSG_KAT_MACS.put("HmacSHA256", HexEncoding.decode(
"0bc25f22b8993d003a95a88c6cfa1c5a7b067a8aae1064ef897712418569bfe9"));
LONG_MSG_KAT_MACS.put("HmacSHA384", HexEncoding.decode(
"595a616295123966126102c06d69f8bb06c11090490186243420c2c4692877d75752b220c1b0447320"
+ "959e28345523fc"));
LONG_MSG_KAT_MACS.put("HmacSHA512", HexEncoding.decode(
"aa97d594d799164d56e6652578f7884d1198bb2663641ad7903e3c0bda4c136e9f94ca0d16c3504302"
+ "2944224e538e88a5410adb38eaa5169b3125738990e6d0"));
}
private static final long DAY_IN_MILLIS = TestUtils.DAY_IN_MILLIS;
public void testAlgorithmList() {
// Assert that Android Keystore Provider exposes exactly the expected MAC algorithms. We
// don't care whether the algorithms are exposed via aliases, as long as the canonical names
// of algorithms are accepted.
// If the Provider exposes extraneous algorithms, it'll be caught because it'll have to
// expose at least one Service for such an algorithm, and this Service's algorithm will
// not be in the expected set.
Provider provider = Security.getProvider(EXPECTED_PROVIDER_NAME);
Set<Service> services = provider.getServices();
Set<String> actualAlgsLowerCase = new HashSet<String>();
Set<String> expectedAlgsLowerCase = new HashSet<String>(
Arrays.asList(TestUtils.toLowerCase(EXPECTED_ALGORITHMS)));
for (Service service : services) {
if ("Mac".equalsIgnoreCase(service.getType())) {
String algLowerCase = service.getAlgorithm().toLowerCase(Locale.US);
actualAlgsLowerCase.add(algLowerCase);
}
}
TestUtils.assertContentsInAnyOrder(actualAlgsLowerCase,
expectedAlgsLowerCase.toArray(new String[0]));
}
public void testAndroidKeyStoreKeysHandledByAndroidKeyStoreProvider() throws Exception {
Provider provider = Security.getProvider(EXPECTED_PROVIDER_NAME);
assertNotNull(provider);
for (String algorithm : EXPECTED_ALGORITHMS) {
try {
SecretKey key = importDefaultKatKey(algorithm);
// Generate a MAC
Mac mac = Mac.getInstance(algorithm);
mac.init(key);
assertSame(provider, mac.getProvider());
} catch (Throwable e) {
throw new RuntimeException(algorithm + " failed", e);
}
}
}
public void testMacGeneratedForEmptyMessage() throws Exception {
Provider provider = Security.getProvider(EXPECTED_PROVIDER_NAME);
assertNotNull(provider);
for (String algorithm : EXPECTED_ALGORITHMS) {
try {
SecretKey key = importDefaultKatKey(algorithm);
// Generate a MAC
Mac mac = Mac.getInstance(algorithm, provider);
mac.init(key);
byte[] macBytes = mac.doFinal();
assertNotNull(macBytes);
if (macBytes.length == 0) {
fail("Empty MAC");
}
} catch (Throwable e) {
throw new RuntimeException(algorithm + " failed", e);
}
}
}
public void testMacGeneratedByAndroidKeyStoreVerifiesByAndroidKeyStore() throws Exception {
Provider provider = Security.getProvider(EXPECTED_PROVIDER_NAME);
assertNotNull(provider);
for (String algorithm : EXPECTED_ALGORITHMS) {
try {
SecretKey key = importDefaultKatKey(algorithm);
// Generate a MAC
Mac mac = Mac.getInstance(algorithm, provider);
mac.init(key);
byte[] message = "This is a test".getBytes("UTF-8");
byte[] macBytes = mac.doFinal(message);
assertMacVerifiesOneShot(algorithm, provider, key, message, macBytes);
} catch (Throwable e) {
throw new RuntimeException(algorithm + " failed", e);
}
}
}
public void testMacGeneratedByAndroidKeyStoreVerifiesByHighestPriorityProvider()
throws Exception {
Provider provider = Security.getProvider(EXPECTED_PROVIDER_NAME);
assertNotNull(provider);
for (String algorithm : EXPECTED_ALGORITHMS) {
try {
SecretKey key = getDefaultKatKey(algorithm);
SecretKey keystoreKey = importDefaultKatKey(algorithm);
// Generate a MAC
Mac mac = Mac.getInstance(algorithm, provider);
mac.init(keystoreKey);
byte[] message = "This is a test".getBytes("UTF-8");
byte[] macBytes = mac.doFinal(message);
assertMacVerifiesOneShot(algorithm, key, message, macBytes);
} catch (Throwable e) {
throw new RuntimeException(algorithm + " failed", e);
}
}
}
public void testMacGeneratedByHighestPriorityProviderVerifiesByAndroidKeyStore()
throws Exception {
Provider keystoreProvider = Security.getProvider(EXPECTED_PROVIDER_NAME);
assertNotNull(keystoreProvider);
for (String algorithm : EXPECTED_ALGORITHMS) {
Provider signingProvider = null;
try {
SecretKey key = getDefaultKatKey(algorithm);
SecretKey keystoreKey = importDefaultKatKey(algorithm);
// Generate a MAC
Mac mac = Mac.getInstance(algorithm);
mac.init(key);
signingProvider = mac.getProvider();
byte[] message = "This is a test".getBytes("UTF-8");
byte[] macBytes = mac.doFinal(message);
assertMacVerifiesOneShot(
algorithm, keystoreProvider, keystoreKey, message, macBytes);
} catch (Throwable e) {
throw new RuntimeException(
algorithm + " failed, signing provider: " + signingProvider, e);
}
}
}
public void testSmallMsgKat() throws Exception {
byte[] message = SHORT_MSG_KAT_MESSAGE;
for (String algorithm : EXPECTED_ALGORITHMS) {
for (KatVector testVector : SHORT_MSG_KAT_MACS.get(algorithm)) {
byte[] keyBytes = testVector.key;
try {
SecretKey key = TestUtils.importIntoAndroidKeyStore(
"test",
new SecretKeySpec(keyBytes, algorithm),
getWorkingImportParams(algorithm)).getKeystoreBackedSecretKey();
byte[] goodMacBytes = testVector.mac.clone();
assertNotNull(goodMacBytes);
assertMacVerifiesOneShot(algorithm, key, message, goodMacBytes);
assertMacVerifiesFedOneByteAtATime(algorithm, key, message, goodMacBytes);
assertMacVerifiesFedUsingFixedSizeChunks(
algorithm, key, message, goodMacBytes, 3);
byte[] messageWithBitFlip = message.clone();
messageWithBitFlip[messageWithBitFlip.length / 2] ^= 1;
assertMacDoesNotVerifyOneShot(algorithm, key, messageWithBitFlip, goodMacBytes);
byte[] goodMacWithBitFlip = goodMacBytes.clone();
goodMacWithBitFlip[goodMacWithBitFlip.length / 2] ^= 1;
assertMacDoesNotVerifyOneShot(algorithm, key, message, goodMacWithBitFlip);
} catch (Throwable e) {
throw new RuntimeException(
"Failed for " + algorithm + " with key " + HexEncoding.encode(keyBytes),
e);
}
}
}
}
public void testLargeMsgKat() throws Exception {
byte[] message = TestUtils.generateLargeKatMsg(LONG_MSG_KAT_SEED, LONG_MSG_KAT_SIZE_BYTES);
for (String algorithm : EXPECTED_ALGORITHMS) {
try {
SecretKey key = importDefaultKatKey(algorithm);
byte[] goodMacBytes = LONG_MSG_KAT_MACS.get(algorithm);
assertNotNull(goodMacBytes);
assertMacVerifiesOneShot(algorithm, key, message, goodMacBytes);
assertMacVerifiesFedUsingFixedSizeChunks(
algorithm, key, message, goodMacBytes, 20389);
assertMacVerifiesFedUsingFixedSizeChunks(
algorithm, key, message, goodMacBytes, 393571);
} catch (Throwable e) {
throw new RuntimeException("Failed for " + algorithm, e);
}
}
}
public void testInitFailsWhenNotAuthorizedToSign() throws Exception {
int badPurposes = KeyProperties.PURPOSE_ENCRYPT | KeyProperties.PURPOSE_DECRYPT
| KeyProperties.PURPOSE_VERIFY;
for (String algorithm : EXPECTED_ALGORITHMS) {
try {
KeyProtection good = getWorkingImportParams(algorithm);
assertInitSucceeds(algorithm, good);
assertInitThrowsInvalidKeyException(algorithm,
TestUtils.buildUpon(good, badPurposes).build());
} catch (Throwable e) {
throw new RuntimeException("Failed for " + algorithm, e);
}
}
}
public void testInitFailsWhenDigestNotAuthorized() throws Exception {
for (String algorithm : EXPECTED_ALGORITHMS) {
try {
KeyProtection good = getWorkingImportParams(algorithm);
assertInitSucceeds(algorithm, good);
String badKeyAlgorithm = ("HmacSHA256".equalsIgnoreCase(algorithm))
? "HmacSHA384" : "HmacSHA256";
assertInitThrowsInvalidKeyException(algorithm, badKeyAlgorithm, good);
} catch (Throwable e) {
throw new RuntimeException("Failed for " + algorithm, e);
}
}
}
public void testInitFailsWhenKeyNotYetValid() throws Exception {
for (String algorithm : EXPECTED_ALGORITHMS) {
try {
KeyProtection good = TestUtils.buildUpon(getWorkingImportParams(algorithm))
.setKeyValidityStart(new Date(System.currentTimeMillis() - DAY_IN_MILLIS))
.build();
assertInitSucceeds(algorithm, good);
Date badStartDate = new Date(System.currentTimeMillis() + DAY_IN_MILLIS);
assertInitThrowsInvalidKeyException(algorithm,
TestUtils.buildUpon(good).setKeyValidityStart(badStartDate).build());
} catch (Throwable e) {
throw new RuntimeException("Failed for " + algorithm, e);
}
}
}
public void testInitFailsWhenKeyNoLongerValidForOrigination() throws Exception {
for (String algorithm : EXPECTED_ALGORITHMS) {
try {
KeyProtection good = TestUtils.buildUpon(getWorkingImportParams(algorithm))
.setKeyValidityForOriginationEnd(
new Date(System.currentTimeMillis() + DAY_IN_MILLIS))
.build();
assertInitSucceeds(algorithm, good);
Date badEndDate = new Date(System.currentTimeMillis() - DAY_IN_MILLIS);
assertInitThrowsInvalidKeyException(algorithm,
TestUtils.buildUpon(good)
.setKeyValidityForOriginationEnd(badEndDate)
.build());
} catch (Throwable e) {
throw new RuntimeException("Failed for " + algorithm, e);
}
}
}
public void testInitIgnoresThatKeyNoLongerValidForConsumption() throws Exception {
for (String algorithm : EXPECTED_ALGORITHMS) {
try {
KeyProtection good = TestUtils.buildUpon(getWorkingImportParams(algorithm))
.setKeyValidityForConsumptionEnd(
new Date(System.currentTimeMillis() + DAY_IN_MILLIS))
.build();
assertInitSucceeds(algorithm, good);
Date badEndDate = new Date(System.currentTimeMillis() - DAY_IN_MILLIS);
assertInitSucceeds(algorithm,
TestUtils.buildUpon(good)
.setKeyValidityForConsumptionEnd(badEndDate)
.build());
} catch (Throwable e) {
throw new RuntimeException("Failed for " + algorithm, e);
}
}
}
private void assertMacVerifiesOneShot(
String algorithm,
SecretKey key,
byte[] message,
byte[] mac) throws Exception {
assertMacVerifiesOneShot(algorithm, null, key, message, mac);
}
private void assertMacVerifiesOneShot(
String algorithm,
Provider provider,
SecretKey key,
byte[] message,
byte[] mac) throws Exception {
Mac m = (provider != null)
? Mac.getInstance(algorithm, provider) : Mac.getInstance(algorithm);
m.init(key);
byte[] mac2 = m.doFinal(message);
if (!Arrays.equals(mac, mac2)) {
fail("MAC did not verify. algorithm: " + algorithm
+ ", provider: " + m.getProvider().getName()
+ ", MAC (" + mac.length + " bytes): " + HexEncoding.encode(mac)
+ ", actual MAC (" + mac2.length + " bytes): " + HexEncoding.encode(mac2));
}
}
private void assertMacDoesNotVerifyOneShot(
String algorithm,
SecretKey key,
byte[] message,
byte[] mac) throws Exception {
Mac m = Mac.getInstance(algorithm);
m.init(key);
byte[] mac2 = m.doFinal(message);
if (Arrays.equals(mac, mac2)) {
fail("MAC verifies unexpectedly. algorithm: " + algorithm
+ ", provider: " + m.getProvider().getName()
+ ", MAC (" + mac.length + " bytes): " + HexEncoding.encode(mac));
}
}
private void assertMacVerifiesFedOneByteAtATime(
String algorithm,
SecretKey key,
byte[] message,
byte[] mac) throws Exception {
Mac m = Mac.getInstance(algorithm);
m.init(key);
for (int i = 0; i < message.length; i++) {
m.update(message[i]);
}
byte[] mac2 = m.doFinal();
if (!Arrays.equals(mac, mac2)) {
fail("MAC did not verify. algorithm: " + algorithm
+ ", provider: " + m.getProvider().getName()
+ ", MAC (" + mac.length + " bytes): " + HexEncoding.encode(mac)
+ ", actual MAC (" + mac2.length + " bytes): " + HexEncoding.encode(mac2));
}
}
private void assertMacVerifiesFedUsingFixedSizeChunks(
String algorithm,
SecretKey key,
byte[] message,
byte[] mac,
int chunkSizeBytes) throws Exception {
Mac m = Mac.getInstance(algorithm);
m.init(key);
int messageRemaining = message.length;
int messageOffset = 0;
while (messageRemaining > 0) {
int actualChunkSizeBytes = Math.min(chunkSizeBytes, messageRemaining);
m.update(message, messageOffset, actualChunkSizeBytes);
messageOffset += actualChunkSizeBytes;
messageRemaining -= actualChunkSizeBytes;
}
byte[] mac2 = m.doFinal();
if (!Arrays.equals(mac, mac2)) {
fail("MAC did not verify. algorithm: " + algorithm
+ ", provider: " + m.getProvider().getName()
+ ", MAC (" + mac.length + " bytes): " + HexEncoding.encode(mac)
+ ", actual MAC (" + mac2.length + " bytes): " + HexEncoding.encode(mac2));
}
}
private void assertInitSucceeds(String algorithm, KeyProtection keyProtection)
throws Exception {
assertInitSucceeds(algorithm, algorithm, keyProtection);
}
private void assertInitSucceeds(
String macAlgorithm, String keyAlgorithm, KeyProtection keyProtection)
throws Exception {
SecretKey key = importDefaultKatKey(keyAlgorithm, keyProtection);
Mac mac = Mac.getInstance(macAlgorithm);
mac.init(key);
}
private void assertInitThrowsInvalidKeyException(String algorithm, KeyProtection keyProtection)
throws Exception {
assertInitThrowsInvalidKeyException(algorithm, algorithm, keyProtection);
}
private void assertInitThrowsInvalidKeyException(
String macAlgorithm, String keyAlgorithm, KeyProtection keyProtection)
throws Exception {
SecretKey key = importDefaultKatKey(keyAlgorithm, keyProtection);
Mac mac = Mac.getInstance(macAlgorithm);
try {
mac.init(key);
fail("InvalidKeyException should have been thrown. MAC algorithm: " + macAlgorithm
+ ", key algorithm: " + keyAlgorithm);
} catch (InvalidKeyException expected) {}
}
private SecretKey getDefaultKatKey(String keyAlgorithm) {
return new SecretKeySpec(KAT_KEY, keyAlgorithm);
}
private SecretKey importDefaultKatKey(String keyAlgorithm) throws Exception {
return importDefaultKatKey(
keyAlgorithm,
new KeyProtection.Builder(KeyProperties.PURPOSE_SIGN).build());
}
private SecretKey importDefaultKatKey(
String keyAlgorithm, KeyProtection keyProtection) throws Exception {
return TestUtils.importIntoAndroidKeyStore(
"test1",
getDefaultKatKey(keyAlgorithm),
keyProtection).getKeystoreBackedSecretKey();
}
private static KeyProtection getWorkingImportParams(
@SuppressWarnings("unused") String algorithm) {
return new KeyProtection.Builder(KeyProperties.PURPOSE_SIGN).build();
}
private static class KatVector {
public byte[] key;
public byte[] mac;
public KatVector(byte[] key, byte[] mac) {
this.key = key;
this.mac = mac;
}
}
}