• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
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