Bug 1240919 - Part 2: Cull more client v1 code. r=rnewman
authorNick Alexander <nalexander@mozilla.com>
Tue, 19 Jan 2016 13:59:30 -0800
changeset 303128 d09ba092a9d28305247f9bdab4513602fab1785e
parent 303127 2cffe56c205e33399c7485b3660476ab4272c0dd
child 303129 902120f314e9bd79dd1fa447688b8ea8adab9f6c
push id8978
push userraliiev@mozilla.com
push dateMon, 25 Jan 2016 14:05:32 +0000
treeherdermozilla-aurora@b9a803752a2c [default view] [failures only]
perfherder[talos] [build metrics] [platform microbench] (compared to previous push)
reviewersrnewman
bugs1240919
milestone46.0a1
Bug 1240919 - Part 2: Cull more client v1 code. r=rnewman
mobile/android/base/android-services.mozbuild
mobile/android/services/src/main/java/org/mozilla/gecko/background/fxa/FxAccount10AuthDelegate.java
mobile/android/services/src/main/java/org/mozilla/gecko/background/fxa/FxAccount10CreateDelegate.java
mobile/android/services/src/main/java/org/mozilla/gecko/background/fxa/FxAccount20CreateDelegate.java
mobile/android/services/src/main/java/org/mozilla/gecko/background/fxa/FxAccount20LoginDelegate.java
mobile/android/services/src/main/java/org/mozilla/gecko/background/fxa/FxAccountClient10.java
--- a/mobile/android/base/android-services.mozbuild
+++ b/mobile/android/base/android-services.mozbuild
@@ -776,18 +776,16 @@ sync_java_files = [TOPSRCDIR + '/mobile/
     'background/common/log/writers/SimpleTagLogWriter.java',
     'background/common/log/writers/StringLogWriter.java',
     'background/common/log/writers/TagLogWriter.java',
     'background/common/log/writers/ThreadLocalTagLogWriter.java',
     'background/common/PrefsBranch.java',
     'background/common/telemetry/TelemetryWrapper.java',
     'background/db/CursorDumper.java',
     'background/db/Tab.java',
-    'background/fxa/FxAccount10AuthDelegate.java',
-    'background/fxa/FxAccount10CreateDelegate.java',
     'background/fxa/FxAccount20CreateDelegate.java',
     'background/fxa/FxAccount20LoginDelegate.java',
     'background/fxa/FxAccountClient.java',
     'background/fxa/FxAccountClient10.java',
     'background/fxa/FxAccountClient20.java',
     'background/fxa/FxAccountClientException.java',
     'background/fxa/FxAccountRemoteError.java',
     'background/fxa/FxAccountUtils.java',
deleted file mode 100644
--- a/mobile/android/services/src/main/java/org/mozilla/gecko/background/fxa/FxAccount10AuthDelegate.java
+++ /dev/null
@@ -1,218 +0,0 @@
-/* This Source Code Form is subject to the terms of the Mozilla Public
- * License, v. 2.0. If a copy of the MPL was not distributed with this
- * file, You can obtain one at http://mozilla.org/MPL/2.0/. */
-
-package org.mozilla.gecko.background.fxa;
-
-import java.io.UnsupportedEncodingException;
-import java.math.BigInteger;
-import java.security.NoSuchAlgorithmException;
-
-import org.json.simple.JSONObject;
-import org.mozilla.gecko.sync.ExtendedJSONObject;
-import org.mozilla.gecko.sync.Utils;
-import org.mozilla.gecko.sync.net.SRPConstants;
-
-public class FxAccount10AuthDelegate implements FxAccountClient10.AuthDelegate {
-  // Fixed by protocol.
-  protected final BigInteger N;
-  protected final BigInteger g;
-  protected final int modNLengthBytes;
-
-  // Configured at construction time.
-  protected final String email;
-  protected final byte[] stretchedPWBytes;
-
-  // Encapsulate state.
-  protected static class AuthState {
-    protected String srpToken;
-    protected String mainSalt;
-    protected String srpSalt;
-
-    protected BigInteger x;
-    protected BigInteger A;
-    protected byte[] Kbytes;
-    protected byte[] Mbytes;
-  }
-
-  // State should be written exactly once.
-  protected AuthState internalAuthState = null;
-
-  public FxAccount10AuthDelegate(String email, byte[] stretchedPWBytes) {
-    this.email = email;
-    this.stretchedPWBytes = stretchedPWBytes;
-    this.N = SRPConstants._2048.N;
-    this.g = SRPConstants._2048.g;
-    this.modNLengthBytes = SRPConstants._2048.byteLength;
-  }
-
-  protected BigInteger generateSecretValue() {
-    return Utils.generateBigIntegerLessThan(N);
-  }
-
-  public static class FxAccountClientMalformedAuthException extends FxAccountClientException {
-    private static final long serialVersionUID = 3585262174699395505L;
-
-    public FxAccountClientMalformedAuthException(String detailMessage) {
-      super(detailMessage);
-    }
-  }
-
-  @SuppressWarnings("unchecked")
-  @Override
-  public JSONObject getAuthStartBody() throws FxAccountClientException {
-    try {
-      final JSONObject body = new JSONObject();
-      body.put("email", FxAccountUtils.bytes(email));
-      return body;
-    } catch (UnsupportedEncodingException e) {
-      throw new FxAccountClientException(e);
-    }
-  }
-
-  @Override
-  public void onAuthStartResponse(final ExtendedJSONObject body) throws FxAccountClientException {
-    if (this.internalAuthState != null) {
-      throw new FxAccountClientException("auth must not be written before calling onAuthStartResponse");
-    }
-
-    String srpToken = null;
-    String srpSalt = null;
-    String srpB = null;
-    String mainSalt = null;
-
-    try {
-      srpToken = body.getString("srpToken");
-      if (srpToken == null) {
-        throw new FxAccountClientMalformedAuthException("srpToken must be a non-null object");
-      }
-      ExtendedJSONObject srp = body.getObject("srp");
-      if (srp == null) {
-        throw new FxAccountClientMalformedAuthException("srp must be a non-null object");
-      }
-      srpSalt = srp.getString("salt");
-      if (srpSalt == null) {
-        throw new FxAccountClientMalformedAuthException("srp.salt must not be null");
-      }
-      srpB = srp.getString("B");
-      if (srpB == null) {
-        throw new FxAccountClientMalformedAuthException("srp.B must not be null");
-      }
-      ExtendedJSONObject passwordStretching = body.getObject("passwordStretching");
-      if (passwordStretching == null) {
-        throw new FxAccountClientMalformedAuthException("passwordStretching must be a non-null object");
-      }
-      mainSalt = passwordStretching.getString("salt");
-      if (mainSalt == null) {
-        throw new FxAccountClientMalformedAuthException("srp.passwordStretching.salt must not be null");
-      }
-      throwIfParametersAreBad(passwordStretching);
-
-      this.internalAuthState = authStateFromParameters(srpToken, mainSalt, srpSalt, srpB, generateSecretValue());
-    } catch (FxAccountClientException e) {
-      throw e;
-    } catch (Exception e) {
-      throw new FxAccountClientException(e);
-    }
-  }
-
-  /**
-   * Expect object like:
-   * "passwordStretching": {
-   *   "type": "PBKDF2/scrypt/PBKDF2/v1",
-   *   "PBKDF2_rounds_1": 20000,
-   *   "scrypt_N": 65536,
-   *   "scrypt_r": 8,
-   *   "scrypt_p": 1,
-   *   "PBKDF2_rounds_2": 20000,
-   *   "salt": "996bc6b1aa63cd69856a2ec81cbf19d5c8a604713362df9ee15c2bf07128efab"
-   * }
-   * @param params to verify.
-   * @throws FxAccountClientMalformedAuthException
-   */
-  protected void throwIfParametersAreBad(ExtendedJSONObject params) throws FxAccountClientMalformedAuthException {
-    if (params == null ||
-        params.size() != 7 ||
-        params.getString("salt") == null ||
-        !("PBKDF2/scrypt/PBKDF2/v1".equals(params.getString("type"))) ||
-        20000 != params.getLong("PBKDF2_rounds_1") ||
-        65536 != params.getLong("scrypt_N") ||
-        8 != params.getLong("scrypt_r") ||
-        1 != params.getLong("scrypt_p") ||
-        20000 != params.getLong("PBKDF2_rounds_2")) {
-      throw new FxAccountClientMalformedAuthException("malformed passwordStretching parameters: '" + params.toJSONString() + "'.");
-    }
-  }
-
-  /**
-   * All state is written in this method.
-   */
-  protected AuthState authStateFromParameters(String srpToken, String mainSalt, String srpSalt, String srpB, BigInteger a) throws NoSuchAlgorithmException, UnsupportedEncodingException {
-    AuthState authState = new AuthState();
-    authState.srpToken = srpToken;
-    authState.mainSalt = mainSalt;
-    authState.srpSalt = srpSalt;
-
-    authState.x = FxAccountUtils.srpVerifierLowercaseX(email.getBytes("UTF-8"), this.stretchedPWBytes, Utils.hex2Byte(srpSalt, FxAccountUtils.SALT_LENGTH_BYTES));
-
-    authState.A = g.modPow(a, N);
-    String srpA = FxAccountUtils.hexModN(authState.A, N);
-    BigInteger B = new BigInteger(srpB, 16);
-
-    byte[] srpABytes = Utils.hex2Byte(srpA, modNLengthBytes);
-    byte[] srpBBytes = Utils.hex2Byte(srpB, modNLengthBytes);
-
-    // u = H(pad(A) | pad(B))
-    byte[] uBytes = Utils.sha256(Utils.concatAll(
-        srpABytes,
-        srpBBytes));
-    BigInteger u = new BigInteger(Utils.byte2Hex(uBytes, FxAccountUtils.HASH_LENGTH_HEX), 16);
-
-    // S = (B - k*g^x)^(a  u*x) % N
-    // k = H(pad(N) | pad(g))
-    int byteLength = (N.bitLength() + 7) / 8;
-    byte[] kBytes = Utils.sha256(Utils.concatAll(
-        Utils.hex2Byte(N.toString(16), byteLength),
-        Utils.hex2Byte(g.toString(16), byteLength)));
-    BigInteger k = new BigInteger(Utils.byte2Hex(kBytes, FxAccountUtils.HASH_LENGTH_HEX), 16);
-
-    BigInteger base = B.subtract(k.multiply(g.modPow(authState.x, N)).mod(N)).mod(N);
-    BigInteger pow = a.add(u.multiply(authState.x));
-    BigInteger S = base.modPow(pow, N);
-    String srpS = FxAccountUtils.hexModN(S, N);
-
-    byte[] sBytes = Utils.hex2Byte(srpS, modNLengthBytes);
-
-    // M = H(pad(A) | pad(B) | pad(S))
-    authState.Mbytes = Utils.sha256(Utils.concatAll(
-        srpABytes,
-        srpBBytes,
-        sBytes));
-
-    // K = H(pad(S))
-    authState.Kbytes = Utils.sha256(sBytes);
-
-    return authState;
-  }
-
-  @SuppressWarnings("unchecked")
-  @Override
-  public JSONObject getAuthFinishBody() throws FxAccountClientException {
-    if (internalAuthState == null) {
-      throw new FxAccountClientException("auth must be successfully written before calling getAuthFinishBody.");
-    }
-    JSONObject body = new JSONObject();
-    body.put("srpToken", internalAuthState.srpToken);
-    body.put("A", FxAccountUtils.hexModN(internalAuthState.A, N));
-    body.put("M", Utils.byte2Hex(internalAuthState.Mbytes, FxAccountUtils.HASH_LENGTH_HEX));
-    return body;
-  }
-
-  @Override
-  public byte[] getSharedBytes() throws FxAccountClientException {
-    if (internalAuthState == null) {
-      throw new FxAccountClientException("auth must be successfully finished before calling getSharedBytes.");
-    }
-    return internalAuthState.Kbytes;
-  }
-}
deleted file mode 100644
--- a/mobile/android/services/src/main/java/org/mozilla/gecko/background/fxa/FxAccount10CreateDelegate.java
+++ /dev/null
@@ -1,57 +0,0 @@
-/* This Source Code Form is subject to the terms of the Mozilla Public
- * License, v. 2.0. If a copy of the MPL was not distributed with this
- * file, You can obtain one at http://mozilla.org/MPL/2.0/. */
-
-package org.mozilla.gecko.background.fxa;
-
-import java.io.UnsupportedEncodingException;
-import java.math.BigInteger;
-import java.security.NoSuchAlgorithmException;
-
-import org.json.simple.JSONObject;
-import org.mozilla.gecko.background.fxa.FxAccountClient10.CreateDelegate;
-import org.mozilla.gecko.sync.Utils;
-import org.mozilla.gecko.sync.net.SRPConstants;
-
-public class FxAccount10CreateDelegate implements CreateDelegate {
-  protected final String     email;
-  protected final String     mainSalt;
-  protected final String     srpSalt;
-  protected final BigInteger v;
-
-  public FxAccount10CreateDelegate(String email, byte[] stretchedPWBytes, String mainSalt, String srpSalt) throws NoSuchAlgorithmException, UnsupportedEncodingException {
-    this.email = email;
-    this.mainSalt = mainSalt;
-    this.srpSalt = srpSalt;
-    byte[] srpSaltBytes = Utils.hex2Byte(srpSalt, FxAccountUtils.SALT_LENGTH_BYTES);
-    this.v = FxAccountUtils.srpVerifierLowercaseV(email.getBytes("UTF-8"), stretchedPWBytes, srpSaltBytes, SRPConstants._2048.g, SRPConstants._2048.N);
-  }
-
-  @SuppressWarnings("unchecked")
-  @Override
-  public JSONObject getCreateBody() throws FxAccountClientException {
-    final JSONObject body = new JSONObject();
-    try {
-      body.put("email", FxAccountUtils.bytes(email));
-    } catch (UnsupportedEncodingException e) {
-      throw new FxAccountClientException(e);
-    }
-
-    final JSONObject stretching = new JSONObject();
-    stretching.put("type", "PBKDF2/scrypt/PBKDF2/v1");
-    stretching.put("PBKDF2_rounds_1", 20000);
-    stretching.put("scrypt_N", 65536);
-    stretching.put("scrypt_r", 8);
-    stretching.put("scrypt_p", 1);
-    stretching.put("PBKDF2_rounds_2", 20000);
-    stretching.put("salt", mainSalt);
-    body.put("passwordStretching", stretching);
-
-    final JSONObject srp = new JSONObject();
-    srp.put("type", "SRP-6a/SHA256/2048/v1");
-    srp.put("verifier", FxAccountUtils.hexModN(v, SRPConstants._2048.N));
-    srp.put("salt", srpSalt);
-    body.put("srp", srp);
-    return body;
-  }
-}
--- a/mobile/android/services/src/main/java/org/mozilla/gecko/background/fxa/FxAccount20CreateDelegate.java
+++ b/mobile/android/services/src/main/java/org/mozilla/gecko/background/fxa/FxAccount20CreateDelegate.java
@@ -1,22 +1,21 @@
 /* This Source Code Form is subject to the terms of the Mozilla Public
  * License, v. 2.0. If a copy of the MPL was not distributed with this
  * file, You can obtain one at http://mozilla.org/MPL/2.0/. */
 
 package org.mozilla.gecko.background.fxa;
 
+import org.json.simple.JSONObject;
+import org.mozilla.gecko.sync.Utils;
+
 import java.io.UnsupportedEncodingException;
 import java.security.GeneralSecurityException;
 
-import org.json.simple.JSONObject;
-import org.mozilla.gecko.background.fxa.FxAccountClient10.CreateDelegate;
-import org.mozilla.gecko.sync.Utils;
-
-public class FxAccount20CreateDelegate implements CreateDelegate {
+public class FxAccount20CreateDelegate {
   protected final byte[] emailUTF8;
   protected final byte[] authPW;
   protected final boolean preVerified;
 
   /**
    * Make a new "create account" delegate.
    *
    * @param emailUTF8
@@ -31,17 +30,16 @@ public class FxAccount20CreateDelegate i
    */
   public FxAccount20CreateDelegate(byte[] emailUTF8, byte[] quickStretchedPW, boolean preVerified) throws UnsupportedEncodingException, GeneralSecurityException {
     this.emailUTF8 = emailUTF8;
     this.authPW = FxAccountUtils.generateAuthPW(quickStretchedPW);
     this.preVerified = preVerified;
   }
 
   @SuppressWarnings("unchecked")
-  @Override
   public JSONObject getCreateBody() throws FxAccountClientException {
     final JSONObject body = new JSONObject();
     try {
       body.put("email", new String(emailUTF8, "UTF-8"));
       body.put("authPW", Utils.byte2Hex(authPW));
       if (preVerified) {
         // Production endpoints do not allow preVerified; this assumes we only
         // set it when it's okay to send it.
--- a/mobile/android/services/src/main/java/org/mozilla/gecko/background/fxa/FxAccount20LoginDelegate.java
+++ b/mobile/android/services/src/main/java/org/mozilla/gecko/background/fxa/FxAccount20LoginDelegate.java
@@ -3,34 +3,32 @@
  * file, You can obtain one at http://mozilla.org/MPL/2.0/. */
 
 package org.mozilla.gecko.background.fxa;
 
 import java.io.UnsupportedEncodingException;
 import java.security.GeneralSecurityException;
 
 import org.json.simple.JSONObject;
-import org.mozilla.gecko.background.fxa.FxAccountClient10.CreateDelegate;
 import org.mozilla.gecko.sync.Utils;
 
 /**
  * An abstraction around providing an email and authorization token to the auth
  * server.
  */
-public class FxAccount20LoginDelegate implements CreateDelegate {
+public class FxAccount20LoginDelegate {
   protected final byte[] emailUTF8;
   protected final byte[] authPW;
 
   public FxAccount20LoginDelegate(byte[] emailUTF8, byte[] quickStretchedPW) throws UnsupportedEncodingException, GeneralSecurityException {
     this.emailUTF8 = emailUTF8;
     this.authPW = FxAccountUtils.generateAuthPW(quickStretchedPW);
   }
 
   @SuppressWarnings("unchecked")
-  @Override
   public JSONObject getCreateBody() throws FxAccountClientException {
     final JSONObject body = new JSONObject();
     try {
       body.put("email", new String(emailUTF8, "UTF-8"));
       body.put("authPW", Utils.byte2Hex(authPW));
       return body;
     } catch (UnsupportedEncodingException e) {
       throw new FxAccountClientException(e);
--- a/mobile/android/services/src/main/java/org/mozilla/gecko/background/fxa/FxAccountClient10.java
+++ b/mobile/android/services/src/main/java/org/mozilla/gecko/background/fxa/FxAccountClient10.java
@@ -150,35 +150,16 @@ public class FxAccountClient10 {
    */
   public interface RequestDelegate<T> {
     public void handleError(Exception e);
     public void handleFailure(FxAccountClientRemoteException e);
     public void handleSuccess(T result);
   }
 
   /**
-   * A <code>CreateDelegate</code> produces the body of a /create request.
-   */
-  public interface CreateDelegate {
-    public JSONObject getCreateBody() throws FxAccountClientException;
-  }
-
-  /**
-   * A <code>AuthDelegate</code> produces the bodies of an /auth/{start,finish}
-   * request pair and exposes state generated by a successful response.
-   */
-  public interface AuthDelegate {
-    public JSONObject getAuthStartBody() throws FxAccountClientException;
-    public void onAuthStartResponse(ExtendedJSONObject body) throws FxAccountClientException;
-    public JSONObject getAuthFinishBody() throws FxAccountClientException;
-
-    public byte[] getSharedBytes() throws FxAccountClientException;
-  }
-
-  /**
    * Thin container for two cryptographic keys.
    */
   public static class TwoKeys {
     public final byte[] kA;
     public final byte[] wrapkB;
     public TwoKeys(byte[] kA, byte[] wrapkB) {
       this.kA = kA;
       this.wrapkB = wrapkB;
@@ -359,152 +340,16 @@ public class FxAccountClient10 {
       message = body.getString(JSON_KEY_MESSAGE);
       info = body.getString(JSON_KEY_INFO);
     } catch (Exception e) {
       throw new FxAccountClientMalformedResponseException(response);
     }
     throw new FxAccountClientRemoteException(response, code, errno, error, message, info, body);
   }
 
-  public void createAccount(final String email, final byte[] stretchedPWBytes,
-      final String srpSalt, final String mainSalt,
-      final RequestDelegate<String> delegate) {
-    try {
-      createAccount(new FxAccount10CreateDelegate(email, stretchedPWBytes, srpSalt, mainSalt), delegate);
-    } catch (final Exception e) {
-      invokeHandleError(delegate, e);
-      return;
-    }
-  }
-
-  protected void createAccount(final CreateDelegate createDelegate, final RequestDelegate<String> delegate) {
-    JSONObject body = null;
-    try {
-      body = createDelegate.getCreateBody();
-    } catch (FxAccountClientException e) {
-      invokeHandleError(delegate, e);
-      return;
-    }
-
-    BaseResource resource;
-    try {
-      resource = getBaseResource("account/create");
-    } catch (URISyntaxException | UnsupportedEncodingException e) {
-      invokeHandleError(delegate, e);
-      return;
-    }
-
-    resource.delegate = new ResourceDelegate<String>(resource, delegate) {
-      @Override
-      public void handleSuccess(int status, HttpResponse response, ExtendedJSONObject body) {
-        String uid = body.getString("uid");
-        if (uid == null) {
-          delegate.handleError(new FxAccountClientException("uid must be a non-null string"));
-          return;
-        }
-        delegate.handleSuccess(uid);
-      }
-    };
-    post(resource, body, delegate);
-  }
-
-  protected void authStart(final AuthDelegate authDelegate, final RequestDelegate<AuthDelegate> delegate) {
-    JSONObject body;
-    try {
-      body = authDelegate.getAuthStartBody();
-    } catch (FxAccountClientException e) {
-      invokeHandleError(delegate, e);
-      return;
-    }
-
-    BaseResource resource;
-    try {
-      resource = getBaseResource("auth/start");
-    } catch (URISyntaxException | UnsupportedEncodingException e) {
-      invokeHandleError(delegate, e);
-      return;
-    }
-
-    resource.delegate = new ResourceDelegate<AuthDelegate>(resource, delegate) {
-      @Override
-      public void handleSuccess(int status, HttpResponse response, ExtendedJSONObject body) {
-        try {
-          authDelegate.onAuthStartResponse(body);
-          delegate.handleSuccess(authDelegate);
-        } catch (Exception e) {
-          delegate.handleError(e);
-          return;
-        }
-      }
-    };
-    post(resource, body, delegate);
-  }
-
-  protected void authFinish(final AuthDelegate authDelegate, RequestDelegate<byte[]> delegate) {
-    JSONObject body;
-    try {
-      body = authDelegate.getAuthFinishBody();
-    } catch (FxAccountClientException e) {
-      invokeHandleError(delegate, e);
-      return;
-    }
-
-    BaseResource resource;
-    try {
-      resource = getBaseResource("auth/finish");
-    } catch (URISyntaxException | UnsupportedEncodingException e) {
-      invokeHandleError(delegate, e);
-      return;
-    }
-
-    resource.delegate = new ResourceDelegate<byte[]>(resource, delegate) {
-      @Override
-      public void handleSuccess(int status, HttpResponse response, ExtendedJSONObject body) {
-        try {
-          byte[] authToken = new byte[32];
-          unbundleBody(body, authDelegate.getSharedBytes(), FxAccountUtils.KW("auth/finish"), authToken);
-          delegate.handleSuccess(authToken);
-        } catch (Exception e) {
-          delegate.handleError(e);
-          return;
-        }
-      }
-    };
-    post(resource, body, delegate);
-  }
-
-  public void login(final String email, final byte[] stretchedPWBytes, final RequestDelegate<byte[]> delegate) {
-    login(new FxAccount10AuthDelegate(email, stretchedPWBytes), delegate);
-  }
-
-  protected void login(final AuthDelegate authDelegate, final RequestDelegate<byte[]> delegate) {
-    authStart(authDelegate, new RequestDelegate<AuthDelegate>() {
-      @Override
-      public void handleSuccess(AuthDelegate srpSession) {
-        authFinish(srpSession, delegate);
-      }
-
-      @Override
-      public void handleError(final Exception e) {
-        invokeHandleError(delegate, e);
-        return;
-      }
-
-      @Override
-      public void handleFailure(final FxAccountClientRemoteException e) {
-        executor.execute(new Runnable() {
-          @Override
-          public void run() {
-            delegate.handleFailure(e);
-          }
-        });
-      }
-    });
-  }
-
   /**
    * Don't call this directly. Use <code>unbundleBody</code> instead.
    */
   protected void unbundleBytes(byte[] bundleBytes, byte[] respHMACKey, byte[] respXORKey, byte[]... rest)
       throws InvalidKeyException, NoSuchAlgorithmException, FxAccountClientException {
     if (bundleBytes.length < 32) {
       throw new IllegalArgumentException("input bundle must include HMAC");
     }