Package javaforce.flash

Source Code of javaforce.flash.InboundHandshake

package javaforce.flash;
//package org.red5.server.net.rtmp;

/*

based on Red5 1.0RC1

modifed by Peter Quiring for JavaForce SDK

*/

import java.security.KeyPair;
import java.util.Arrays;

import javax.crypto.Cipher;
import javax.crypto.spec.SecretKeySpec;

//import org.apache.commons.codec.binary.Hex;
//import org.apache.mina.core.buffer.IoBuffer;
//import org.red5.server.api.Red5;
//import org.red5.server.net.rtmp.message.Constants;

import javaforce.JFLog;

/**
* Performs handshaking for server connections.
*
* @author Paul Gregoire
*/
class InboundHandshake extends RTMPHandshake {

  private byte input[];
  private int input_pos;
  private int input_saved_pos;
  private void input_mark() { input_saved_pos = input_pos; }
  private void input_reset() { input_pos = input_saved_pos; }
  private byte input_get() {
    return input[input_pos++];
  }
  private void input_get(byte buf[]) {
    input_get(buf, 0, buf.length);
  }
  private void input_get(byte buf[], int pos, int len) {
    int avail = input.length - input_pos;
    if (len > avail) len = avail;
    System.arraycopy(input, input_pos, buf, pos, len);
    input_pos += len;
  }
  private int input_remaining() { return input.length - input_pos; }
  private void input_position(int newpos) { input_pos = newpos; }
  private int input_limit() {return input.length - input_pos; }
 
  private byte output[];
  private int output_pos;
  private void output_put(byte data) {
    output[output_pos++] = data;
  }
  private void output_put(byte data[]) {
    System.arraycopy(data, 0, output, output_pos, data.length);
    output_pos += data.length;
  }
  private void output_putInt(int val) {
    output[output_pos+3] = (byte)(val & 0xff);
    val >>= 8;
    output[output_pos+2] = (byte)(val & 0xff);
    val >>= 8;
    output[output_pos+1] = (byte)(val & 0xff);
    val >>= 8;
    output[output_pos+0] = (byte)(val & 0xff);
    output_pos += 4;
  }
  private void output_flip() { }  //???

  /**
   * Marker byte for standard or non-encrypted RTMP data.
   */
  public static final byte RTMP_NON_ENCRYPTED = (byte) 0x03;

  /**
   * Marker byte for encrypted RTMP data.
   */
  public static final byte RTMP_ENCRYPTED = (byte) 0x06;

