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

Wed, 31 Dec 2014 07:22:50 +0100

author
Michael Schloh von Bennewitz <michael@schloh.com>
date
Wed, 31 Dec 2014 07:22:50 +0100
branch
TOR_BUG_3246
changeset 4
fc2d59ddac77
permissions
-rw-r--r--

Correct previous dual key logic pending first delivery installment.

     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.io.UnsupportedEncodingException;
     8 import java.security.InvalidKeyException;
     9 import java.security.NoSuchAlgorithmException;
    10 import java.util.Arrays;
    12 import javax.crypto.KeyGenerator;
    13 import javax.crypto.Mac;
    15 import org.mozilla.apache.commons.codec.binary.Base64;
    16 import org.mozilla.gecko.sync.Utils;
    18 public class KeyBundle {
    19     private static final String KEY_ALGORITHM_SPEC = "AES";
    20     private static final int    KEY_SIZE           = 256;
    22     private byte[] encryptionKey;
    23     private byte[] hmacKey;
    25     // These are the same for every sync key bundle.
    26     private static final byte[] EMPTY_BYTES      = {};
    27     private static final byte[] ENCR_INPUT_BYTES = {1};
    28     private static final byte[] HMAC_INPUT_BYTES = {2};
    30     /*
    31      * Mozilla's use of HKDF for getting keys from the Sync Key string.
    32      *
    33      * We do exactly 2 HKDF iterations and make the first iteration the
    34      * encryption key and the second iteration the HMAC key.
    35      *
    36      */
    37     public KeyBundle(String username, String base32SyncKey) throws CryptoException {
    38       if (base32SyncKey == null) {
    39         throw new IllegalArgumentException("No sync key provided.");
    40       }
    41       if (username == null || username.equals("")) {
    42         throw new IllegalArgumentException("No username provided.");
    43       }
    44       // Hash appropriately.
    45       try {
    46         username = Utils.usernameFromAccount(username);
    47       } catch (NoSuchAlgorithmException e) {
    48         throw new IllegalArgumentException("Invalid username.");
    49       } catch (UnsupportedEncodingException e) {
    50         throw new IllegalArgumentException("Invalid username.");
    51       }
    53       byte[] syncKey = Utils.decodeFriendlyBase32(base32SyncKey);
    54       byte[] user    = username.getBytes();
    56       Mac hmacHasher;
    57       try {
    58         hmacHasher = HKDF.makeHMACHasher(syncKey);
    59       } catch (NoSuchAlgorithmException e) {
    60         throw new CryptoException(e);
    61       } catch (InvalidKeyException e) {
    62         throw new CryptoException(e);
    63       }
    64       assert(hmacHasher != null); // If makeHMACHasher doesn't throw, then hmacHasher is non-null.
    66       byte[] encrBytes = Utils.concatAll(EMPTY_BYTES, HKDF.HMAC_INPUT, user, ENCR_INPUT_BYTES);
    67       byte[] encrKey   = HKDF.digestBytes(encrBytes, hmacHasher);
    68       byte[] hmacBytes = Utils.concatAll(encrKey, HKDF.HMAC_INPUT, user, HMAC_INPUT_BYTES);
    70       this.hmacKey       = HKDF.digestBytes(hmacBytes, hmacHasher);
    71       this.encryptionKey = encrKey;
    72     }
    74     public KeyBundle(byte[] encryptionKey, byte[] hmacKey) {
    75        this.setEncryptionKey(encryptionKey);
    76        this.setHMACKey(hmacKey);
    77     }
    79     /**
    80      * Make a KeyBundle with the specified base64-encoded keys.
    81      *
    82      * @return A KeyBundle with the specified keys.
    83      */
    84     public static KeyBundle fromBase64EncodedKeys(String base64EncryptionKey, String base64HmacKey) throws UnsupportedEncodingException {
    85       return new KeyBundle(Base64.decodeBase64(base64EncryptionKey.getBytes("UTF-8")),
    86                            Base64.decodeBase64(base64HmacKey.getBytes("UTF-8")));
    87     }
    89     /**
    90      * Make a KeyBundle with two random 256 bit keys (encryption and HMAC).
    91      *
    92      * @return A KeyBundle with random keys.
    93      */
    94     public static KeyBundle withRandomKeys() throws CryptoException {
    95       KeyGenerator keygen;
    96       try {
    97         keygen = KeyGenerator.getInstance(KEY_ALGORITHM_SPEC);
    98       } catch (NoSuchAlgorithmException e) {
    99         throw new CryptoException(e);
   100       }
   102       keygen.init(KEY_SIZE);
   103       byte[] encryptionKey = keygen.generateKey().getEncoded();
   104       byte[] hmacKey = keygen.generateKey().getEncoded();
   106       return new KeyBundle(encryptionKey, hmacKey);
   107     }
   109     public byte[] getEncryptionKey() {
   110         return encryptionKey;
   111     }
   113     public void setEncryptionKey(byte[] encryptionKey) {
   114         this.encryptionKey = encryptionKey;
   115     }
   117     public byte[] getHMACKey() {
   118         return hmacKey;
   119     }
   121     public void setHMACKey(byte[] hmacKey) {
   122         this.hmacKey = hmacKey;
   123     }
   125     @Override
   126     public boolean equals(Object o) {
   127       if (!(o instanceof KeyBundle)) {
   128         return false;
   129       }
   130       KeyBundle other = (KeyBundle) o;
   131       return Arrays.equals(other.encryptionKey, this.encryptionKey) &&
   132              Arrays.equals(other.hmacKey, this.hmacKey);
   133     }
   135     @Override
   136     public int hashCode() {
   137       throw new UnsupportedOperationException("No hashCode for KeyBundle.");
   138     }
   139 }

mercurial