Home | History | Annotate | Download | only in jsse
      1 /*
      2  *  Licensed to the Apache Software Foundation (ASF) under one or more
      3  *  contributor license agreements.  See the NOTICE file distributed with
      4  *  this work for additional information regarding copyright ownership.
      5  *  The ASF licenses this file to You under the Apache License, Version 2.0
      6  *  (the "License"); you may not use this file except in compliance with
      7  *  the License.  You may obtain a copy of the License at
      8  *
      9  *     http://www.apache.org/licenses/LICENSE-2.0
     10  *
     11  *  Unless required by applicable law or agreed to in writing, software
     12  *  distributed under the License is distributed on an "AS IS" BASIS,
     13  *  WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
     14  *  See the License for the specific language governing permissions and
     15  *  limitations under the License.
     16  */
     17 
     18 package org.apache.harmony.xnet.provider.jsse;
     19 
     20 import org.apache.harmony.xnet.provider.jsse.AlertException;
     21 import org.apache.harmony.xnet.provider.jsse.Logger;
     22 
     23 import java.security.GeneralSecurityException;
     24 import java.security.MessageDigest;
     25 import java.security.NoSuchAlgorithmException;
     26 import java.util.Arrays;
     27 import javax.net.ssl.SSLException;
     28 import javax.crypto.Mac;
     29 import javax.crypto.spec.SecretKeySpec;
     30 
     31 /**
     32  * This class provides functionality for computation
     33  * of PRF values for TLS (http://www.ietf.org/rfc/rfc2246.txt)
     34  * and SSL v3 (http://wp.netscape.com/eng/ssl3) protocols.
     35  */
     36 public class PRF {
     37     private static Logger.Stream logger = Logger.getStream("prf");
     38 
     39     private static Mac md5_mac;
     40     private static Mac sha_mac;
     41     protected static MessageDigest md5;
     42     protected static MessageDigest sha;
     43     private static int md5_mac_length;
     44     private static int sha_mac_length;
     45 
     46     static private void init() {
     47         try {
     48             md5_mac = Mac.getInstance("HmacMD5");
     49             sha_mac = Mac.getInstance("HmacSHA1");
     50         } catch (NoSuchAlgorithmException e) {
     51             throw new AlertException(AlertProtocol.INTERNAL_ERROR,
     52                     new SSLException(
     53                 "There is no provider of HmacSHA1 or HmacMD5 "
     54                 + "algorithms installed in the system"));
     55         }
     56         md5_mac_length = md5_mac.getMacLength();
     57         sha_mac_length = sha_mac.getMacLength();
     58         try {
     59             md5 = MessageDigest.getInstance("MD5");
     60             sha = MessageDigest.getInstance("SHA-1");
     61         } catch (Exception e) {
     62             throw new AlertException(AlertProtocol.INTERNAL_ERROR,
     63                     new SSLException(
     64                     "Could not initialize the Digest Algorithms."));
     65         }
     66     }
     67 
     68     /**
     69      * Computes the value of SSLv3 pseudo random function.
     70      * @param   out:    the buffer to fill up with the value of the function.
     71      * @param   secret: the buffer containing the secret value to generate prf.
     72      * @param   seed:   the seed to be used.
     73      */
     74     static synchronized void computePRF_SSLv3(byte[] out, byte[] secret, byte[] seed) {
     75         if (sha == null) {
     76             init();
     77         }
     78         int pos = 0;
     79         int iteration = 1;
     80         byte[] digest;
     81         while (pos < out.length) {
     82             byte[] pref = new byte[iteration];
     83             Arrays.fill(pref, (byte) (64 + iteration++));
     84             sha.update(pref);
     85             sha.update(secret);
     86             sha.update(seed);
     87             md5.update(secret);
     88             md5.update(sha.digest());
     89             digest = md5.digest(); // length == 16
     90             if (pos + 16 > out.length) {
     91                 System.arraycopy(digest, 0, out, pos, out.length - pos);
     92                 pos = out.length;
     93             } else {
     94                 System.arraycopy(digest, 0, out, pos, 16);
     95                 pos += 16;
     96             }
     97         }
     98     }
     99 
    100     /**
    101      * Computes the value of TLS pseudo random function.
    102      * @param   out:    the buffer to fill up with the value of the function.
    103      * @param   secret: the buffer containing the secret value to generate prf.
    104      * @param   str_bytes:  the label bytes to be used.
    105      * @param   seed:   the seed to be used.
    106      */
    107     synchronized static void computePRF(byte[] out, byte[] secret,
    108             byte[] str_byts, byte[] seed) throws GeneralSecurityException {
    109         if (sha_mac == null) {
    110             init();
    111         }
    112         // Do concatenation of the label with the seed:
    113         // (metterings show that is is faster to concatenate the arrays
    114         // and to call HMAC.update on cancatenation, than twice call for
    115         // each of the part, i.e.:
    116         // time(HMAC.update(label+seed))
    117         //          < time(HMAC.update(label)) + time(HMAC.update(seed))
    118         // but it takes more memmory (approximaty on 4%)
    119         /*
    120         byte[] tmp_seed = new byte[seed.length + str_byts.length];
    121         System.arraycopy(str_byts, 0, tmp_seed, 0, str_byts.length);
    122         System.arraycopy(seed, 0, tmp_seed, str_byts.length, seed.length);
    123         seed = tmp_seed;
    124         */
    125         SecretKeySpec keyMd5;
    126         SecretKeySpec keySha1;
    127         if ((secret == null) || (secret.length == 0)) {
    128             secret = new byte[8];
    129             keyMd5 = new SecretKeySpec(secret, "HmacMD5");
    130             keySha1 = new SecretKeySpec(secret, "HmacSHA1");
    131         } else {
    132             int length = secret.length >> 1; // division by 2
    133             int offset = secret.length & 1;  // remainder
    134             keyMd5 = new SecretKeySpec(secret, 0, length + offset,
    135                     "HmacMD5");
    136             keySha1 = new SecretKeySpec(secret, length, length
    137                     + offset, "HmacSHA1");
    138         }
    139 
    140         //byte[] str_byts = label.getBytes();
    141 
    142         if (logger != null) {
    143             logger.println("secret["+secret.length+"]: ");
    144             logger.printAsHex(16, "", " ", secret);
    145             logger.println("label["+str_byts.length+"]: ");
    146             logger.printAsHex(16, "", " ", str_byts);
    147             logger.println("seed["+seed.length+"]: ");
    148             logger.printAsHex(16, "", " ", seed);
    149             logger.println("MD5 key:");
    150             logger.printAsHex(16, "", " ", keyMd5.getEncoded());
    151             logger.println("SHA1 key:");
    152             logger.printAsHex(16, "", " ", keySha1.getEncoded());
    153         }
    154 
    155         md5_mac.init(keyMd5);
    156         sha_mac.init(keySha1);
    157 
    158         int pos = 0;
    159         md5_mac.update(str_byts);
    160         byte[] hash = md5_mac.doFinal(seed); // A(1)
    161         while (pos < out.length) {
    162             md5_mac.update(hash);
    163             md5_mac.update(str_byts);
    164             md5_mac.update(seed);
    165             if (pos + md5_mac_length < out.length) {
    166                 md5_mac.doFinal(out, pos);
    167                 pos += md5_mac_length;
    168             } else {
    169                 System.arraycopy(md5_mac.doFinal(), 0, out,
    170                         pos, out.length - pos);
    171                 break;
    172             }
    173             // make A(i)
    174             hash = md5_mac.doFinal(hash);
    175         }
    176         if (logger != null) {
    177             logger.println("P_MD5:");
    178             logger.printAsHex(md5_mac_length, "", " ", out);
    179         }
    180 
    181         pos = 0;
    182         sha_mac.update(str_byts);
    183         hash = sha_mac.doFinal(seed); // A(1)
    184         byte[] sha1hash;
    185         while (pos < out.length) {
    186             sha_mac.update(hash);
    187             sha_mac.update(str_byts);
    188             sha1hash = sha_mac.doFinal(seed);
    189             for (int i = 0; (i < sha_mac_length) & (pos < out.length); i++) {
    190                 out[pos++] ^= sha1hash[i];
    191             }
    192             // make A(i)
    193             hash = sha_mac.doFinal(hash);
    194         }
    195 
    196         if (logger != null) {
    197             logger.println("PRF:");
    198             logger.printAsHex(sha_mac_length, "", " ", out);
    199         }
    200     }
    201 }
    202