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