1 /*
   2  * Copyright (c) 2005, 2013, 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.security.*;
  29 import java.security.spec.AlgorithmParameterSpec;
  30 
  31 import javax.crypto.*;
  32 import javax.crypto.spec.*;
  33 
  34 import sun.security.internal.spec.*;
  35 
  36 import static com.sun.crypto.provider.TlsPrfGenerator.*;
  37 
  38 /**
  39  * KeyGenerator implementation for the SSL/TLS master secret derivation.
  40  *
  41  * @author  Andreas Sterbenz
  42  * @since   1.6
  43  */
  44 public final class TlsKeyMaterialGenerator extends KeyGeneratorSpi {
  45 
  46     private final static String MSG = "TlsKeyMaterialGenerator must be "
  47         + "initialized using a TlsKeyMaterialParameterSpec";
  48 
  49     private TlsKeyMaterialParameterSpec spec;
  50 
  51     private int protocolVersion;
  52 
  53     public TlsKeyMaterialGenerator() {
  54     }
  55 
  56     protected void engineInit(SecureRandom random) {
  57         throw new InvalidParameterException(MSG);
  58     }
  59 
  60     protected void engineInit(AlgorithmParameterSpec params,
  61             SecureRandom random) throws InvalidAlgorithmParameterException {
  62         if (params instanceof TlsKeyMaterialParameterSpec == false) {
  63             throw new InvalidAlgorithmParameterException(MSG);
  64         }
  65         this.spec = (TlsKeyMaterialParameterSpec)params;
  66         if ("RAW".equals(spec.getMasterSecret().getFormat()) == false) {
  67             throw new InvalidAlgorithmParameterException(
  68                 "Key format must be RAW");
  69         }
  70         protocolVersion = (spec.getMajorVersion() << 8)
  71             | spec.getMinorVersion();
  72         if ((protocolVersion < 0x0300) || (protocolVersion > 0x0303)) {
  73             throw new InvalidAlgorithmParameterException(
  74                 "Only SSL 3.0, TLS 1.0/1.1/1.2 supported");
  75         }
  76     }
  77 
  78     protected void engineInit(int keysize, SecureRandom random) {
  79         throw new InvalidParameterException(MSG);
  80     }
  81 
  82     protected SecretKey engineGenerateKey() {
  83         if (spec == null) {
  84             throw new IllegalStateException(
  85                 "TlsKeyMaterialGenerator must be initialized");
  86         }
  87         try {
  88             return engineGenerateKey0();
  89         } catch (GeneralSecurityException e) {
  90             throw new ProviderException(e);
  91         }
  92     }
  93 
  94     private SecretKey engineGenerateKey0() throws GeneralSecurityException {
  95         byte[] masterSecret = spec.getMasterSecret().getEncoded();
  96 
  97         byte[] clientRandom = spec.getClientRandom();
  98         byte[] serverRandom = spec.getServerRandom();
  99 
 100         SecretKey clientMacKey = null;
 101         SecretKey serverMacKey = null;
 102         SecretKey clientCipherKey = null;
 103         SecretKey serverCipherKey = null;
 104         IvParameterSpec clientIv = null;
 105         IvParameterSpec serverIv = null;
 106 
 107         int macLength = spec.getMacKeyLength();
 108         int expandedKeyLength = spec.getExpandedCipherKeyLength();
 109         boolean isExportable = (expandedKeyLength != 0);
 110         int keyLength = spec.getCipherKeyLength();
 111         int ivLength = spec.getIvLength();
 112 
 113         int keyBlockLen = macLength + keyLength
 114             + (isExportable ? 0 : ivLength);
 115         keyBlockLen <<= 1;
 116         byte[] keyBlock = new byte[keyBlockLen];
 117 
 118         // These may be used again later for exportable suite calculations.
 119         MessageDigest md5 = null;
 120         MessageDigest sha = null;
 121 
 122         // generate key block
 123         if (protocolVersion >= 0x0303) {
 124             // TLS 1.2
 125             byte[] seed = concat(serverRandom, clientRandom);
 126             keyBlock = doTLS12PRF(masterSecret, LABEL_KEY_EXPANSION, seed,
 127                         keyBlockLen, spec.getPRFHashAlg(),
 128                         spec.getPRFHashLength(), spec.getPRFBlockSize());
 129         } else if (protocolVersion >= 0x0301) {
 130             // TLS 1.0/1.1
 131             md5 = MessageDigest.getInstance("MD5");
 132             sha = MessageDigest.getInstance("SHA1");
 133             byte[] seed = concat(serverRandom, clientRandom);
 134             keyBlock = doTLS10PRF(masterSecret, LABEL_KEY_EXPANSION, seed,
 135                         keyBlockLen, md5, sha);
 136         } else {
 137             // SSL
 138             md5 = MessageDigest.getInstance("MD5");
 139             sha = MessageDigest.getInstance("SHA1");
 140             keyBlock = new byte[keyBlockLen];
 141 
 142             byte[] tmp = new byte[20];
 143             for (int i = 0, remaining = keyBlockLen;
 144                  remaining > 0;
 145                  i++, remaining -= 16) {
 146 
 147                 sha.update(SSL3_CONST[i]);
 148                 sha.update(masterSecret);
 149                 sha.update(serverRandom);
 150                 sha.update(clientRandom);
 151                 sha.digest(tmp, 0, 20);
 152 
 153                 md5.update(masterSecret);
 154                 md5.update(tmp);
 155 
 156                 if (remaining >= 16) {
 157                     md5.digest(keyBlock, i << 4, 16);
 158                 } else {
 159                     md5.digest(tmp, 0, 16);
 160                     System.arraycopy(tmp, 0, keyBlock, i << 4, remaining);
 161                 }
 162             }
 163         }
 164 
 165         // partition keyblock into individual secrets
 166 
 167         int ofs = 0;
 168         if (macLength != 0) {
 169             byte[] tmp = new byte[macLength];
 170 
 171             // mac keys
 172             System.arraycopy(keyBlock, ofs, tmp, 0, macLength);
 173             ofs += macLength;
 174             clientMacKey = new SecretKeySpec(tmp, "Mac");
 175 
 176             System.arraycopy(keyBlock, ofs, tmp, 0, macLength);
 177             ofs += macLength;
 178             serverMacKey = new SecretKeySpec(tmp, "Mac");
 179         }
 180 
 181         if (keyLength == 0) { // SSL_RSA_WITH_NULL_* ciphersuites
 182             return new TlsKeyMaterialSpec(clientMacKey, serverMacKey);
 183         }
 184 
 185         String alg = spec.getCipherAlgorithm();
 186 
 187         // cipher keys
 188         byte[] clientKeyBytes = new byte[keyLength];
 189         System.arraycopy(keyBlock, ofs, clientKeyBytes, 0, keyLength);
 190         ofs += keyLength;
 191 
 192         byte[] serverKeyBytes = new byte[keyLength];
 193         System.arraycopy(keyBlock, ofs, serverKeyBytes, 0, keyLength);
 194         ofs += keyLength;
 195 
 196         if (isExportable == false) {
 197             // cipher keys
 198             clientCipherKey = new SecretKeySpec(clientKeyBytes, alg);
 199             serverCipherKey = new SecretKeySpec(serverKeyBytes, alg);
 200 
 201             // IV keys if needed.
 202             if (ivLength != 0) {
 203                 byte[] tmp = new byte[ivLength];
 204 
 205                 System.arraycopy(keyBlock, ofs, tmp, 0, ivLength);
 206                 ofs += ivLength;
 207                 clientIv = new IvParameterSpec(tmp);
 208 
 209                 System.arraycopy(keyBlock, ofs, tmp, 0, ivLength);
 210                 ofs += ivLength;
 211                 serverIv = new IvParameterSpec(tmp);
 212             }
 213         } else {
 214             // if exportable suites, calculate the alternate
 215             // cipher key expansion and IV generation
 216             if (protocolVersion >= 0x0302) {
 217                 // TLS 1.1+
 218                 throw new RuntimeException(
 219                     "Internal Error:  TLS 1.1+ should not be negotiating" +
 220                     "exportable ciphersuites");
 221             } else if (protocolVersion == 0x0301) {
 222                 // TLS 1.0
 223                 byte[] seed = concat(clientRandom, serverRandom);
 224 
 225                 byte[] tmp = doTLS10PRF(clientKeyBytes,
 226                     LABEL_CLIENT_WRITE_KEY, seed, expandedKeyLength, md5, sha);
 227                 clientCipherKey = new SecretKeySpec(tmp, alg);
 228 
 229                 tmp = doTLS10PRF(serverKeyBytes, LABEL_SERVER_WRITE_KEY, seed,
 230                             expandedKeyLength, md5, sha);
 231                 serverCipherKey = new SecretKeySpec(tmp, alg);
 232 
 233                 if (ivLength != 0) {
 234                     tmp = new byte[ivLength];
 235                     byte[] block = doTLS10PRF(null, LABEL_IV_BLOCK, seed,
 236                                 ivLength << 1, md5, sha);
 237                     System.arraycopy(block, 0, tmp, 0, ivLength);
 238                     clientIv = new IvParameterSpec(tmp);
 239                     System.arraycopy(block, ivLength, tmp, 0, ivLength);
 240                     serverIv = new IvParameterSpec(tmp);
 241                 }
 242             } else {
 243                 // SSLv3
 244                 byte[] tmp = new byte[expandedKeyLength];
 245 
 246                 md5.update(clientKeyBytes);
 247                 md5.update(clientRandom);
 248                 md5.update(serverRandom);
 249                 System.arraycopy(md5.digest(), 0, tmp, 0, expandedKeyLength);
 250                 clientCipherKey = new SecretKeySpec(tmp, alg);
 251 
 252                 md5.update(serverKeyBytes);
 253                 md5.update(serverRandom);
 254                 md5.update(clientRandom);
 255                 System.arraycopy(md5.digest(), 0, tmp, 0, expandedKeyLength);
 256                 serverCipherKey = new SecretKeySpec(tmp, alg);
 257 
 258                 if (ivLength != 0) {
 259                     tmp = new byte[ivLength];
 260 
 261                     md5.update(clientRandom);
 262                     md5.update(serverRandom);
 263                     System.arraycopy(md5.digest(), 0, tmp, 0, ivLength);
 264                     clientIv = new IvParameterSpec(tmp);
 265 
 266                     md5.update(serverRandom);
 267                     md5.update(clientRandom);
 268                     System.arraycopy(md5.digest(), 0, tmp, 0, ivLength);
 269                     serverIv = new IvParameterSpec(tmp);
 270                 }
 271             }
 272         }
 273 
 274         return new TlsKeyMaterialSpec(clientMacKey, serverMacKey,
 275             clientCipherKey, clientIv, serverCipherKey, serverIv);
 276     }
 277 
 278 }