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

Wed, 31 Dec 2014 06:09:35 +0100

author
Michael Schloh von Bennewitz <michael@schloh.com>
date
Wed, 31 Dec 2014 06:09:35 +0100
changeset 0
6474c204b198
permissions
-rw-r--r--

Cloned upstream origin tor-browser at tor-browser-31.3.0esr-4.5-1-build1
revision ID fc1c9ff7c1b2defdbc039f12214767608f46423f for hacking purpose.

michael@0 1 /* This Source Code Form is subject to the terms of the Mozilla Public
michael@0 2 * License, v. 2.0. If a copy of the MPL was not distributed with this
michael@0 3 * file, You can obtain one at http://mozilla.org/MPL/2.0/. */
michael@0 4
michael@0 5 package org.mozilla.gecko.sync.crypto;
michael@0 6
michael@0 7 import java.security.GeneralSecurityException;
michael@0 8 import java.security.InvalidKeyException;
michael@0 9 import java.security.NoSuchAlgorithmException;
michael@0 10 import java.util.Arrays;
michael@0 11
michael@0 12 import javax.crypto.BadPaddingException;
michael@0 13 import javax.crypto.Cipher;
michael@0 14 import javax.crypto.IllegalBlockSizeException;
michael@0 15 import javax.crypto.Mac;
michael@0 16 import javax.crypto.NoSuchPaddingException;
michael@0 17 import javax.crypto.spec.IvParameterSpec;
michael@0 18 import javax.crypto.spec.SecretKeySpec;
michael@0 19
michael@0 20 import org.mozilla.apache.commons.codec.binary.Base64;
michael@0 21
michael@0 22 /*
michael@0 23 * All info in these objects should be decoded (i.e. not BaseXX encoded).
michael@0 24 */
michael@0 25 public class CryptoInfo {
michael@0 26 private static final String TRANSFORMATION = "AES/CBC/PKCS5Padding";
michael@0 27 private static final String KEY_ALGORITHM_SPEC = "AES";
michael@0 28
michael@0 29 private byte[] message;
michael@0 30 private byte[] iv;
michael@0 31 private byte[] hmac;
michael@0 32 private KeyBundle keys;
michael@0 33
michael@0 34 /**
michael@0 35 * Return a CryptoInfo with given plaintext encrypted using given keys.
michael@0 36 */
michael@0 37 public static CryptoInfo encrypt(byte[] plaintextBytes, KeyBundle keys) throws CryptoException {
michael@0 38 CryptoInfo info = new CryptoInfo(plaintextBytes, keys);
michael@0 39 info.encrypt();
michael@0 40 return info;
michael@0 41 }
michael@0 42
michael@0 43 /**
michael@0 44 * Return a CryptoInfo with given plaintext encrypted using given keys and initial vector.
michael@0 45 */
michael@0 46 public static CryptoInfo encrypt(byte[] plaintextBytes, byte[] iv, KeyBundle keys) throws CryptoException {
michael@0 47 CryptoInfo info = new CryptoInfo(plaintextBytes, iv, null, keys);
michael@0 48 info.encrypt();
michael@0 49 return info;
michael@0 50 }
michael@0 51
michael@0 52 /**
michael@0 53 * Return a CryptoInfo with given ciphertext decrypted using given keys and initial vector, verifying that given HMAC validates.
michael@0 54 */
michael@0 55 public static CryptoInfo decrypt(byte[] ciphertext, byte[] iv, byte[] hmac, KeyBundle keys) throws CryptoException {
michael@0 56 CryptoInfo info = new CryptoInfo(ciphertext, iv, hmac, keys);
michael@0 57 info.decrypt();
michael@0 58 return info;
michael@0 59 }
michael@0 60
michael@0 61 /*
michael@0 62 * Constructor typically used when encrypting.
michael@0 63 */
michael@0 64 public CryptoInfo(byte[] message, KeyBundle keys) {
michael@0 65 this.setMessage(message);
michael@0 66 this.setKeys(keys);
michael@0 67 }
michael@0 68
michael@0 69 /*
michael@0 70 * Constructor typically used when decrypting.
michael@0 71 */
michael@0 72 public CryptoInfo(byte[] message, byte[] iv, byte[] hmac, KeyBundle keys) {
michael@0 73 this.setMessage(message);
michael@0 74 this.setIV(iv);
michael@0 75 this.setHMAC(hmac);
michael@0 76 this.setKeys(keys);
michael@0 77 }
michael@0 78
michael@0 79 public byte[] getMessage() {
michael@0 80 return message;
michael@0 81 }
michael@0 82
michael@0 83 public void setMessage(byte[] message) {
michael@0 84 this.message = message;
michael@0 85 }
michael@0 86
michael@0 87 public byte[] getIV() {
michael@0 88 return iv;
michael@0 89 }
michael@0 90
michael@0 91 public void setIV(byte[] iv) {
michael@0 92 this.iv = iv;
michael@0 93 }
michael@0 94
michael@0 95 public byte[] getHMAC() {
michael@0 96 return hmac;
michael@0 97 }
michael@0 98
michael@0 99 public void setHMAC(byte[] hmac) {
michael@0 100 this.hmac = hmac;
michael@0 101 }
michael@0 102
michael@0 103 public KeyBundle getKeys() {
michael@0 104 return keys;
michael@0 105 }
michael@0 106
michael@0 107 public void setKeys(KeyBundle keys) {
michael@0 108 this.keys = keys;
michael@0 109 }
michael@0 110
michael@0 111 /*
michael@0 112 * Generate HMAC for given cipher text.
michael@0 113 */
michael@0 114 public static byte[] generatedHMACFor(byte[] message, KeyBundle keys) throws NoSuchAlgorithmException, InvalidKeyException {
michael@0 115 Mac hmacHasher = HKDF.makeHMACHasher(keys.getHMACKey());
michael@0 116 return hmacHasher.doFinal(Base64.encodeBase64(message));
michael@0 117 }
michael@0 118
michael@0 119 /*
michael@0 120 * Return true if generated HMAC is the same as the specified HMAC.
michael@0 121 */
michael@0 122 public boolean generatedHMACIsHMAC() throws NoSuchAlgorithmException, InvalidKeyException {
michael@0 123 byte[] generatedHMAC = generatedHMACFor(getMessage(), getKeys());
michael@0 124 byte[] expectedHMAC = getHMAC();
michael@0 125 return Arrays.equals(generatedHMAC, expectedHMAC);
michael@0 126 }
michael@0 127
michael@0 128 /**
michael@0 129 * Performs functionality common to both encryption and decryption.
michael@0 130 *
michael@0 131 * @param cipher
michael@0 132 * @param inputMessage non-BaseXX-encoded message
michael@0 133 * @return encrypted/decrypted message
michael@0 134 * @throws CryptoException
michael@0 135 */
michael@0 136 private static byte[] commonCrypto(Cipher cipher, byte[] inputMessage)
michael@0 137 throws CryptoException {
michael@0 138 byte[] outputMessage = null;
michael@0 139 try {
michael@0 140 outputMessage = cipher.doFinal(inputMessage);
michael@0 141 } catch (IllegalBlockSizeException e) {
michael@0 142 throw new CryptoException(e);
michael@0 143 } catch (BadPaddingException e) {
michael@0 144 throw new CryptoException(e);
michael@0 145 }
michael@0 146 return outputMessage;
michael@0 147 }
michael@0 148
michael@0 149 /**
michael@0 150 * Encrypt a CryptoInfo in-place.
michael@0 151 *
michael@0 152 * @throws CryptoException
michael@0 153 */
michael@0 154 public void encrypt() throws CryptoException {
michael@0 155
michael@0 156 Cipher cipher = CryptoInfo.getCipher(TRANSFORMATION);
michael@0 157 try {
michael@0 158 byte[] encryptionKey = getKeys().getEncryptionKey();
michael@0 159 SecretKeySpec spec = new SecretKeySpec(encryptionKey, KEY_ALGORITHM_SPEC);
michael@0 160
michael@0 161 // If no IV is provided, we allow the cipher to provide one.
michael@0 162 if (getIV() == null || getIV().length == 0) {
michael@0 163 cipher.init(Cipher.ENCRYPT_MODE, spec);
michael@0 164 } else {
michael@0 165 cipher.init(Cipher.ENCRYPT_MODE, spec, new IvParameterSpec(getIV()));
michael@0 166 }
michael@0 167 } catch (GeneralSecurityException ex) {
michael@0 168 throw new CryptoException(ex);
michael@0 169 }
michael@0 170
michael@0 171 // Encrypt.
michael@0 172 byte[] encryptedBytes = commonCrypto(cipher, getMessage());
michael@0 173 byte[] iv = cipher.getIV();
michael@0 174
michael@0 175 byte[] hmac;
michael@0 176 // Generate HMAC.
michael@0 177 try {
michael@0 178 hmac = generatedHMACFor(encryptedBytes, keys);
michael@0 179 } catch (NoSuchAlgorithmException e) {
michael@0 180 throw new CryptoException(e);
michael@0 181 } catch (InvalidKeyException e) {
michael@0 182 throw new CryptoException(e);
michael@0 183 }
michael@0 184
michael@0 185 // Update in place. keys is already set.
michael@0 186 this.setHMAC(hmac);
michael@0 187 this.setIV(iv);
michael@0 188 this.setMessage(encryptedBytes);
michael@0 189 }
michael@0 190
michael@0 191 /**
michael@0 192 * Decrypt a CryptoInfo in-place.
michael@0 193 *
michael@0 194 * @throws CryptoException
michael@0 195 */
michael@0 196 public void decrypt() throws CryptoException {
michael@0 197
michael@0 198 // Check HMAC.
michael@0 199 try {
michael@0 200 if (!generatedHMACIsHMAC()) {
michael@0 201 throw new HMACVerificationException();
michael@0 202 }
michael@0 203 } catch (NoSuchAlgorithmException e) {
michael@0 204 throw new CryptoException(e);
michael@0 205 } catch (InvalidKeyException e) {
michael@0 206 throw new CryptoException(e);
michael@0 207 }
michael@0 208
michael@0 209 Cipher cipher = CryptoInfo.getCipher(TRANSFORMATION);
michael@0 210 try {
michael@0 211 byte[] encryptionKey = getKeys().getEncryptionKey();
michael@0 212 SecretKeySpec spec = new SecretKeySpec(encryptionKey, KEY_ALGORITHM_SPEC);
michael@0 213 cipher.init(Cipher.DECRYPT_MODE, spec, new IvParameterSpec(getIV()));
michael@0 214 } catch (GeneralSecurityException ex) {
michael@0 215 throw new CryptoException(ex);
michael@0 216 }
michael@0 217 byte[] decryptedBytes = commonCrypto(cipher, getMessage());
michael@0 218 byte[] iv = cipher.getIV();
michael@0 219
michael@0 220 // Update in place. keys is already set.
michael@0 221 this.setHMAC(null);
michael@0 222 this.setIV(iv);
michael@0 223 this.setMessage(decryptedBytes);
michael@0 224 }
michael@0 225
michael@0 226 /**
michael@0 227 * Helper to get a Cipher object.
michael@0 228 *
michael@0 229 * @param transformation The type of Cipher to get.
michael@0 230 */
michael@0 231 private static Cipher getCipher(String transformation) throws CryptoException {
michael@0 232 try {
michael@0 233 return Cipher.getInstance(transformation);
michael@0 234 } catch (NoSuchAlgorithmException e) {
michael@0 235 throw new CryptoException(e);
michael@0 236 } catch (NoSuchPaddingException e) {
michael@0 237 throw new CryptoException(e);
michael@0 238 }
michael@0 239 }
michael@0 240 }

mercurial