mobile/android/base/background/fxa/FxAccount10AuthDelegate.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.background.fxa;
     7 import java.io.UnsupportedEncodingException;
     8 import java.math.BigInteger;
     9 import java.security.NoSuchAlgorithmException;
    11 import org.json.simple.JSONObject;
    12 import org.mozilla.gecko.sync.ExtendedJSONObject;
    13 import org.mozilla.gecko.sync.Utils;
    14 import org.mozilla.gecko.sync.net.SRPConstants;
    16 public class FxAccount10AuthDelegate implements FxAccountClient10.AuthDelegate {
    17   // Fixed by protocol.
    18   protected final BigInteger N;
    19   protected final BigInteger g;
    20   protected final int modNLengthBytes;
    22   // Configured at construction time.
    23   protected final String email;
    24   protected final byte[] stretchedPWBytes;
    26   // Encapsulate state.
    27   protected static class AuthState {
    28     protected String srpToken;
    29     protected String mainSalt;
    30     protected String srpSalt;
    32     protected BigInteger x;
    33     protected BigInteger A;
    34     protected byte[] Kbytes;
    35     protected byte[] Mbytes;
    36   }
    38   // State should be written exactly once.
    39   protected AuthState internalAuthState = null;
    41   public FxAccount10AuthDelegate(String email, byte[] stretchedPWBytes) {
    42     this.email = email;
    43     this.stretchedPWBytes = stretchedPWBytes;
    44     this.N = SRPConstants._2048.N;
    45     this.g = SRPConstants._2048.g;
    46     this.modNLengthBytes = SRPConstants._2048.byteLength;
    47   }
    49   protected BigInteger generateSecretValue() {
    50     return Utils.generateBigIntegerLessThan(N);
    51   }
    53   public static class FxAccountClientMalformedAuthException extends FxAccountClientException {
    54     private static final long serialVersionUID = 3585262174699395505L;
    56     public FxAccountClientMalformedAuthException(String detailMessage) {
    57       super(detailMessage);
    58     }
    59   }
    61   @SuppressWarnings("unchecked")
    62   @Override
    63   public JSONObject getAuthStartBody() throws FxAccountClientException {
    64     try {
    65       final JSONObject body = new JSONObject();
    66       body.put("email", FxAccountUtils.bytes(email));
    67       return body;
    68     } catch (UnsupportedEncodingException e) {
    69       throw new FxAccountClientException(e);
    70     }
    71   }
    73   @Override
    74   public void onAuthStartResponse(final ExtendedJSONObject body) throws FxAccountClientException {
    75     if (this.internalAuthState != null) {
    76       throw new FxAccountClientException("auth must not be written before calling onAuthStartResponse");
    77     }
    79     String srpToken = null;
    80     String srpSalt = null;
    81     String srpB = null;
    82     String mainSalt = null;
    84     try {
    85       srpToken = body.getString("srpToken");
    86       if (srpToken == null) {
    87         throw new FxAccountClientMalformedAuthException("srpToken must be a non-null object");
    88       }
    89       ExtendedJSONObject srp = body.getObject("srp");
    90       if (srp == null) {
    91         throw new FxAccountClientMalformedAuthException("srp must be a non-null object");
    92       }
    93       srpSalt = srp.getString("salt");
    94       if (srpSalt == null) {
    95         throw new FxAccountClientMalformedAuthException("srp.salt must not be null");
    96       }
    97       srpB = srp.getString("B");
    98       if (srpB == null) {
    99         throw new FxAccountClientMalformedAuthException("srp.B must not be null");
   100       }
   101       ExtendedJSONObject passwordStretching = body.getObject("passwordStretching");
   102       if (passwordStretching == null) {
   103         throw new FxAccountClientMalformedAuthException("passwordStretching must be a non-null object");
   104       }
   105       mainSalt = passwordStretching.getString("salt");
   106       if (mainSalt == null) {
   107         throw new FxAccountClientMalformedAuthException("srp.passwordStretching.salt must not be null");
   108       }
   109       throwIfParametersAreBad(passwordStretching);
   111       this.internalAuthState = authStateFromParameters(srpToken, mainSalt, srpSalt, srpB, generateSecretValue());
   112     } catch (FxAccountClientException e) {
   113       throw e;
   114     } catch (Exception e) {
   115       throw new FxAccountClientException(e);
   116     }
   117   }
   119   /**
   120    * Expect object like:
   121    * "passwordStretching": {
   122    *   "type": "PBKDF2/scrypt/PBKDF2/v1",
   123    *   "PBKDF2_rounds_1": 20000,
   124    *   "scrypt_N": 65536,
   125    *   "scrypt_r": 8,
   126    *   "scrypt_p": 1,
   127    *   "PBKDF2_rounds_2": 20000,
   128    *   "salt": "996bc6b1aa63cd69856a2ec81cbf19d5c8a604713362df9ee15c2bf07128efab"
   129    * }
   130    * @param params to verify.
   131    * @throws FxAccountClientMalformedAuthException
   132    */
   133   protected void throwIfParametersAreBad(ExtendedJSONObject params) throws FxAccountClientMalformedAuthException {
   134     if (params == null ||
   135         params.size() != 7 ||
   136         params.getString("salt") == null ||
   137         !("PBKDF2/scrypt/PBKDF2/v1".equals(params.getString("type"))) ||
   138         20000 != params.getLong("PBKDF2_rounds_1") ||
   139         65536 != params.getLong("scrypt_N") ||
   140         8 != params.getLong("scrypt_r") ||
   141         1 != params.getLong("scrypt_p") ||
   142         20000 != params.getLong("PBKDF2_rounds_2")) {
   143       throw new FxAccountClientMalformedAuthException("malformed passwordStretching parameters: '" + params.toJSONString() + "'.");
   144     }
   145   }
   147   /**
   148    * All state is written in this method.
   149    */
   150   protected AuthState authStateFromParameters(String srpToken, String mainSalt, String srpSalt, String srpB, BigInteger a) throws NoSuchAlgorithmException, UnsupportedEncodingException {
   151     AuthState authState = new AuthState();
   152     authState.srpToken = srpToken;
   153     authState.mainSalt = mainSalt;
   154     authState.srpSalt = srpSalt;
   156     authState.x = FxAccountUtils.srpVerifierLowercaseX(email.getBytes("UTF-8"), this.stretchedPWBytes, Utils.hex2Byte(srpSalt, FxAccountUtils.SALT_LENGTH_BYTES));
   158     authState.A = g.modPow(a, N);
   159     String srpA = FxAccountUtils.hexModN(authState.A, N);
   160     BigInteger B = new BigInteger(srpB, 16);
   162     byte[] srpABytes = Utils.hex2Byte(srpA, modNLengthBytes);
   163     byte[] srpBBytes = Utils.hex2Byte(srpB, modNLengthBytes);
   165     // u = H(pad(A) | pad(B))
   166     byte[] uBytes = Utils.sha256(Utils.concatAll(
   167         srpABytes,
   168         srpBBytes));
   169     BigInteger u = new BigInteger(Utils.byte2Hex(uBytes, FxAccountUtils.HASH_LENGTH_HEX), 16);
   171     // S = (B - k*g^x)^(a  u*x) % N
   172     // k = H(pad(N) | pad(g))
   173     int byteLength = (N.bitLength() + 7) / 8;
   174     byte[] kBytes = Utils.sha256(Utils.concatAll(
   175         Utils.hex2Byte(N.toString(16), byteLength),
   176         Utils.hex2Byte(g.toString(16), byteLength)));
   177     BigInteger k = new BigInteger(Utils.byte2Hex(kBytes, FxAccountUtils.HASH_LENGTH_HEX), 16);
   179     BigInteger base = B.subtract(k.multiply(g.modPow(authState.x, N)).mod(N)).mod(N);
   180     BigInteger pow = a.add(u.multiply(authState.x));
   181     BigInteger S = base.modPow(pow, N);
   182     String srpS = FxAccountUtils.hexModN(S, N);
   184     byte[] sBytes = Utils.hex2Byte(srpS, modNLengthBytes);
   186     // M = H(pad(A) | pad(B) | pad(S))
   187     authState.Mbytes = Utils.sha256(Utils.concatAll(
   188         srpABytes,
   189         srpBBytes,
   190         sBytes));
   192     // K = H(pad(S))
   193     authState.Kbytes = Utils.sha256(sBytes);
   195     return authState;
   196   }
   198   @SuppressWarnings("unchecked")
   199   @Override
   200   public JSONObject getAuthFinishBody() throws FxAccountClientException {
   201     if (internalAuthState == null) {
   202       throw new FxAccountClientException("auth must be successfully written before calling getAuthFinishBody.");
   203     }
   204     JSONObject body = new JSONObject();
   205     body.put("srpToken", internalAuthState.srpToken);
   206     body.put("A", FxAccountUtils.hexModN(internalAuthState.A, N));
   207     body.put("M", Utils.byte2Hex(internalAuthState.Mbytes, FxAccountUtils.HASH_LENGTH_HEX));
   208     return body;
   209   }
   211   @Override
   212   public byte[] getSharedBytes() throws FxAccountClientException {
   213     if (internalAuthState == null) {
   214       throw new FxAccountClientException("auth must be successfully finished before calling getSharedBytes.");
   215     }
   216     return internalAuthState.Kbytes;
   217   }
   218 }

mercurial