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
17package org.conscrypt;
18
19import java.security.InvalidKeyException;
20import java.security.InvalidParameterException;
21import java.security.NoSuchAlgorithmException;
22import java.security.PrivateKey;
23import java.security.PublicKey;
24import java.security.Signature;
25import java.security.SignatureException;
26import java.security.interfaces.RSAPrivateCrtKey;
27import java.security.interfaces.RSAPrivateKey;
28import java.security.interfaces.RSAPublicKey;
29
30/**
31 * Implements the JDK Signature interface needed for RAW RSA signature
32 * generation and verification using OpenSSL.
33 */
34public class OpenSSLSignatureRawRSA extends Signature {
35    /**
36     * The current OpenSSL key we're operating on.
37     */
38    private OpenSSLKey key;
39
40    /**
41     * Buffer to hold value to be signed or verified.
42     */
43    private byte[] inputBuffer;
44
45    /**
46     * Current offset in input buffer.
47     */
48    private int inputOffset;
49
50    /**
51     * Provides a flag to specify when the input is too long.
52     */
53    private boolean inputIsTooLong;
54
55    /**
56     * Creates a new OpenSSLSignature instance for the given algorithm name.
57     */
58    public OpenSSLSignatureRawRSA() throws NoSuchAlgorithmException {
59        super("NONEwithRSA");
60    }
61
62    @Override
63    protected void engineUpdate(byte input) {
64        final int oldOffset = inputOffset++;
65
66        if (inputOffset > inputBuffer.length) {
67            inputIsTooLong = true;
68            return;
69        }
70
71        inputBuffer[oldOffset] = input;
72    }
73
74    @Override
75    protected void engineUpdate(byte[] input, int offset, int len) {
76        final int oldOffset = inputOffset;
77        inputOffset += len;
78
79        if (inputOffset > inputBuffer.length) {
80            inputIsTooLong = true;
81            return;
82        }
83
84        System.arraycopy(input, offset, inputBuffer, oldOffset, len);
85    }
86
87    @Override
88    protected Object engineGetParameter(String param) throws InvalidParameterException {
89        return null;
90    }
91
92    @Override
93    protected void engineInitSign(PrivateKey privateKey) throws InvalidKeyException {
94        if (privateKey instanceof OpenSSLRSAPrivateKey) {
95            OpenSSLRSAPrivateKey rsaPrivateKey = (OpenSSLRSAPrivateKey) privateKey;
96            key = rsaPrivateKey.getOpenSSLKey();
97        } else if (privateKey instanceof RSAPrivateCrtKey) {
98            RSAPrivateCrtKey rsaPrivateKey = (RSAPrivateCrtKey) privateKey;
99            key = OpenSSLRSAPrivateCrtKey.getInstance(rsaPrivateKey);
100        } else if (privateKey instanceof RSAPrivateKey) {
101            RSAPrivateKey rsaPrivateKey = (RSAPrivateKey) privateKey;
102            key = OpenSSLRSAPrivateKey.getInstance(rsaPrivateKey);
103        } else {
104            throw new InvalidKeyException("Need RSA private key");
105        }
106
107        // Allocate buffer according to RSA modulus size.
108        int maxSize = NativeCrypto.RSA_size(key.getPkeyContext());
109        inputBuffer = new byte[maxSize];
110        inputOffset = 0;
111    }
112
113    @Override
114    protected void engineInitVerify(PublicKey publicKey) throws InvalidKeyException {
115        if (publicKey instanceof OpenSSLRSAPublicKey) {
116            OpenSSLRSAPublicKey rsaPublicKey = (OpenSSLRSAPublicKey) publicKey;
117            key = rsaPublicKey.getOpenSSLKey();
118        } else if (publicKey instanceof RSAPublicKey) {
119            RSAPublicKey rsaPublicKey = (RSAPublicKey) publicKey;
120            key = OpenSSLRSAPublicKey.getInstance(rsaPublicKey);
121        } else {
122            throw new InvalidKeyException("Need RSA public key");
123        }
124
125        // Allocate buffer according to RSA modulus size.
126        int maxSize = NativeCrypto.RSA_size(key.getPkeyContext());
127        inputBuffer = new byte[maxSize];
128        inputOffset = 0;
129    }
130
131    @Override
132    protected void engineSetParameter(String param, Object value) throws InvalidParameterException {
133    }
134
135    @Override
136    protected byte[] engineSign() throws SignatureException {
137        if (key == null) {
138            // This can't actually happen, but you never know...
139            throw new SignatureException("Need RSA private key");
140        }
141
142        if (inputIsTooLong) {
143            throw new SignatureException("input length " + inputOffset + " != "
144                    + inputBuffer.length + " (modulus size)");
145        }
146
147        byte[] outputBuffer = new byte[inputBuffer.length];
148        try {
149            NativeCrypto.RSA_private_encrypt(inputOffset, inputBuffer, outputBuffer,
150                    key.getPkeyContext(), NativeCrypto.RSA_PKCS1_PADDING);
151            return outputBuffer;
152        } catch (Exception ex) {
153            throw new SignatureException(ex);
154        } finally {
155            inputOffset = 0;
156        }
157    }
158
159    @Override
160    protected boolean engineVerify(byte[] sigBytes) throws SignatureException {
161        if (key == null) {
162            // This can't actually happen, but you never know...
163            throw new SignatureException("Need RSA public key");
164        }
165
166        if (inputIsTooLong) {
167            return false;
168        }
169
170        byte[] outputBuffer = new byte[inputBuffer.length];
171        try {
172            final int resultSize;
173            try {
174                resultSize = NativeCrypto.RSA_public_decrypt(sigBytes.length, sigBytes,
175                        outputBuffer, key.getPkeyContext(), NativeCrypto.RSA_PKCS1_PADDING);
176            } catch (SignatureException e) {
177                throw e;
178            } catch (Exception e) {
179                return false;
180            }
181            /* Make this constant time by comparing every byte. */
182            boolean matches = (resultSize == inputOffset);
183            for (int i = 0; i < resultSize; i++) {
184                if (inputBuffer[i] != outputBuffer[i]) {
185                    matches = false;
186                }
187            }
188            return matches;
189        } catch (Exception ex) {
190            throw new SignatureException(ex);
191        } finally {
192            inputOffset = 0;
193        }
194    }
195}
196