Package com.subgraph.orchid.crypto

Source Code of com.subgraph.orchid.crypto.TorTapKeyAgreement

package com.subgraph.orchid.crypto;

import java.math.BigInteger;
import java.nio.ByteBuffer;
import java.security.GeneralSecurityException;
import java.security.KeyFactory;
import java.security.KeyPair;
import java.security.KeyPairGenerator;
import java.security.PublicKey;
import java.util.Arrays;

import javax.crypto.KeyAgreement;
import javax.crypto.interfaces.DHPublicKey;
import javax.crypto.spec.DHParameterSpec;
import javax.crypto.spec.DHPublicKeySpec;

import com.subgraph.orchid.TorException;
/**
* The <code>TorKeyAgreement</code> class implements the diffie-hellman key agreement
* protocol using the parameters specified in the main Tor specification (tor-spec.txt).
*
* An instance of this class can only be used to perform a single key agreement operation.
*
* After instantiating the class, a user calls {@link #getPublicValue()} or {@link #getPublicKeyBytes()}
* to retrieve the public value to transmit to the peer in the key agreement operation.  After receiving
* a public value from the peer, this value should be converted into a <code>BigInteger</code> and
* {@link #isValidPublicValue(BigInteger)} should be called to verify that the peer has sent a safe
* and legal public value.  If {@link #isValidPublicValue(BigInteger)} returns true, the peer public
* value is valid and {@link #getSharedSecret(BigInteger)} can be called to complete the key agreement
* protocol and return the shared secret value.
*
*/
public class TorTapKeyAgreement implements TorKeyAgreement {
  public final static int DH_LEN = 128;
  public final static int DH_SEC_LEN = 40;
  /*
   * tor-spec 0.3
   *
   * For Diffie-Hellman, we use a generator (g) of 2.  For the modulus (p), we
     * use the 1024-bit safe prime from rfc2409 section 6.2 whose hex
     * representation is:
   */
  private static final BigInteger P1024 = new BigInteger(
    "00FFFFFFFFFFFFFFFFC90FDAA22168C234C4C6628B80DC1CD129024E08"
    + "8A67CC74020BBEA63B139B22514A08798E3404DDEF9519B3CD3A431B"
    + "302B0A6DF25F14374FE1356D6D51C245E485B576625E7EC6F44C42E9"
    + "A637ED6B0BFF5CB6F406B7EDEE386BFB5A899FA5AE9F24117C4B1FE6"
    + "49286651ECE65381FFFFFFFFFFFFFFFF", 16);
  private static final BigInteger G = new BigInteger("2");
 
  /*
   * tor-spec 0.3
   *
   * As an optimization, implementations SHOULD choose DH private keys (x) of
     * 320 bits.
   */
  private static final int PRIVATE_KEY_SIZE = 320;
  private static final DHParameterSpec DH_PARAMETER_SPEC = new DHParameterSpec(P1024, G, PRIVATE_KEY_SIZE);
 
  private final KeyAgreement dh;
  private final KeyPair keyPair;
  private final TorPublicKey onionKey;
 
  /**
   * Create a new <code>TorKeyAgreement</code> instance which can be used to perform a single
   * key agreement operation.  A new set of ephemeral Diffie-Hellman parameters are generated
   * when this class is instantiated.
   */
  public TorTapKeyAgreement(TorPublicKey onionKey) {
    this.keyPair = generateKeyPair();
    this.dh = createDH();
    this.onionKey = onionKey;
   
  }
 
  public TorTapKeyAgreement() {
    this(null);
  }
 
  /**
   * Return the generated public value for this key agreement operation as a <code>BigInteger</code>.
   *
   * @return The diffie-hellman public value as a <code>BigInteger</code>.
   */
  public BigInteger getPublicValue() {
    DHPublicKey pubKey = (DHPublicKey) keyPair.getPublic();
    return pubKey.getY();
  }
 
