1 /*
   2  * Copyright (c) 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.io.IOException;
  29 import java.security.AlgorithmParametersSpi;
  30 import java.security.spec.AlgorithmParameterSpec;
  31 import java.security.spec.InvalidParameterSpecException;
  32 import javax.crypto.spec.IvParameterSpec;
  33 import sun.security.util.*;
  34 
  35 /**
  36  * This class implements the parameter set used with the ChaCha20-Poly1305
  37  * algorithm.  The parameter definition comes from
  38  * <a href="https://tools.ietf.org/html/rfc8103"><i>RFC 8103</i></a>
  39  * and is defined according to the following ASN.1:
  40  *
  41  * <pre>
  42  * id-alg-AEADChaCha20Poly1305 OBJECT IDENTIFIER ::=
  43           { iso(1) member-body(2) us(840) rsadsi(113549) pkcs(1)
  44             pkcs9(9) smime(16) alg(3) 18 }
  45 
  46  * AEADChaCha20Poly1305Nonce ::= OCTET STRING (SIZE(12))
  47  * </pre>
  48  *
  49  * The AlgorithmParameters may be instantiated either by its name
  50  * ("ChaCha20-Poly1305") or via its OID (1.2.840.113549.1.9.16.3.18)
  51  *
  52  * @since 11
  53  */
  54 public final class ChaCha20Poly1305Parameters extends AlgorithmParametersSpi {
  55 
  56     private static final String DEFAULT_FMT = "ASN.1";
  57     private byte[] nonce;
  58 
  59     public ChaCha20Poly1305Parameters() {}
  60 
  61     /**
  62      * Initialize the ChaCha20Poly1305Parameters using an IvParameterSpec.
  63      *
  64      * @param paramSpec the {@code IvParameterSpec} used to configure
  65      *      this object.
  66      *
  67      * @throws InvalidParameterSpecException if an object of a type other
  68      *      than {@code IvParameterSpec} is used.
  69      */
  70     @Override
  71     protected void engineInit(AlgorithmParameterSpec paramSpec)
  72         throws InvalidParameterSpecException {
  73 
  74         if (!(paramSpec instanceof IvParameterSpec)) {
  75             throw new InvalidParameterSpecException
  76                 ("Inappropriate parameter specification");
  77         }
  78         IvParameterSpec ivps = (IvParameterSpec)paramSpec;
  79 
  80         // Obtain the nonce
  81         nonce = ivps.getIV();
  82         if (nonce.length != 12) {
  83             throw new InvalidParameterSpecException("ChaCha20-Poly1305 nonce" +
  84                     " must be 96 bits in length");
  85         }
  86     }
  87 
  88     /**
  89      * Initialize the ChaCha20Poly1305Parameters from a DER encoded
  90      * parameter block.
  91 
  92      * @param encoded the DER encoding of the nonce as an OCTET STRING.
  93      *
  94      * @throws IOException if the encoded nonce is not 12 bytes long or a DER
  95      *      decoding error occurs.
  96      */
  97     @Override
  98     protected void engineInit(byte[] encoded) throws IOException {
  99         DerValue val = new DerValue(encoded);
 100 
 101         // Make sure we're dealing with an OCTET STRING
 102         if (val.tag == DerValue.tag_OctetString) {
 103             // Get the nonce value
 104             nonce = val.getOctetString();
 105             if (nonce.length != 12) {
 106                throw new IOException(
 107                        "ChaCha20-Poly1305 nonce must be 96 bits in length");
 108             }
 109         } else {
 110             throw new IOException(
 111                     "ChaCha20-Poly1305 Parameter ASN.1 encoding error");
 112         }
 113     }
 114 
 115     /**
 116      * Initialize the ChaCha20Poly1305Parameters from a DER encoded
 117      * parameter block.
 118      *
 119      * @param encoded the DER encoding of the nonce and initial block counter.
 120      * @param decodingMethod the decoding method.  The only currently accepted
 121      *      value is "ASN.1"
 122      *
 123      * @throws IOException if the encoded nonce is not 12 bytes long or a DER
 124      *      decoding error occurs.
 125      * @throws IllegalArgumentException if the decodingMethod parameter does
 126      *      not specify a supported format.
 127      */
 128     @Override
 129     protected void engineInit(byte[] encoded, String decodingMethod)
 130         throws IOException {
 131         if (decodingMethod.equalsIgnoreCase(DEFAULT_FMT)) {
 132             engineInit(encoded);
 133         } else {
 134             throw new IllegalArgumentException(
 135                     "Unsupported parameter format: " + decodingMethod);
 136         }
 137     }
 138 
 139     /**
 140      * Return an IvParameterSpec with the same parameters as those
 141      * held in this object.
 142      *
 143      * @param paramSpec the class name of the spec.  In this case it should
 144      *      be {@code IvParameterSpec.class}.
 145      *
 146      * @return a {@code IvParameterSpec} object containing the nonce
 147      *      value held in this object.
 148      *
 149      * @throws InvalidParameterSpecException if a class other than
 150      *      {@code IvParameterSpec.class} was specified in the paramSpec
 151      *      parameter.
 152      */
 153     @Override
 154     protected <T extends AlgorithmParameterSpec>
 155             T engineGetParameterSpec(Class<T> paramSpec)
 156         throws InvalidParameterSpecException {
 157 
 158         if (IvParameterSpec.class.isAssignableFrom(paramSpec)) {
 159             return paramSpec.cast(new IvParameterSpec(nonce));
 160         } else {
 161             throw new InvalidParameterSpecException
 162                 ("Inappropriate parameter specification");
 163         }
 164     }
 165 
 166     /**
 167      * Return the encoded parameters in ASN.1 form.
 168      *
 169      * @return a byte array containing the DER-encoding for the
 170      *      ChaCha20-Poly1305 parameters.  This will be the nonce
 171      *      encoded as a DER OCTET STRING.
 172      *
 173      * @throws IOException if any DER encoding error occurs.
 174      */
 175     @Override
 176     protected byte[] engineGetEncoded() throws IOException {
 177         DerOutputStream out = new DerOutputStream();
 178         out.write(DerValue.tag_OctetString, nonce);
 179         return out.toByteArray();
 180     }
 181 
 182     /**
 183      * Return the encoded parameters in ASN.1 form.
 184      *
 185      * @param encodingMethod the encoding method to be used.  This parameter
 186      *      must be "ASN.1" as it is the only currently supported encoding
 187      *      format.
 188      *
 189      * @return a byte array containing the DER-encoding for the
 190      *      ChaCha20-Poly1305 parameters.
 191      *
 192      * @throws IOException if any DER encoding error occurs.
 193      * @throws IllegalArgumentException if the encodingMethod parameter does
 194      *      not specify a supported format.
 195      */
 196     @Override
 197     protected byte[] engineGetEncoded(String encodingMethod)
 198         throws IOException {
 199         if (encodingMethod.equalsIgnoreCase(DEFAULT_FMT)) {
 200             return engineGetEncoded();
 201         } else {
 202             throw new IllegalArgumentException(
 203                     "Unsupported encoding format: " + encodingMethod);
 204         }
 205     }
 206 
 207     /**
 208      * Creates a formatted string describing the parameters.
 209      *
 210      * @return a string representation of the ChaCha20 parameters.
 211      */
 212     @Override
 213     protected String engineToString() {
 214         String LINE_SEP = System.getProperty("line.separator");
 215         HexDumpEncoder encoder = new HexDumpEncoder();
 216         StringBuilder sb = new StringBuilder(LINE_SEP + "nonce:" +
 217                 LINE_SEP + "[" + encoder.encodeBuffer(nonce) + "]");
 218         return sb.toString();
 219     }
 220 }