1 /*
2  * Copyright (C) 2015 The Android Open Source Project
3  *
4  * Licensed under the Apache License, Version 2.0 (the "License"); you may not use this file except
5  * in compliance with the License. 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 distributed under the License
10  * is distributed on an "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express
11  * or implied. See the License for the specific language governing permissions and limitations under
12  * the License.
13  */
14 
15 package android.keystore.cts;
16 
17 import static org.junit.Assert.assertEquals;
18 import static org.junit.Assert.assertNotNull;
19 import static org.junit.Assert.assertTrue;
20 import static org.junit.Assert.fail;
21 
22 import java.security.InvalidKeyException;
23 import java.security.KeyPair;
24 import java.security.PrivateKey;
25 import java.security.Provider;
26 import java.security.PublicKey;
27 import java.security.Security;
28 import java.security.Signature;
29 import java.security.SignatureException;
30 import java.security.interfaces.RSAKey;
31 import java.util.ArrayList;
32 import java.util.Arrays;
33 import java.util.Collection;
34 import java.util.List;
35 
36 import android.content.Context;
37 import android.keystore.cts.R;
38 import android.keystore.cts.util.ImportedKey;
39 import android.keystore.cts.util.TestUtils;
40 import android.security.keystore.KeyProperties;
41 import android.security.keystore.KeyProtection;
42 import androidx.test.InstrumentationRegistry;
43 import androidx.test.runner.AndroidJUnit4;
44 
45 import org.junit.Test;
46 import org.junit.runner.RunWith;
47 
48 @RunWith(AndroidJUnit4.class)
49 public class RSASignatureTest {
50 
51     private static final String EXPECTED_PROVIDER_NAME = SignatureTest.EXPECTED_PROVIDER_NAME;
52 
53     private static final String[] SIGNATURE_ALGORITHMS;
54 
55     static {
56         List<String> sigAlgs = new ArrayList<>();
57         for (String algorithm : SignatureTest.EXPECTED_SIGNATURE_ALGORITHMS) {
58             String keyAlgorithm = TestUtils.getSignatureAlgorithmKeyAlgorithm(algorithm);
59             if (KeyProperties.KEY_ALGORITHM_RSA.equalsIgnoreCase(keyAlgorithm)) {
60                 sigAlgs.add(algorithm);
61             }
62         }
63         SIGNATURE_ALGORITHMS = sigAlgs.toArray(new String[sigAlgs.size()]);
64     }
65 
getContext()66     private Context getContext() {
67         return InstrumentationRegistry.getInstrumentation().getTargetContext();
68     }
69 
70     @Test
testMaxMessageSizeWhenNoDigestUsed()71     public void testMaxMessageSizeWhenNoDigestUsed() throws Exception {
72         Provider provider = Security.getProvider(EXPECTED_PROVIDER_NAME);
73         assertNotNull(provider);
74 
75         for (ImportedKey keyPair : importKatKeyPairs("NONEwithRSA")) {
76             PublicKey publicKey = keyPair.getKeystoreBackedKeyPair().getPublic();
77             PrivateKey privateKey = keyPair.getKeystoreBackedKeyPair().getPrivate();
78             int modulusSizeBits = ((RSAKey) publicKey).getModulus().bitLength();
79             try {
80                 int modulusSizeBytes = (modulusSizeBits + 7) / 8;
81                 // PKCS#1 signature padding must be at least 11 bytes long (00 || 01 || PS || 00)
82                 // where PS must be at least 8 bytes long).
83                 int expectedMaxMessageSizeBytes = modulusSizeBytes - 11;
84                 byte[] msg = new byte[expectedMaxMessageSizeBytes + 1];
85                 Arrays.fill(msg, (byte) 0xf0);
86 
87                 // Assert that a message of expected maximum length is accepted
88                 Signature signature = Signature.getInstance("NONEwithRSA", provider);
89                 signature.initSign(privateKey);
90                 signature.update(msg, 0, expectedMaxMessageSizeBytes);
91                 byte[] sigBytes = signature.sign();
92 
93                 signature.initVerify(publicKey);
94                 signature.update(msg, 0, expectedMaxMessageSizeBytes);
95                 assertTrue(signature.verify(sigBytes));
96 
97                 // Assert that a message longer than expected maximum length is rejected
98                 signature = Signature.getInstance(signature.getAlgorithm(), provider);
99                 signature.initSign(privateKey);
100                 try {
101                     signature.update(msg, 0, expectedMaxMessageSizeBytes + 1);
102                     signature.sign();
103                     fail();
104                 } catch (SignatureException expected) {
105                 }
106 
107                 signature.initVerify(publicKey);
108                 try {
109                     signature.update(msg, 0, expectedMaxMessageSizeBytes + 1);
110                     if (signature.verify(sigBytes)) {
111                         fail();
112                     }
113                 } catch (SignatureException expected) {
114                 }
115             } catch (Throwable e) {
116                 throw new RuntimeException("Failed for " + modulusSizeBits + " bit key", e);
117             }
118         }
119     }
120 
121     @Test
testSmallKeyRejected()122     public void testSmallKeyRejected() throws Exception {
123         // Use a 512 bit key which should prevent the use of any digests larger than SHA-256
124         // because the padded form of the digested message will be larger than modulus size.
125         Provider provider = Security.getProvider(EXPECTED_PROVIDER_NAME);
126         assertNotNull(provider);
127 
128         for (String algorithm : SIGNATURE_ALGORITHMS) {
129             try {
130                 String digest = TestUtils.getSignatureAlgorithmDigest(algorithm);
131                 if (KeyProperties.DIGEST_NONE.equalsIgnoreCase(digest)) {
132                     // Ignore signature algorithms without digest -- this is tested in a separate
133                     // test above.
134                     continue;
135                 }
136                 int digestOutputSizeBits = TestUtils.getDigestOutputSizeBits(digest);
137                 if (digestOutputSizeBits <= 256) {
138                     // 256-bit and shorter digests are short enough to work with a 512 bit key.
139                     continue;
140                 }
141 
142                 KeyPair keyPair = TestUtils.importIntoAndroidKeyStore("test1",
143                         getContext(),
144                         R.raw.rsa_key5_512_pkcs8,
145                         R.raw.rsa_key5_512_cert,
146                         TestUtils.getMinimalWorkingImportParametersForSigningingWith(algorithm))
147                         .getKeystoreBackedKeyPair();
148                 assertEquals(512, ((RSAKey) keyPair.getPrivate()).getModulus().bitLength());
149                 assertEquals(512, ((RSAKey) keyPair.getPublic()).getModulus().bitLength());
150 
151                 Signature signature = Signature.getInstance(algorithm, provider);
152                 // Assert that either initSign or sign fails. We don't expect all keymaster
153                 // implementations to fail early, during initSign.
154                 try {
155                     signature.initSign(keyPair.getPrivate());
156                     signature.update("A message".getBytes("UTF-8"));
157                     byte[] sigBytes = signature.sign();
158                     fail("Unexpectedly generated a signature (" + sigBytes.length + " bytes): "
159                             + HexEncoding.encode(sigBytes));
160                 } catch (InvalidKeyException | SignatureException expected) {
161                 }
162             } catch (Throwable e) {
163                 throw new RuntimeException("Failed for " + algorithm, e);
164             }
165         }
166     }
167 
importKatKeyPairs(String signatureAlgorithm)168     private Collection<ImportedKey> importKatKeyPairs(String signatureAlgorithm)
169             throws Exception {
170         KeyProtection params =
171                 TestUtils.getMinimalWorkingImportParametersForSigningingWith(signatureAlgorithm);
172         return importKatKeyPairs(getContext(), params);
173     }
174 
importKatKeyPairs( Context context, KeyProtection importParams)175     static Collection<ImportedKey> importKatKeyPairs(
176             Context context, KeyProtection importParams) throws Exception {
177         return Arrays.asList(new ImportedKey[] {
178                 TestUtils.importIntoAndroidKeyStore("testRSA512", context,
179                         R.raw.rsa_key5_512_pkcs8, R.raw.rsa_key5_512_cert, importParams),
180                 TestUtils.importIntoAndroidKeyStore("testRSA768", context,
181                         R.raw.rsa_key6_768_pkcs8, R.raw.rsa_key6_768_cert, importParams),
182                 TestUtils.importIntoAndroidKeyStore("testRSA1024", context,
183                         R.raw.rsa_key3_1024_pkcs8, R.raw.rsa_key3_1024_cert, importParams),
184                 TestUtils.importIntoAndroidKeyStore("testRSA2048", context,
185                         R.raw.rsa_key8_2048_pkcs8, R.raw.rsa_key8_2048_cert, importParams),
186                 TestUtils.importIntoAndroidKeyStore("testRSA3072", context,
187                         R.raw.rsa_key7_3072_pksc8, R.raw.rsa_key7_3072_cert, importParams),
188                 TestUtils.importIntoAndroidKeyStore("testRSA4096", context,
189                         R.raw.rsa_key4_4096_pkcs8, R.raw.rsa_key4_4096_cert, importParams),
190                 });
191     }
192 }
193