From abfa8e1b36b8de5c8b6cdf92f4bb3525ba6fdd4e Mon Sep 17 00:00:00 2001 From: "justin.fiedler" Date: Fri, 30 Jun 2023 21:32:38 +0000 Subject: [PATCH] fix: remove md5 usage --- .../com/amplitude/api/AmplitudeClient.java | 21 - src/main/java/com/amplitude/security/MD5.java | 618 ------------------ .../java/com/amplitude/security/MD5Test.java | 105 --- 3 files changed, 744 deletions(-) delete mode 100644 src/main/java/com/amplitude/security/MD5.java delete mode 100644 src/test/java/com/amplitude/security/MD5Test.java diff --git a/src/main/java/com/amplitude/api/AmplitudeClient.java b/src/main/java/com/amplitude/api/AmplitudeClient.java index feeb28ad..40b10ea8 100644 --- a/src/main/java/com/amplitude/api/AmplitudeClient.java +++ b/src/main/java/com/amplitude/api/AmplitudeClient.java @@ -13,7 +13,6 @@ import com.amplitude.analytics.connector.Identity; import com.amplitude.analytics.connector.util.JSONUtil; import com.amplitude.eventexplorer.EventExplorer; -import com.amplitude.security.MD5; import com.amplitude.util.DoubleCheck; import com.amplitude.util.Provider; @@ -22,8 +21,6 @@ import org.json.JSONObject; import java.io.IOException; -import java.io.UnsupportedEncodingException; -import java.security.MessageDigest; import java.util.HashMap; import java.util.HashSet; import java.util.Iterator; @@ -2268,29 +2265,11 @@ protected void makeEventUploadPostRequest(Call.Factory client, String events, fi String apiVersionString = "" + Constants.API_VERSION; String timestampString = "" + getCurrentTimeMillis(); - String checksumString = ""; - try { - String preimage = apiVersionString + apiKey + events + timestampString; - - // MessageDigest.getInstance(String) is not threadsafe on Android. - // See https://code.google.com/p/android/issues/detail?id=37937 - // Use MD5 implementation from http://org.rodage.com/pub/java/security/MD5.java - // This implementation does not throw NoSuchAlgorithm exceptions. - MessageDigest messageDigest = new MD5(); - checksumString = bytesToHexString(messageDigest.digest(preimage.getBytes("UTF-8"))); - } catch (UnsupportedEncodingException e) { - // According to - // http://stackoverflow.com/questions/5049524/is-java-utf-8-charset-exception-possible, - // this will never be thrown - logger.e(TAG, e.toString()); - } - FormBody body = new FormBody.Builder() .add("v", apiVersionString) .add("client", apiKey) .add("e", events) .add("upload_time", timestampString) - .add("checksum", checksumString) .build(); Request request; diff --git a/src/main/java/com/amplitude/security/MD5.java b/src/main/java/com/amplitude/security/MD5.java deleted file mode 100644 index 5e375b77..00000000 --- a/src/main/java/com/amplitude/security/MD5.java +++ /dev/null @@ -1,618 +0,0 @@ -package com.amplitude.security; - -/* @(#)MD5.java 1.13 2006-02-11 - * This file was freely contributed to the LimeWire project and is covered - * by its existing GPL licence, but it may be used individually as a public - * domain implementation of a published algorithm (see below for references). - * It was also freely contributed to the Bitzi public domain sources. - * @author Philippe Verdy - */ - -/* Sun may wish to change the following package name, if integrating this - * class in the Sun JCE Security Provider for Java 1.5 (code-named Tiger). - * - * You can include it in your own Security Provider by inserting - * this property in your Provider derived class: - * put("MessageDigest.MD5", "org.rodage.pub.java.security.MD5"); - */ -//package org.rodage.pub.java.security -import java.security.DigestException; -import java.security.MessageDigest; -//--+---+1--+---+--2+---+---+3--+---+--4+---+---+5--+---+--6+---+---+7--+---+- -//3456789012345678901234567890123456789012345678901234567890123456789012345678 - -/* Implementation note: - * This class is highly optimized: - * - to reduce indirect accesses to frequently used member variables with - * by using local variables to - * cache their value, - * - to limit the number of .. or by reusing - * intermediate results in the same expression with , or by avoiding - * multiple assignments with predictable results, - * - to benefit from the abbreviated bytecodes .. rather - * than .., - * - to order local variables by decreasing usage so frequent local variables - * will get abbreviated bytecodes .. or - * .., - * - to limit the operand stack usage by swapping commutative operands so - * that expressions can be evaluated directly from left to right, - * - to avoid pairing dependancies with that impact the - * performance on pipelined processors, by swapping some commutative - * operands. - * - to maximize the CPU cache hit by scheduling instructions if - * possible when the same variable value is needed multiple times, - * because the HotSpot JIT compiler (in the Client or Server VMs) will almost - * never optimize these cases itself due to restrictions in the Java VM - * Specification. Other optimizations are still possible, but most of them - * would require programming with bytecodes directly with BCEL, as they seem - * impossible to acheive with the Java language itself (there's nearly no - * optimization of the bytecode generated by the Java compiler tool, because - * it adopts a too strict generation mode just to get sure to comply with the - * Java Language specification that prohibits some optimizations.) You may get - * higher performances by compiling this source with the IBM or Borland Java - * Language compilers, rather than with the basic Sun "javac" implementation. - * - * Shamefully and unlike with traditional language compilers to CPU native - * code, there's currently no Java bytecode assembler tool to create very - * optimized pure 100% Java classes from a source in Java assembly language - * (and no complete specification for such an assembly language, which is - * only partly defined in the Java VM Specification, and partly used by the - * "javap" disassember tool, or by the newer BCEL package). If such a tool - * exists, let me know so that I can translate this class to this language. - */ - -/** - *

The MD5 message-digest algorithm takes as input a message of arbitrary - * length and produces as output a 128-bit "fingerprint" or "message digest" - * of the input. It is conjectured that it is computationally infeasible to - * produce two messages having the same message digest, or to produce any - * message having a given prespecified target message digest.

- * - *

References:

- *
    - *
  1. Rivest R., "The MD5 - * Message-Digest Algorithm", Informational RFC 1321, MIT Laboratory for - * Computer Science, and RSA Data Security, Inc., April 1992.
  2. - *
  3. RFC Editor, "Erratum - * for RFC 1321", 2002-06-14, 2001-01-19, 2000-04-12.
  4. - *
- */ -public final class MD5 extends MessageDigest implements Cloneable { - /** - * Private contextual byte count, stored at end of the last block, - * after the ending padded block. - */ - private long bytes; - - /** - * Private context for incomplete blocks and padded bytes.
- * INVARIANT: padded must be in 0..63.
- * When the padded reaches 64 bytes, a new block is computed. - * Up to 56 last bytes are kept in the padded history. - */ - private int padded; - private byte[] pad; - - /** - * Private context that contains the current digest key. - */ - private int hA, hB, hC, hD; - - /** - * Creates a MD5 object with default initial state. - */ - public MD5() { - super("MD5"); - pad = new byte[64]; - init(); - } - - /** - * Clones this object. - */ - public Object clone() throws CloneNotSupportedException { - final MD5 that = (MD5)super.clone(); - that.pad = (byte[])this.pad.clone(); - return that; - } - - /** - * Reset then initialize the digest context.
- * - * Overrides the protected abstract method of - * java.security.MessageDigestSpi. - * @modifies this - */ - public void engineReset() { - padded = 0; - bytes = 0L; - int i = 60; - final byte[] buf = pad; - do { - buf[i-4] = (byte)0x00; - buf[i-3] = (byte)0x00; - buf[i-2] = (byte)0x00; - buf[i-1] = (byte)0x00; - buf[i ] = (byte)0x00; - buf[i+1] = (byte)0x00; - buf[i+2] = (byte)0x00; - buf[i+3] = (byte)0x00; - } while ((i-=8) >= 0); - init(); - } - - /** - * Initialize the digest context. - * @modifies this - */ - protected void init() { - /* Set to MD5/RIPEMD128 magic values. */ - hA = 0x67452301; - hB = 0xefcdab89; - hC = 0x98badcfe; - hD = 0x10325476; - } - - /** - * Updates the digest using the specified byte. - * Requires internal buffering, and may be slow.
- * - * Overrides the protected abstract method of - * java.security.MessageDigestSpi. - * @param input the byte to use for the update. - * @modifies this - */ - public void engineUpdate(final byte input) { - bytes++; - if (padded < 63) { - pad[padded++] = input; - return; - } - pad[63] = input; - engineUpdate(pad, padded); - padded = 0; - } - - /** - * Updates the digest using the specified array of bytes, - * starting at the specified offset.
- * - * Input length can be any size. May require internal buffering, - * if input blocks are not multiple of 64 bytes.
- * - * Overrides the protected abstract method of - * java.security.MessageDigestSpi. - * @param input the array of bytes to use for the update. - * @param offset the offset to start from in the array of bytes. - * @param length the number of bytes to use, starting at offset. - * @modifies this - */ - public void engineUpdate(byte[] input, int offset, int length) { - if (offset >= 0 && length >= 0 && offset + length <= input.length) { - bytes += length; - /* Terminate the previous block. */ - if (padded > 0 && padded + length >= 64) { - final int remaining; - System.arraycopy(input, offset, pad, padded, - remaining = 64 - padded); - engineUpdate(pad, padded = 0); - offset += remaining; - length -= remaining; - } - /* Loop on large sets of complete blocks. */ - while (length >= 512) { - engineUpdate(input, offset); - engineUpdate(input, offset + 64); - engineUpdate(input, offset + 128); - engineUpdate(input, offset + 192); - engineUpdate(input, offset + 256); - engineUpdate(input, offset + 320); - engineUpdate(input, offset + 384); - engineUpdate(input, offset + 448); - offset += 512; - length -= 512; - } - /* Loop on remaining complete blocks. */ - while (length >= 64) { - engineUpdate(input, offset); - offset += 64; - length -= 64; - } - /* remaining bytes kept for next block. */ - if (length > 0) { - System.arraycopy(input, offset, pad, padded, length); - padded += length; - } - return; - } - throw new ArrayIndexOutOfBoundsException(offset); - } - - /** - * Completes the hash computation by performing final operations - * such as padding. Computes the final hash and returns the final - * value as a byte[16] array. Once engineDigest has been called, - * the engine will be automatically reset as specified in the - * Java Security MessageDigest specification.
- * - * For faster operations with multiple digests, allocate your own - * array and use engineDigest(byte[], int offset, int len).
- * - * Overrides the protected abstract method of - * java.security.MessageDigestSpi. - * @return the length of the digest stored in the output buffer. - * @modifies this - */ - public byte[] engineDigest() { - try { - final byte hashvalue[] = new byte[16]; /* digest length in bytes */ - engineDigest(hashvalue, 0, 16); /* digest length in bytes */ - return hashvalue; - } catch (DigestException e) { - return null; - } - } - - /** - * Returns the digest length in bytes. Can be used to allocate your own - * output buffer when computing multiple digests.
- * - * Overrides the protected abstract method of - * java.security.MessageDigestSpi. - * @return the digest length in bytes. - */ - public int engineGetDigestLength() { - return 16; /* digest length in bytes */ - } - - /** - * Completes the hash computation by performing final operations - * such as padding. Once engineDigest has been called, the engine - * will be automatically reset (see engineReset).
- * - * Overrides the protected abstract method of - * java.security.MessageDigestSpi. - * @param hashvalue the output buffer in which to store the digest. - * @param offset offset to start from in the output buffer - * @param length number of bytes within buf allotted for the digest. - * Both this default implementation and the SUN provider - * do not return partial digests. The presence of this - * parameter is solely for consistency in our API's. - * If the value of this parameter is less than the - * actual digest length, the method will throw a - * DigestException. This parameter is ignored if its - * value is greater than or equal to the actual digest - * length. - * @return the length of the digest stored in the output buffer. - * @modifies this - */ - public int engineDigest(final byte[] hashvalue, int offset, - final int length) throws DigestException { - if (length >= 16) { /* digest length in bytes */ - if (hashvalue.length - offset >= 16) { /* digest length in bytes */ - /* Flush the trailing bytes, adding padded bytes into last - * blocks. */ - int i; - /* Add padded null bytes but replace the last 8 padded bytes - * by the little-endian 64-bit digested message bit-length. */ - final byte[] buf; - (buf=pad)[i=padded] = (byte)0x80;/* required 1st padded byte */ - /* Check if 8 bytes are available in pad to store the total - * message size */ - switch (i) { /* INVARIANT: i must be in [0..63] */ - case 56: buf[57] = (byte)0x00; /* no break; falls thru */ - case 57: buf[58] = (byte)0x00; /* no break; falls thru */ - case 58: buf[59] = (byte)0x00; /* no break; falls thru */ - case 59: buf[60] = (byte)0x00; /* no break; falls thru */ - case 60: buf[61] = (byte)0x00; /* no break; falls thru */ - case 61: buf[62] = (byte)0x00; /* no break; falls thru */ - case 62: buf[63] = (byte)0x00; /* no break; falls thru */ - case 63: engineUpdate(buf, 0); - i = -1; - } - /* Clear the rest of the 56 first bytes of pad[]. */ - switch (i & 7) { - case 7: i-=3; - break; - case 6: buf[(i-=2)+3] = (byte)0x00; - break; - case 5: buf[(i-=1)+2] = (byte)0x00; - buf[i +3] = (byte)0x00; - break; - case 4: buf[i +1] = (byte)0x00; - buf[i +2] = (byte)0x00; - buf[i +3] = (byte)0x00; - break; - case 3: buf[(i+=1) ] = (byte)0x00; - buf[i +1] = (byte)0x00; - buf[i +2] = (byte)0x00; - buf[i +3] = (byte)0x00; - break; - case 2: buf[(i+=2)-1] = (byte)0x00; - buf[i ] = (byte)0x00; - buf[i +1] = (byte)0x00; - buf[i +2] = (byte)0x00; - buf[i +3] = (byte)0x00; - break; - case 1: buf[(i+=3)-2] = (byte)0x00; - buf[i -1] = (byte)0x00; - buf[i ] = (byte)0x00; - buf[i +1] = (byte)0x00; - buf[i +2] = (byte)0x00; - buf[i +3] = (byte)0x00; - break; - case 0: buf[(i+=4)-3] = (byte)0x00; - buf[i -2] = (byte)0x00; - buf[i -1] = (byte)0x00; - buf[i ] = (byte)0x00; - buf[i +1] = (byte)0x00; - buf[i +2] = (byte)0x00; - buf[i +3] = (byte)0x00; - break; - } - while ((i+=8) <= 52) { - buf[i-4] = (byte)0x00; - buf[i-3] = (byte)0x00; - buf[i-2] = (byte)0x00; - buf[i-1] = (byte)0x00; - buf[i ] = (byte)0x00; - buf[i+1] = (byte)0x00; - buf[i+2] = (byte)0x00; - buf[i+3] = (byte)0x00; - } - /* Convert the message size from bytes to little-endian bits. */ - buf[56] = (byte)((i = (int)bytes << 3)); - buf[57] = (byte)(i >>> 8); - buf[58] = (byte)(i >>> 16); - buf[59] = (byte)(i >>> 24); - buf[60] = (byte)((i = (int)(bytes >>> 29))); - buf[61] = (byte)(i >>> 8); - buf[62] = (byte)(i >>> 16); - buf[63] = (byte)(i >>> 24); - engineUpdate(buf, 0); - /* Return the computed digest in little-endian byte order. */ - hashvalue[ offset ] = (byte)(i = hA); - hashvalue[ offset +1] = (byte)(i >>> 8); - hashvalue[ offset +2] = (byte)(i >>> 16); - hashvalue[ offset +3] = (byte)(i >>> 24); - hashvalue[ offset +4] = (byte)(i = hB); - hashvalue[ offset +5] = (byte)(i >>> 8); - hashvalue[(offset+=10)-4] = (byte)(i >>> 16); - hashvalue[ offset -3] = (byte)(i >>> 24); - hashvalue[ offset -2] = (byte)(i = hC); - hashvalue[ offset -1] = (byte)(i >>> 8); - hashvalue[ offset ] = (byte)(i >>> 16); - hashvalue[ offset +1] = (byte)(i >>> 24); - hashvalue[ offset +2] = (byte)(i = hD); - hashvalue[ offset +3] = (byte)(i >>> 8); - hashvalue[ offset +4] = (byte)(i >>> 16); - hashvalue[ offset +5] = (byte)(i >>> 24); - engineReset(); /* clear the evidence */ - return 16; /* digest length in bytes */ - } - throw new DigestException( - "insufficient space in output buffer to store the digest"); - } - throw new DigestException("partial digests not returned"); - } - - /** - * Updates the digest using the specified array of bytes, - * starting at the specified offset, but an implied length - * of exactly 64 bytes.
- * - * Requires no internal buffering, but assumes a fixed input size, - * in which the required padding bytes must have been added. - * - * @param input the array of bytes to use for the update. - * @param offset the offset to start from in the array of bytes. - * @modifies this - */ - private final void engineUpdate(final byte[] input, int offset) { - /* Intermediate sub-digest values. */ - int a, b, c, d; - /* Store the input block into the local working set of 32-bit values, - * in little-endian byte order. Be careful when widening bytes or - * integers due to sign extension in Java ! Note: One weakness of MD5 - * is that this input is not scheduled between each round, so that - * meet-in-the-middle collision attacks are possible between rounds - * 1..2 and rounds 3..4 for each of its input word, reducing the - * cryptographic strength by 16 bit. The actual cryptographic strength - * of MD5 is not 64 bits as one would expected from a strong 128-bit - * digest, but at most 48 bits, allowing brute-force collision attacks - * on the remaining bits with modest computing resources with an - * algorithm based on this bit-reduction property. For this reason, - * signatures based on MD5 are not secure today, unless MD5 is used as - * the hash function of a HMAC signature, that shields the MD5 digest - * from meet-in-the-middle attacks with other rounds. It is expected - * that MD5 will be cracked soon by detecting other internal collision - * problems. More modern digest algorithms (such as SHA-1) should have - * at least 5 rounds and use a cryptographically strong scheduling - * function for its input. (Tiger, another 192-bit digest algorithm, - * schedules its 8-word input using simple and strong arithmetic, but - * with only 3 rounds so that its strength is around 88 bits. For - * SHA-1, which is a 160-bit digest, the strength is around 72 bits.) - * Note also that the additive constants in the first round of MD5 do - * not provide any strength against inner collisions of the input but - * just reduce the frequency of collisions in the chained digest - * values, making meet-in-the-middle collision attacks just more - * complex to compute on chained blocks; if the digest is not chained - * with multiple input blocks, or if input blocks are known as clear - * text, this effect on digest values strength becomes void, so MD5 - * should not be used for digesting private messages of less than 56 - * bytes (such as passwords stored in their digested form in static - * databases), or to sign files against data corruption, but it - * can still be used to secure password exchanges with a randomized - * challenge exchange (for example in SSL with a strong random - * generator on the SSL server and a HMAC-like shield; anyway the - * security of MD5 will never exceed the 64-bits strength face to - * simple brute-force attacks). - */ - int i0, i1, i2, i3, i4, i5, i6, i7, i8, i9, iA, iB, iC, iD, iE, iF; - /* The 64 additive constants are T[i] = floor(2\88\8832 * abs(sin(i))), - * where i is in radians and varies from 1 to 64. */ - - /* Assignments in round 4 and final compression. */ - hB = (b = hB) + - (c = (d = (a = (b = (c = (d = (a = - (b = (c = (d = (a = (b = (c = (d = (a = - /* Assignments in round 3. */ - (b = (c = (d = (a = (b = (c = (d = (a = - (b = (c = (d = (a = (b = (c = (d = (a = - /* Assignments in round 2. */ - (b = (c = (d = (a = (b = (c = (d = (a = - (b = (c = (d = (a = (b = (c = (d = (a = - /* Assignments in round 1. */ - (b = (c = (d = (a = (b = (c = (d = (a = - (b = (c = (d = (a = (b = (c = (d = (a = - /* Round 1 (steps 1..16). - * - left rotations: 7, 12, 17, 22 bits. - * - key schedule (multiplexer) function: - * F(x,y,z) = (~x & z) | (x & y) = ((y ^ z) & x) ^ z. */ - b + ((a = ((((c = hC) - ^ (d = hD) - ) & b) ^ d) + (i0= - (input[ offset ] & 0xff) - | (input[ offset +1] & 0xff)<<8 - | (input[ offset +2] & 0xff)<<16 - | input[ offset +3]<<24) + 0xd76aa478 + hA - )<< 7 | a>>>25)) - + ((d = (((b ^ c) & a) ^ c) + (i1= - (input[ offset +4] & 0xff) - | (input[ offset +5] & 0xff)<<8 - | (input[(offset+=10)-4] & 0xff)<<16 - | input[ offset -3]<<24) + 0xe8c7b756 + d)<<12 | d>>>20)) - + ((c = (((a ^ b) & d) ^ b) + (i2= - (input[ offset -2] & 0xff) - | (input[ offset -1] & 0xff)<<8 - | (input[ offset ] & 0xff)<<16 - | input[ offset +1]<<24) + 0x242070db + c)<<17 | c>>>15)) - + ((b = (((d ^ a) & c) ^ a) + (i3= - (input[ offset +2] & 0xff) - | (input[ offset +3] & 0xff)<<8 - | (input[ offset +4] & 0xff)<<16 - | input[ offset +5]<<24) + 0xc1bdceee + b)<<22 | b>>>10)) - + ((a = (((c ^ d) & b) ^ d) + (i4= - (input[(offset+=10)-4] & 0xff) - | (input[ offset -3] & 0xff)<<8 - | (input[ offset -2] & 0xff)<<16 - | input[ offset -1]<<24) + 0xf57c0faf + a)<< 7 | a>>>25)) - + ((d = (((b ^ c) & a) ^ c) + (i5= - (input[ offset ] & 0xff) - | (input[ offset +1] & 0xff)<<8 - | (input[ offset +2] & 0xff)<<16 - | input[ offset +3]<<24) + 0x4787c62a + d)<<12 | d>>>20)) - + ((c = (((a ^ b) & d) ^ b) + (i6= - (input[ offset +4] & 0xff) - | (input[ offset +5] & 0xff)<<8 - | (input[(offset+=10)-4] & 0xff)<<16 - | input[ offset -3]<<24) + 0xa8304613 + c)<<17 | c>>>15)) - + ((b = (((d ^ a) & c) ^ a) + (i7= - (input[ offset -2] & 0xff) - | (input[ offset -1] & 0xff)<<8 - | (input[ offset ] & 0xff)<<16 - | input[ offset +1]<<24) + 0xfd469501 + b)<<22 | b>>>10)) - + ((a = (((c ^ d) & b) ^ d) + (i8= - (input[ offset +2] & 0xff) - | (input[ offset +3] & 0xff)<<8 - | (input[ offset +4] & 0xff)<<16 - | input[ offset +5]<<24) + 0x698098d8 + a)<< 7 | a>>>25)) - + ((d = (((b ^ c) & a) ^ c) + (i9= - (input[(offset+=10)-4] & 0xff) - | (input[ offset -3] & 0xff)<<8 - | (input[ offset -2] & 0xff)<<16 - | input[ offset -1]<<24) + 0x8b44f7af + d)<<12 | d>>>20)) - + ((c = (((a ^ b) & d) ^ b) + (iA= - (input[ offset ] & 0xff) - | (input[ offset +1] & 0xff)<<8 - | (input[ offset +2] & 0xff)<<16 - | input[ offset +3]<<24) + 0xffff5bb1 + c)<<17 | c>>>15)) - + ((b = (((d ^ a) & c) ^ a) + (iB= - (input[ offset +4] & 0xff) - | (input[ offset +5] & 0xff)<<8 - | (input[(offset+=10)-4] & 0xff)<<16 - | input[ offset -3]<<24) + 0x895cd7be + b)<<22 | b>>>10)) - + ((a = (((c ^ d) & b) ^ d) + (iC= - (input[ offset -2] & 0xff) - | (input[ offset -1] & 0xff)<<8 - | (input[ offset ] & 0xff)<<16 - | input[ offset +1]<<24) + 0x6b901122 + a)<< 7 | a>>>25)) - + ((d = (((b ^ c) & a) ^ c) + (iD= - (input[ offset +2] & 0xff) - | (input[ offset +3] & 0xff)<<8 - | (input[ offset +4] & 0xff)<<16 - | input[ offset +5]<<24) + 0xfd987193 + d)<<12 | d>>>20)) - + ((c = (((a ^ b) & d) ^ b) + (iE= - (input[(offset=offset+10)-4] & 0xff) - | (input[ offset -3] & 0xff)<<8 - | (input[ offset -2] & 0xff)<<16 - | input[ offset -1]<<24) + 0xa679438e + c)<<17 | c>>>15)) - + ((b = (((d ^ a) & c) ^ a) + (iF= - (input[ offset ] & 0xff) - | (input[ offset +1] & 0xff)<<8 - | (input[ offset +2] & 0xff)<<16 - | input[ offset +3]<<24) + 0x49b40821 + b)<<22 | b>>>10)) - /* Round 2 (steps 17..32). - * - left rotations: 5, 9, 14, 20 bits. - * - key schedule (multiplexer) function: - * G(x,y,z) = F(z,x,y) = (~z & y) | (z & x) = ((y ^ x) & z) ^ y. */ - + ((a = (((c ^ b) & d) ^ c) + i1 + 0xf61e2562 + a)<< 5 | a>>>27)) - + ((d = (((b ^ a) & c) ^ b) + i6 + 0xc040b340 + d)<< 9 | d>>>23)) - + ((c = (((a ^ d) & b) ^ a) + iB + 0x265e5a51 + c)<<14 | c>>>18)) - + ((b = (((d ^ c) & a) ^ d) + i0 + 0xe9b6c7aa + b)<<20 | b>>>12)) - + ((a = (((c ^ b) & d) ^ c) + i5 + 0xd62f105d + a)<< 5 | a>>>27)) - + ((d = (((b ^ a) & c) ^ b) + iA + 0x02441453 + d)<< 9 | d>>>23)) - + ((c = (((a ^ d) & b) ^ a) + iF + 0xd8a1e681 + c)<<14 | c>>>18)) - + ((b = (((d ^ c) & a) ^ d) + i4 + 0xe7d3fbc8 + b)<<20 | b>>>12)) - + ((a = (((c ^ b) & d) ^ c) + i9 + 0x21e1cde6 + a)<< 5 | a>>>27)) - + ((d = (((b ^ a) & c) ^ b) + iE + 0xc33707d6 + d)<< 9 | d>>>23)) - + ((c = (((a ^ d) & b) ^ a) + i3 + 0xf4d50d87 + c)<<14 | c>>>18)) - + ((b = (((d ^ c) & a) ^ d) + i8 + 0x455a14ed + b)<<20 | b>>>12)) - + ((a = (((c ^ b) & d) ^ c) + iD + 0xa9e3e905 + a)<< 5 | a>>>27)) - + ((d = (((b ^ a) & c) ^ b) + i2 + 0xfcefa3f8 + d)<< 9 | d>>>23)) - + ((c = (((a ^ d) & b) ^ a) + i7 + 0x676f02d9 + c)<<14 | c>>>18)) - + ((b = (((d ^ c) & a) ^ d) + iC + 0x8d2a4c8a + b)<<20 | b>>>12)) - /* Round 3 (steps 33..48). - * - left rotations: 4, 11, 16, 23 bits. - * - key schedule function: H(x,y,z) = y ^ x ^ z. */ - + ((a = (c ^ b ^ d) + i5 + 0xfffa3942 + a)<< 4 | a>>>28)) - + ((d = (b ^ a ^ c) + i8 + 0x8771f681 + d)<<11 | d>>>21)) - + ((c = (a ^ d ^ b) + iB + 0x6d9d6122 + c)<<16 | c>>>16)) - + ((b = (d ^ c ^ a) + iE + 0xfde5380c + b)<<23 | b>>> 9)) - + ((a = (c ^ b ^ d) + i1 + 0xa4beea44 + a)<< 4 | a>>>28)) - + ((d = (b ^ a ^ c) + i4 + 0x4bdecfa9 + d)<<11 | d>>>21)) - + ((c = (a ^ d ^ b) + i7 + 0xf6bb4b60 + c)<<16 | c>>>16)) - + ((b = (d ^ c ^ a) + iA + 0xbebfbc70 + b)<<23 | b>>> 9)) - + ((a = (c ^ b ^ d) + iD + 0x289b7ec6 + a)<< 4 | a>>>28)) - + ((d = (b ^ a ^ c) + i0 + 0xeaa127fa + d)<<11 | d>>>21)) - + ((c = (a ^ d ^ b) + i3 + 0xd4ef3085 + c)<<16 | c>>>16)) - + ((b = (d ^ c ^ a) + i6 + 0x04881d05 + b)<<23 | b>>> 9)) - + ((a = (c ^ b ^ d) + i9 + 0xd9d4d039 + a)<< 4 | a>>>28)) - + ((d = (b ^ a ^ c) + iC + 0xe6db99e5 + d)<<11 | d>>>21)) - + ((c = (a ^ d ^ b) + iF + 0x1fa27cf8 + c)<<16 | c>>>16)) - + ((b = (d ^ c ^ a) + i2 + 0xc4ac5665 + b)<<23 | b>>> 9)) - /* Round 4 (steps 49..64) and final compression. - * - left rotations: 6, 10, 15, 21 bits. - * - key schedule function I(x,y,z) = (~z | x) ^ y. */ - + ((a = ((~d | b) ^ c) + i0 + 0xf4292244 + a)<< 6 | a>>>26)) - + ((d = ((~c | a) ^ b) + i7 + 0x432aff97 + d)<<10 | d>>>22)) - + ((c = ((~b | d) ^ a) + iE + 0xab9423a7 + c)<<15 | c>>>17)) - + ((b = ((~a | c) ^ d) + i5 + 0xfc93a039 + b)<<21 | b>>>11)) - + ((a = ((~d | b) ^ c) + iC + 0x655b59c3 + a)<< 6 | a>>>26)) - + ((d = ((~c | a) ^ b) + i3 + 0x8f0ccc92 + d)<<10 | d>>>22)) - + ((c = ((~b | d) ^ a) + iA + 0xffeff47d + c)<<15 | c>>>17)) - + ((b = ((~a | c) ^ d) + i1 + 0x85845dd1 + b)<<21 | b>>>11)) - + ((a = ((~d | b) ^ c) + i8 + 0x6fa87e4f + a)<< 6 | a>>>26)) - + ((d = ((~c | a) ^ b) + iF + 0xfe2ce6e0 + d)<<10 | d>>>22)) - + ((c = ((~b | d) ^ a) + i6 + 0xa3014314 + c)<<15 | c>>>17)) - + ((b = ((~a | c) ^ d) + iD + 0x4e0811a1 + b)<<21 | b>>>11)) - + ((a = ((~d | b) ^ c) + i4 + 0xf7537e82 + a)<< 6 | a>>>26)) - + ((d = ((~c | a) ^ b) + iB + 0xbd3af235 + d)<<10 | d>>>22)) - + ((c = ((~b | d) ^ a) + i2 + 0x2ad7d2bb + c)<<15 | c>>>17)) - + ((b = ((~a | c) ^ d) + i9 + 0xeb86d391 + b)<<21 | b>>>11); - hC += c; - hD += d; - hA += a; - } -} diff --git a/src/test/java/com/amplitude/security/MD5Test.java b/src/test/java/com/amplitude/security/MD5Test.java deleted file mode 100644 index db71f0ed..00000000 --- a/src/test/java/com/amplitude/security/MD5Test.java +++ /dev/null @@ -1,105 +0,0 @@ -package com.amplitude.security; - -import com.amplitude.api.Constants; - -import static org.junit.Assert.assertArrayEquals; -import static org.junit.Assert.assertTrue; -import static org.junit.Assert.fail; - -import org.junit.After; -import org.junit.Before; -import org.junit.Test; - -import java.io.UnsupportedEncodingException; -import java.math.BigInteger; -import java.security.MessageDigest; -import java.security.NoSuchAlgorithmException; -import java.util.UUID; - - -/* - Regression tests for alternative MD5 implementation. - */ - -public class MD5Test { - - @Before - public void setUp() throws Exception { return; } - - @After - public void tearDown() throws Exception { return; } - - private static String toHex(byte[] bytes) { - BigInteger bi = new BigInteger(1, bytes); - return String.format("%0" + (bytes.length << 1) + "x", bi); - } - - private void compareMD5(String input, String truth) { - try { - MessageDigest androidMD5 = MessageDigest.getInstance("MD5"); - byte[] androidBytes = androidMD5.digest(input.getBytes("UTF-8")); - - MessageDigest alternateMD5 = new MD5(); - byte[] altBytes = alternateMD5.digest(input.getBytes("UTF-8")); - - assertArrayEquals(androidBytes, altBytes); - - if (truth != null) { - assertTrue(truth.equals(toHex(androidBytes))); - assertTrue(truth.equals(toHex(altBytes))); - } - - } catch (NoSuchAlgorithmException e) { - fail (e.toString()); - } catch (UnsupportedEncodingException e) { - fail (e.toString()); - } - } - - @Test - public void testMD5() { - compareMD5("", "d41d8cd98f00b204e9800998ecf8427e"); - compareMD5("foobar", "3858f62230ac3c915f300c664312c63f"); - } - - @Test - public void testMD5WithAmplitudeData() { - - String apiVersionString = "" + Constants.API_VERSION; - String apiKey = "1cc2c1978ebab0f6451112a8f5df4f4e"; - String timestampString = "" + System.currentTimeMillis(); - String events = "[{\"version_name\":null,\"device_manufacturer\":\"unknown\",\"user_" + - "properties\":{},\"platform\":\"Android\",\"api_properties\":{\"special\":\"" + - "session_start\",\"limit_ad_tracking\":false},\"session_id\":1439421597509,\"" + - "event_type\":\"session_start\",\"event_id\":1,\"event_properties\":{},\"devi" + - "ce_id\":\"610e21eb-2f27-48ca-bf4e-f977ce6391d1R\",\"device_brand\":\"unknown" + - "\",\"country\":\"US\",\"os_version\":\"unknown\",\"timestamp\":1439421597509" + - ",\"device_model\":\"unknown\",\"os_name\":\"android\",\"library\":{\"name\":" + - "\"amplitude-android\",\"version\":\"1.7.0\"},\"carrier\":null,\"user_id\":\"" + - "610e21eb-2f27-48ca-bf4e-f977ce6391d1R\",\"language\":\"en\"},{\"version_name" + - "\":null,\"device_manufacturer\":\"unknown\",\"user_properties\":{},\"platfor" + - "m\":\"Android\",\"api_properties\":{\"limit_ad_tracking\":false},\"session_i" + - "d\":1439421597509,\"event_type\":\"test\",\"event_id\":2,\"event_properties" + - "\":{},\"device_id\":\"610e21eb-2f27-48ca-bf4e-f977ce6391d1R\",\"device_brand" + - "\":\"unknown\",\"country\":\"US\",\"os_version\":\"unknown\",\"timestamp\":1" + - "439421597509,\"device_model\":\"unknown\",\"os_name\":\"android\",\"library" + - "\":{\"name\":\"amplitude-android\",\"version\":\"1.7.0\"},\"carrier\":null," + - "\"user_id\":\"610e21eb-2f27-48ca-bf4e-f977ce6391d1R\",\"language\":\"en\"}]"; - - String preImage = apiVersionString + apiKey + events + timestampString; - compareMD5(preImage, null); - } - - @Test - public void testMD5WithRandomStrings() { - for (int i = 0; i < 5; i++) { - compareMD5(UUID.randomUUID().toString(), null); - } - } - - @Test - public void testMD5WithUnicodeStrings() { - compareMD5("\u2661", "db36e9b42b9fa2863f94280206fb4d74"); - compareMD5("\uD83D\uDE1C", "8fb34591f1a56cf3ca9837774f4b7bd7"); - } -}