1/*
2 * Copyright (c) 2003, 2015, 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
26package com.sun.crypto.provider;
27
28import java.math.BigInteger;
29import java.io.*;
30import sun.security.util.*;
31import sun.security.x509.*;
32import java.security.AlgorithmParametersSpi;
33import java.security.NoSuchAlgorithmException;
34import java.security.spec.AlgorithmParameterSpec;
35import java.security.spec.InvalidParameterSpecException;
36import java.security.spec.MGF1ParameterSpec;
37import javax.crypto.spec.PSource;
38import 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
53public 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
77    public OAEPParameters() {
78    }
79
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
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 {
141                    throw new IOException(
142                        "Unrecognized message digest algorithm");
143                }
144            } else if (data.isContextSpecific((byte) 0x02)) {
145                // pSource algid
146                AlgorithmId val = AlgorithmId.parse(data.data.getDerValue());
147                if (!val.getOID().equals(OID_PSpecified)) {
148                    throw new IOException("Wrong OID for pSpecified");
149                }
150                DerInputStream dis = new DerInputStream(val.getEncodedParams());
151                p = dis.getOctetString();
152                if (dis.available() != 0) {
153                    throw new IOException("Extra data for pSpecified");
154                }
155            } else {
156                throw new IOException("Invalid encoded OAEPParameters");
157            }
158        }
159    }
160
161    protected void engineInit(byte[] encoded, String decodingMethod)
162        throws IOException {
163        if ((decodingMethod != null) &&
164            (!decodingMethod.equalsIgnoreCase("ASN.1"))) {
165            throw new IllegalArgumentException("Only support ASN.1 format");
166        }
167        engineInit(encoded);
168    }
169
170    protected <T extends AlgorithmParameterSpec>
171        T engineGetParameterSpec(Class<T> paramSpec)
172        throws InvalidParameterSpecException {
173        if (OAEPParameterSpec.class.isAssignableFrom(paramSpec)) {
174            return paramSpec.cast(
175                new OAEPParameterSpec(mdName, "MGF1", mgfSpec,
176                                      new PSource.PSpecified(p)));
177        } else {
178            throw new InvalidParameterSpecException
179                ("Inappropriate parameter specification");
180        }
181    }
182
183    protected byte[] engineGetEncoded() throws IOException {
184        DerOutputStream tmp = new DerOutputStream();
185        DerOutputStream tmp2, tmp3;
186
187        // MD
188        AlgorithmId mdAlgId;
189        try {
190            mdAlgId = AlgorithmId.get(mdName);
191        } catch (NoSuchAlgorithmException nsae) {
192            throw new IOException("AlgorithmId " + mdName +
193                                  " impl not found");
194        }
195        tmp2 = new DerOutputStream();
196        mdAlgId.derEncode(tmp2);
197        tmp.write(DerValue.createTag(DerValue.TAG_CONTEXT, true, (byte)0),
198                      tmp2);
199
200        // MGF
201        tmp2 = new DerOutputStream();
202        tmp2.putOID(OID_MGF1);
203        AlgorithmId mgfDigestId;
204        try {
205            mgfDigestId = AlgorithmId.get(mgfSpec.getDigestAlgorithm());
206        } catch (NoSuchAlgorithmException nase) {
207            throw new IOException("AlgorithmId " +
208                    mgfSpec.getDigestAlgorithm() + " impl not found");
209        }
210        mgfDigestId.encode(tmp2);
211        tmp3 = new DerOutputStream();
212        tmp3.write(DerValue.tag_Sequence, tmp2);
213        tmp.write(DerValue.createTag(DerValue.TAG_CONTEXT, true, (byte)1),
214                  tmp3);
215
216        // PSource
217        tmp2 = new DerOutputStream();
218        tmp2.putOID(OID_PSpecified);
219        tmp2.putOctetString(p);
220        tmp3 = new DerOutputStream();
221        tmp3.write(DerValue.tag_Sequence, tmp2);
222        tmp.write(DerValue.createTag(DerValue.TAG_CONTEXT, true, (byte)2),
223                  tmp3);
224
225        // Put all together under a SEQUENCE tag
226        DerOutputStream out = new DerOutputStream();
227        out.write(DerValue.tag_Sequence, tmp);
228        return out.toByteArray();
229    }
230
231    protected byte[] engineGetEncoded(String encodingMethod)
232        throws IOException {
233        if ((encodingMethod != null) &&
234            (!encodingMethod.equalsIgnoreCase("ASN.1"))) {
235            throw new IllegalArgumentException("Only support ASN.1 format");
236        }
237        return engineGetEncoded();
238    }
239
240    protected String engineToString() {
241        StringBuilder sb = new StringBuilder();
242        sb.append("MD: " + mdName + "\n");
243        sb.append("MGF: MGF1" + mgfSpec.getDigestAlgorithm() + "\n");
244        sb.append("PSource: PSpecified " +
245            (p.length==0? "":Debug.toHexString(new BigInteger(p))) + "\n");
246        return sb.toString();
247    }
248}
249