/* 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.sync.jpake.stage; import java.io.IOException; import java.io.Reader; import java.io.StringReader; import java.io.UnsupportedEncodingException; import org.json.simple.JSONObject; import org.json.simple.parser.JSONParser; import org.json.simple.parser.ParseException; import org.mozilla.gecko.sync.ExtendedJSONObject; import org.mozilla.gecko.sync.Logger; import org.mozilla.gecko.sync.NonObjectJSONException; import org.mozilla.gecko.sync.Utils; import org.mozilla.gecko.sync.crypto.CryptoException; import org.mozilla.gecko.sync.crypto.CryptoInfo; import org.mozilla.gecko.sync.crypto.KeyBundle; import org.mozilla.gecko.sync.jpake.JPakeClient; import org.mozilla.gecko.sync.setup.Constants; public class DecryptDataStage extends JPakeStage { @Override public void execute(JPakeClient jClient) { Logger.debug(LOG_TAG, "Decrypting their payload."); if (!(jClient.theirSignerId + "3").equals((String) jClient.jIncoming .get(Constants.JSON_KEY_TYPE))) { Logger.error(LOG_TAG, "Invalid round 3 data: " + jClient.jIncoming.toJSONString()); jClient.abort(Constants.JPAKE_ERROR_WRONGMESSAGE); return; } // Decrypt payload and verify HMAC. Logger.debug(LOG_TAG, "Decrypting payload."); ExtendedJSONObject iPayload = null; try { iPayload = jClient.jIncoming.getObject(Constants.JSON_KEY_PAYLOAD); } catch (NonObjectJSONException e1) { Logger.error(LOG_TAG, "Invalid round 3 data.", e1); jClient.abort(Constants.JPAKE_ERROR_WRONGMESSAGE); return; } Logger.debug(LOG_TAG, "Decrypting data."); String cleartext = null; try { cleartext = new String(decryptPayload(iPayload, jClient.myKeyBundle), "UTF-8"); } catch (UnsupportedEncodingException e) { Logger.error(LOG_TAG, "Failed to decrypt data.", e); jClient.abort(Constants.JPAKE_ERROR_INTERNAL); return; } catch (CryptoException e) { Logger.error(LOG_TAG, "Failed to decrypt data.", e); jClient.abort(Constants.JPAKE_ERROR_KEYMISMATCH); return; } try { jClient.jCreds = getJSONObject(cleartext); } catch (IOException e) { Logger.error(LOG_TAG, "I/O exception while creating JSON object.", e); jClient.abort(Constants.JPAKE_ERROR_INVALID); return; } catch (ParseException e) { Logger.error(LOG_TAG, "JSON parse error.", e); jClient.abort(Constants.JPAKE_ERROR_INVALID); return; } // Check that credentials were actually sent over. if (!checkCredentials(jClient.jCreds)) { Logger.error(LOG_TAG, "Credentials contain nulls, setup cannot be completed."); jClient.abort(Constants.JPAKE_ERROR_INTERNAL); return; } jClient.runNextStage(); } /** * Helper method for doing actual decryption. * * Input: JSONObject containing a valid payload (cipherText, IV, HMAC), * KeyBundle with keys for decryption. Output: byte[] clearText * * @throws CryptoException * @throws UnsupportedEncodingException */ private byte[] decryptPayload(ExtendedJSONObject payload, KeyBundle keybundle) throws CryptoException, UnsupportedEncodingException { String sCiphertext = (String) payload.get(Constants.JSON_KEY_CIPHERTEXT); String sIv = (String) payload.get(Constants.JSON_KEY_IV); String sHmac = (String) payload.get(Constants.JSON_KEY_HMAC); byte[] ciphertext = Utils.decodeBase64(sCiphertext); byte[] iv = Utils.decodeBase64(sIv); byte[] hmac = Utils.hex2Byte(sHmac); CryptoInfo decrypted = CryptoInfo.decrypt(ciphertext, iv, hmac, keybundle); return decrypted.getMessage(); } /** * * @param jsonString * String to be packaged as JSON object. * @return JSONObject * @throws ParseException * @throws IOException * @throws Exception */ private JSONObject getJSONObject(String jsonString) throws IOException, ParseException{ final Reader in = new StringReader(jsonString); return (JSONObject) new JSONParser().parse(in); } private boolean checkCredentials(JSONObject creds) { final String accountName = (String) creds.get(Constants.JSON_KEY_ACCOUNT); final String password = (String) creds.get(Constants.JSON_KEY_PASSWORD); final String syncKey = (String) creds.get(Constants.JSON_KEY_SYNCKEY); final String serverUrl = (String) creds.get(Constants.JSON_KEY_SERVER); if (accountName == null || accountName.equals("") || password == null || password.equals("") || syncKey == null || syncKey.equals("") || serverUrl == null || serverUrl.equals("")) { return false; } return true; } }