1 // Copyright 2017 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.assertThrows; 21 22 import com.google.crypto.tink.PublicKeySign; 23 import com.google.crypto.tink.config.TinkFips; 24 import com.google.crypto.tink.signature.Ed25519PrivateKey; 25 import com.google.crypto.tink.signature.internal.testing.Ed25519TestUtil; 26 import com.google.crypto.tink.signature.internal.testing.SignatureTestVector; 27 import com.google.crypto.tink.testing.WycheproofTestUtil; 28 import com.google.gson.JsonArray; 29 import com.google.gson.JsonObject; 30 import java.security.GeneralSecurityException; 31 import java.util.ArrayList; 32 import org.junit.Assume; 33 import org.junit.Test; 34 import org.junit.runner.RunWith; 35 import org.junit.runners.JUnit4; 36 37 /** 38 * Unit tests for {@link Ed25519Sign}. 39 * 40 */ 41 @RunWith(JUnit4.class) 42 public final class Ed25519SignTest { 43 44 @Test testSigningOneKeyWithMultipleMessages()45 public void testSigningOneKeyWithMultipleMessages() throws Exception { 46 Assume.assumeFalse(TinkFips.useOnlyFips()); 47 48 Ed25519Sign.KeyPair keyPair = Ed25519Sign.KeyPair.newKeyPair(); 49 Ed25519Sign signer = new Ed25519Sign(keyPair.getPrivateKey()); 50 Ed25519Verify verifier = new Ed25519Verify(keyPair.getPublicKey()); 51 for (int i = 0; i < 100; i++) { 52 byte[] msg = Random.randBytes(20); 53 byte[] sig = signer.sign(msg); 54 verifier.verify(sig, msg); 55 } 56 } 57 58 @Test testSignIsDeterministic()59 public void testSignIsDeterministic() throws Exception { 60 Assume.assumeFalse(TinkFips.useOnlyFips()); 61 62 Ed25519Sign.KeyPair keyPair = Ed25519Sign.KeyPair.newKeyPair(); 63 Ed25519Sign signer = new Ed25519Sign(keyPair.getPrivateKey()); 64 Ed25519Verify verifier = new Ed25519Verify(keyPair.getPublicKey()); 65 byte[] msg = Random.randBytes(20); 66 byte[] sig = signer.sign(msg); 67 verifier.verify(sig, msg); 68 69 for (int i = 0; i < 100; i++) { 70 // Ed25519 is deterministic, expect a unique signature for the same message. 71 assertThat(signer.sign(msg)).isEqualTo(sig); 72 } 73 } 74 75 @Test testSignWithPrivateKeyLengthDifferentFrom32Byte()76 public void testSignWithPrivateKeyLengthDifferentFrom32Byte() throws Exception { 77 Assume.assumeFalse(TinkFips.useOnlyFips()); 78 79 assertThrows( 80 IllegalArgumentException.class, 81 () -> { 82 Ed25519Sign unused = new Ed25519Sign(new byte[31]); 83 }); 84 assertThrows( 85 IllegalArgumentException.class, 86 () -> { 87 Ed25519Sign unused = new Ed25519Sign(new byte[33]); 88 }); 89 } 90 91 @Test testSigningWithMultipleRandomKeysAndMessages()92 public void testSigningWithMultipleRandomKeysAndMessages() throws Exception { 93 Assume.assumeFalse(TinkFips.useOnlyFips()); 94 95 for (int i = 0; i < 100; i++) { 96 Ed25519Sign.KeyPair keyPair = Ed25519Sign.KeyPair.newKeyPair(); 97 Ed25519Sign signer = new Ed25519Sign(keyPair.getPrivateKey()); 98 Ed25519Verify verifier = new Ed25519Verify(keyPair.getPublicKey()); 99 byte[] msg = Random.randBytes(20); 100 byte[] sig = signer.sign(msg); 101 verifier.verify(sig, msg); 102 } 103 } 104 getMessage(JsonObject testcase)105 private byte[] getMessage(JsonObject testcase) throws Exception { 106 if (testcase.has("msg")) { 107 return Hex.decode(testcase.get("msg").getAsString()); 108 } else { 109 return Hex.decode(testcase.get("message").getAsString()); 110 } 111 } 112 113 @Test testSigningWithWycheproofVectors()114 public void testSigningWithWycheproofVectors() throws Exception { 115 Assume.assumeFalse(TinkFips.useOnlyFips()); 116 117 JsonObject json = 118 WycheproofTestUtil.readJson("../wycheproof/testvectors/eddsa_test.json"); 119 ArrayList<String> errors = new ArrayList<>(); 120 JsonArray testGroups = json.get("testGroups").getAsJsonArray(); 121 for (int i = 0; i < testGroups.size(); i++) { 122 JsonObject group = testGroups.get(i).getAsJsonObject(); 123 JsonObject key = group.get("key").getAsJsonObject(); 124 byte[] privateKey = Hex.decode(key.get("sk").getAsString()); 125 JsonArray tests = group.get("tests").getAsJsonArray(); 126 for (int j = 0; j < tests.size(); j++) { 127 JsonObject testcase = tests.get(j).getAsJsonObject(); 128 String tcId = 129 String.format( 130 "testcase %d (%s)", 131 testcase.get("tcId").getAsInt(), testcase.get("comment").getAsString()); 132 byte[] msg = getMessage(testcase); 133 byte[] sig = Hex.decode(testcase.get("sig").getAsString()); 134 String result = testcase.get("result").getAsString(); 135 if (result.equals("invalid")) { 136 continue; 137 } 138 Ed25519Sign signer = new Ed25519Sign(privateKey); 139 byte[] computedSig = signer.sign(msg); 140 if (!Bytes.equal(sig, computedSig)) { 141 errors.add( 142 "FAIL " + tcId + ": got " + Hex.encode(computedSig) + ", want " + Hex.encode(sig)); 143 } 144 } 145 } 146 assertThat(errors).isEmpty(); 147 } 148 149 @Test testKeyPairFromSeedTestVector()150 public void testKeyPairFromSeedTestVector() throws Exception { 151 Assume.assumeFalse(TinkFips.useOnlyFips()); 152 153 byte[] secretSeed = 154 Hex.decode("000102030405060708090a0b0c0d0e0f000102030405060708090a0b0c0d0e0f"); 155 Ed25519Sign.KeyPair keyPair = Ed25519Sign.KeyPair.newKeyPairFromSeed(secretSeed); 156 assertThat(keyPair.getPrivateKey()).isEqualTo(secretSeed); 157 assertThat(keyPair.getPublicKey()) 158 .isEqualTo(Hex.decode("9b62773323ef41a11834824194e55164d325eb9cdcc10ddda7d10ade4fbd8f6d")); 159 } 160 161 @Test testKeyPairFromSeedTooShort()162 public void testKeyPairFromSeedTooShort() throws Exception { 163 Assume.assumeFalse(TinkFips.useOnlyFips()); 164 165 byte[] keyMaterial = Random.randBytes(10); 166 assertThrows( 167 IllegalArgumentException.class, () -> Ed25519Sign.KeyPair.newKeyPairFromSeed(keyMaterial)); 168 } 169 170 @Test testFailIfFipsModuleNotAvailable()171 public void testFailIfFipsModuleNotAvailable() throws Exception { 172 Assume.assumeTrue(TinkFips.useOnlyFips()); 173 174 byte[] key = Random.randBytes(32); 175 assertThrows(GeneralSecurityException.class, () -> new Ed25519Sign(key)); 176 } 177 178 @Test testSignOutputsSameSignatureAsInTestVector()179 public void testSignOutputsSameSignatureAsInTestVector() throws Exception { 180 Assume.assumeFalse(TinkFips.useOnlyFips()); 181 // We are not using parameterized tests because the next line cannot be run if useOnlyFips. 182 SignatureTestVector[] testVectors = Ed25519TestUtil.createEd25519TestVectors(); 183 for (SignatureTestVector testVector : testVectors) { 184 Ed25519PrivateKey key = (Ed25519PrivateKey) testVector.getPrivateKey(); 185 PublicKeySign signer = Ed25519Sign.create(key); 186 byte[] signature = signer.sign(testVector.getMessage()); 187 // Ed25519 is deterministic, so signature must be the same as in the test vector. 188 assertThat(signature).isEqualTo(testVector.getSignature()); 189 } 190 } 191 } 192