blob: 3d08ac4402c7aee53d9603b5c8af3e9c1f5ba2ea [file] [log] [blame]
// 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 com.google.crypto.tink.subtle.prf;
import static com.google.common.truth.Truth.assertThat;
import static java.nio.charset.StandardCharsets.UTF_8;
import com.google.crypto.tink.prf.Prf;
import com.google.crypto.tink.subtle.Enums.HashType;
import com.google.crypto.tink.subtle.Hex;
import com.google.crypto.tink.subtle.Hkdf;
import com.google.crypto.tink.subtle.Random;
import com.google.crypto.tink.testing.TestUtil;
import java.io.InputStream;
import org.junit.Test;
import org.junit.runner.RunWith;
import org.junit.runners.JUnit4;
/** Tests for HkdfStreamingPrf */
@RunWith(JUnit4.class)
public final class HkdfStreamingPrfTest {
// GENERIC TESTS ===============================================================
// These can be used for any streaming prf which generates enough output.
@Test
public void testComputePrf_basic() throws Exception {
HkdfStreamingPrf prf =
new HkdfStreamingPrf(HashType.SHA1, "key0123456".getBytes(UTF_8), "salt".getBytes(UTF_8));
InputStream input = prf.computePrf("input".getBytes(UTF_8));
byte[] output = new byte[10];
assertThat(input.read(output)).isEqualTo(10);
}
@Test
public void testComputePrf_differentInputDifferentValues() throws Exception {
HkdfStreamingPrf prf =
new HkdfStreamingPrf(HashType.SHA1, "key0123456".getBytes(UTF_8), "salt".getBytes(UTF_8));
InputStream input = prf.computePrf("input".getBytes(UTF_8));
byte[] output = new byte[10];
assertThat(input.read(output)).isEqualTo(10);
InputStream input2 = prf.computePrf("input2".getBytes(UTF_8));
byte[] output2 = new byte[10];
assertThat(input2.read(output2)).isEqualTo(10);
assertThat(output).isNotEqualTo(output2);
}
@Test
public void testComputePrf_sameInputSameValue() throws Exception {
HkdfStreamingPrf prf =
new HkdfStreamingPrf(HashType.SHA1, "key0123456".getBytes(UTF_8), "salt".getBytes(UTF_8));
InputStream input = prf.computePrf("input".getBytes(UTF_8));
byte[] output = new byte[10];
assertThat(input.read(output)).isEqualTo(10);
InputStream input2 = prf.computePrf("input".getBytes(UTF_8));
byte[] output2 = new byte[10];
assertThat(input2.read(output2)).isEqualTo(10);
assertThat(output).isEqualTo(output2);
}
@Test
public void testComputePrf_sameInputDifferentInterfacesSameValue() throws Exception {
HkdfStreamingPrf prf =
new HkdfStreamingPrf(HashType.SHA1, "key0123456".getBytes(UTF_8), "salt".getBytes(UTF_8));
InputStream input = prf.computePrf("input".getBytes(UTF_8));
byte[] output = new byte[100];
assertThat(input.read(output)).isEqualTo(100);
// Use the other interface to read the input.
InputStream input2 = prf.computePrf("input".getBytes(UTF_8));
byte[] output2 = new byte[100];
output2[0] = (byte) input2.read();
output2[1] = (byte) input2.read();
assertThat(input2.read(output2, 2, 33)).isEqualTo(33);
output2[35] = (byte) input2.read();
assertThat(input2.read(output2, 36, 64)).isEqualTo(64);
assertThat(output).isEqualTo(output2);
}
@Test
public void testComputePrf_exhaustStream() throws Exception {
HkdfStreamingPrf prf =
new HkdfStreamingPrf(HashType.SHA512, "key0123456".getBytes(UTF_8), "salt".getBytes(UTF_8));
InputStream input = prf.computePrf("input".getBytes(UTF_8));
final int maxOutputLength = 255 * (512 / 8);
byte[] output = new byte[maxOutputLength + 50];
assertThat(input.read(output)).isEqualTo(maxOutputLength);
}
// https://tools.ietf.org/html/rfc5869#appendix-A.1
@Test
public void testComputePrf_rfc589vector1() throws Exception {
HashType hash = HashType.SHA256;
byte[] ikm = Hex.decode("0b0b0b0b0b0b0b0b0b0b0b0b0b0b0b0b0b0b0b0b0b0b");
byte[] salt = Hex.decode("000102030405060708090a0b0c");
byte[] info = Hex.decode("f0f1f2f3f4f5f6f7f8f9");
byte[] expectedResult =
Hex.decode(
"3cb25f25faacd57a90434f64d0362f2a"
+ "2d2d0a90cf1a5a4c5db02d56ecc4c5bf"
+ "34007208d5b887185865");
HkdfStreamingPrf prf = new HkdfStreamingPrf(hash, ikm, salt);
InputStream input = prf.computePrf(info);
byte[] output = new byte[expectedResult.length];
assertThat(input.read(output)).isEqualTo(expectedResult.length);
assertThat(output).isEqualTo(expectedResult);
}
// https://tools.ietf.org/html/rfc5869#appendix-A.2
@Test
public void testComputePrf_rfc589vector2() throws Exception {
HashType hash = HashType.SHA256;
byte[] ikm =
Hex.decode(
"000102030405060708090a0b0c0d0e0f"
+ "101112131415161718191a1b1c1d1e1f"
+ "202122232425262728292a2b2c2d2e2f"
+ "303132333435363738393a3b3c3d3e3f"
+ "404142434445464748494a4b4c4d4e4f");
byte[] salt =
Hex.decode(
"606162636465666768696a6b6c6d6e6f"
+ "707172737475767778797a7b7c7d7e7f"
+ "808182838485868788898a8b8c8d8e8f"
+ "909192939495969798999a9b9c9d9e9f"
+ "a0a1a2a3a4a5a6a7a8a9aaabacadaeaf");
byte[] info =
Hex.decode(
"b0b1b2b3b4b5b6b7b8b9babbbcbdbebf"
+ "c0c1c2c3c4c5c6c7c8c9cacbcccdcecf"
+ "d0d1d2d3d4d5d6d7d8d9dadbdcdddedf"
+ "e0e1e2e3e4e5e6e7e8e9eaebecedeeef"
+ "f0f1f2f3f4f5f6f7f8f9fafbfcfdfeff");
byte[] expectedResult =
Hex.decode(
"b11e398dc80327a1c8e7f78c596a4934"
+ "4f012eda2d4efad8a050cc4c19afa97c"
+ "59045a99cac7827271cb41c65e590e09"
+ "da3275600c2f09b8367793a9aca3db71"
+ "cc30c58179ec3e87c14c01d5c1f3434f"
+ "1d87");
HkdfStreamingPrf prf = new HkdfStreamingPrf(hash, ikm, salt);
InputStream input = prf.computePrf(info);
byte[] output = new byte[expectedResult.length];
assertThat(input.read(output)).isEqualTo(expectedResult.length);
assertThat(output).isEqualTo(expectedResult);
}
// https://tools.ietf.org/html/rfc5869#appendix-A.3
@Test
public void testComputePrf_rfc589vector3() throws Exception {
HashType hash = HashType.SHA256;
byte[] ikm = Hex.decode("0b0b0b0b0b0b0b0b0b0b0b0b0b0b0b0b0b0b0b0b0b0b");
byte[] salt = Hex.decode("");
byte[] info = Hex.decode("");
byte[] expectedResult =
Hex.decode(
"8da4e775a563c18f715f802a063c5a31"
+ "b8a11f5c5ee1879ec3454e5f3c738d2d"
+ "9d201395faa4b61a96c8");
HkdfStreamingPrf prf = new HkdfStreamingPrf(hash, ikm, salt);
InputStream input = prf.computePrf(info);
byte[] output = new byte[expectedResult.length];
assertThat(input.read(output)).isEqualTo(expectedResult.length);
assertThat(output).isEqualTo(expectedResult);
}
// https://tools.ietf.org/html/rfc5869#appendix-A.4
@Test
public void testComputePrf_rfc589vector4() throws Exception {
HashType hash = HashType.SHA1;
byte[] ikm = Hex.decode("0b0b0b0b0b0b0b0b0b0b0b");
byte[] salt = Hex.decode("000102030405060708090a0b0c");
byte[] info = Hex.decode("f0f1f2f3f4f5f6f7f8f9");
byte[] expectedResult =
Hex.decode(
"085a01ea1b10f36933068b56efa5ad81"
+ "a4f14b822f5b091568a9cdd4f155fda2"
+ "c22e422478d305f3f896");
HkdfStreamingPrf prf = new HkdfStreamingPrf(hash, ikm, salt);
InputStream input = prf.computePrf(info);
byte[] output = new byte[expectedResult.length];
assertThat(input.read(output)).isEqualTo(expectedResult.length);
assertThat(output).isEqualTo(expectedResult);
}
// https://tools.ietf.org/html/rfc5869#appendix-A.5
@Test
public void testComputePrf_rfc589vector5() throws Exception {
HashType hash = HashType.SHA1;
byte[] ikm =
Hex.decode(
"000102030405060708090a0b0c0d0e0f"
+ "101112131415161718191a1b1c1d1e1f"
+ "202122232425262728292a2b2c2d2e2f"
+ "303132333435363738393a3b3c3d3e3f"
+ "404142434445464748494a4b4c4d4e4f");
byte[] salt =
Hex.decode(
"606162636465666768696a6b6c6d6e6f"
+ "707172737475767778797a7b7c7d7e7f"
+ "808182838485868788898a8b8c8d8e8f"
+ "909192939495969798999a9b9c9d9e9f"
+ "a0a1a2a3a4a5a6a7a8a9aaabacadaeaf");
byte[] info =
Hex.decode(
"b0b1b2b3b4b5b6b7b8b9babbbcbdbebf"
+ "c0c1c2c3c4c5c6c7c8c9cacbcccdcecf"
+ "d0d1d2d3d4d5d6d7d8d9dadbdcdddedf"
+ "e0e1e2e3e4e5e6e7e8e9eaebecedeeef"
+ "f0f1f2f3f4f5f6f7f8f9fafbfcfdfeff");
byte[] expectedResult =
Hex.decode(
"0bd770a74d1160f7c9f12cd5912a06eb"
+ "ff6adcae899d92191fe4305673ba2ffe"
+ "8fa3f1a4e5ad79f3f334b3b202b2173c"
+ "486ea37ce3d397ed034c7f9dfeb15c5e"
+ "927336d0441f4c4300e2cff0d0900b52"
+ "d3b4");
HkdfStreamingPrf prf = new HkdfStreamingPrf(hash, ikm, salt);
InputStream input = prf.computePrf(info);
byte[] output = new byte[expectedResult.length];
assertThat(input.read(output)).isEqualTo(expectedResult.length);
assertThat(output).isEqualTo(expectedResult);
}
// https://tools.ietf.org/html/rfc5869#appendix-A.6
@Test
public void testComputePrf_rfc589vector6() throws Exception {
HashType hash = HashType.SHA1;
byte[] ikm = Hex.decode("0b0b0b0b0b0b0b0b0b0b0b0b0b0b0b0b0b0b0b0b0b0b");
byte[] salt = Hex.decode("");
byte[] info = Hex.decode("");
byte[] expectedResult =
Hex.decode(
"0ac1af7002b3d761d1e55298da9d0506"
+ "b9ae52057220a306e07b6b87e8df21d0"
+ "ea00033de03984d34918");
HkdfStreamingPrf prf = new HkdfStreamingPrf(hash, ikm, salt);
InputStream input = prf.computePrf(info);
byte[] output = new byte[expectedResult.length];
assertThat(input.read(output)).isEqualTo(expectedResult.length);
assertThat(output).isEqualTo(expectedResult);
}
// https://tools.ietf.org/html/rfc5869#appendix-A.7
@Test
public void testComputePrf_rfc589vector7() throws Exception {
HashType hash = HashType.SHA1;
byte[] ikm = Hex.decode("0b0b0b0b0b0b0b0b0b0b0b0b0b0b0b0b0b0b0b0b0b0b");
// Since HMAC anyhow pads, this is the same as an absent salt.
byte[] salt = Hex.decode("");
byte[] info = Hex.decode("");
byte[] expectedResult =
Hex.decode(
"0ac1af7002b3d761d1e55298da9d0506"
+ "b9ae52057220a306e07b6b87e8df21d0"
+ "ea00033de03984d34918");
HkdfStreamingPrf prf = new HkdfStreamingPrf(hash, ikm, salt);
InputStream input = prf.computePrf(info);
byte[] output = new byte[expectedResult.length];
assertThat(input.read(output)).isEqualTo(expectedResult.length);
assertThat(output).isEqualTo(expectedResult);
}
@Test
public void testComputePrf_compareToHkdfUtil() throws Exception {
HashType hash = HashType.SHA1;
byte[] ikm = Random.randBytes(123);
byte[] salt = Random.randBytes(234);
byte[] info = Random.randBytes(345);
byte[] result = new byte[456];
HkdfStreamingPrf prf = new HkdfStreamingPrf(hash, ikm, salt);
InputStream input = prf.computePrf(info);
input.read(result);
assertThat(Hkdf.computeHkdf("HmacSha1", ikm, salt, info, result.length)).isEqualTo(result);
}
@Test
public void testComputePrf_compareToHkdfUtilSha384() throws Exception {
HashType hash = HashType.SHA384;
byte[] ikm = Random.randBytes(123);
byte[] salt = Random.randBytes(234);
byte[] info = Random.randBytes(345);
byte[] result = new byte[456];
HkdfStreamingPrf prf = new HkdfStreamingPrf(hash, ikm, salt);
InputStream input = prf.computePrf(info);
input.read(result);
assertThat(Hkdf.computeHkdf("HmacSha384", ikm, salt, info, result.length)).isEqualTo(result);
}
@Test
public void testPrfUniformity() throws Exception {
for (int i = 0; i < HashType.values().length; i++) {
byte[] ikm = Random.randBytes(128);
byte[] salt = Random.randBytes(128);
byte[] message = Random.randBytes(1024);
Prf prf = PrfImpl.wrap(new HkdfStreamingPrf(HashType.SHA256, ikm, salt));
byte[] prBytes = prf.compute(message, message.length);
TestUtil.ztestUniformString(prBytes);
TestUtil.ztestAutocorrelationUniformString(prBytes);
TestUtil.ztestCrossCorrelationUniformStrings(prBytes, message);
}
}
}