1 /*
   2  * Copyright (c) 2003, 2010, 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.util.*;
  30 import java.io.*;
  31 import sun.security.util.*;
  32 import sun.security.x509.*;
  33 import java.security.AlgorithmParametersSpi;
  34 import java.security.NoSuchAlgorithmException;
  35 import java.security.spec.AlgorithmParameterSpec;
  36 import java.security.spec.InvalidParameterSpecException;
  37 import java.security.spec.MGF1ParameterSpec;
  38 import javax.crypto.spec.PSource;
  39 import javax.crypto.spec.OAEPParameterSpec;
  40 
  41 /**
  42  * This class implements the OAEP parameters used with the RSA
  43  * algorithm in OAEP padding. Here is its ASN.1 definition:
  44  * RSAES-OAEP-params ::= SEQUENCE {
  45  *   hashAlgorithm      [0] HashAlgorithm     DEFAULT sha1,
  46  *   maskGenAlgorithm   [1] MaskGenAlgorithm  DEFAULT mgf1SHA1,
  47  *   pSourceAlgorithm   [2] PSourceAlgorithm  DEFAULT pSpecifiedEmpty
  48  * }
  49  *
  50  * @author Valerie Peng
  51  *
  52  */
  53 
  54 public final class OAEPParameters extends AlgorithmParametersSpi {
  55 
  56     private String mdName;
  57     private MGF1ParameterSpec mgfSpec;
  58     private byte[] p;
  59     private static ObjectIdentifier OID_MGF1;
  60     private static ObjectIdentifier OID_PSpecified;
  61 
  62     static {
  63         try {
  64             OID_MGF1 = new ObjectIdentifier(new int[] {1,2,840,113549,1,1,8});
  65         } catch (IOException ioe) {
  66             // should not happen
  67             OID_MGF1 = null;
  68         }
  69         try {
  70             OID_PSpecified =
  71                 new ObjectIdentifier(new int[] {1,2,840,113549,1,1,9});
  72         } catch (IOException ioe) {
  73             // should not happen
  74             OID_PSpecified = null;
  75         }
  76     }
  77 
  78     public OAEPParameters() {
  79     }
  80 
  81     protected void engineInit(AlgorithmParameterSpec paramSpec)
  82         throws InvalidParameterSpecException {
  83         if (!(paramSpec instanceof OAEPParameterSpec)) {
  84             throw new InvalidParameterSpecException
  85                 ("Inappropriate parameter specification");
  86         }
  87         OAEPParameterSpec spec = (OAEPParameterSpec) paramSpec;
  88         mdName = spec.getDigestAlgorithm();
  89         String mgfName = spec.getMGFAlgorithm();
  90         if (!mgfName.equalsIgnoreCase("MGF1")) {
  91             throw new InvalidParameterSpecException("Unsupported mgf " +
  92                 mgfName + "; MGF1 only");
  93         }
  94         AlgorithmParameterSpec mgfSpec = spec.getMGFParameters();
  95         if (!(mgfSpec instanceof MGF1ParameterSpec)) {
  96             throw new InvalidParameterSpecException("Inappropriate mgf " +
  97                 "parameters; non-null MGF1ParameterSpec only");
  98         }
  99         this.mgfSpec = (MGF1ParameterSpec) mgfSpec;
 100         PSource pSrc = spec.getPSource();
 101         if (pSrc.getAlgorithm().equals("PSpecified")) {
 102             p = ((PSource.PSpecified) pSrc).getValue();
 103         } else {
 104             throw new InvalidParameterSpecException("Unsupported pSource " +
 105                 pSrc.getAlgorithm() + "; PSpecified only");
 106         }
 107     }
 108 
 109     protected void engineInit(byte[] encoded)
 110         throws IOException {
 111         DerInputStream der = new DerInputStream(encoded);
 112         mdName = "SHA-1";
 113         mgfSpec = MGF1ParameterSpec.SHA1;
 114         p = new byte[0];
 115         DerValue[] datum = der.getSequence(3);
 116         for (int i=0; i<datum.length; i++) {
 117             DerValue data = datum[i];
 118             if (data.isContextSpecific((byte) 0x00)) {
 119                 // hash algid
 120                 mdName = AlgorithmId.getStandardDigestName(AlgorithmId.parse
 121                     (data.data.getDerValue()).getName());
 122             } else if (data.isContextSpecific((byte) 0x01)) {
 123                 // mgf algid
 124                 AlgorithmId val = AlgorithmId.parse(data.data.getDerValue());
 125                 if (!val.getOID().equals((Object) OID_MGF1)) {
 126                     throw new IOException("Only MGF1 mgf is supported");
 127                 }
 128                 AlgorithmId params = AlgorithmId.parse(
 129                     new DerValue(val.getEncodedParams()));
 130                 String mgfDigestName = AlgorithmId.getStandardDigestName(
 131                         params.getName());
 132                 if (mgfDigestName.equals("SHA-1")) {
 133                     mgfSpec = MGF1ParameterSpec.SHA1;
 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((Object) 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 AlgorithmParameterSpec engineGetParameterSpec(Class paramSpec)
 171         throws InvalidParameterSpecException {
 172         if (OAEPParameterSpec.class.isAssignableFrom(paramSpec)) {
 173             return new OAEPParameterSpec(mdName, "MGF1", mgfSpec,
 174                 new PSource.PSpecified(p));
 175         } else {
 176             throw new InvalidParameterSpecException
 177                 ("Inappropriate parameter specification");
 178         }
 179     }
 180 
 181     protected byte[] engineGetEncoded() throws IOException {
 182         DerOutputStream tmp = new DerOutputStream();
 183         DerOutputStream tmp2, tmp3;
 184 
 185         // MD
 186         AlgorithmId mdAlgId;
 187         try {
 188             mdAlgId = AlgorithmId.get(mdName);
 189         } catch (NoSuchAlgorithmException nsae) {
 190             throw new IOException("AlgorithmId " + mdName +
 191                                   " impl not found");
 192         }
 193         tmp2 = new DerOutputStream();
 194         mdAlgId.derEncode(tmp2);
 195         tmp.write(DerValue.createTag(DerValue.TAG_CONTEXT, true, (byte)0),
 196                       tmp2);
 197 
 198         // MGF
 199         tmp2 = new DerOutputStream();
 200         tmp2.putOID(OID_MGF1);
 201         AlgorithmId mgfDigestId;
 202         try {
 203             mgfDigestId = AlgorithmId.get(mgfSpec.getDigestAlgorithm());
 204         } catch (NoSuchAlgorithmException nase) {
 205             throw new IOException("AlgorithmId " +
 206                     mgfSpec.getDigestAlgorithm() + " impl not found");
 207         }
 208         mgfDigestId.encode(tmp2);
 209         tmp3 = new DerOutputStream();
 210         tmp3.write(DerValue.tag_Sequence, tmp2);
 211         tmp.write(DerValue.createTag(DerValue.TAG_CONTEXT, true, (byte)1),
 212                   tmp3);
 213 
 214         // PSource
 215         tmp2 = new DerOutputStream();
 216         tmp2.putOID(OID_PSpecified);
 217         tmp2.putOctetString(p);
 218         tmp3 = new DerOutputStream();
 219         tmp3.write(DerValue.tag_Sequence, tmp2);
 220         tmp.write(DerValue.createTag(DerValue.TAG_CONTEXT, true, (byte)2),
 221                   tmp3);
 222 
 223         // Put all together under a SEQUENCE tag
 224         DerOutputStream out = new DerOutputStream();
 225         out.write(DerValue.tag_Sequence, tmp);
 226         return out.toByteArray();
 227     }
 228 
 229     protected byte[] engineGetEncoded(String encodingMethod)
 230         throws IOException {
 231         if ((encodingMethod != null) &&
 232             (!encodingMethod.equalsIgnoreCase("ASN.1"))) {
 233             throw new IllegalArgumentException("Only support ASN.1 format");
 234         }
 235         return engineGetEncoded();
 236     }
 237 
 238     protected String engineToString() {
 239         StringBuffer sb = new StringBuffer();
 240         sb.append("MD: " + mdName + "\n");
 241         sb.append("MGF: MGF1" + mgfSpec.getDigestAlgorithm() + "\n");
 242         sb.append("PSource: PSpecified " +
 243             (p.length==0? "":Debug.toHexString(new BigInteger(p))) + "\n");
 244         return sb.toString();
 245     }
 246 }