1 /*
2  * Copyright (C) 2012 The Android Open Source Project
3  *
4  * Licensed under the Apache License, Version 2.0 (the "License");
5  * you may not use this file except in compliance with the License.
6  * You may obtain a copy of the License at
7  *
8  *      http://www.apache.org/licenses/LICENSE-2.0
9  *
10  * Unless required by applicable law or agreed to in writing, software
11  * distributed under the License is distributed on an "AS IS" BASIS,
12  * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13  * See the License for the specific language governing permissions and
14  * limitations under the License.
15  */
16 
17 package org.conscrypt;
18 
19 import java.security.InvalidKeyException;
20 import java.security.InvalidParameterException;
21 import java.security.PrivateKey;
22 import java.security.PublicKey;
23 import java.security.SignatureException;
24 import java.security.SignatureSpi;
25 import java.security.interfaces.RSAPrivateCrtKey;
26 import java.security.interfaces.RSAPrivateKey;
27 import java.security.interfaces.RSAPublicKey;
28 
29 /**
30  * Implements the JDK Signature interface needed for RAW RSA signature
31  * generation and verification using OpenSSL.
32  */
33 public class OpenSSLSignatureRawRSA extends SignatureSpi {
34     /**
35      * The current OpenSSL key we're operating on.
36      */
37     private OpenSSLKey key;
38 
39     /**
40      * Buffer to hold value to be signed or verified.
41      */
42     private byte[] inputBuffer;
43 
44     /**
45      * Current offset in input buffer.
46      */
47     private int inputOffset;
48 
49     /**
50      * Provides a flag to specify when the input is too long.
51      */
52     private boolean inputIsTooLong;
53 
54     @Override
engineUpdate(byte input)55     protected void engineUpdate(byte input) {
56         final int oldOffset = inputOffset++;
57 
58         if (inputOffset > inputBuffer.length) {
59             inputIsTooLong = true;
60             return;
61         }
62 
63         inputBuffer[oldOffset] = input;
64     }
65 
66     @Override
engineUpdate(byte[] input, int offset, int len)67     protected void engineUpdate(byte[] input, int offset, int len) {
68         final int oldOffset = inputOffset;
69         inputOffset += len;
70 
71         if (inputOffset > inputBuffer.length) {
72             inputIsTooLong = true;
73             return;
74         }
75 
76         System.arraycopy(input, offset, inputBuffer, oldOffset, len);
77     }
78 
79     @Override
engineGetParameter(String param)80     protected Object engineGetParameter(String param) throws InvalidParameterException {
81         return null;
82     }
83 
84     @Override
engineInitSign(PrivateKey privateKey)85     protected void engineInitSign(PrivateKey privateKey) throws InvalidKeyException {
86         if (privateKey instanceof OpenSSLRSAPrivateKey) {
87             OpenSSLRSAPrivateKey rsaPrivateKey = (OpenSSLRSAPrivateKey) privateKey;
88             key = rsaPrivateKey.getOpenSSLKey();
89         } else if (privateKey instanceof RSAPrivateCrtKey) {
90             RSAPrivateCrtKey rsaPrivateKey = (RSAPrivateCrtKey) privateKey;
91             key = OpenSSLRSAPrivateCrtKey.getInstance(rsaPrivateKey);
92         } else if (privateKey instanceof RSAPrivateKey) {
93             RSAPrivateKey rsaPrivateKey = (RSAPrivateKey) privateKey;
94             key = OpenSSLRSAPrivateKey.getInstance(rsaPrivateKey);
95         } else {
96             throw new InvalidKeyException("Need RSA private key");
97         }
98 
99         // Allocate buffer according to RSA modulus size.
100         int maxSize = NativeCrypto.RSA_size(key.getNativeRef());
101         inputBuffer = new byte[maxSize];
102         inputOffset = 0;
103     }
104 
105     @Override
engineInitVerify(PublicKey publicKey)106     protected void engineInitVerify(PublicKey publicKey) throws InvalidKeyException {
107         if (publicKey instanceof OpenSSLRSAPublicKey) {
108             OpenSSLRSAPublicKey rsaPublicKey = (OpenSSLRSAPublicKey) publicKey;
109             key = rsaPublicKey.getOpenSSLKey();
110         } else if (publicKey instanceof RSAPublicKey) {
111             RSAPublicKey rsaPublicKey = (RSAPublicKey) publicKey;
112             key = OpenSSLRSAPublicKey.getInstance(rsaPublicKey);
113         } else {
114             throw new InvalidKeyException("Need RSA public key");
115         }
116 
117         // Allocate buffer according to RSA modulus size.
118         int maxSize = NativeCrypto.RSA_size(key.getNativeRef());
119         inputBuffer = new byte[maxSize];
120         inputOffset = 0;
121     }
122 
123     @Override
engineSetParameter(String param, Object value)124     protected void engineSetParameter(String param, Object value) throws InvalidParameterException {
125     }
126 
127     @Override
engineSign()128     protected byte[] engineSign() throws SignatureException {
129         if (key == null) {
130             // This can't actually happen, but you never know...
131             throw new SignatureException("Need RSA private key");
132         }
133 
134         if (inputIsTooLong) {
135             throw new SignatureException("input length " + inputOffset + " != "
136                     + inputBuffer.length + " (modulus size)");
137         }
138 
139         byte[] outputBuffer = new byte[inputBuffer.length];
140         try {
141             NativeCrypto.RSA_private_encrypt(inputOffset, inputBuffer, outputBuffer,
142                     key.getNativeRef(), NativeConstants.RSA_PKCS1_PADDING);
143             return outputBuffer;
144         } catch (Exception ex) {
145             throw new SignatureException(ex);
146         } finally {
147             inputOffset = 0;
148         }
149     }
150 
151     @Override
engineVerify(byte[] sigBytes)152     protected boolean engineVerify(byte[] sigBytes) throws SignatureException {
153         if (key == null) {
154             // This can't actually happen, but you never know...
155             throw new SignatureException("Need RSA public key");
156         }
157 
158         if (inputIsTooLong) {
159             return false;
160         }
161 
162         // We catch this case here instead of BoringSSL so we can throw an
163         // exception that matches other implementations.
164         if (sigBytes.length > inputBuffer.length) {
165             throw new SignatureException("Input signature length is too large: " + sigBytes.length
166                     + " > " + inputBuffer.length);
167         }
168 
169         byte[] outputBuffer = new byte[inputBuffer.length];
170         try {
171             final int resultSize;
172             try {
173                 resultSize = NativeCrypto.RSA_public_decrypt(sigBytes.length, sigBytes,
174                         outputBuffer, key.getNativeRef(), NativeConstants.RSA_PKCS1_PADDING);
175             } catch (SignatureException e) {
176                 throw e;
177             } catch (Exception e) {
178                 return false;
179             }
180             /* Make this constant time by comparing every byte. */
181             boolean matches = (resultSize == inputOffset);
182             for (int i = 0; i < resultSize; i++) {
183                 if (inputBuffer[i] != outputBuffer[i]) {
184                     matches = false;
185                 }
186             }
187             return matches;
188         } catch (Exception ex) {
189             throw new SignatureException(ex);
190         } finally {
191             inputOffset = 0;
192         }
193     }
194 }
195