/* 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.UnsupportedEncodingException; import org.mozilla.apache.commons.codec.binary.Base64; import org.mozilla.gecko.sync.ExtendedJSONObject; import org.mozilla.gecko.sync.Logger; import org.mozilla.gecko.sync.NonObjectJSONException; 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 VerifyPairingStage extends JPakeStage { @Override public void execute(JPakeClient jClient) { Logger.debug(LOG_TAG, "Verifying their key."); ExtendedJSONObject verificationObj = jClient.jIncoming; String signerId = (String) verificationObj.get(Constants.JSON_KEY_TYPE); if (!signerId.equals(jClient.theirSignerId + "3")) { Logger.error(LOG_TAG, "Invalid round 3 message: " + verificationObj.toJSONString()); jClient.abort(Constants.JPAKE_ERROR_WRONGMESSAGE); return; } ExtendedJSONObject payload; try { payload = verificationObj.getObject(Constants.JSON_KEY_PAYLOAD); } catch (NonObjectJSONException e) { Logger.error(LOG_TAG, "JSON exception.", e); jClient.abort(Constants.JPAKE_ERROR_INVALID); return; } String theirCiphertext = (String) payload.get(Constants.JSON_KEY_CIPHERTEXT); String iv = (String) payload.get(Constants.JSON_KEY_IV); boolean correctPairing; try { correctPairing = verifyCiphertext(theirCiphertext, iv, jClient.myKeyBundle); } catch (UnsupportedEncodingException e) { Logger.error(LOG_TAG, "Unsupported encoding.", e); jClient.abort(Constants.JPAKE_ERROR_INTERNAL); return; } catch (CryptoException e) { Logger.error(LOG_TAG, "Crypto exception.", e); jClient.abort(Constants.JPAKE_ERROR_INTERNAL); return; } if (correctPairing) { Logger.debug(LOG_TAG, "Keys verified successfully."); jClient.paired = true; jClient.onPaired(); } else { Logger.error(LOG_TAG, "Keys don't match."); jClient.abort(Constants.JPAKE_ERROR_KEYMISMATCH); return; } } /* * Helper function to verify an incoming ciphertext and IV against derived * keyBundle. * * (Made 'public' for testing and is a stateless function.) */ public boolean verifyCiphertext(String theirCiphertext, String iv, KeyBundle keyBundle) throws UnsupportedEncodingException, CryptoException { byte[] cleartextBytes = JPakeClient.JPAKE_VERIFY_VALUE.getBytes("UTF-8"); CryptoInfo encrypted = CryptoInfo.encrypt(cleartextBytes, Base64.decodeBase64(iv), keyBundle); String myCiphertext = new String(Base64.encodeBase64(encrypted.getMessage()), "UTF-8"); return myCiphertext.equals(theirCiphertext); } }