blob: c706c2ab36a9bb2dae18743e02cd9437b8412069 [file] [log] [blame]
/*
* Copyright 2010 Google Inc.
*
* Licensed 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 java.security;
import static javaemul.internal.Coercions.ensureInt;
/**
* Message Digest algorithm - <a href=
* "http://java.sun.com/j2se/1.4.2/docs/api/java/security/MessageDigest.html"
* >[Sun's docs]</a>.
*/
public abstract class MessageDigest extends MessageDigestSpi {
private static class Md5Digest extends MessageDigest {
// 16 * 4 bytes
static byte padding[] = {
(byte) 0x80, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0};
/**
* Converts a long to a 8-byte array using low order first.
*
* @param n A long.
* @return A byte[].
*/
public static byte[] toBytes(long n) {
byte[] b = new byte[8];
b[0] = (byte) (n);
n >>>= 8;
b[1] = (byte) (n);
n >>>= 8;
b[2] = (byte) (n);
n >>>= 8;
b[3] = (byte) (n);
n >>>= 8;
b[4] = (byte) (n);
n >>>= 8;
b[5] = (byte) (n);
n >>>= 8;
b[6] = (byte) (n);
n >>>= 8;
b[7] = (byte) (n);
return b;
}
/**
* Converts a 64-byte array into a 16-int array.
*
* @param in A byte[].
* @param out An int[].
*/
private static void byte2int(byte[] in, int[] out) {
for (int inpos = 0, outpos = 0; outpos < 16; outpos++) {
out[outpos] = ((in[inpos++] & 0xff) | ((in[inpos++] & 0xff) << 8)
| ((in[inpos++] & 0xff) << 16) | ((in[inpos++] & 0xff) << 24));
}
}
/*
* Method F.
*
* @param x An int.
* @param y An int.
* @param z An int.
* @return An int.
*/
private static int f(int x, int y, int z) {
return (z ^ (x & (y ^ z)));
}
/*
* Method FF.
*
* @param a An int.
* @param b An int.
* @param c An int.
* @param d An int.
* @param x An int.
* @param s An int.
* @param ac An int.
* @return An int.
*/
private static int ff(int a, int b, int c, int d, int x, int s, int ac) {
a += x + ac + f(b, c, d);
a = (a << s | a >>> -s);
return a + b;
}
/*
* Method G.
*
* @param x An int.
* @param y An int.
* @param z An int.
* @return An int.
*/
private static int g(int x, int y, int z) {
return (y ^ (z & (x ^ y)));
}
/*
* Method GG.
*
* @param a An int.
* @param b An int.
* @param c An int.
* @param d An int.
* @param x An int.
* @param s An int.
* @param ac An int.
* @return An int.
*/
private static int gg(int a, int b, int c, int d, int x, int s, int ac) {
a += x + ac + g(b, c, d);
a = (a << s | a >>> -s);
return a + b;
}
/*
* Method H.
*
* @param x An int.
* @param y An int.
* @param z An int.
* @return An int.
*/
private static int h(int x, int y, int z) {
return (x ^ y ^ z);
}
/*
* Method HH.
*
* @param a An int.
* @param b An int.
* @param c An int.
* @param d An int.
* @param x An int.
* @param s An int.
* @param ac An int.
* @return An int.
*/
private static int hh(int a, int b, int c, int d, int x, int s, int ac) {
a += x + ac + h(b, c, d);
a = (a << s | a >>> -s);
return a + b;
}
/*
* Method I.
*
* @param x An int.
* @param y An int.
* @param z An int.
* @return An int.
*/
private static int i(int x, int y, int z) {
return (y ^ (x | ~z));
}
/*
* Method II.
*
* @param a An int.
* @param b An int.
* @param c An int.
* @param d An int.
* @param x An int.
* @param s An int.
* @param ac An int.
* @return An int.
*/
private static int ii(int a, int b, int c, int d, int x, int s, int ac) {
a += x + ac + i(b, c, d);
a = (a << s | a >>> -s);
return a + b;
}
/**
* Converts a 4-int array into a 16-byte array.
*
* @param in An int[].
* @param out A byte[].
*/
private static void int2byte(int[] in, byte[] out) {
for (int inpos = 0, outpos = 0; inpos < 4; inpos++) {
out[outpos++] = (byte) (in[inpos] & 0xff);
out[outpos++] = (byte) ((in[inpos] >>> 8) & 0xff);
out[outpos++] = (byte) ((in[inpos] >>> 16) & 0xff);
out[outpos++] = (byte) ((in[inpos] >>> 24) & 0xff);
}
}
private byte buffer[];
// TODO(jat): consider doing away with long math
private long counter;
private final byte[] oneByte = new byte[1];
private int remainder;
private int state[];
private int x[];
public Md5Digest() {
super("MD5");
engineReset();
}
@Override
protected byte[] engineDigest() {
byte[] bits = toBytes(counter << 3);
byte[] digest = new byte[16];
if (remainder > 8) {
engineUpdate(padding, 0, remainder - 8);
} else {
engineUpdate(padding, 0, 64 + (remainder - 8));
}
engineUpdate(bits, 0, 8);
int2byte(state, digest);
this.reset();
return digest;
}
@Override
protected int engineGetDigestLength() {
return 16;
}
@Override
protected void engineReset() {
buffer = new byte[64];
state = new int[4];
x = new int[16];
state[0] = 0x67452301;
state[1] = 0xefcdab89;
state[2] = 0x98badcfe;
state[3] = 0x10325476;
counter = 0;
remainder = 64;
}
@Override
protected void engineUpdate(byte input) {
// TODO(jat): better implementation
oneByte [0] = input;
engineUpdate(oneByte, 0, 1);
}
@Override
protected void engineUpdate(byte[] input, int offset, int len) {
while (true) {
if (len >= remainder) {
System.arraycopy(input, offset, buffer, (int) (counter & 63L),
remainder);
transform(buffer);
counter += remainder;
offset += remainder;
len -= remainder;
remainder = 64;
} else {
System.arraycopy(input, offset, buffer, (int) (counter & 63L), len);
counter += len;
remainder -= len;
break;
}
}
}
/*
* TODO: Document.
*
* @param buffer A byte[].
*/
private void transform(byte[] buffer) {
int a, b, c, d;
byte2int(buffer, x);
a = state[0];
b = state[1];
c = state[2];
d = state[3];
a = ff(a, b, c, d, x[0], 7, 0xd76aa478);
d = ff(d, a, b, c, x[1], 12, 0xe8c7b756);
c = ff(c, d, a, b, x[2], 17, 0x242070db);
b = ff(b, c, d, a, x[3], 22, 0xc1bdceee);
a = ff(a, b, c, d, x[4], 7, 0xf57c0faf);
d = ff(d, a, b, c, x[5], 12, 0x4787c62a);
c = ff(c, d, a, b, x[6], 17, 0xa8304613);
b = ff(b, c, d, a, x[7], 22, 0xfd469501);
a = ff(a, b, c, d, x[8], 7, 0x698098d8);
d = ff(d, a, b, c, x[9], 12, 0x8b44f7af);
c = ff(c, d, a, b, x[10], 17, 0xffff5bb1);
b = ff(b, c, d, a, x[11], 22, 0x895cd7be);
a = ff(a, b, c, d, x[12], 7, 0x6b901122);
d = ff(d, a, b, c, x[13], 12, 0xfd987193);
c = ff(c, d, a, b, x[14], 17, 0xa679438e);
b = ff(b, c, d, a, x[15], 22, 0x49b40821);
a = gg(a, b, c, d, x[1], 5, 0xf61e2562);
d = gg(d, a, b, c, x[6], 9, 0xc040b340);
c = gg(c, d, a, b, x[11], 14, 0x265e5a51);
b = gg(b, c, d, a, x[0], 20, 0xe9b6c7aa);
a = gg(a, b, c, d, x[5], 5, 0xd62f105d);
d = gg(d, a, b, c, x[10], 9, 0x2441453);
c = gg(c, d, a, b, x[15], 14, 0xd8a1e681);
b = gg(b, c, d, a, x[4], 20, 0xe7d3fbc8);
a = gg(a, b, c, d, x[9], 5, 0x21e1cde6);
d = gg(d, a, b, c, x[14], 9, 0xc33707d6);
c = gg(c, d, a, b, x[3], 14, 0xf4d50d87);
b = gg(b, c, d, a, x[8], 20, 0x455a14ed);
a = gg(a, b, c, d, x[13], 5, 0xa9e3e905);
d = gg(d, a, b, c, x[2], 9, 0xfcefa3f8);
c = gg(c, d, a, b, x[7], 14, 0x676f02d9);
b = gg(b, c, d, a, x[12], 20, 0x8d2a4c8a);
a = hh(a, b, c, d, x[5], 4, 0xfffa3942);
d = hh(d, a, b, c, x[8], 11, 0x8771f681);
c = hh(c, d, a, b, x[11], 16, 0x6d9d6122);
b = hh(b, c, d, a, x[14], 23, 0xfde5380c);
a = hh(a, b, c, d, x[1], 4, 0xa4beea44);
d = hh(d, a, b, c, x[4], 11, 0x4bdecfa9);
c = hh(c, d, a, b, x[7], 16, 0xf6bb4b60);
b = hh(b, c, d, a, x[10], 23, 0xbebfbc70);
a = hh(a, b, c, d, x[13], 4, 0x289b7ec6);
d = hh(d, a, b, c, x[0], 11, 0xeaa127fa);
c = hh(c, d, a, b, x[3], 16, 0xd4ef3085);
b = hh(b, c, d, a, x[6], 23, 0x4881d05);
a = hh(a, b, c, d, x[9], 4, 0xd9d4d039);
d = hh(d, a, b, c, x[12], 11, 0xe6db99e5);
c = hh(c, d, a, b, x[15], 16, 0x1fa27cf8);
b = hh(b, c, d, a, x[2], 23, 0xc4ac5665);
a = ii(a, b, c, d, x[0], 6, 0xf4292244);
d = ii(d, a, b, c, x[7], 10, 0x432aff97);
c = ii(c, d, a, b, x[14], 15, 0xab9423a7);
b = ii(b, c, d, a, x[5], 21, 0xfc93a039);
a = ii(a, b, c, d, x[12], 6, 0x655b59c3);
d = ii(d, a, b, c, x[3], 10, 0x8f0ccc92);
c = ii(c, d, a, b, x[10], 15, 0xffeff47d);
b = ii(b, c, d, a, x[1], 21, 0x85845dd1);
a = ii(a, b, c, d, x[8], 6, 0x6fa87e4f);
d = ii(d, a, b, c, x[15], 10, 0xfe2ce6e0);
c = ii(c, d, a, b, x[6], 15, 0xa3014314);
b = ii(b, c, d, a, x[13], 21, 0x4e0811a1);
a = ii(a, b, c, d, x[4], 6, 0xf7537e82);
d = ii(d, a, b, c, x[11], 10, 0xbd3af235);
c = ii(c, d, a, b, x[2], 15, 0x2ad7d2bb);
b = ii(b, c, d, a, x[9], 21, 0xeb86d391);
// Coerce to 32-bits to compute the expression correctly in JavaScript.
state[0] = ensureInt(state[0] + a);
state[1] = ensureInt(state[1] + b);
state[2] = ensureInt(state[2] + c);
state[3] = ensureInt(state[3] + d);
}
}
public static MessageDigest getInstance(String algorithm)
throws NoSuchAlgorithmException {
switch(algorithm) {
case "MD5":
return new Md5Digest();
case "SHA-256":
return new SHA256Digest();
default:
throw new NoSuchAlgorithmException(algorithm + " not supported");
}
}
public static boolean isEqual(byte[] digestA, byte[] digestB) {
int n = digestA.length;
if (n != digestB.length) {
return false;
}
for (int i = 0; i < n; ++i) {
if (digestA[i] != digestB[i]) {
return false;
}
}
return true;
}
private final String algorithm;
protected MessageDigest(String algorithm) {
this.algorithm = algorithm;
}
public byte[] digest() {
return engineDigest();
}
public byte[] digest(byte[] input) {
update(input);
return digest();
}
public int digest(byte[] buf, int offset, int len) throws DigestException {
return engineDigest(buf, offset, len);
}
public final String getAlgorithm() {
return algorithm;
}
public final int getDigestLength() {
return engineGetDigestLength();
}
public void reset() {
engineReset();
}
public void update(byte input) {
engineUpdate(input);
}
public void update(byte[] input) {
engineUpdate(input, 0, input.length);
}
public void update(byte[] input, int offset, int len) {
engineUpdate(input, offset, len);
}
}