Wed, 31 Dec 2014 07:22:50 +0100
Correct previous dual key logic pending first delivery installment.
1 /* Any copyright is dedicated to the Public Domain.
2 http://creativecommons.org/publicdomain/zero/1.0/ */
4 package org.mozilla.gecko.background.healthreport;
6 import java.io.UnsupportedEncodingException;
7 import java.security.MessageDigest;
8 import java.security.NoSuchAlgorithmException;
9 import java.util.Arrays;
10 import java.util.LinkedList;
12 import org.mozilla.apache.commons.codec.binary.Base64;
13 import org.mozilla.gecko.background.healthreport.EnvironmentV1.EnvironmentAppender;
14 import org.mozilla.gecko.background.healthreport.EnvironmentV1.HashAppender;
15 import org.mozilla.gecko.background.helpers.FakeProfileTestCase;
16 import org.mozilla.gecko.sync.Utils;
18 /**
19 * Tests the HashAppender functionality. Note that these tests must be run on an Android
20 * device because the SHA-1 native library needs to be loaded.
21 */
22 public class TestEnvironmentV1HashAppender extends FakeProfileTestCase {
23 // input and expected values via: http://oauth.googlecode.com/svn/code/c/liboauth/src/sha1.c
24 private final static String[] INPUTS = new String[] {
25 "abc",
26 "abcdbcdecdefdefgefghfghighijhijkijkljklmklmnlmnomnopnopq",
27 "" // To be filled in below.
28 };
29 static {
30 final String baseStr = "01234567";
31 final int repetitions = 80;
32 final StringBuilder builder = new StringBuilder(baseStr.length() * repetitions);
33 for (int i = 0; i < 80; ++i) {
34 builder.append(baseStr);
35 }
36 INPUTS[2] = builder.toString();
37 }
39 private final static String[] EXPECTEDS = new String[] {
40 "a9993e364706816aba3e25717850c26c9cd0d89d",
41 "84983e441c3bd26ebaae4aa1f95129e5e54670f1",
42 "dea356a2cddd90c7a7ecedc5ebb563934f460452"
43 };
44 static {
45 for (int i = 0; i < EXPECTEDS.length; ++i) {
46 EXPECTEDS[i] = new Base64(-1, null, false).encodeAsString(Utils.hex2Byte(EXPECTEDS[i]));
47 }
48 }
50 public void testSHA1Hashing() throws Exception {
51 for (int i = 0; i < INPUTS.length; ++i) {
52 final String input = INPUTS[i];
53 final String expected = EXPECTEDS[i];
55 final HashAppender appender = new HashAppender();
56 addStringToAppenderInParts(appender, input);
57 final String result = appender.toString();
59 assertEquals(expected, result);
60 }
61 }
63 /**
64 * Tests to ensure output is the same as the former MessageDigest implementation (bug 959652).
65 */
66 public void testAgainstMessageDigestImpl() throws Exception {
67 // List.add doesn't allow add(null) so we make a LinkedList here.
68 final LinkedList<String> inputs = new LinkedList<String>(Arrays.asList(INPUTS));
69 inputs.add(null);
71 for (final String input : inputs) {
72 final HashAppender hAppender = new HashAppender();
73 final MessageDigestHashAppender mdAppender = new MessageDigestHashAppender();
75 hAppender.append(input);
76 mdAppender.append(input);
78 final String hResult = hAppender.toString();
79 final String mdResult = mdAppender.toString();
80 assertEquals(mdResult, hResult);
81 }
82 }
84 public void testIntegersAgainstMessageDigestImpl() throws Exception {
85 final int[] INPUTS = {Integer.MIN_VALUE, -1337, -42, 0, 42, 1337, Integer.MAX_VALUE};
86 for (final int input : INPUTS) {
87 final HashAppender hAppender = new HashAppender();
88 final MessageDigestHashAppender mdAppender = new MessageDigestHashAppender();
90 hAppender.append(input);
91 mdAppender.append(input);
93 final String hResult = hAppender.toString();
94 final String mdResult = mdAppender.toString();
95 assertEquals(mdResult, hResult);
96 }
97 }
99 private void addStringToAppenderInParts(final EnvironmentAppender appender, final String input) {
100 int substrInd = 0;
101 int substrLength = 1;
102 while (substrInd < input.length()) {
103 final int endInd = Math.min(substrInd + substrLength, input.length());
105 appender.append(input.substring(substrInd, endInd));
107 substrInd = endInd;
108 ++substrLength;
109 }
110 }
112 // --- COPY-PASTA'D CODE, FOR TESTING PURPOSES. ---
113 public static class MessageDigestHashAppender extends EnvironmentAppender {
114 final MessageDigest hasher;
116 public MessageDigestHashAppender() throws NoSuchAlgorithmException {
117 // Note to the security-minded reader: we deliberately use SHA-1 here, not
118 // a stronger hash. These identifiers don't strictly need a cryptographic
119 // hash function, because there is negligible value in attacking the hash.
120 // We use SHA-1 because it's *shorter* -- the exact same reason that Git
121 // chose SHA-1.
122 hasher = MessageDigest.getInstance("SHA-1");
123 }
125 @Override
126 public void append(String s) {
127 try {
128 hasher.update(((s == null) ? "null" : s).getBytes("UTF-8"));
129 } catch (UnsupportedEncodingException e) {
130 // This can never occur. Thanks, Java.
131 }
132 }
134 @Override
135 public void append(int profileCreation) {
136 append(Integer.toString(profileCreation, 10));
137 }
139 @Override
140 public String toString() {
141 // We *could* use ASCII85… but the savings would be negated by the
142 // inclusion of JSON-unsafe characters like double-quote.
143 return new Base64(-1, null, false).encodeAsString(hasher.digest());
144 }
145 }
146 }