  /**
   * Generates response for versioned connections.
   *
   * @param input incoming RTMP bytes (1537 bytes)
   * @return outgoing handshake (1537 bytes)
   */
  public synchronized byte[] doHandshake(byte input[]) {
    this.input = input;
    input_pos = 0;
    byte versionByte = input_get();
    if (versionByte == 0) {
      JFLog.log("unversioned response");
      return generateUnversionedHandshake(input);
    }
    //JFLog.log("versioned response" + versionByte);
    //create output buffer
    output = new byte[HANDSHAKE_SIZE_SERVER];
    output_pos = 0;
    input_mark();
    //make sure this is a client we can communicate with
    validate(input);
    input_reset();
    input_mark()
    //create all the dh stuff and add to handshake bytes
    prepareResponse(input);
    input_reset();
    if (handshakeType == RTMP_ENCRYPTED) {
      byte[] sharedSecret = getSharedSecret(outgoingPublicKey, keyAgreement);
      // create output cipher
      byte[] digestOut = calculateHMAC_SHA256(outgoingPublicKey, sharedSecret);
      try {
        cipherOut = Cipher.getInstance("RC4");
        cipherOut.init(Cipher.ENCRYPT_MODE, new SecretKeySpec(digestOut, 0, 16, "RC4"));
      } catch (Exception e) {
        JFLog.log("Encryption cipher creation failed" + e);
      }
      // create input cipher
      byte[] digestIn = calculateHMAC_SHA256(incomingPublicKey, sharedSecret);
      try {
        cipherIn = Cipher.getInstance("RC4");
        cipherIn.init(Cipher.DECRYPT_MODE, new SecretKeySpec(digestIn, 0, 16, "RC4"));
      } catch (Exception e) {
        JFLog.log("Decryption cipher creation failed" + e);
      }
      // update 'encoder / decoder state' for the RC4 keys
      // both parties *pretend* as if handshake part 2 (1536 bytes) was encrypted
      // effectively this hides / discards the first few bytes of encrypted session
      // which is known to increase the secure-ness of RC4
      // RC4 state is just a function of number of bytes processed so far
      // that's why we just run 1536 arbitrary bytes through the keys below
      byte[] dummyBytes = new byte[Constants.HANDSHAKE_SIZE];
      cipherIn.update(dummyBytes);
      cipherOut.update(dummyBytes);
    }           
    input_mark();
    //create the server digest
    int serverDigestOffset = getDigestOffset(handshakeBytes);
    byte[] tempBuffer = new byte[Constants.HANDSHAKE_SIZE - DIGEST_LENGTH];
    System.arraycopy(handshakeBytes, 0, tempBuffer, 0, serverDigestOffset);
    System.arraycopy(handshakeBytes, serverDigestOffset + DIGEST_LENGTH, tempBuffer, serverDigestOffset, Constants.HANDSHAKE_SIZE - serverDigestOffset - DIGEST_LENGTH);
    //calculate the hash
    byte[] tempHash = calculateHMAC_SHA256(tempBuffer, GENUINE_FMS_KEY, 36);
    //add the digest
    System.arraycopy(tempHash, 0, handshakeBytes, serverDigestOffset, DIGEST_LENGTH);
    //compute the challenge digest
    byte[] inputBuffer = new byte[Constants.HANDSHAKE_SIZE - DIGEST_LENGTH];
    input_get(inputBuffer);
    int keyChallengeIndex = getDigestOffset(inputBuffer);
    byte[] challengeKey = new byte[DIGEST_LENGTH];
    input_position(keyChallengeIndex);
    input_get(challengeKey, 0, DIGEST_LENGTH);
    input_reset();
    //compute key
    tempHash = calculateHMAC_SHA256(challengeKey, GENUINE_FMS_KEY, 68);
    //generate hash
    byte[] randBytes = new byte[Constants.HANDSHAKE_SIZE - DIGEST_LENGTH];
    random.nextBytes(randBytes);
    byte[] lastHash = calculateHMAC_SHA256(randBytes, tempHash, DIGEST_LENGTH);
    //set handshake with encryption type
    output_put(handshakeType);
    output_put(handshakeBytes);
    output_put(randBytes);
    output_put(lastHash);
    output_flip();
    return output;
 
 
  /**
   * Generates response for non-versioned connections, such as those before FP9.
   *
   * @param input incoming RTMP bytes
   * @return outgoing handshake
   */
  private byte[] generateUnversionedHandshake(byte input[]) {
    //save resource by only doing this after the first request
    if (HANDSHAKE_PAD_BYTES == null) {
      HANDSHAKE_PAD_BYTES = new byte[Constants.HANDSHAKE_SIZE - 4];
      //fill pad bytes
      Arrays.fill(HANDSHAKE_PAD_BYTES, (byte) 0x00);
    }
    byte output[] = new byte[HANDSHAKE_SIZE_SERVER];
    //non-encrypted
    output_put(RTMP_NON_ENCRYPTED);
    //set server uptime in seconds
    output_putInt(0x01)//(int) Red5.getUpTime() / 1000); //0x01
    output_put(RTMPHandshake.HANDSHAKE_PAD_BYTES);
    output_put(input);
    output_flip();
    return output;
  }
 
  /**
   * Creates the servers handshake bytes
   */
  @Override
  protected void createHandshakeBytes() {
    handshakeBytes = new byte[Constants.HANDSHAKE_SIZE];
    //timestamp
    handshakeBytes[0] = 0;
    handshakeBytes[1] = 0;
    handshakeBytes[2] = 0;
    handshakeBytes[3] = 0;
    //version (0x01020304)
    handshakeBytes[4] = 1;
    handshakeBytes[5] = 2;
    handshakeBytes[6] = 3;
    handshakeBytes[7] = 4;
    //fill the rest with random bytes
    byte[] rndBytes = new byte[Constants.HANDSHAKE_SIZE - 8];
    random.nextBytes(rndBytes);   
    //copy random bytes into our handshake array
    System.arraycopy(rndBytes, 0, handshakeBytes, 8, (Constants.HANDSHAKE_SIZE - 8))
 
 
  /**
   * Gets the DH offset in the handshake bytes array based on validation scheme
   * Generates DH keypair
   * Adds public key to handshake bytes
   * @param input
   */
  private void prepareResponse(byte input[]) {
    //put the clients input into a byte array
    byte[] inputBuffer = new byte[input_limit()];
    input_get(inputBuffer);
    //get the clients dh offset
    int clientDHOffset = getDHOffset(inputBuffer);
    //get the clients public key
    outgoingPublicKey = new byte[KEY_LENGTH];

    System.arraycopy(inputBuffer, clientDHOffset, outgoingPublicKey, 0, KEY_LENGTH);   
    //get the servers dh offset
    int serverDHOffset = getDHOffset(handshakeBytes);
    //create keypair
    KeyPair keys = generateKeyPair();
    //get public key
    incomingPublicKey = getPublicKey(keys);
    //add to handshake bytes
    System.arraycopy(incomingPublicKey, 0, handshakeBytes, serverDHOffset, KEY_LENGTH);
 
 
  /**
   * Determines the validation scheme for given input.
   *
   * @param input
   * @return true if client used a supported validation scheme, false if unsupported
   */
  @Override
  public boolean validate(byte input[]) {
    byte[] pBuffer = new byte[input_remaining()];
    //put all the input bytes into our buffer
    input_get(pBuffer, 0, input_remaining());
    if (validateScheme(pBuffer, 0)) {
      validationScheme = 0;
      return true;
    }
    if (validateScheme(pBuffer, 1)) {
      validationScheme = 1;
      return true;
    }
    return false;
  }
 
  private boolean validateScheme(byte[] pBuffer, int scheme) {
    int digestOffset = -1;
    switch (scheme) {
      case 0:
        digestOffset = getDigestOffset0(pBuffer);
        break;
      case 1:
        digestOffset = getDigestOffset1(pBuffer);
        break;
      default:
    }  

    byte[] tempBuffer = new byte[Constants.HANDSHAKE_SIZE - DIGEST_LENGTH];
    System.arraycopy(pBuffer, 0, tempBuffer, 0, digestOffset);
    System.arraycopy(pBuffer, digestOffset + DIGEST_LENGTH, tempBuffer, digestOffset, Constants.HANDSHAKE_SIZE - digestOffset - DIGEST_LENGTH);   

    byte[] tempHash = calculateHMAC_SHA256(tempBuffer, GENUINE_FP_KEY, 30);

    boolean result = true;
    for (int i = 0; i < DIGEST_LENGTH; i++) {
      //log.trace("Digest: {} Temp: {}", (pBuffer[digestOffset + i] & 0x0ff), (tempHash[i] & 0x0ff));
      if (pBuffer[digestOffset + i] != tempHash[i]) {
        result = false;
        break;
      }
    }

    return result; 
  }

}

TOP

Related Classes of javaforce.flash.InboundHandshake

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.