• 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.PublicKeyVerify;
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 java.util.Arrays;
33 import org.junit.Assume;
34 import org.junit.Test;
35 import org.junit.runner.RunWith;
36 import org.junit.runners.JUnit4;
37 
38 /** Unit tests for {@link Ed25519Verify}. */
39 @RunWith(JUnit4.class)
40 public final class Ed25519VerifyTest {
41   @Test
testVerificationWithPublicKeyLengthDifferentFrom32Byte()42   public void testVerificationWithPublicKeyLengthDifferentFrom32Byte() throws Exception {
43     Assume.assumeFalse(TinkFips.useOnlyFips());
44 
45     assertThrows(
46         IllegalArgumentException.class,
47         () -> {
48           Ed25519Verify unused = new Ed25519Verify(new byte[31]);
49         });
50     assertThrows(
51         IllegalArgumentException.class,
52         () -> {
53           Ed25519Verify unused = new Ed25519Verify(new byte[33]);
54         });
55   }
56 
getMessage(JsonObject testcase)57   private byte[] getMessage(JsonObject testcase) throws Exception {
58     if (testcase.has("msg")) {
59       return Hex.decode(testcase.get("msg").getAsString());
60     } else {
61       return Hex.decode(testcase.get("message").getAsString());
62     }
63   }
64 
65   @Test
testVerificationWithWycheproofVectors()66   public void testVerificationWithWycheproofVectors() throws Exception {
67     Assume.assumeFalse(TinkFips.useOnlyFips());
68 
69     JsonObject json =
70         WycheproofTestUtil.readJson("../wycheproof/testvectors/eddsa_test.json");
71     ArrayList<String> errors = new ArrayList<>();
72     JsonArray testGroups = json.get("testGroups").getAsJsonArray();
73     for (int i = 0; i < testGroups.size(); i++) {
74       JsonObject group = testGroups.get(i).getAsJsonObject();
75       JsonObject key = group.get("key").getAsJsonObject();
76       byte[] publicKey = Hex.decode(key.get("pk").getAsString());
77       JsonArray tests = group.get("tests").getAsJsonArray();
78       for (int j = 0; j < tests.size(); j++) {
79         JsonObject testcase = tests.get(j).getAsJsonObject();
80         String tcId =
81             String.format("testcase %d (%s)",
82                 testcase.get("tcId").getAsInt(), testcase.get("comment").getAsString());
83         byte[] msg = getMessage(testcase);
84         byte[] sig = Hex.decode(testcase.get("sig").getAsString());
85         String result = testcase.get("result").getAsString();
86         Ed25519Verify verifier = new Ed25519Verify(publicKey);
87         try {
88           verifier.verify(sig, msg);
89           if (result.equals("invalid")) {
90             errors.add("FAIL " + tcId + ": accepting invalid signature");
91           }
92         } catch (GeneralSecurityException ex) {
93           if (result.equals("valid")) {
94             errors.add(
95                 "FAIL " + tcId + ": rejecting valid signature, exception: " + ex.getMessage());
96           }
97         }
98       }
99     }
100     assertThat(errors).isEmpty();
101   }
102 
103   @Test
testFailIfFipsModuleNotAvailable()104   public void testFailIfFipsModuleNotAvailable() throws Exception {
105     Assume.assumeTrue(TinkFips.useOnlyFips());
106 
107     assertThrows(RuntimeException.class, () -> new Ed25519Verify(new byte[32]));
108   }
109 
110   /**
111    * Tests that the verifier can verify a the signature for the message and key in the test vector.
112    */
113   @Test
test_validateSignatureInTestVector()114   public void test_validateSignatureInTestVector() throws Exception {
115     Assume.assumeFalse(TinkFips.useOnlyFips());
116     // We are not using parameterized tests because the next line cannot be run if useOnlyFips.
117     SignatureTestVector[] testVectors = Ed25519TestUtil.createEd25519TestVectors();
118     for (SignatureTestVector testVector : testVectors) {
119 
120       Ed25519PrivateKey key = (Ed25519PrivateKey) testVector.getPrivateKey();
121       PublicKeyVerify verifier = Ed25519Verify.create(key.getPublicKey());
122       verifier.verify(testVector.getSignature(), testVector.getMessage());
123     }
124   }
125 
126   @Test
test_computeAndValidate_modifiedMessage_throws()127   public void test_computeAndValidate_modifiedMessage_throws() throws Exception {
128     Assume.assumeFalse(TinkFips.useOnlyFips());
129     // We are not using parameterized tests because the next line cannot be run if useOnlyFips.
130     SignatureTestVector[] testVectors = Ed25519TestUtil.createEd25519TestVectors();
131     for (SignatureTestVector testVector : testVectors) {
132       Ed25519PrivateKey key = (Ed25519PrivateKey) testVector.getPrivateKey();
133       byte[] modifiedMessage = Bytes.concat(testVector.getMessage(), new byte[] {1});
134       PublicKeyVerify verifier = Ed25519Verify.create(key.getPublicKey());
135       assertThrows(
136           GeneralSecurityException.class,
137           () -> verifier.verify(testVector.getSignature(), modifiedMessage));
138     }
139   }
140 
141   /** Tests that the verification fails if we modify the output prefix. */
142   @Test
test_computeAndValidate_modifiedOutputPrefix_throws()143   public void test_computeAndValidate_modifiedOutputPrefix_throws() throws Exception {
144     Assume.assumeFalse(TinkFips.useOnlyFips());
145     // We are not using parameterized tests because the next line cannot be run if useOnlyFips.
146     SignatureTestVector[] testVectors = Ed25519TestUtil.createEd25519TestVectors();
147     for (SignatureTestVector testVector : testVectors) {
148       Ed25519PrivateKey key = (Ed25519PrivateKey) testVector.getPrivateKey();
149       if (key.getOutputPrefix().size() == 0) {
150         return;
151       }
152       byte[] modifiedSignature = testVector.getSignature();
153       modifiedSignature[1] ^= 0x01;
154       PublicKeyVerify verifier = Ed25519Verify.create(key.getPublicKey());
155       assertThrows(
156           GeneralSecurityException.class,
157           () ->
158               verifier.verify(
159                   Arrays.copyOf(modifiedSignature, modifiedSignature.length),
160                   testVector.getMessage()));
161     }
162   }
163 }
164