mobile/android/base/sync/crypto/HKDF.java

changeset 0
6474c204b198
equal deleted inserted replaced
-1:000000000000 0:5a67e8b7eb57
1 /* This Source Code Form is subject to the terms of the Mozilla Public
2 * License, v. 2.0. If a copy of the MPL was not distributed with this
3 * file, You can obtain one at http://mozilla.org/MPL/2.0/. */
4
5 package org.mozilla.gecko.sync.crypto;
6
7 import java.security.InvalidKeyException;
8 import java.security.Key;
9 import java.security.NoSuchAlgorithmException;
10
11 import javax.crypto.Mac;
12 import javax.crypto.spec.SecretKeySpec;
13
14 import org.mozilla.gecko.sync.Utils;
15
16 /*
17 * A standards-compliant implementation of RFC 5869
18 * for HMAC-based Key Derivation Function.
19 * HMAC uses HMAC SHA256 standard.
20 */
21 public class HKDF {
22 public static String HMAC_ALGORITHM = "hmacSHA256";
23
24 /**
25 * Used for conversion in cases in which you *know* the encoding exists.
26 */
27 public static final byte[] bytes(String in) {
28 try {
29 return in.getBytes("UTF-8");
30 } catch (java.io.UnsupportedEncodingException e) {
31 return null;
32 }
33 }
34
35 public static final int BLOCKSIZE = 256 / 8;
36 public static final byte[] HMAC_INPUT = bytes("Sync-AES_256_CBC-HMAC256");
37
38 /*
39 * Step 1 of RFC 5869
40 * Get sha256HMAC Bytes
41 * Input: salt (message), IKM (input keyring material)
42 * Output: PRK (pseudorandom key)
43 */
44 public static byte[] hkdfExtract(byte[] salt, byte[] IKM) throws NoSuchAlgorithmException, InvalidKeyException {
45 return digestBytes(IKM, makeHMACHasher(salt));
46 }
47
48 /*
49 * Step 2 of RFC 5869.
50 * Input: PRK from step 1, info, length.
51 * Output: OKM (output keyring material).
52 */
53 public static byte[] hkdfExpand(byte[] prk, byte[] info, int len) throws NoSuchAlgorithmException, InvalidKeyException {
54 Mac hmacHasher = makeHMACHasher(prk);
55
56 byte[] T = {};
57 byte[] Tn = {};
58
59 int iterations = (int) Math.ceil(((double)len) / ((double)BLOCKSIZE));
60 for (int i = 0; i < iterations; i++) {
61 Tn = digestBytes(Utils.concatAll(Tn, info, Utils.hex2Byte(Integer.toHexString(i + 1))),
62 hmacHasher);
63 T = Utils.concatAll(T, Tn);
64 }
65
66 byte[] result = new byte[len];
67 System.arraycopy(T, 0, result, 0, len);
68 return result;
69 }
70
71 /*
72 * Make HMAC key
73 * Input: key (salt)
74 * Output: Key HMAC-Key
75 */
76 public static Key makeHMACKey(byte[] key) {
77 if (key.length == 0) {
78 key = new byte[BLOCKSIZE];
79 }
80 return new SecretKeySpec(key, HMAC_ALGORITHM);
81 }
82
83 /*
84 * Make an HMAC hasher
85 * Input: Key hmacKey
86 * Ouput: An HMAC Hasher
87 */
88 public static Mac makeHMACHasher(byte[] key) throws NoSuchAlgorithmException, InvalidKeyException {
89 Mac hmacHasher = null;
90 hmacHasher = Mac.getInstance(HMAC_ALGORITHM);
91
92 // If Mac.getInstance doesn't throw NoSuchAlgorithmException, hmacHasher is
93 // non-null.
94 assert(hmacHasher != null);
95
96 hmacHasher.init(makeHMACKey(key));
97 return hmacHasher;
98 }
99
100 /*
101 * Hash bytes with given hasher
102 * Input: message to hash, HMAC hasher
103 * Output: hashed byte[].
104 */
105 public static byte[] digestBytes(byte[] message, Mac hasher) {
106 hasher.update(message);
107 byte[] ret = hasher.doFinal();
108 hasher.reset();
109 return ret;
110 }
111
112 public static byte[] derive(byte[] skm, byte[] xts, byte[] ctxInfo, int dkLen) throws InvalidKeyException, NoSuchAlgorithmException {
113 return hkdfExpand(hkdfExtract(xts, skm), ctxInfo, dkLen);
114 }
115
116 public static void deriveMany(byte[] skm, byte[] xts, byte[] ctxInfo, byte[]... keys) throws InvalidKeyException, NoSuchAlgorithmException {
117 int length = 0;
118 for (byte[] key : keys) {
119 length += key.length;
120 }
121 byte[] derived = hkdfExpand(hkdfExtract(xts, skm), ctxInfo, length);
122 int offset = 0;
123 for (byte[] key : keys) {
124 System.arraycopy(derived, offset, key, 0, key.length);
125 offset += key.length;
126 }
127 }
128 }

mercurial