Wed, 31 Dec 2014 07:22:50 +0100
Correct previous dual key logic pending first delivery installment.
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.fxa.login; |
michael@0 | 6 | |
michael@0 | 7 | import java.io.IOException; |
michael@0 | 8 | import java.io.UnsupportedEncodingException; |
michael@0 | 9 | import java.security.GeneralSecurityException; |
michael@0 | 10 | import java.security.InvalidKeyException; |
michael@0 | 11 | import java.security.NoSuchAlgorithmException; |
michael@0 | 12 | import java.util.ArrayList; |
michael@0 | 13 | import java.util.Collections; |
michael@0 | 14 | import java.util.HashMap; |
michael@0 | 15 | |
michael@0 | 16 | import org.json.simple.parser.ParseException; |
michael@0 | 17 | import org.mozilla.gecko.background.fxa.FxAccountUtils; |
michael@0 | 18 | import org.mozilla.gecko.browserid.BrowserIDKeyPair; |
michael@0 | 19 | import org.mozilla.gecko.browserid.JSONWebTokenUtils; |
michael@0 | 20 | import org.mozilla.gecko.fxa.FxAccountConstants; |
michael@0 | 21 | import org.mozilla.gecko.fxa.login.FxAccountLoginStateMachine.ExecuteDelegate; |
michael@0 | 22 | import org.mozilla.gecko.fxa.login.FxAccountLoginTransition.LogMessage; |
michael@0 | 23 | import org.mozilla.gecko.sync.ExtendedJSONObject; |
michael@0 | 24 | import org.mozilla.gecko.sync.NonObjectJSONException; |
michael@0 | 25 | import org.mozilla.gecko.sync.Utils; |
michael@0 | 26 | import org.mozilla.gecko.sync.crypto.KeyBundle; |
michael@0 | 27 | |
michael@0 | 28 | public class Married extends TokensAndKeysState { |
michael@0 | 29 | private static final String LOG_TAG = Married.class.getSimpleName(); |
michael@0 | 30 | |
michael@0 | 31 | protected final String certificate; |
michael@0 | 32 | protected final String clientState; |
michael@0 | 33 | |
michael@0 | 34 | public Married(String email, String uid, byte[] sessionToken, byte[] kA, byte[] kB, BrowserIDKeyPair keyPair, String certificate) { |
michael@0 | 35 | super(StateLabel.Married, email, uid, sessionToken, kA, kB, keyPair); |
michael@0 | 36 | Utils.throwIfNull(certificate); |
michael@0 | 37 | this.certificate = certificate; |
michael@0 | 38 | try { |
michael@0 | 39 | this.clientState = FxAccountUtils.computeClientState(kB); |
michael@0 | 40 | } catch (NoSuchAlgorithmException e) { |
michael@0 | 41 | // This should never occur. |
michael@0 | 42 | throw new IllegalStateException("Unable to compute client state from kB."); |
michael@0 | 43 | } |
michael@0 | 44 | } |
michael@0 | 45 | |
michael@0 | 46 | @Override |
michael@0 | 47 | public ExtendedJSONObject toJSONObject() { |
michael@0 | 48 | ExtendedJSONObject o = super.toJSONObject(); |
michael@0 | 49 | // Fields are non-null by constructor. |
michael@0 | 50 | o.put("certificate", certificate); |
michael@0 | 51 | return o; |
michael@0 | 52 | } |
michael@0 | 53 | |
michael@0 | 54 | @Override |
michael@0 | 55 | public void execute(final ExecuteDelegate delegate) { |
michael@0 | 56 | delegate.handleTransition(new LogMessage("staying married"), this); |
michael@0 | 57 | } |
michael@0 | 58 | |
michael@0 | 59 | public String generateAssertion(String audience, String issuer) throws NonObjectJSONException, IOException, ParseException, GeneralSecurityException { |
michael@0 | 60 | // We generate assertions with no iat and an exp after 2050 to avoid |
michael@0 | 61 | // invalid-timestamp errors from the token server. |
michael@0 | 62 | final long expiresAt = JSONWebTokenUtils.DEFAULT_FUTURE_EXPIRES_AT_IN_MILLISECONDS; |
michael@0 | 63 | String assertion = JSONWebTokenUtils.createAssertion(keyPair.getPrivate(), certificate, audience, issuer, null, expiresAt); |
michael@0 | 64 | if (!FxAccountConstants.LOG_PERSONAL_INFORMATION) { |
michael@0 | 65 | return assertion; |
michael@0 | 66 | } |
michael@0 | 67 | |
michael@0 | 68 | try { |
michael@0 | 69 | FxAccountConstants.pii(LOG_TAG, "Generated assertion: " + assertion); |
michael@0 | 70 | ExtendedJSONObject a = JSONWebTokenUtils.parseAssertion(assertion); |
michael@0 | 71 | if (a != null) { |
michael@0 | 72 | FxAccountConstants.pii(LOG_TAG, "aHeader : " + a.getObject("header")); |
michael@0 | 73 | FxAccountConstants.pii(LOG_TAG, "aPayload : " + a.getObject("payload")); |
michael@0 | 74 | FxAccountConstants.pii(LOG_TAG, "aSignature: " + a.getString("signature")); |
michael@0 | 75 | String certificate = a.getString("certificate"); |
michael@0 | 76 | if (certificate != null) { |
michael@0 | 77 | ExtendedJSONObject c = JSONWebTokenUtils.parseCertificate(certificate); |
michael@0 | 78 | FxAccountConstants.pii(LOG_TAG, "cHeader : " + c.getObject("header")); |
michael@0 | 79 | FxAccountConstants.pii(LOG_TAG, "cPayload : " + c.getObject("payload")); |
michael@0 | 80 | FxAccountConstants.pii(LOG_TAG, "cSignature: " + c.getString("signature")); |
michael@0 | 81 | // Print the relevant timestamps in sorted order with labels. |
michael@0 | 82 | HashMap<Long, String> map = new HashMap<Long, String>(); |
michael@0 | 83 | map.put(a.getObject("payload").getLong("iat"), "aiat"); |
michael@0 | 84 | map.put(a.getObject("payload").getLong("exp"), "aexp"); |
michael@0 | 85 | map.put(c.getObject("payload").getLong("iat"), "ciat"); |
michael@0 | 86 | map.put(c.getObject("payload").getLong("exp"), "cexp"); |
michael@0 | 87 | ArrayList<Long> values = new ArrayList<Long>(map.keySet()); |
michael@0 | 88 | Collections.sort(values); |
michael@0 | 89 | for (Long value : values) { |
michael@0 | 90 | FxAccountConstants.pii(LOG_TAG, map.get(value) + ": " + value); |
michael@0 | 91 | } |
michael@0 | 92 | } else { |
michael@0 | 93 | FxAccountConstants.pii(LOG_TAG, "Could not parse certificate!"); |
michael@0 | 94 | } |
michael@0 | 95 | } else { |
michael@0 | 96 | FxAccountConstants.pii(LOG_TAG, "Could not parse assertion!"); |
michael@0 | 97 | } |
michael@0 | 98 | } catch (Exception e) { |
michael@0 | 99 | FxAccountConstants.pii(LOG_TAG, "Got exception dumping assertion debug info."); |
michael@0 | 100 | } |
michael@0 | 101 | return assertion; |
michael@0 | 102 | } |
michael@0 | 103 | |
michael@0 | 104 | public KeyBundle getSyncKeyBundle() throws InvalidKeyException, NoSuchAlgorithmException, UnsupportedEncodingException { |
michael@0 | 105 | // TODO Document this choice for deriving from kB. |
michael@0 | 106 | return FxAccountUtils.generateSyncKeyBundle(kB); |
michael@0 | 107 | } |
michael@0 | 108 | |
michael@0 | 109 | public String getClientState() { |
michael@0 | 110 | if (FxAccountConstants.LOG_PERSONAL_INFORMATION) { |
michael@0 | 111 | FxAccountConstants.pii(LOG_TAG, "Client state: " + this.clientState); |
michael@0 | 112 | } |
michael@0 | 113 | return this.clientState; |
michael@0 | 114 | } |
michael@0 | 115 | |
michael@0 | 116 | public State makeCohabitingState() { |
michael@0 | 117 | return new Cohabiting(email, uid, sessionToken, kA, kB, keyPair); |
michael@0 | 118 | } |
michael@0 | 119 | } |