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