Home | History | Annotate | Download | only in conscrypt
      1 /*
      2  * Copyright 2017 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.io.IOException;
     20 import java.security.AlgorithmParametersSpi;
     21 import java.security.spec.AlgorithmParameterSpec;
     22 import java.security.spec.InvalidParameterSpecException;
     23 import java.security.spec.MGF1ParameterSpec;
     24 import java.util.HashMap;
     25 import java.util.Map;
     26 import javax.crypto.spec.OAEPParameterSpec;
     27 import javax.crypto.spec.PSource;
     28 
     29 /**
     30  * AlgorithmParameters implementation for OAEP.  The only supported encoding format is ASN.1,
     31  * as specified in RFC 4055 section 4.1.
     32  *
     33  * @hide
     34  */
     35 @Internal
     36 public class OAEPParameters extends AlgorithmParametersSpi {
     37 
     38     private static final Map<String, String> OID_TO_NAME = new HashMap<String, String>();
     39     private static final Map<String, String> NAME_TO_OID = new HashMap<String, String>();
     40     static {
     41         OID_TO_NAME.put("1.3.14.3.2.26", "SHA-1");
     42         OID_TO_NAME.put("2.16.840.1.101.3.4.2.4", "SHA-224");
     43         OID_TO_NAME.put("2.16.840.1.101.3.4.2.1", "SHA-256");
     44         OID_TO_NAME.put("2.16.840.1.101.3.4.2.2", "SHA-384");
     45         OID_TO_NAME.put("2.16.840.1.101.3.4.2.3", "SHA-512");
     46         for (Map.Entry<String, String> entry : OID_TO_NAME.entrySet()) {
     47             NAME_TO_OID.put(entry.getValue(), entry.getKey());
     48         }
     49     }
     50     private static final String MGF1_OID = "1.2.840.113549.1.1.8";
     51     private static final String PSPECIFIED_OID = "1.2.840.113549.1.1.9";
     52 
     53     private OAEPParameterSpec spec = OAEPParameterSpec.DEFAULT;
     54 
     55     @Override
     56     protected void engineInit(AlgorithmParameterSpec algorithmParameterSpec)
     57             throws InvalidParameterSpecException {
     58         if (algorithmParameterSpec instanceof OAEPParameterSpec) {
     59             this.spec = (OAEPParameterSpec) algorithmParameterSpec;
     60         } else {
     61             throw new InvalidParameterSpecException("Only OAEPParameterSpec is supported");
     62         }
     63     }
     64 
     65     @Override
     66     protected void engineInit(byte[] bytes) throws IOException {
     67         long readRef = 0;
     68         long seqRef = 0;
     69         try {
     70             readRef = NativeCrypto.asn1_read_init(bytes);
     71             seqRef = NativeCrypto.asn1_read_sequence(readRef);
     72             String hash = "SHA-1";
     73             String mgfHash = "SHA-1";
     74             PSource.PSpecified pSpecified = PSource.PSpecified.DEFAULT;
     75             if (NativeCrypto.asn1_read_next_tag_is(seqRef, 0)) {
     76                 long hashRef = 0;
     77                 try {
     78                     hashRef = NativeCrypto.asn1_read_tagged(seqRef);
     79                     hash = getHashName(hashRef);
     80                 } finally {
     81                     NativeCrypto.asn1_read_free(hashRef);
     82                 }
     83             }
     84             if (NativeCrypto.asn1_read_next_tag_is(seqRef, 1)) {
     85                 long mgfRef = 0;
     86                 long mgfSeqRef = 0;
     87                 try {
     88                     mgfRef = NativeCrypto.asn1_read_tagged(seqRef);
     89                     mgfSeqRef = NativeCrypto.asn1_read_sequence(mgfRef);
     90                     String mgfOid = NativeCrypto.asn1_read_oid(mgfSeqRef);
     91                     if (!mgfOid.equals(MGF1_OID)) {
     92                         throw new IOException("Error reading ASN.1 encoding");
     93                     }
     94                     mgfHash = getHashName(mgfSeqRef);
     95                     if (!NativeCrypto.asn1_read_is_empty(mgfSeqRef)) {
     96                         throw new IOException("Error reading ASN.1 encoding");
     97                     }
     98                 } finally {
     99                     NativeCrypto.asn1_read_free(mgfSeqRef);
    100                     NativeCrypto.asn1_read_free(mgfRef);
    101                 }
    102             }
    103             if (NativeCrypto.asn1_read_next_tag_is(seqRef, 2)) {
    104                 long pSourceRef = 0;
    105                 long pSourceSeqRef = 0;
    106                 try {
    107                     pSourceRef = NativeCrypto.asn1_read_tagged(seqRef);
    108                     pSourceSeqRef = NativeCrypto.asn1_read_sequence(pSourceRef);
    109                     String pSourceOid = NativeCrypto.asn1_read_oid(pSourceSeqRef);
    110                     if (!pSourceOid.equals(PSPECIFIED_OID)) {
    111                         throw new IOException("Error reading ASN.1 encoding");
    112                     }
    113                     pSpecified = new PSource.PSpecified(
    114                             NativeCrypto.asn1_read_octetstring(pSourceSeqRef));
    115                     if (!NativeCrypto.asn1_read_is_empty(pSourceSeqRef)) {
    116                         throw new IOException("Error reading ASN.1 encoding");
    117                     }
    118                 } finally {
    119                     NativeCrypto.asn1_read_free(pSourceSeqRef);
    120                     NativeCrypto.asn1_read_free(pSourceRef);
    121                 }
    122             }
    123 
    124             if (!NativeCrypto.asn1_read_is_empty(seqRef)
    125                     || !NativeCrypto.asn1_read_is_empty(readRef)) {
    126                 throw new IOException("Error reading ASN.1 encoding");
    127             }
    128             this.spec = new OAEPParameterSpec(hash, "MGF1", new MGF1ParameterSpec(mgfHash),
    129                     pSpecified);
    130         } finally {
    131             NativeCrypto.asn1_read_free(seqRef);
    132             NativeCrypto.asn1_read_free(readRef);
    133         }
    134     }
    135 
    136     @Override
    137     protected void engineInit(byte[] bytes, String format) throws IOException {
    138         if ((format == null) || format.equals("ASN.1")) {
    139             engineInit(bytes);
    140         } else {
    141             throw new IOException("Unsupported format: " + format);
    142         }
    143     }
    144 
    145     private static String getHashName(long hashRef) throws IOException {
    146         long hashSeqRef = 0;
    147         try {
    148             hashSeqRef = NativeCrypto.asn1_read_sequence(hashRef);
    149             String hashOid = NativeCrypto.asn1_read_oid(hashSeqRef);
    150             if (!NativeCrypto.asn1_read_is_empty(hashSeqRef)) {
    151                 NativeCrypto.asn1_read_null(hashSeqRef);
    152             }
    153             if (!NativeCrypto.asn1_read_is_empty(hashSeqRef)
    154                     || !OID_TO_NAME.containsKey(hashOid)) {
    155                 throw new IOException("Error reading ASN.1 encoding");
    156             }
    157             return OID_TO_NAME.get(hashOid);
    158         } finally {
    159             NativeCrypto.asn1_read_free(hashSeqRef);
    160         }
    161     }
    162 
    163     @Override
    164     @SuppressWarnings("unchecked")
    165     protected <T extends AlgorithmParameterSpec> T engineGetParameterSpec(Class<T> aClass)
    166             throws InvalidParameterSpecException {
    167         if ((aClass != null) && aClass == OAEPParameterSpec.class) {
    168             return (T) spec;
    169         } else {
    170             throw new InvalidParameterSpecException("Unsupported class: " + aClass);
    171         }
    172     }
    173 
    174     @Override
    175     protected byte[] engineGetEncoded() throws IOException {
    176         long cbbRef = 0;
    177         long seqRef = 0;
    178         try {
    179             cbbRef = NativeCrypto.asn1_write_init();
    180             seqRef = NativeCrypto.asn1_write_sequence(cbbRef);
    181             // Implementations are prohibited from writing the default value for any of the fields
    182             if (!spec.getDigestAlgorithm().equals("SHA-1")) {
    183                 long hashRef = 0;
    184                 long hashParamsRef = 0;
    185                 try {
    186                     hashRef = NativeCrypto.asn1_write_tag(seqRef, 0);
    187                     hashParamsRef = writeAlgorithmIdentifier(
    188                             hashRef, NAME_TO_OID.get(spec.getDigestAlgorithm()));
    189                     NativeCrypto.asn1_write_null(hashParamsRef);
    190                 } finally {
    191                     NativeCrypto.asn1_write_flush(seqRef);
    192                     NativeCrypto.asn1_write_free(hashParamsRef);
    193                     NativeCrypto.asn1_write_free(hashRef);
    194                 }
    195             }
    196             MGF1ParameterSpec mgfSpec = (MGF1ParameterSpec) spec.getMGFParameters();
    197             if (!mgfSpec.getDigestAlgorithm().equals("SHA-1")) {
    198                 long mgfRef = 0;
    199                 long mgfParamsRef = 0;
    200                 long hashParamsRef = 0;
    201                 try {
    202                     mgfRef = NativeCrypto.asn1_write_tag(seqRef, 1);
    203                     mgfParamsRef = writeAlgorithmIdentifier(mgfRef, MGF1_OID);
    204                     hashParamsRef = writeAlgorithmIdentifier(
    205                             mgfParamsRef, NAME_TO_OID.get(mgfSpec.getDigestAlgorithm()));
    206                     NativeCrypto.asn1_write_null(hashParamsRef);
    207                 } finally {
    208                     NativeCrypto.asn1_write_flush(seqRef);
    209                     NativeCrypto.asn1_write_free(hashParamsRef);
    210                     NativeCrypto.asn1_write_free(mgfParamsRef);
    211                     NativeCrypto.asn1_write_free(mgfRef);
    212                 }
    213             }
    214             PSource.PSpecified pSource = (PSource.PSpecified) spec.getPSource();
    215             if (pSource.getValue().length != 0) {
    216                 long pSourceRef = 0;
    217                 long pSourceParamsRef = 0;
    218                 try {
    219                     pSourceRef = NativeCrypto.asn1_write_tag(seqRef, 2);
    220                     pSourceParamsRef = writeAlgorithmIdentifier(pSourceRef, PSPECIFIED_OID);
    221                     NativeCrypto.asn1_write_octetstring(pSourceParamsRef, pSource.getValue());
    222                 } finally {
    223                     NativeCrypto.asn1_write_flush(seqRef);
    224                     NativeCrypto.asn1_write_free(pSourceParamsRef);
    225                     NativeCrypto.asn1_write_free(pSourceRef);
    226                 }
    227             }
    228             return NativeCrypto.asn1_write_finish(cbbRef);
    229         } catch (IOException e) {
    230             NativeCrypto.asn1_write_cleanup(cbbRef);
    231             throw e;
    232         } finally {
    233             NativeCrypto.asn1_write_free(seqRef);
    234             NativeCrypto.asn1_write_free(cbbRef);
    235         }
    236     }
    237 
    238     @Override
    239     protected byte[] engineGetEncoded(String format) throws IOException {
    240         if ((format == null) || format.equals("ASN.1")) {
    241             return engineGetEncoded();
    242         }
    243         throw new IOException("Unsupported format: " + format);
    244     }
    245 
    246     /**
    247      * Writes an ASN.1 AlgorithmIdentifier structure into container, which looks like
    248      * <pre>
    249      * SEQUENCE
    250      *   OBJECT IDENTIFIER
    251      *   PARAMS (based on the particular algorithm)
    252      * </pre>
    253      * This method returns a reference to the sequence such that the params may be added to it.
    254      * The reference needs to be freed with asn1_write_free once it's used.
    255      */
    256     private static long writeAlgorithmIdentifier(long container, String oid) throws IOException {
    257         long seqRef = 0;
    258         try {
    259             seqRef = NativeCrypto.asn1_write_sequence(container);
    260             NativeCrypto.asn1_write_oid(seqRef, oid);
    261         } catch (IOException e) {
    262             NativeCrypto.asn1_write_free(seqRef);
    263             throw e;
    264         }
    265         return seqRef;
    266     }
    267 
    268     @Override
    269     protected String engineToString() {
    270         return "Conscrypt OAEP AlgorithmParameters";
    271     }
    272 }
    273