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