/*
* Licensed to the Apache Software Foundation (ASF) under one
* or more contributor license agreements. See the NOTICE file
* distributed with this work for additional information
* regarding copyright ownership. The ASF licenses this file
* to you 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 org.apache.sshd.common.mac;
import java.nio.charset.StandardCharsets;
import java.util.ArrayList;
import java.util.Arrays;
import java.util.Collection;
import java.util.Collections;
import java.util.List;
import java.util.Objects;
import org.apache.sshd.common.Factory;
import org.apache.sshd.common.util.GenericUtils;
import org.apache.sshd.common.util.Pair;
import org.apache.sshd.common.util.ValidateUtils;
import org.apache.sshd.common.util.buffer.BufferUtils;
import org.apache.sshd.util.test.BaseTestSupport;
import org.junit.FixMethodOrder;
import org.junit.Test;
import org.junit.runner.RunWith;
import org.junit.runners.MethodSorters;
import org.junit.runners.Parameterized;
import org.junit.runners.Parameterized.Parameters;
/**
* @author <a href="mailto:dev@mina.apache.org">Apache MINA SSHD Project</a>
*/
@FixMethodOrder(MethodSorters.NAME_ASCENDING)
@RunWith(Parameterized.class) // see https://github.com/junit-team/junit/wiki/Parameterized-tests
public class MacVectorsTest extends BaseTestSupport {
private final VectorSeed seed;
private final Factory<? extends Mac> macFactory;
private final byte[] expected;
public MacVectorsTest(VectorSeed seed, String factoryName, String expected) {
this.seed = Objects.requireNonNull(seed, "No seed");
this.macFactory = ValidateUtils.checkNotNull(BuiltinMacs.fromFactoryName(factoryName), "Unknown MAC: %s", factoryName);
this.expected = BufferUtils.decodeHex(BufferUtils.EMPTY_HEX_SEPARATOR, expected);
}
@Parameters(name = "factory={1}, expected={2}, seed={0}")
public static Collection<Object[]> parameters() {
List<Object[]> ret = new ArrayList<>();
for (VectorTestData vector : Collections.unmodifiableList(
Arrays.asList(
///////////////// Test Cases for HMAC-MD5 ///////////////////////
// see https://tools.ietf.org/html/rfc2202
new VectorTestData("0b0b0b0b0b0b0b0b0b0b0b0b0b0b0b0b", false, "Hi There",
Arrays.asList(new Pair<>(BuiltinMacs.Constants.HMAC_MD5, // test case 1
"9294727a3638bb1c13f48ef8158bfc9d"))),
new VectorTestData("Jefe", "what do ya want for nothing?",
Arrays.asList(new Pair<>(BuiltinMacs.Constants.HMAC_MD5, // test case 2
"750c783e6ab0b503eaa86e310a5db738"))),
new VectorTestData("aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa", false, repeat("dd", 50), false,
Arrays.asList(new Pair<>(BuiltinMacs.Constants.HMAC_MD5, // test case 3
"56be34521d144c88dbb8c733f0e8b3f6"))),
/* TODO see why it fails
new VectorTestData("0102030405060708090a0b0c0d0e0f10111213141516171819", false, repeat("cd", 50), false,
Arrays.asList(new Pair<>(BuiltinMacs.Constants.HMAC_MD5, // test case 4
"697eaf0aca3a3aea3a75164746ffaa79"))),
*/
new VectorTestData("0c0c0c0c0c0c0c0c0c0c0c0c0c0c0c0c", false, "Test With Truncation",
Arrays.asList(new Pair<>(BuiltinMacs.Constants.HMAC_MD5, // test case 5
"56461ef2342edc00f9bab995690efd4c"),
new Pair<>(BuiltinMacs.Constants.HMAC_MD5_96,
"56461ef2342edc00f9bab995"))),
/* TODO see why it fails
new VectorTestData(repeat("aa", 80), false, "Test Using Larger Than Block-Size Key - Hash Key First",
Arrays.asList(new Pair<>(BuiltinMacs.Constants.HMAC_MD5, // test case 6
"6b1ab7fe4bd7bf8f0b62e6ce61b9d0cd"))),
*/
/* TODO see why it fails
new VectorTestData(repeat("aa", 80), false, "Test Using Larger Than Block-Size Key and Larger Than One Block-Size Data",
Arrays.asList(new Pair<>(BuiltinMacs.Constants.HMAC_MD5, // test case 7
"6f630fad67cda0ee1fb1f562db3aa53e"))),
*/
///////////////// Test Cases for HMAC-SHA-1 ///////////////////////
// see https://tools.ietf.org/html/rfc2202
new VectorTestData("0b0b0b0b0b0b0b0b0b0b0b0b0b0b0b0b0b0b0b0b", false, "Hi There",
Arrays.asList(new Pair<>(BuiltinMacs.Constants.HMAC_SHA1, // test case 1
"b617318655057264e28bc0b6fb378c8ef146be00"))),
new VectorTestData("Jefe", "what do ya want for nothing?",
Arrays.asList(new Pair<>(BuiltinMacs.Constants.HMAC_SHA1, // test case 2
"effcdf6ae5eb2fa2d27416d5f184df9c259a7c79"))),
new VectorTestData("aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa", false, repeat("dd", 50), false,
Arrays.asList(new Pair<>(BuiltinMacs.Constants.HMAC_SHA1, // test case 3
"125d7342b9ac11cd91a39af48aa17b4f63f175d3"))),
/* TODO see why it fails
new VectorTestData("0102030405060708090a0b0c0d0e0f10111213141516171819", false, repeat("cd", 50), false,
Arrays.asList(new Pair<>(BuiltinMacs.Constants.HMAC_SHA1, // test case 4
"4c9007f4026250c6bc8414f9bf50c86c2d7235da"))),
*/
new VectorTestData("0c0c0c0c0c0c0c0c0c0c0c0c0c0c0c0c0c0c0c0c", false, "Test With Truncation",
Arrays.asList(new Pair<>(BuiltinMacs.Constants.HMAC_SHA1, // test case 5
"4c1a03424b55e07fe7f27be1d58bb9324a9a5a04"),
new Pair<>(BuiltinMacs.Constants.HMAC_SHA1_96,
"4c1a03424b55e07fe7f27be1"))),
/* TODO see why this fails
new VectorTestData(repeat("aa", 80), false, "Test Using Larger Than Block-Size Key - Hash Key First",
Arrays.asList(new Pair<>(BuiltinMacs.Constants.HMAC_SHA1, // test case 6
"aa4ae5e15272d00e95705637ce8a3b55ed402112"))),
*/
/* TODO see why it fails
new VectorTestData(repeat("aa", 80), false, "Test Using Larger Than Block-Size Key and Larger Than One Block-Size Data",
Arrays.asList(new Pair<>(BuiltinMacs.Constants.HMAC_SHA1, // test case 7
"4c1a03424b55e07fe7f27be1d58bb9324a9a5a04"),
new Pair<>(BuiltinMacs.Constants.HMAC_SHA1_96,
"4c1a03424b55e07fe7f27be1"))),
*/
/* TODO see why it fails
new VectorTestData(repeat("aa", 80), false, "Test Using Larger Than Block-Size Key - Hash Key First",
Arrays.asList(new Pair<>(BuiltinMacs.Constants.HMAC_SHA1, // test case 8
"aa4ae5e15272d00e95705637ce8a3b55ed402112"))),
*/
/* TODO see why it fails
new VectorTestData(repeat("aa", 80), false, "Test Using Larger Than Block-Size Key and Larger Than One Block-Size Data",
Arrays.asList(new Pair<>(BuiltinMacs.Constants.HMAC_SHA1, // test case 9
"e8e99d0f45237d786d6bbaa7965c7808bbff1a91"))),
*/
///////////////// Test Cases for HMAC-SHA-2 ///////////////////////
// see https://tools.ietf.org/html/rfc4231
new VectorTestData(repeat("0b", 20), false, "Hi There",
// test case 1
Arrays.asList(
new Pair<>(BuiltinMacs.Constants.HMAC_SHA2_256,
"b0344c61d8db38535ca8afceaf0bf12b881dc200c9833da726e9376c2e32cff7"),
new Pair<>(BuiltinMacs.Constants.HMAC_SHA2_512,
"87aa7cdea5ef619d4ff0b4241a1d6cb02379f4e2ce4ec2787ad0b30545e17cdedaa833b7d6b8a702038b274eaea3f4e4be9d914eeb61f1702e696c203a126854"))),
new VectorTestData("Jefe", "what do ya want for nothing?",
// test case 2
Arrays.asList(
new Pair<>(BuiltinMacs.Constants.HMAC_SHA2_256,
"5bdcc146bf60754e6a042426089575c75a003f089d2739839dec58b964ec3843"),
new Pair<>(BuiltinMacs.Constants.HMAC_SHA2_512,
"164b7a7bfcf819e2e395fbe73b56e0a387bd64222e831fd610270cd7ea2505549758bf75c05a994a6d034f65f8f0e6fdcaeab1a34d4a6b4b636e070a38bce737"))),
new VectorTestData(repeat("aa", 20), false, repeat("dd", 50), false,
// test case 3
Arrays.asList(
new Pair<>(BuiltinMacs.Constants.HMAC_SHA2_256,
"773ea91e36800e46854db8ebd09181a72959098b3ef8c122d9635514ced565fe"),
new Pair<>(BuiltinMacs.Constants.HMAC_SHA2_512,
"fa73b0089d56a284efb0f0756c890be9b1b5dbdd8ee81a3655f83e33b2279d39bf3e848279a722c806b485a47e67c807b946a337bee8942674278859e13292fb"))),
new VectorTestData("0102030405060708090a0b0c0d0e0f10111213141516171819", false, repeat("cd", 50), false,
// test case 4
Arrays.asList(
new Pair<>(BuiltinMacs.Constants.HMAC_SHA2_256,
"82558a389a443c0ea4cc819899f2083a85f0faa3e578f8077a2e3ff46729665b"),
new Pair<>(BuiltinMacs.Constants.HMAC_SHA2_512,
"b0ba465637458c6990e5a8c5f61d4af7e576d97ff94b872de76f8050361ee3dba91ca5c11aa25eb4d679275cc5788063a5f19741120c4f2de2adebeb10a298dd"))),
/* TODO see why it fails
new VectorTestData(repeat("0c", 20), false, "Test With Truncation",
Arrays.asList( // test case 5
new Pair<>(BuiltinMacs.Constants.HMAC_SHA2_256,
"a3b6167473100ee06e0c796c2955552b"),
new Pair<>(BuiltinMacs.Constants.HMAC_SHA2_512,
"415fad6271580a531d4179bc891d87a6"))),
*/
/* TODO see why it fails
new VectorTestData(repeat("aa", 131), false, "Test Using Larger Than Block-Size Key - Hash Key First",
Arrays.asList( // test case 6
new Pair<>(BuiltinMacs.Constants.HMAC_SHA2_256,
"60e431591ee0b67f0d8a26aacbf5b77f8e0bc6213728c5140546040f0ee37f54"),
new Pair<>(BuiltinMacs.Constants.HMAC_SHA2_512,
"80b24263c7c1a3ebb71493c1dd7be8b49b46d1f41b4aeec1121b013783f8f3526b56d037e05f2598bd0fd2215d6a1e5295e64f73f63f0aec8b915a985d786598"))),
*/
/* TODO see why it fails
new VectorTestData(repeat("aa", 131), false, "This is a test using a larger than block-size"
+ " key and a larger than block-size data."
+ " The key needs to be hashed before being used"
+ " by the HMAC algorithm",
Arrays.asList( // test case 7
new Pair<>(BuiltinMacs.Constants.HMAC_SHA2_256,
"9b09ffa71b942fcb27635fbcd5b0e944bfdc63644f0713938a7f51535c3a35e2"),
new Pair<>(BuiltinMacs.Constants.HMAC_SHA2_512,
"e37b6a775dc87dbaa4dfa9f96e5e3ffddebd71f8867289865df5a32d20cdc944b6022cac3c4982b10d5eeb55c3e4de15134676fb6de0446065c97440fa8c6a58")))
*/
// mark end
new VectorTestData("", false, "", false, Collections.emptyList())))) {
for (Pair<String, String> tc : vector.getResults()) {
ret.add(new Object[]{vector, tc.getFirst(), tc.getSecond()});
}
}
return ret;
}
@Test
public void testStandardVectorMac() throws Exception {
Mac mac = macFactory.create();
mac.init(seed.getKey());
mac.update(seed.getData());
byte[] actual = new byte[mac.getBlockSize()];
mac.doFinal(actual);
assertArrayEquals("Mismatched results for actual=" + BufferUtils.toHex(BufferUtils.EMPTY_HEX_SEPARATOR, actual), expected, actual);
}
private static class VectorSeed {
private final byte[] key;
private final String keyString;
private final byte[] data;
private final String dataString;
VectorSeed(String key, String data) {
this.key = key.getBytes(StandardCharsets.UTF_8);
this.keyString = key;
this.data = data.getBytes(StandardCharsets.UTF_8);
this.dataString = data;
}
VectorSeed(String key, boolean useKeyString, String data) {
this.key = BufferUtils.decodeHex(BufferUtils.EMPTY_HEX_SEPARATOR, key);
this.keyString = useKeyString ? new String(this.key, StandardCharsets.UTF_8) : key;
this.data = data.getBytes(StandardCharsets.UTF_8);
this.dataString = data;
}
VectorSeed(String key, boolean useKeyString, String data, boolean useDataString) {
this.key = BufferUtils.decodeHex(BufferUtils.EMPTY_HEX_SEPARATOR, key);
this.keyString = useKeyString ? new String(this.key, StandardCharsets.UTF_8) : key;
this.data = BufferUtils.decodeHex(BufferUtils.EMPTY_HEX_SEPARATOR, data);
this.dataString = useDataString ? new String(this.data, StandardCharsets.UTF_8) : data;
}
public byte[] getKey() {
return key.clone(); // clone to avoid inadvertent change
}
public String getKeyString() {
return keyString;
}
public byte[] getData() {
return data.clone(); // clone to avoid inadvertent change
}
public String getDataString() {
return dataString;
}
@Override
public String toString() {
return "key=" + trimToLength(getKeyString(), 32) + ", data=" + trimToLength(getDataString(), 32);
}
private static CharSequence trimToLength(CharSequence csq, int maxLen) {
if (GenericUtils.length(csq) <= maxLen) {
return csq;
}
return csq.subSequence(0, maxLen) + "...";
}
}
private static class VectorTestData extends VectorSeed {
private final Collection<Pair<String, String>> results;
VectorTestData(String key, String data, Collection<Pair<String, String>> results) {
super(key, data);
this.results = results;
}
VectorTestData(String key, boolean useKeyString, String data, Collection<Pair<String, String>> results) {
super(key, useKeyString, data);
this.results = results;
}
VectorTestData(String key, boolean useKeyString, String data, boolean useDataString, Collection<Pair<String, String>> results) {
super(key, useKeyString, data, useDataString);
this.results = results;
}
public Collection<Pair<String, String>> getResults() {
return results;
}
}
}