blob: 6013d383a37be8030fa24b4dabbd950b42c75681 [file] [log] [blame]
/*
* Licensed to the Apache Software Foundation (ASF) under one or more
* contributor license agreements. See the NOTICE file distributed with
* this work for additional information regarding copyright ownership.
* The ASF licenses this file to You under the Apache License, Version 2.0
* (the "License"); you may not use this file except in compliance with
* the License. You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
package org.apache.harmony.xnet.provider.jsse;
import java.security.GeneralSecurityException;
import java.security.MessageDigest;
import java.security.NoSuchAlgorithmException;
import java.util.Arrays;
import javax.crypto.Mac;
import javax.crypto.spec.SecretKeySpec;
import javax.net.ssl.SSLException;
/**
* This class provides functionality for computation
* of PRF values for TLS (http://www.ietf.org/rfc/rfc2246.txt)
* and SSL v3 (http://wp.netscape.com/eng/ssl3) protocols.
*/
public class PRF {
private static Logger.Stream logger = Logger.getStream("prf");
private static Mac md5_mac;
private static Mac sha_mac;
protected static MessageDigest md5;
protected static MessageDigest sha;
private static int md5_mac_length;
private static int sha_mac_length;
static private void init() {
try {
md5_mac = Mac.getInstance("HmacMD5");
sha_mac = Mac.getInstance("HmacSHA1");
} catch (NoSuchAlgorithmException e) {
throw new AlertException(AlertProtocol.INTERNAL_ERROR,
new SSLException(
"There is no provider of HmacSHA1 or HmacMD5 "
+ "algorithms installed in the system"));
}
md5_mac_length = md5_mac.getMacLength();
sha_mac_length = sha_mac.getMacLength();
try {
md5 = MessageDigest.getInstance("MD5");
sha = MessageDigest.getInstance("SHA-1");
} catch (Exception e) {
throw new AlertException(AlertProtocol.INTERNAL_ERROR,
new SSLException(
"Could not initialize the Digest Algorithms."));
}
}
/**
* Computes the value of SSLv3 pseudo random function.
* @param out: the buffer to fill up with the value of the function.
* @param secret: the buffer containing the secret value to generate prf.
* @param seed: the seed to be used.
*/
static synchronized void computePRF_SSLv3(byte[] out, byte[] secret, byte[] seed) {
if (sha == null) {
init();
}
int pos = 0;
int iteration = 1;
byte[] digest;
while (pos < out.length) {
byte[] pref = new byte[iteration];
Arrays.fill(pref, (byte) (64 + iteration++));
sha.update(pref);
sha.update(secret);
sha.update(seed);
md5.update(secret);
md5.update(sha.digest());
digest = md5.digest(); // length == 16
if (pos + 16 > out.length) {
System.arraycopy(digest, 0, out, pos, out.length - pos);
pos = out.length;
} else {
System.arraycopy(digest, 0, out, pos, 16);
pos += 16;
}
}
}
/**
* Computes the value of TLS pseudo random function.
* @param out: the buffer to fill up with the value of the function.
* @param secret: the buffer containing the secret value to generate prf.
* @param str_bytes: the label bytes to be used.
* @param seed: the seed to be used.
*/
synchronized static void computePRF(byte[] out, byte[] secret,
byte[] str_byts, byte[] seed) throws GeneralSecurityException {
if (sha_mac == null) {
init();
}
// Do concatenation of the label with the seed:
// (metterings show that is is faster to concatenate the arrays
// and to call HMAC.update on cancatenation, than twice call for
// each of the part, i.e.:
// time(HMAC.update(label+seed))
// < time(HMAC.update(label)) + time(HMAC.update(seed))
// but it takes more memmory (approximaty on 4%)
/*
byte[] tmp_seed = new byte[seed.length + str_byts.length];
System.arraycopy(str_byts, 0, tmp_seed, 0, str_byts.length);
System.arraycopy(seed, 0, tmp_seed, str_byts.length, seed.length);
seed = tmp_seed;
*/
SecretKeySpec keyMd5;
SecretKeySpec keySha1;
if ((secret == null) || (secret.length == 0)) {
secret = new byte[8];
keyMd5 = new SecretKeySpec(secret, "HmacMD5");
keySha1 = new SecretKeySpec(secret, "HmacSHA1");
} else {
int length = secret.length >> 1; // division by 2
int offset = secret.length & 1; // remainder
keyMd5 = new SecretKeySpec(secret, 0, length + offset,
"HmacMD5");
keySha1 = new SecretKeySpec(secret, length, length
+ offset, "HmacSHA1");
}
//byte[] str_byts = label.getBytes();
if (logger != null) {
logger.println("secret["+secret.length+"]: ");
logger.printAsHex(16, "", " ", secret);
logger.println("label["+str_byts.length+"]: ");
logger.printAsHex(16, "", " ", str_byts);
logger.println("seed["+seed.length+"]: ");
logger.printAsHex(16, "", " ", seed);
logger.println("MD5 key:");
logger.printAsHex(16, "", " ", keyMd5.getEncoded());
logger.println("SHA1 key:");
logger.printAsHex(16, "", " ", keySha1.getEncoded());
}
md5_mac.init(keyMd5);
sha_mac.init(keySha1);
int pos = 0;
md5_mac.update(str_byts);
byte[] hash = md5_mac.doFinal(seed); // A(1)
while (pos < out.length) {
md5_mac.update(hash);
md5_mac.update(str_byts);
md5_mac.update(seed);
if (pos + md5_mac_length < out.length) {
md5_mac.doFinal(out, pos);
pos += md5_mac_length;
} else {
System.arraycopy(md5_mac.doFinal(), 0, out,
pos, out.length - pos);
break;
}
// make A(i)
hash = md5_mac.doFinal(hash);
}
if (logger != null) {
logger.println("P_MD5:");
logger.printAsHex(md5_mac_length, "", " ", out);
}
pos = 0;
sha_mac.update(str_byts);
hash = sha_mac.doFinal(seed); // A(1)
byte[] sha1hash;
while (pos < out.length) {
sha_mac.update(hash);
sha_mac.update(str_byts);
sha1hash = sha_mac.doFinal(seed);
for (int i = 0; (i < sha_mac_length) & (pos < out.length); i++) {
out[pos++] ^= sha1hash[i];
}
// make A(i)
hash = sha_mac.doFinal(hash);
}
if (logger != null) {
logger.println("PRF:");
logger.printAsHex(sha_mac_length, "", " ", out);
}
}
}