• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
1 // Copyright 2018 Google Inc.
2 //
3 // Licensed under the Apache License, Version 2.0 (the "License");
4 // you may not use this file except in compliance with the License.
5 // You may obtain a copy of the License at
6 //
7 //      http://www.apache.org/licenses/LICENSE-2.0
8 //
9 // Unless required by applicable law or agreed to in writing, software
10 // distributed under the License is distributed on an "AS IS" BASIS,
11 // WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
12 // See the License for the specific language governing permissions and
13 // limitations under the License.
14 //
15 ////////////////////////////////////////////////////////////////////////////////
16 
17 package com.google.crypto.tink.subtle;
18 
19 import static com.google.common.truth.Truth.assertThat;
20 import static org.junit.Assert.assertArrayEquals;
21 import static org.junit.Assert.assertThrows;
22 
23 import java.security.GeneralSecurityException;
24 import java.security.InvalidKeyException;
25 import org.junit.Test;
26 import org.junit.runner.RunWith;
27 import org.junit.runners.JUnit4;
28 
29 /** Unit tests for {@link XChaCha20} */
30 @RunWith(JUnit4.class)
31 public class XChaCha20Test {
createInstance(final byte[] key)32   public IndCpaCipher createInstance(final byte[] key) throws InvalidKeyException {
33     return new XChaCha20(key, 0 /* initialCounter */);
34   }
35 
36   @Test
testEncryptDecrypt()37   public void testEncryptDecrypt() throws Exception {
38     for (int i = 0; i < 64; i++) {
39       byte[] key = Random.randBytes(32);
40       IndCpaCipher cipher = createInstance(key);
41       for (int j = 0; j < 64; j++) {
42         byte[] expectedInput = Random.randBytes(new java.util.Random().nextInt(300));
43         byte[] output = cipher.encrypt(expectedInput);
44         byte[] actualInput = cipher.decrypt(output);
45         assertArrayEquals(
46             String.format(
47                 "\n\nMessage: %s\nKey: %s\nOutput: %s\nDecrypted Msg: %s\n",
48                 Hex.encode(expectedInput),
49                 Hex.encode(key),
50                 Hex.encode(output),
51                 Hex.encode(actualInput)),
52             expectedInput,
53             actualInput);
54       }
55     }
56   }
57 
58   @Test
testNewCipherThrowsIllegalArgExpWhenKeyLenIsLessThan32()59   public void testNewCipherThrowsIllegalArgExpWhenKeyLenIsLessThan32() throws Exception {
60     InvalidKeyException e =
61         assertThrows(InvalidKeyException.class, () -> createInstance(new byte[1]));
62     assertThat(e).hasMessageThat().containsMatch("The key length in bytes must be 32.");
63   }
64 
65   @Test
testNewCipherThrowsIllegalArgExpWhenKeyLenIsGreaterThan32()66   public void testNewCipherThrowsIllegalArgExpWhenKeyLenIsGreaterThan32() throws Exception {
67     InvalidKeyException e =
68         assertThrows(InvalidKeyException.class, () -> createInstance(new byte[33]));
69     assertThat(e).hasMessageThat().containsMatch("The key length in bytes must be 32.");
70   }
71 
72   @Test
testDecryptThrowsGeneralSecurityExpWhenCiphertextIsTooShort()73   public void testDecryptThrowsGeneralSecurityExpWhenCiphertextIsTooShort() throws Exception {
74     IndCpaCipher cipher = createInstance(Random.randBytes(32));
75 
76     GeneralSecurityException e =
77         assertThrows(GeneralSecurityException.class, () -> cipher.decrypt(new byte[2]));
78     assertThat(e).hasMessageThat().containsMatch("ciphertext too short");
79   }
80 
81   private static class XChaCha20TestVector {
82     public byte[] key;
83     public byte[] nonce;
84     public byte[] ciphertext;
85     public byte[] plaintext;
86 
XChaCha20TestVector(String key, String nonce, String ciphertext, String plaintext)87     public XChaCha20TestVector(String key, String nonce, String ciphertext, String plaintext) {
88       this.key = Hex.decode(key);
89       this.nonce = Hex.decode(nonce);
90       this.ciphertext = Hex.decode(ciphertext);
91       this.plaintext = Hex.decode(plaintext);
92       if (plaintext.length() == 0) {
93         this.plaintext = new byte[this.ciphertext.length];
94       }
95     }
96   }
97 
98   // From libsodium's test/default/xchacha20.c (tv_stream_xchacha20)
99   private static final XChaCha20TestVector[] xChaCha20TestVectors = {
100     new XChaCha20TestVector(
101         "79c99798ac67300bbb2704c95c341e3245f3dcb21761b98e52ff45b24f304fc4",
102         "b33ffd3096479bcfbc9aee49417688a0a2554f8d95389419",
103         "c6e9758160083ac604ef90e712ce6e75d7797590744e0cf060f013739c",
104         ""),
105     new XChaCha20TestVector(
106         "ddf7784fee099612c40700862189d0397fcc4cc4b3cc02b5456b3a97d1186173",
107         "a9a04491e7bf00c3ca91ac7c2d38a777d88993a7047dfcc4",
108         "2f289d371f6f0abc3cb60d11d9b7b29adf6bc5ad843e8493e928448d",
109         ""),
110     new XChaCha20TestVector(
111         "3d12800e7b014e88d68a73f0a95b04b435719936feba60473f02a9e61ae60682",
112         "56bed2599eac99fb27ebf4ffcb770a64772dec4d5849ea2d",
113         "a2c3c1406f33c054a92760a8e0666b84f84fa3a618f0",
114         ""),
115     new XChaCha20TestVector(
116         "5f5763ff9a30c95da5c9f2a8dfd7cc6efd9dfb431812c075aa3e4f32e04f53e4",
117         "a5fa890efa3b9a034d377926ce0e08ee6d7faccaee41b771",
118         "8a1a5ba898bdbcff602b1036e469a18a5e45789d0e8d9837d81a2388a52b0b6a0f51891528f424c4a7f492"
119             + "a8dd7bce8bac19fbdbe1fb379ac0",
120         ""),
121     new XChaCha20TestVector(
122         "eadc0e27f77113b5241f8ca9d6f9a5e7f09eee68d8a5cf30700563bf01060b4e",
123         "a171a4ef3fde7c4794c5b86170dc5a099b478f1b852f7b64",
124         "23839f61795c3cdbcee2c749a92543baeeea3cbb721402aa42e6cae140447575f2916c5d71108e3b13357e"
125             + "af86f060cb",
126         ""),
127     new XChaCha20TestVector(
128         "91319c9545c7c804ba6b712e22294c386fe31c4ff3d278827637b959d3dbaab2",
129         "410e854b2a911f174aaf1a56540fc3855851f41c65967a4e",
130         "cbe7d24177119b7fdfa8b06ee04dade4256ba7d35ffda6b89f014e479faef6",
131         ""),
132     new XChaCha20TestVector(
133         "6a6d3f412fc86c4450fc31f89f64ed46baa3256ffcf8616e8c23a06c422842b6",
134         "6b7773fce3c2546a5db4829f53a9165f41b08faae2fb72d5",
135         "8b23e35b3cdd5f3f75525fc37960ec2b68918e8c046d8a832b9838f1546be662e54feb1203e2",
136         ""),
137     new XChaCha20TestVector(
138         "d45e56368ebc7ba9be7c55cfd2da0feb633c1d86cab67cd5627514fd20c2b391",
139         "fd37da2db31e0c738754463edadc7dafb0833bd45da497fc",
140         "47950efa8217e3dec437454bd6b6a80a287e2570f0a48b3fa1ea3eb868be3d486f6516606d85e5643becc4"
141             + "73b370871ab9ef8e2a728f73b92bd98e6e26ea7c8ff96ec5a9e8de95e1eee9300c",
142         ""),
143     new XChaCha20TestVector(
144         "aface41a64a9a40cbc604d42bd363523bd762eb717f3e08fe2e0b4611eb4dcf3",
145         "6906e0383b895ab9f1cf3803f42f27c79ad47b681c552c63",
146         "a5fa7c0190792ee17675d52ad7570f1fb0892239c76d6e802c26b5b3544d13151e67513b8aaa1ac5af2d7f"
147             + "d0d5e4216964324838",
148         ""),
149     new XChaCha20TestVector(
150         "9d23bd4149cb979ccf3c5c94dd217e9808cb0e50cd0f67812235eaaf601d6232",
151         "c047548266b7c370d33566a2425cbf30d82d1eaf5294109e",
152         "a21209096594de8c5667b1d13ad93f744106d054df210e4782cd396fec692d3515a20bf351eec011a92c36"
153             + "7888bc464c32f0807acd6c203a247e0db854148468e9f96bee4cf718d68d5f637cbd5a376457788e"
154             + "6fae90fc31097cfc",
155         ""),
156     // https://tools.ietf.org/html/draft-arciszewski-xchacha-00.
157     new XChaCha20TestVector(
158         "808182838485868788898a8b8c8d8e8f909192939495969798999a9b9c9d9e9f",
159         "404142434445464748494a4b4c4d4e4f5051525354555658",
160         "4559abba4e48c16102e8bb2c05e6947f50a786de162f9b0b7e592a9b53d0d4e98d8d6410d540a1a6375b26"
161             + "d80dace4fab52384c731acbf16a5923c0c48d3575d4d0d2c673b666faa731061277701093a6bf7a15"
162             + "8a8864292a41c48e3a9b4c0daece0f8d98d0d7e05b37a307bbb66333164ec9e1b24ea0d6c3ffddcec"
163             + "4f68e7443056193a03c810e11344ca06d8ed8a2bfb1e8d48cfa6bc0eb4e2464b748142407c9f431ae"
164             + "e769960e15ba8b96890466ef2457599852385c661f752ce20f9da0c09ab6b19df74e76a95967446f8"
165             + "d0fd415e7bee2a12a114c20eb5292ae7a349ae577820d5520a1f3fb62a17ce6a7e68fa7c79111d886"
166             + "0920bc048ef43fe84486ccb87c25f0ae045f0cce1e7989a9aa220a28bdd4827e751a24a6d5c62d790"
167             + "a66393b93111c1a55dd7421a10184974c7c5",
168         "5468652064686f6c65202870726f6e6f756e6365642022646f6c65222920697320616c736f206b6e6f776e2"
169             + "061732074686520417369617469632077696c6420646f672c2072656420646f672c20616e64207768"
170             + "6973746c696e6720646f672e2049742069732061626f7574207468652073697a65206f66206120476"
171             + "5726d616e20736865706865726420627574206c6f6f6b73206d6f7265206c696b652061206c6f6e67"
172             + "2d6c656767656420666f782e205468697320686967686c7920656c757369766520616e6420736b696"
173             + "c6c6564206a756d70657220697320636c6173736966696564207769746820776f6c7665732c20636f"
174             + "796f7465732c206a61636b616c732c20616e6420666f78657320696e20746865207461786f6e6f6d6"
175             + "9632066616d696c792043616e696461652e")
176   };
177 
178   @Test
testXChaCha20TestVectors()179   public void testXChaCha20TestVectors() throws Exception {
180     for (XChaCha20TestVector test : xChaCha20TestVectors) {
181       IndCpaCipher cipher = new XChaCha20(test.key, 0 /* initialCounter */);
182       byte[] message = cipher.decrypt(Bytes.concat(test.nonce, test.ciphertext));
183       assertThat(message).isEqualTo(test.plaintext);
184     }
185   }
186 }
187