View Javadoc
1   /*
2    * Licensed to the Apache Software Foundation (ASF) under one or more
3    * contributor license agreements.  See the NOTICE file distributed with
4    * this work for additional information regarding copyright ownership.
5    * The ASF licenses this file to You under the Apache License, Version 2.0
6    * (the "License"); you may not use this file except in compliance with
7    * the License.  You may obtain a copy of the License at
8    *
9    *      https://www.apache.org/licenses/LICENSE-2.0
10   *
11   * Unless required by applicable law or agreed to in writing, software
12   * distributed under the License is distributed on an "AS IS" BASIS,
13   * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
14   * See the License for the specific language governing permissions and
15   * limitations under the License.
16   */
17  package org.apache.commons.codec.digest;
18  
19  import static org.junit.jupiter.api.Assertions.assertArrayEquals;
20  import static org.junit.jupiter.api.Assertions.assertFalse;
21  import static org.junit.jupiter.api.Assertions.assertThrows;
22  
23  import java.nio.charset.StandardCharsets;
24  import java.util.Arrays;
25  
26  import org.junit.jupiter.api.Test;
27  import org.junit.jupiter.params.ParameterizedTest;
28  import org.junit.jupiter.params.provider.ValueSource;
29  
30  class Blake3Test {
31      private static void assertThrowsProperExceptionWithKeySize(final int keySize) {
32          assertThrows(IllegalArgumentException.class, () -> Blake3.initKeyedHash(new byte[keySize]), "Blake3 keys must be 32 bytes");
33      }
34  
35      private static byte[] input(final int length) {
36          final byte[] input = new byte[length];
37          for (int i = 0; i < length; i++) {
38              input[i] = (byte) (i % 251);
39          }
40          return input;
41      }
42  
43      private static Blake3[] newHashers() {
44          return new Blake3[] {
45              Blake3.initHash(),
46              Blake3.initKeyedHash("whats the Elvish word for friend".getBytes(StandardCharsets.UTF_8)),
47              Blake3.initKeyDerivationFunction("org.apache.commons.codec.digest.Blake3Test".getBytes(StandardCharsets.UTF_8))
48          };
49      }
50  
51      @ParameterizedTest
52      @ValueSource(ints = {0, 1, 63, 64, 65, 1023, 1024, 1025, 2048, 3073})
53      void testFinalizeIsIdempotent(final int inputLength) {
54          for (final Blake3 hasher : newHashers()) {
55              hasher.update(input(inputLength));
56              final byte[] expected = hasher.doFinalize(131);
57              assertArrayEquals(expected, hasher.doFinalize(131));
58              assertArrayEquals(Arrays.copyOf(expected, 32), hasher.doFinalize(32));
59              assertArrayEquals(new byte[0], hasher.doFinalize(0));
60              final byte[] actual = new byte[131];
61              hasher.doFinalize(actual);
62              assertArrayEquals(expected, actual);
63              final byte[] destination = new byte[137];
64              Arrays.fill(destination, (byte) 0x5a);
65              final byte[] expectedDestination = destination.clone();
66              System.arraycopy(expected, 0, expectedDestination, 3, expected.length);
67              hasher.doFinalize(destination, 3, expected.length);
68              assertArrayEquals(expectedDestination, destination);
69              assertArrayEquals(expected, hasher.doFinalize(131));
70          }
71      }
72  
73      @Test
74      void testKdfExampleDerivesDistinctKeys() {
75          final Blake3 kdf = Blake3.initKeyDerivationFunction("org.apache.commons.codec.digest.Blake3Example".getBytes(StandardCharsets.UTF_8));
76          kdf.update("shared secret".getBytes(StandardCharsets.UTF_8));
77          kdf.update("sender".getBytes(StandardCharsets.UTF_8));
78          kdf.update("recipient".getBytes(StandardCharsets.UTF_8));
79          final byte[] keys = kdf.doFinalize(64);
80          final byte[] txKey = Arrays.copyOfRange(keys, 0, 32);
81          final byte[] rxKey = Arrays.copyOfRange(keys, 32, 64);
82          assertFalse(Arrays.equals(txKey, rxKey));
83          assertArrayEquals(txKey, kdf.doFinalize(32));
84          assertArrayEquals(txKey, kdf.doFinalize(32));
85      }
86  
87      @ParameterizedTest
88      @ValueSource(ints = {0, 1, 63, 64, 65, 1023, 1024, 1025, 2048, 3073})
89      void testResetAfterFinalize(final int inputLength) {
90          final Blake3[] hashers = newHashers();
91          final Blake3[] freshHashers = newHashers();
92          final byte[] message = input(inputLength);
93          for (int i = 0; i < hashers.length; i++) {
94              final Blake3 hasher = hashers[i];
95              final byte[] expected = hasher.update(message).doFinalize(131);
96              assertArrayEquals(freshHashers[i].doFinalize(131), hasher.reset().doFinalize(131));
97              assertArrayEquals(expected, hasher.update(message).doFinalize(131));
98          }
99      }
100 
101     @Test
102     void testShouldThrowIllegalArgumentExceptionWhenIncorrectKeySize() {
103         for (int i = 0; i < 32; i++) {
104             assertThrowsProperExceptionWithKeySize(i);
105         }
106         assertThrowsProperExceptionWithKeySize(33);
107     }
108 
109     @ParameterizedTest
110     @ValueSource(ints = {0, 1, 63, 64, 65, 1023, 1024, 1025, 2048, 3073})
111     void testUpdateAfterFinalize(final int inputLength) {
112         final byte[] message = input(inputLength + 1025);
113         final Blake3[] hashers = newHashers();
114         final Blake3[] freshHashers = newHashers();
115         for (int i = 0; i < hashers.length; i++) {
116             final Blake3 hasher = hashers[i];
117             hasher.update(message, 0, inputLength).doFinalize(131);
118             hasher.update(message, inputLength, message.length - inputLength);
119             final byte[] expected = freshHashers[i].update(message).doFinalize(131);
120             assertArrayEquals(expected, hasher.doFinalize(131));
121             assertArrayEquals(expected, hasher.doFinalize(131));
122         }
123     }
124 }