1 /*
2  * Copyright (c) 2003, 2018, Oracle and/or its affiliates. All rights reserved.
3  * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
4  *
5  * This code is free software; you can redistribute it and/or modify it
6  * under the terms of the GNU General Public License version 2 only, as
7  * published by the Free Software Foundation.  Oracle designates this
8  * particular file as subject to the "Classpath" exception as provided
9  * by Oracle in the LICENSE file that accompanied this code.
10  *
11  * This code is distributed in the hope that it will be useful, but WITHOUT
12  * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
13  * FITNESS FOR A PARTICULAR PURPOSE.  See the GNU General Public License
14  * version 2 for more details (a copy is included in the LICENSE file that
15  * accompanied this code).
16  *
17  * You should have received a copy of the GNU General Public License version
18  * 2 along with this work; if not, write to the Free Software Foundation,
19  * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA.
20  *
21  * Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA
22  * or visit www.oracle.com if you need additional information or have any
23  * questions.
24  */
25 
26 package com.sun.crypto.provider;
27 
28 import java.math.BigInteger;
29 import java.io.*;
30 import sun.security.util.*;
31 import sun.security.x509.*;
32 import java.security.AlgorithmParametersSpi;
33 import java.security.NoSuchAlgorithmException;
34 import java.security.spec.AlgorithmParameterSpec;
35 import java.security.spec.InvalidParameterSpecException;
36 import java.security.spec.MGF1ParameterSpec;
37 import javax.crypto.spec.PSource;
38 import javax.crypto.spec.OAEPParameterSpec;
39 
40 /**
41  * This class implements the OAEP parameters used with the RSA
42  * algorithm in OAEP padding. Here is its ASN.1 definition:
43  * RSAES-OAEP-params ::= SEQUENCE {
44  *   hashAlgorithm      [0] HashAlgorithm     DEFAULT sha1,
45  *   maskGenAlgorithm   [1] MaskGenAlgorithm  DEFAULT mgf1SHA1,
46  *   pSourceAlgorithm   [2] PSourceAlgorithm  DEFAULT pSpecifiedEmpty
47  * }
48  *
49  * @author Valerie Peng
50  *
51  */
52 
53 public final class OAEPParameters extends AlgorithmParametersSpi {
54 
55     private String mdName;
56     private MGF1ParameterSpec mgfSpec;
57     private byte[] p;
58     private static ObjectIdentifier OID_MGF1;
59     private static ObjectIdentifier OID_PSpecified;
60 
61     static {
62         try {
63             OID_MGF1 = new ObjectIdentifier(new int[] {1,2,840,113549,1,1,8});
64         } catch (IOException ioe) {
65             // should not happen
66             OID_MGF1 = null;
67         }
68         try {
69             OID_PSpecified =
70                 new ObjectIdentifier(new int[] {1,2,840,113549,1,1,9});
71         } catch (IOException ioe) {
72             // should not happen
73             OID_PSpecified = null;
74         }
75     }
76 
OAEPParameters()77     public OAEPParameters() {
78     }
79 
engineInit(AlgorithmParameterSpec paramSpec)80     protected void engineInit(AlgorithmParameterSpec paramSpec)
81         throws InvalidParameterSpecException {
82         if (!(paramSpec instanceof OAEPParameterSpec)) {
83             throw new InvalidParameterSpecException
84                 ("Inappropriate parameter specification");
85         }
86         OAEPParameterSpec spec = (OAEPParameterSpec) paramSpec;
87         mdName = spec.getDigestAlgorithm();
88         String mgfName = spec.getMGFAlgorithm();
89         if (!mgfName.equalsIgnoreCase("MGF1")) {
90             throw new InvalidParameterSpecException("Unsupported mgf " +
91                 mgfName + "; MGF1 only");
92         }
93         AlgorithmParameterSpec mgfSpec = spec.getMGFParameters();
94         if (!(mgfSpec instanceof MGF1ParameterSpec)) {
95             throw new InvalidParameterSpecException("Inappropriate mgf " +
96                 "parameters; non-null MGF1ParameterSpec only");
97         }
98         this.mgfSpec = (MGF1ParameterSpec) mgfSpec;
99         PSource pSrc = spec.getPSource();
100         if (pSrc.getAlgorithm().equals("PSpecified")) {
101             p = ((PSource.PSpecified) pSrc).getValue();
102         } else {
103             throw new InvalidParameterSpecException("Unsupported pSource " +
104                 pSrc.getAlgorithm() + "; PSpecified only");
105         }
106     }
107 
engineInit(byte[] encoded)108     protected void engineInit(byte[] encoded)
109         throws IOException {
110         DerInputStream der = new DerInputStream(encoded);
111         mdName = "SHA-1";
112         mgfSpec = MGF1ParameterSpec.SHA1;
113         p = new byte[0];
114         DerValue[] datum = der.getSequence(3);
115         for (int i=0; i<datum.length; i++) {
116             DerValue data = datum[i];
117             if (data.isContextSpecific((byte) 0x00)) {
118                 // hash algid
119                 mdName = AlgorithmId.parse
120                     (data.data.getDerValue()).getName();
121             } else if (data.isContextSpecific((byte) 0x01)) {
122                 // mgf algid
123                 AlgorithmId val = AlgorithmId.parse(data.data.getDerValue());
124                 if (!val.getOID().equals(OID_MGF1)) {
125                     throw new IOException("Only MGF1 mgf is supported");
126                 }
127                 AlgorithmId params = AlgorithmId.parse(
128                     new DerValue(val.getEncodedParams()));
129                 String mgfDigestName = params.getName();
130                 if (mgfDigestName.equals("SHA-1")) {
131                     mgfSpec = MGF1ParameterSpec.SHA1;
132                 } else if (mgfDigestName.equals("SHA-224")) {
133                     mgfSpec = MGF1ParameterSpec.SHA224;
134                 } else if (mgfDigestName.equals("SHA-256")) {
135                     mgfSpec = MGF1ParameterSpec.SHA256;
136                 } else if (mgfDigestName.equals("SHA-384")) {
137                     mgfSpec = MGF1ParameterSpec.SHA384;
138                 } else if (mgfDigestName.equals("SHA-512")) {
139                     mgfSpec = MGF1ParameterSpec.SHA512;
140                 } else if (mgfDigestName.equals("SHA-512/224")) {
141                     mgfSpec = MGF1ParameterSpec.SHA512_224;
142                 } else if (mgfDigestName.equals("SHA-512/256")) {
143                     mgfSpec = MGF1ParameterSpec.SHA512_256;
144                 } else {
145                     throw new IOException(
146                         "Unrecognized message digest algorithm");
147                 }
148             } else if (data.isContextSpecific((byte) 0x02)) {
149                 // pSource algid
150                 AlgorithmId val = AlgorithmId.parse(data.data.getDerValue());
151                 if (!val.getOID().equals(OID_PSpecified)) {
152                     throw new IOException("Wrong OID for pSpecified");
153                 }
154                 DerInputStream dis = new DerInputStream(val.getEncodedParams());
155                 p = dis.getOctetString();
156                 if (dis.available() != 0) {
157                     throw new IOException("Extra data for pSpecified");
158                 }
159             } else {
160                 throw new IOException("Invalid encoded OAEPParameters");
161             }
162         }
163     }
164 
engineInit(byte[] encoded, String decodingMethod)165     protected void engineInit(byte[] encoded, String decodingMethod)
166         throws IOException {
167         if ((decodingMethod != null) &&
168             (!decodingMethod.equalsIgnoreCase("ASN.1"))) {
169             throw new IllegalArgumentException("Only support ASN.1 format");
170         }
171         engineInit(encoded);
172     }
173 
174     protected <T extends AlgorithmParameterSpec>
engineGetParameterSpec(Class<T> paramSpec)175         T engineGetParameterSpec(Class<T> paramSpec)
176         throws InvalidParameterSpecException {
177         if (OAEPParameterSpec.class.isAssignableFrom(paramSpec)) {
178             return paramSpec.cast(
179                 new OAEPParameterSpec(mdName, "MGF1", mgfSpec,
180                                       new PSource.PSpecified(p)));
181         } else {
182             throw new InvalidParameterSpecException
183                 ("Inappropriate parameter specification");
184         }
185     }
186 
engineGetEncoded()187     protected byte[] engineGetEncoded() throws IOException {
188         DerOutputStream tmp = new DerOutputStream();
189         DerOutputStream tmp2, tmp3;
190 
191         // MD
192         AlgorithmId mdAlgId;
193         try {
194             mdAlgId = AlgorithmId.get(mdName);
195         } catch (NoSuchAlgorithmException nsae) {
196             throw new IOException("AlgorithmId " + mdName +
197                                   " impl not found");
198         }
199         tmp2 = new DerOutputStream();
200         mdAlgId.derEncode(tmp2);
201         tmp.write(DerValue.createTag(DerValue.TAG_CONTEXT, true, (byte)0),
202                       tmp2);
203 
204         // MGF
205         tmp2 = new DerOutputStream();
206         tmp2.putOID(OID_MGF1);
207         AlgorithmId mgfDigestId;
208         try {
209             mgfDigestId = AlgorithmId.get(mgfSpec.getDigestAlgorithm());
210         } catch (NoSuchAlgorithmException nase) {
211             throw new IOException("AlgorithmId " +
212                     mgfSpec.getDigestAlgorithm() + " impl not found");
213         }
214         mgfDigestId.encode(tmp2);
215         tmp3 = new DerOutputStream();
216         tmp3.write(DerValue.tag_Sequence, tmp2);
217         tmp.write(DerValue.createTag(DerValue.TAG_CONTEXT, true, (byte)1),
218                   tmp3);
219 
220         // PSource
221         tmp2 = new DerOutputStream();
222         tmp2.putOID(OID_PSpecified);
223         tmp2.putOctetString(p);
224         tmp3 = new DerOutputStream();
225         tmp3.write(DerValue.tag_Sequence, tmp2);
226         tmp.write(DerValue.createTag(DerValue.TAG_CONTEXT, true, (byte)2),
227                   tmp3);
228 
229         // Put all together under a SEQUENCE tag
230         DerOutputStream out = new DerOutputStream();
231         out.write(DerValue.tag_Sequence, tmp);
232         return out.toByteArray();
233     }
234 
engineGetEncoded(String encodingMethod)235     protected byte[] engineGetEncoded(String encodingMethod)
236         throws IOException {
237         if ((encodingMethod != null) &&
238             (!encodingMethod.equalsIgnoreCase("ASN.1"))) {
239             throw new IllegalArgumentException("Only support ASN.1 format");
240         }
241         return engineGetEncoded();
242     }
243 
engineToString()244     protected String engineToString() {
245         StringBuilder sb = new StringBuilder();
246         sb.append("MD: " + mdName + "\n");
247         sb.append("MGF: MGF1" + mgfSpec.getDigestAlgorithm() + "\n");
248         sb.append("PSource: PSpecified " +
249             (p.length==0? "":Debug.toHexString(new BigInteger(p))) + "\n");
250         return sb.toString();
251     }
252 }
253