src/java.base/share/classes/sun/security/ssl/HKDF.java
branchJDK-8145252-TLS13-branch
changeset 56542 56aaa6cb3693
child 56637 d66751750b72
equal deleted inserted replaced
56541:92cbbfc996f3 56542:56aaa6cb3693
       
     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 sun.security.ssl;
       
    27 
       
    28 import java.security.NoSuchAlgorithmException;
       
    29 import java.security.InvalidKeyException;
       
    30 import javax.crypto.Mac;
       
    31 import javax.crypto.SecretKey;
       
    32 import javax.crypto.ShortBufferException;
       
    33 import javax.crypto.spec.SecretKeySpec;
       
    34 import java.util.Objects;
       
    35 
       
    36 /**
       
    37  * An implementation of the HKDF key derivation algorithm outlined in RFC 5869,
       
    38  * specific to the needs of TLS 1.3 key derivation in JSSE.  This is not a
       
    39  * general purpose HKDF implementation and is suited only to single-key output
       
    40  * derivations.
       
    41  *
       
    42  * HKDF objects are created by specifying a message digest algorithm.  That
       
    43  * digest algorithm will be used by the HMAC function as part of the HKDF
       
    44  * derivation process.
       
    45  */
       
    46 class HKDF {
       
    47     private final String hmacAlg;
       
    48     private final Mac hmacObj;
       
    49     private final int hmacLen;
       
    50 
       
    51     /**
       
    52      * Create an HDKF object, specifying the underlying message digest
       
    53      * algorithm.
       
    54      *
       
    55      * @param hashAlg a standard name corresponding to a supported message
       
    56      * digest algorithm.
       
    57      *
       
    58      * @throws NoSuchAlgorithmException if that message digest algorithm does
       
    59      * not have an HMAC variant supported on any available provider.
       
    60      */
       
    61     HKDF(String hashAlg) throws NoSuchAlgorithmException {
       
    62         Objects.requireNonNull(hashAlg,
       
    63                 "Must provide underlying HKDF Digest algorithm.");
       
    64         hmacAlg = "Hmac" + hashAlg.replace("-", "");
       
    65         hmacObj = Mac.getInstance(hmacAlg);
       
    66         hmacLen = hmacObj.getMacLength();
       
    67     }
       
    68 
       
    69     /**
       
    70      * Perform the HMAC-Extract derivation.
       
    71      *
       
    72      * @param salt a salt value, implemented as a {@code SecretKey}.  A
       
    73      * {@code null} value is allowed, which will internally use an array of
       
    74      * zero bytes the same size as the underlying hash output length.
       
    75      * @param inputKey the input keying material provided as a
       
    76      * {@code SecretKey}.
       
    77      * @param keyAlg the algorithm name assigned to the resulting
       
    78      * {@code SecretKey} object.
       
    79      *
       
    80      * @return a {@code SecretKey} that is the result of the HKDF extract
       
    81      * operation.
       
    82      *
       
    83      * @throws InvalidKeyException if the {@code salt} parameter cannot be
       
    84      * used to initialize the underlying HMAC.
       
    85      */
       
    86     SecretKey extract(SecretKey salt, SecretKey inputKey, String keyAlg)
       
    87             throws InvalidKeyException {
       
    88         if (salt == null) {
       
    89             salt = new SecretKeySpec(new byte[hmacLen], "HKDF-Salt");
       
    90         }
       
    91         hmacObj.init(salt);
       
    92 
       
    93         return new SecretKeySpec(hmacObj.doFinal(inputKey.getEncoded()),
       
    94                 keyAlg);
       
    95     }
       
    96 
       
    97     /**
       
    98      * Perform the HMAC-Extract derivation.
       
    99      *
       
   100      * @param salt a salt value as cleartext bytes.  A {@code null} value is
       
   101      * allowed, which will internally use an array of zero bytes the same
       
   102      * size as the underlying hash output length.
       
   103      * @param inputKey the input keying material provided as a
       
   104      * {@code SecretKey}.
       
   105      * @param keyAlg the algorithm name assigned to the resulting
       
   106      * {@code SecretKey} object.
       
   107      *
       
   108      * @return a {@code SecretKey} that is the result of the HKDF extract
       
   109      * operation.
       
   110      *
       
   111      * @throws InvalidKeyException if the {@code salt} parameter cannot be
       
   112      * used to initialize the underlying HMAC.
       
   113      */
       
   114     SecretKey extract(byte[] salt, SecretKey inputKey, String keyAlg)
       
   115             throws InvalidKeyException {
       
   116         if (salt == null) {
       
   117             salt = new byte[hmacLen];
       
   118         }
       
   119         return extract(new SecretKeySpec(salt, "HKDF-Salt"), inputKey, keyAlg);
       
   120     }
       
   121 
       
   122     /**
       
   123      * Perform the HKDF-Expand derivation for a single-key output.
       
   124      *
       
   125      * @param pseudoRandKey the pseudo random key (PRK).
       
   126      * @param info optional context-specific info.  A {@code null} value is
       
   127      * allowed in which case a zero-length byte array will be used.
       
   128      * @param outLen the length of the resulting {@code SecretKey}
       
   129      * @param keyAlg the algorithm name applied to the resulting
       
   130      * {@code SecretKey}
       
   131      *
       
   132      * @return the resulting key derivation as a {@code SecretKey} object
       
   133      *
       
   134      * @throws InvalidKeyException if the underlying HMAC operation cannot
       
   135      * be initialized using the provided {@code pseudoRandKey} object.
       
   136      */
       
   137     SecretKey expand(SecretKey pseudoRandKey, byte[] info, int outLen,
       
   138             String keyAlg) throws InvalidKeyException {
       
   139         byte[] kdfOutput;
       
   140 
       
   141         // Calculate the number of rounds of HMAC that are needed to
       
   142         // meet the requested data.  Then set up the buffers we will need.
       
   143         Objects.requireNonNull(pseudoRandKey, "A null PRK is not allowed.");
       
   144         hmacObj.init(pseudoRandKey);
       
   145         if (info == null) {
       
   146             info = new byte[0];
       
   147         }
       
   148         int rounds = (outLen + hmacLen - 1) / hmacLen;
       
   149         kdfOutput = new byte[rounds * hmacLen];
       
   150         int offset = 0;
       
   151         int tLength = 0;
       
   152 
       
   153         for (int i = 0; i < rounds ; i++) {
       
   154 
       
   155             // Calculate this round
       
   156             try {
       
   157                  // Add T(i).  This will be an empty string on the first
       
   158                  // iteration since tLength starts at zero.  After the first
       
   159                  // iteration, tLength is changed to the HMAC length for the
       
   160                  // rest of the loop.
       
   161                 hmacObj.update(kdfOutput,
       
   162                         Math.max(0, offset - hmacLen), tLength);
       
   163                 hmacObj.update(info);                       // Add info
       
   164                 hmacObj.update((byte)(i + 1));              // Add round number
       
   165                 hmacObj.doFinal(kdfOutput, offset);
       
   166 
       
   167                 tLength = hmacLen;
       
   168                 offset += hmacLen;                       // For next iteration
       
   169             } catch (ShortBufferException sbe) {
       
   170                 // This really shouldn't happen given that we've
       
   171                 // sized the buffers to their largest possible size up-front,
       
   172                 // but just in case...
       
   173                 throw new RuntimeException(sbe);
       
   174             }
       
   175         }
       
   176 
       
   177         return new SecretKeySpec(kdfOutput, 0, outLen, keyAlg);
       
   178     }
       
   179 
       
   180     /**
       
   181      * Perform the HKDF Extract-then-Expand operation.
       
   182      *
       
   183      * @param inputKey the input keying material provided as a
       
   184      * {@code SecretKey}.
       
   185      * @param salt a salt value, implemented as a {@code SecretKey}.  A
       
   186      * {@code null} value is allowed, which will internally use an array of
       
   187      * zero bytes the same size as the underlying hash output length.
       
   188      * @param info optional context-specific info.  A {@code null} value is
       
   189      * allowed in which case a zero-length byte array will be used.
       
   190      * @param outLen the length of the resulting {@code SecretKey}
       
   191      * @param keyAlg the algorithm name applied to the resulting
       
   192      * {@code SecretKey}
       
   193      *
       
   194      * @return the resulting derivation stored in a {@code SecretKey} object.
       
   195      *
       
   196      * @throws InvalidKeyException if initialization of the underlying HMAC
       
   197      * process fails with the salt during the extract phase, or with the
       
   198      * resulting PRK during the expand phase.
       
   199      */
       
   200     SecretKey extractExpand(SecretKey inputKey, SecretKey salt, byte[] info,
       
   201             int outLen, String keyAlg) throws InvalidKeyException {
       
   202         SecretKey prk = extract(salt, inputKey, "HKDF-PRK");
       
   203         return expand(prk, info, outLen, keyAlg);
       
   204     }
       
   205 
       
   206     /**
       
   207      * Perform the HKDF Extract-then-Expand operation.
       
   208      *
       
   209      * @param inputKey the input keying material provided as a
       
   210      * {@code SecretKey}.
       
   211      * @param salt a salt value as cleartext bytes.  A {@code null} value is
       
   212      * allowed, which will internally use an array of zero bytes the same
       
   213      * size as the underlying hash output length.
       
   214      * @param info optional context-specific info.  A {@code null} value is
       
   215      * allowed in which case a zero-length byte array will be used.
       
   216      * @param outLen the length of the resulting {@code SecretKey}
       
   217      * @param keyAlg the algorithm name applied to the resulting
       
   218      * {@code SecretKey}
       
   219      *
       
   220      * @return the resulting derivation stored in a {@code SecretKey} object.
       
   221      *
       
   222      * @throws InvalidKeyException if initialization of the underlying HMAC
       
   223      * process fails with the salt during the extract phase, or with the
       
   224      * resulting PRK during the expand phase.
       
   225      */
       
   226     SecretKey extractExpand(SecretKey inputKey, byte[] salt, byte[] info,
       
   227             int outLen, String keyAlg) throws InvalidKeyException {
       
   228         byte[] saltBytes = (salt != null) ? salt : new byte[hmacLen];
       
   229         return extractExpand(inputKey,
       
   230             new SecretKeySpec(saltBytes, "HKDF-PRK"), info, outLen, keyAlg);
       
   231     }
       
   232 }