  /**
   * Return the generated public value for this key agreement operation as an array with the value
   * encoded in big-endian byte order.
   *
   * @return A byte array containing the encoded public value for this key agreement operation.
   */
  public byte[] getPublicKeyBytes() {
    final byte[] output = new byte[128];
    final byte[] yBytes = getPublicValue().toByteArray();
    if(yBytes[0] == 0 && yBytes.length == (DH_LEN + 1)) {
      System.arraycopy(yBytes, 1, output, 0, DH_LEN);
    } else if (yBytes.length <= DH_LEN) {
      final int offset = DH_LEN - yBytes.length;
      System.arraycopy(yBytes, 0, output, offset, yBytes.length);
    } else {
      throw new IllegalStateException("Public value is longer than DH_LEN but not because of sign bit");
    }
    return output;
  }
 
 
 
  /**
   * Return <code>true</code> if the specified value is a legal public
   * value rather than a dangerous degenerate or confined subgroup value. 
   *
   * tor-spec 5.2
   * Before computing g^xy, both client and server MUST verify that
   * the received g^x or g^y value is not degenerate; that is, it must
   * be strictly greater than 1 and strictly less than p-1 where p is
   * the DH modulus.  Implementations MUST NOT complete a handshake
   * with degenerate keys.
   */
  public static boolean isValidPublicValue(BigInteger publicValue) {
    if(publicValue.signum() < 1 || publicValue.equals(BigInteger.ONE))
      return false;
    if(publicValue.compareTo(P1024.subtract(BigInteger.ONE)) >= 0)
      return false;
    return true;
  }

  /**
   * Complete the key agreement protocol with the peer public value
   * <code>otherPublic</code> and return the calculated shared secret.
   *
   * @param otherPublic The peer public value.
   * @return The shared secret value produced by the protocol.
   */
  public byte[] getSharedSecret(BigInteger otherPublic) {
    try {
      KeyFactory factory = KeyFactory.getInstance("DH");
      DHPublicKeySpec pub = new DHPublicKeySpec(otherPublic, P1024, G);
      PublicKey key = factory.generatePublic(pub);
      dh.doPhase(key, true);
      return dh.generateSecret();
    } catch (GeneralSecurityException e) {
      throw new TorException(e);
    }
  }
  private final KeyAgreement createDH() {
    try {
      KeyAgreement dh = KeyAgreement.getInstance("DH");
      dh.init(keyPair.getPrivate());
      return dh;
    } catch (GeneralSecurityException e) {
      throw new TorException(e);
    }
  }
 
  private final KeyPair generateKeyPair() {
    try {
      KeyPairGenerator keyGen = KeyPairGenerator.getInstance("DH");
      keyGen.initialize(DH_PARAMETER_SPEC);
      return keyGen.generateKeyPair()
    } catch (GeneralSecurityException e) {
      throw new TorException(e);
    }
  }

  public byte[] createOnionSkin() {
    final byte[] yBytes = getPublicKeyBytes();
    final HybridEncryption hybrid = new HybridEncryption();
    return hybrid.encrypt(yBytes, onionKey);
  }

  public boolean deriveKeysFromHandshakeResponse(byte[] handshakeResponse,
      byte[] keyMaterialOut, byte[] verifyHashOut) {
    ByteBuffer bb = ByteBuffer.wrap(handshakeResponse);
    byte[] dhPublic = new byte[DH_LEN];
    byte[] keyHash = new byte[TorMessageDigest.TOR_DIGEST_SIZE];
    bb.get(dhPublic);
    bb.get(keyHash);
    BigInteger peerPublic = new BigInteger(1, dhPublic);
    return deriveKeysFromDHPublicAndHash(peerPublic, keyHash, keyMaterialOut, verifyHashOut);
  }
 
  public boolean deriveKeysFromDHPublicAndHash(BigInteger peerPublic, byte[] keyHash, byte[] keyMaterialOut, byte[] verifyHashOut) {
    if(!isValidPublicValue(peerPublic)) {
      throw new TorException("Illegal DH public value");
    }
    final byte[] sharedSecret = getSharedSecret(peerPublic);
    final TorKeyDerivation kdf = new TorKeyDerivation(sharedSecret);
    kdf.deriveKeys(keyMaterialOut, verifyHashOut);
    return Arrays.equals(verifyHashOut, keyHash);
  }
}
TOP

Related Classes of com.subgraph.orchid.crypto.TorTapKeyAgreement

TOP
Copyright © 2018 www.massapi.com. All rights reserved.
All source code are property of their respective owners. Java is a trademark of Sun Microsystems, Inc and owned by ORACLE Inc. Contact coftware#gmail.com.