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.

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

mercurial