Files
session-android/src/org/thoughtcrime/redphone/crypto/zrtp/ZRTPInitiatorSocket.java
Moxie Marlinspike d83a3d71bc Support for Signal calls.
Merge in RedPhone

// FREEBIE
2015-09-30 14:30:09 -07:00

210 lines
7.5 KiB
Java

/*
* Copyright (C) 2011 Whisper Systems
*
* This program is free software: you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
* the Free Software Foundation, either version 3 of the License, or
* (at your option) any later version.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with this program. If not, see <http://www.gnu.org/licenses/>.
*/
package org.thoughtcrime.redphone.crypto.zrtp;
import android.content.Context;
import org.thoughtcrime.redphone.crypto.SecureRtpSocket;
import org.thoughtcrime.redphone.crypto.zrtp.retained.InitiatorRetainedSecretsCalculator;
import org.thoughtcrime.redphone.crypto.zrtp.retained.RetainedSecrets;
import org.thoughtcrime.redphone.crypto.zrtp.retained.RetainedSecretsCalculator;
import org.thoughtcrime.redphone.crypto.zrtp.retained.RetainedSecretsDerivatives;
import java.security.MessageDigest;
import java.security.NoSuchAlgorithmException;
/**
* The "initiator" side of a ZRTP handshake. This side just hangs out and waits
* for the "responder" to send a hello packet, then proceeds through the ZRTP handshake.
*
* @author Moxie Marlinspike
*
*/
public class ZRTPInitiatorSocket extends ZRTPSocket {
private HelloPacket foreignHello;
private HelloPacket localHello;
private CommitPacket commitPacket;
private DHPartOnePacket foreignDH;
private DHPartTwoPacket localDH;
private ConfirmOnePacket confirmPacket;
private RetainedSecretsCalculator retainedSecretsCalculator;
private boolean includeLegacyHeaderBug;
public ZRTPInitiatorSocket(Context context, SecureRtpSocket socket,
byte[] localZid, String foreignNumber)
{
super(context, socket, localZid, foreignNumber, EXPECTING_HELLO);
this.includeLegacyHeaderBug = false;
}
@Override
protected void handleCommit(HandshakePacket packet) {
throw new AssertionError("Invalid state!");
}
@Override
protected void handleConfirmAck(HandshakePacket packet) {
boolean continuity = retainedSecretsCalculator.hasContinuity(foreignDH.getDerivativeSecretOne(),
foreignDH.getDerivativeSecretTwo());
byte[] foreignZid = foreignHello.getZID();
byte[] rs1 = masterSecret.getRetainedSecret();
long expiration = System.currentTimeMillis() + (confirmPacket.getCacheTime() * 1000L);
// cacheRetainedSecret(remoteNumber, foreignZid, rs1, expiration, continuity);
setState(HANDSHAKE_COMPLETE);
}
@Override
protected void handleConfirmOne(HandshakePacket packet) throws InvalidPacketException {
confirmPacket = new ConfirmOnePacket(packet, isLegacyConfirmConnection());
confirmPacket.verifyMac(masterSecret.getResponderMacKey());
confirmPacket.decrypt(masterSecret.getResponderZrtpKey());
byte[] preimage = confirmPacket.getPreimage();
foreignDH.verifyMac(preimage);
setState(EXPECTING_CONFIRM_ACK);
sendFreshPacket(new ConfirmTwoPacket(masterSecret.getInitiatorMacKey(),
masterSecret.getInitiatorZrtpKey(),
this.hashChain, isLegacyConfirmConnection(),
includeLegacyHeaderBug));
}
@Override
protected void handleConfirmTwo(HandshakePacket packet) throws InvalidPacketException {
throw new InvalidPacketException("Initiator received a Confirm2 packet?");
}
@Override
protected void handleDH(HandshakePacket packet) throws InvalidPacketException {
assert(localDH != null);
SecretCalculator calculator;
switch (getKeyAgreementType()) {
case KA_TYPE_EC25:
foreignDH = new EC25DHPartOnePacket(packet, true);
calculator = new EC25SecretCalculator();
break;
case KA_TYPE_DH3K:
foreignDH = new DH3KDHPartOnePacket(packet, true);
calculator = new DH3KSecretCalculator();
break;
default:
throw new AssertionError("Unknown KA type: " + getKeyAgreementType());
}
byte[] h1 = foreignDH.getHash();
byte[] h2 = calculateH2(h1);
foreignHello.verifyMac(h2);
byte[] dhResult = calculator.calculateKeyAgreement(getKeyPair(), foreignDH.getPvr());
byte[] totalHash = calculator.calculateTotalHash(foreignHello, commitPacket,
foreignDH, localDH);
byte[] s1 = retainedSecretsCalculator.getS1(foreignDH.getDerivativeSecretOne(),
foreignDH.getDerivativeSecretTwo());
byte[] sharedSecret = calculator.calculateSharedSecret(dhResult, totalHash, s1,
localHello.getZID(),
foreignHello.getZID());
this.masterSecret = new MasterSecret(sharedSecret, totalHash, localHello.getZID(),
foreignHello.getZID());
setState(EXPECTING_CONFIRM_ONE);
sendFreshPacket(localDH);
}
@Override
protected void handleHelloAck(HandshakePacket packet) throws InvalidPacketException {
// RetainedSecrets retainedSecrets = getRetainedSecrets(remoteNumber, foreignHello.getZID());
RetainedSecrets retainedSecrets = new RetainedSecrets(null, null);
retainedSecretsCalculator = new InitiatorRetainedSecretsCalculator(retainedSecrets);
RetainedSecretsDerivatives derivatives = retainedSecretsCalculator.getRetainedSecretsDerivatives();
switch (getKeyAgreementType()) {
case KA_TYPE_EC25:
localDH = new EC25DHPartTwoPacket(hashChain, getPublicKey(), derivatives, includeLegacyHeaderBug);
break;
case KA_TYPE_DH3K:
localDH = new DH3KDHPartTwoPacket(hashChain, getPublicKey(), derivatives, includeLegacyHeaderBug);
break;
}
commitPacket = new CommitPacket(hashChain, foreignHello.getMessageBytes(), localDH, localZid, includeLegacyHeaderBug);
setState(EXPECTING_DH_1);
sendFreshPacket(commitPacket);
}
@Override
protected void handleHello(HandshakePacket packet) throws InvalidPacketException {
foreignHello = new HelloPacket(packet, true);
includeLegacyHeaderBug = foreignHello.isLegacyHeaderBugPresent();
localHello = new HelloPacket(hashChain, localZid, includeLegacyHeaderBug);
setState(EXPECTING_HELLO_ACK);
sendFreshPacket(localHello);
}
private byte[] calculateH2(byte[] h1) {
try {
MessageDigest md = MessageDigest.getInstance("SHA-256");
return md.digest(h1);
} catch (NoSuchAlgorithmException e) {
throw new IllegalArgumentException(e);
}
}
@Override
public void negotiateStart() throws NegotiationFailedException {
super.negotiateStart();
}
@Override
protected int getKeyAgreementType() {
if (foreignHello == null)
throw new AssertionError("We can't project agreement type until we've seen a hello!");
RedPhoneClientId foreignClientId = new RedPhoneClientId(foreignHello.getClientId());
if (foreignClientId.isImplicitDh3kVersion() ||
foreignHello.getKeyAgreementOptions().contains("EC25"))
{
return KA_TYPE_EC25;
} else {
return KA_TYPE_DH3K;
}
}
@Override
protected HelloPacket getForeignHello() {
return foreignHello;
}
}