• 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.assertThrows;
21 
22 import com.google.crypto.tink.AccessesPartialKey;
23 import com.google.crypto.tink.PublicKeyVerify;
24 import com.google.crypto.tink.internal.Util;
25 import com.google.crypto.tink.signature.RsaSsaPkcs1Parameters;
26 import com.google.crypto.tink.signature.RsaSsaPkcs1PublicKey;
27 import com.google.crypto.tink.signature.internal.testing.RsaSsaPkcs1TestUtil;
28 import com.google.crypto.tink.signature.internal.testing.SignatureTestVector;
29 import com.google.crypto.tink.subtle.Enums.HashType;
30 import com.google.crypto.tink.testing.WycheproofTestUtil;
31 import com.google.gson.JsonArray;
32 import com.google.gson.JsonObject;
33 import java.math.BigInteger;
34 import java.security.GeneralSecurityException;
35 import java.security.KeyFactory;
36 import java.security.Provider;
37 import java.security.Security;
38 import java.security.interfaces.RSAPublicKey;
39 import java.security.spec.RSAPublicKeySpec;
40 import org.conscrypt.Conscrypt;
41 import org.junit.Assume;
42 import org.junit.Before;
43 import org.junit.Test;
44 import org.junit.experimental.theories.DataPoints;
45 import org.junit.experimental.theories.FromDataPoints;
46 import org.junit.experimental.theories.Theories;
47 import org.junit.experimental.theories.Theory;
48 import org.junit.runner.RunWith;
49 
50 /** Unit tests for RsaSsaPkcs1VerifyJce. */
51 @RunWith(Theories.class)
52 public class RsaSsaPkcs1VerifyJceTest {
53 
54   @DataPoints("allTests")
55   public static final SignatureTestVector[] ALL_TEST_VECTORS =
56       RsaSsaPkcs1TestUtil.createRsaSsaPkcs1TestVectors();
57 
getSubtleHashType(RsaSsaPkcs1Parameters.HashType hash)58   private static HashType getSubtleHashType(RsaSsaPkcs1Parameters.HashType hash)
59       throws GeneralSecurityException {
60     if (hash == RsaSsaPkcs1Parameters.HashType.SHA256) {
61       return HashType.SHA256;
62     } else if (hash == RsaSsaPkcs1Parameters.HashType.SHA384) {
63       return HashType.SHA384;
64     } else if (hash == RsaSsaPkcs1Parameters.HashType.SHA512) {
65       return HashType.SHA512;
66     } else {
67       throw new GeneralSecurityException("Unsupported hash: " + hash);
68     }
69   }
70 
71   @Before
useConscrypt()72   public void useConscrypt() throws Exception {
73     if (!Util.isAndroid()) {
74       Conscrypt.checkAvailability();
75       Security.addProvider(Conscrypt.newProvider());
76     }
77   }
78 
79   @Theory
create_verifySignatureInTestVector_works( @romDataPoints"allTests") SignatureTestVector testVector)80   public void create_verifySignatureInTestVector_works(
81       @FromDataPoints("allTests") SignatureTestVector testVector) throws Exception {
82     PublicKeyVerify verify =
83         RsaSsaPkcs1VerifyJce.create(
84             (RsaSsaPkcs1PublicKey) testVector.getPrivateKey().getPublicKey());
85     verify.verify(testVector.getSignature(), testVector.getMessage());
86     assertThrows(
87         GeneralSecurityException.class,
88         () -> verify.verify(testVector.getSignature(), new byte[] {1, 2, 3}));
89   }
90 
91   @AccessesPartialKey
92   @Theory
constructor_verifySignatureInTestVector_works( @romDataPoints"allTests") SignatureTestVector testVector)93   public void constructor_verifySignatureInTestVector_works(
94       @FromDataPoints("allTests") SignatureTestVector testVector) throws Exception {
95     RsaSsaPkcs1PublicKey testPublicKey =
96         (RsaSsaPkcs1PublicKey) testVector.getPrivateKey().getPublicKey();
97     if (testPublicKey.getParameters().getVariant() != RsaSsaPkcs1Parameters.Variant.NO_PREFIX) {
98       // Constructor only supports NO_PREFIX variant.
99       return;
100     }
101     KeyFactory keyFactory = EngineFactory.KEY_FACTORY.getInstance("RSA");
102     RSAPublicKey publicKey =
103         (RSAPublicKey)
104             keyFactory.generatePublic(
105                 new RSAPublicKeySpec(
106                     testPublicKey.getModulus(), testPublicKey.getParameters().getPublicExponent()));
107     RsaSsaPkcs1VerifyJce verify =
108         new RsaSsaPkcs1VerifyJce(
109             publicKey, getSubtleHashType(testPublicKey.getParameters().getHashType()));
110     verify.verify(testVector.getSignature(), testVector.getMessage());
111     assertThrows(
112         GeneralSecurityException.class,
113         () -> verify.verify(testVector.getSignature(), new byte[] {1, 2, 3}));
114   }
115 
116   @Test
sha1IsNotSupported()117   public void sha1IsNotSupported() throws Exception {
118     RsaSsaPkcs1PublicKey testPublicKey =
119         (RsaSsaPkcs1PublicKey) ALL_TEST_VECTORS[0].getPrivateKey().getPublicKey();
120     KeyFactory keyFactory = EngineFactory.KEY_FACTORY.getInstance("RSA");
121     RSAPublicKey publicKey =
122         (RSAPublicKey)
123             keyFactory.generatePublic(
124                 new RSAPublicKeySpec(
125                     testPublicKey.getModulus(), testPublicKey.getParameters().getPublicExponent()));
126     assertThrows(
127         GeneralSecurityException.class, () -> new RsaSsaPkcs1VerifyJce(publicKey, HashType.SHA1));
128   }
129 
130   @Test
constructorWithSmallExponent_throws()131   public void constructorWithSmallExponent_throws() throws Exception {
132     RsaSsaPkcs1PublicKey testPublicKey =
133         (RsaSsaPkcs1PublicKey) ALL_TEST_VECTORS[0].getPrivateKey().getPublicKey();
134     KeyFactory keyFactory = EngineFactory.KEY_FACTORY.getInstance("RSA");
135     RSAPublicKey publicKey =
136         (RSAPublicKey)
137             keyFactory.generatePublic(
138                 new RSAPublicKeySpec(testPublicKey.getModulus(), BigInteger.valueOf(3)));
139     assertThrows(
140         GeneralSecurityException.class, () -> new RsaSsaPkcs1VerifyJce(publicKey, HashType.SHA256));
141   }
142 
getHashType(String sha)143   private static RsaSsaPkcs1Parameters.HashType getHashType(String sha) {
144     switch (sha) {
145       case "SHA-256":
146         return RsaSsaPkcs1Parameters.HashType.SHA256;
147       case "SHA-512":
148         return RsaSsaPkcs1Parameters.HashType.SHA512;
149       default:
150         throw new IllegalArgumentException("Unsupported hash: " + sha);
151     }
152   }
153 
154   @DataPoints("wycheproofTestVectorPaths")
155   public static final String[] WYCHEPROOF_TEST_VECTORS_PATHS =
156       new String[] {
157         "../wycheproof/testvectors/rsa_signature_2048_sha256_test.json",
158         "../wycheproof/testvectors/rsa_signature_3072_sha512_test.json",
159         "../wycheproof/testvectors/rsa_signature_4096_sha512_test.json"
160       };
161 
162   @AccessesPartialKey
163   @Theory
wycheproofVectors(@romDataPoints"wycheproofTestVectorPaths") String path)164   public void wycheproofVectors(@FromDataPoints("wycheproofTestVectorPaths") String path)
165       throws Exception {
166     JsonObject jsonObj = WycheproofTestUtil.readJson(path);
167 
168     int errors = 0;
169     JsonArray testGroups = jsonObj.getAsJsonArray("testGroups");
170     for (int i = 0; i < testGroups.size(); i++) {
171       JsonObject group = testGroups.get(i).getAsJsonObject();
172       BigInteger modulus = new BigInteger(group.get("n").getAsString(), 16);
173       BigInteger exponent = new BigInteger(1, Hex.decode(group.get("e").getAsString()));
174       RsaSsaPkcs1Parameters.HashType hashType = getHashType(group.get("sha").getAsString());
175       JsonArray tests = group.getAsJsonArray("tests");
176       for (int j = 0; j < tests.size(); j++) {
177         JsonObject testcase = tests.get(j).getAsJsonObject();
178         // Do not perform the Wycheproof test if the RSA public exponent is small.
179         if (WycheproofTestUtil.checkFlags(testcase, "SmallPublicKey")) {
180           continue;
181         }
182         String tcId =
183             String.format(
184                 "testcase %d (%s)",
185                 testcase.get("tcId").getAsInt(), testcase.get("comment").getAsString());
186         RsaSsaPkcs1Parameters parameters =
187             RsaSsaPkcs1Parameters.builder()
188                 .setModulusSizeBits(modulus.bitLength())
189                 .setPublicExponent(exponent)
190                 .setHashType(hashType)
191                 .setVariant(RsaSsaPkcs1Parameters.Variant.NO_PREFIX)
192                 .build();
193         RsaSsaPkcs1PublicKey publicKey =
194             RsaSsaPkcs1PublicKey.builder().setParameters(parameters).setModulus(modulus).build();
195         PublicKeyVerify verifier = RsaSsaPkcs1VerifyJce.create(publicKey);
196         byte[] msg = getMessage(testcase);
197         byte[] sig = Hex.decode(testcase.get("sig").getAsString());
198         String result = testcase.get("result").getAsString();
199         try {
200           verifier.verify(sig, msg);
201           if (result.equals("invalid")) {
202             System.out.printf("FAIL %s: accepting invalid signature%n", tcId);
203             errors++;
204           }
205         } catch (GeneralSecurityException ex) {
206           if (result.equals("valid")) {
207             System.out.printf("FAIL %s: rejecting valid signature, exception: %s%n", tcId, ex);
208             errors++;
209           }
210         }
211       }
212     }
213     assertThat(errors).isEqualTo(0);
214   }
215 
getMessage(JsonObject testcase)216   private static byte[] getMessage(JsonObject testcase) {
217     // Previous version of Wycheproof test vectors uses "message" while the new one uses "msg".
218     if (testcase.has("msg")) {
219       return Hex.decode(testcase.get("msg").getAsString());
220     } else {
221       return Hex.decode(testcase.get("message").getAsString());
222     }
223   }
224 
225   @Test
usesConscryptImplementationIfInstalled()226   public void usesConscryptImplementationIfInstalled() throws Exception {
227     Assume.assumeFalse(Util.isAndroid());
228 
229     RsaSsaPkcs1PublicKey testPublicKey =
230         (RsaSsaPkcs1PublicKey) ALL_TEST_VECTORS[0].getPrivateKey().getPublicKey();
231 
232     // Conscrypt is already installed, so RsaSsaPkcs1VerifyConscrypt is used.
233     PublicKeyVerify verifier = RsaSsaPkcs1VerifyJce.create(testPublicKey);
234     assertThat(verifier.getClass().getSimpleName()).isEqualTo("RsaSsaPkcs1VerifyConscrypt");
235 
236     Provider conscrypt = Conscrypt.newProvider();
237     Security.removeProvider(conscrypt.getName());
238 
239     PublicKeyVerify verifier2 = RsaSsaPkcs1VerifyJce.create(testPublicKey);
240     assertThat(verifier2.getClass().getSimpleName()).isEqualTo("InternalJavaImpl");
241 
242     Security.addProvider(conscrypt);
243   }
244 }
245