<commit_before>/*
 * RED5 Open Source Flash Server - https://github.com/Red5/
 * 
 * Copyright 2006-2015 by respective authors (see below). All rights reserved.
 * 
 * Licensed under the Apache License, Version 2.0 (the "License");
 * you may not use this file except in compliance with the License.
 * You may obtain a copy of the License at
 * 
 * http://www.apache.org/licenses/LICENSE-2.0
 * 
 * Unless required by applicable law or agreed to in writing, software
 * distributed under the License is distributed on an "AS IS" BASIS,
 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
 * See the License for the specific language governing permissions and
 * limitations under the License.
 */

package org.red5.client.net.rtmp;

import java.io.File;
import java.math.BigInteger;
import java.nio.ByteBuffer;
import java.security.KeyPair;
import java.util.Arrays;

import org.apache.commons.codec.binary.Hex;
import org.apache.mina.core.buffer.IoBuffer;
import org.bouncycastle.util.BigIntegers;
import org.red5.server.net.rtmp.RTMPConnection;
import org.red5.server.net.rtmp.RTMPHandshake;
import org.red5.server.net.rtmp.message.Constants;
import org.red5.server.util.FileUtil;
import org.slf4j.LoggerFactory;

/**
 * Performs handshaking for client connections.
 * 
 * @author <NAME>
 */
public class OutboundHandshake extends RTMPHandshake {

    private byte[] outgoingDigest = new byte[DIGEST_LENGTH];

    private byte[] incomingDigest = new byte[DIGEST_LENGTH];

    private byte[] swfHash;

    private int digestPosClient;

    private int digestPosServer;

    // client initial request C1
    private byte[] c1 = null;

    // server initial response S1
    private byte[] s1 = null;

    public OutboundHandshake() {
        super(RTMPConnection.RTMP_NON_ENCRYPTED);
        log = LoggerFactory.getLogger(OutboundHandshake.class);
    }

    public OutboundHandshake(byte handshakeType) {
        super(handshakeType);
        log = LoggerFactory.getLogger(OutboundHandshake.class);
    }

    public OutboundHandshake(byte handshakeType, int algorithm) {
        this(handshakeType);
        this.algorithm = algorithm;
    }

    @Override
    public IoBuffer doHandshake(IoBuffer input) {
        throw new UnsupportedOperationException("Not used, call server response decoders directly");
    }

    /**
     * Creates the servers handshake bytes
     */
    @Override
    protected void createHandshakeBytes() {
        log.trace("createHandshakeBytes");
        BigInteger bi = new BigInteger((Constants.HANDSHAKE_SIZE * 8), random);
        handshakeBytes = BigIntegers.asUnsignedByteArray(bi);
        // prevent AOOB error that can occur, sometimes
        if (handshakeBytes.length < Constants.HANDSHAKE_SIZE) {
            // resize the handshake bytes
            ByteBuffer b = ByteBuffer.allocate(Constants.HANDSHAKE_SIZE);
            b.put(handshakeBytes);
            b.put((byte) 0x13);
            b.flip();
            handshakeBytes = b.array();
        }
    }

    /**
     * Create the first part of the outgoing connection request (C0 and C1).
     * <pre>
     * C0 = 0x03 (client handshake type - 0x03, 0x06, 0x08, or 0x09)
     * C1 = 1536 bytes from the client
     * </pre>
     * @return outgoing handshake C0+C1
     */
    public IoBuffer generateClientRequest1() {
        log.debug("generateClientRequest1");
        IoBuffer request = IoBuffer.allocate(Constants.HANDSHAKE_SIZE + 1);
        // set the handshake type byte
        request.put(handshakeType);
        if (useEncryption() || swfSize > 0) {
            fp9Handshake = true;
            algorithm = 1;
        } else {
            //fp9Handshake = false;
        }
        // timestamp
        int time = 5;
        handshakeBytes[0] = (byte) (time >>> 24);
        handshakeBytes[1] = (byte) (time >>> 16);
        handshakeBytes[2] = (byte) (time >>> 8);
        handshakeBytes[3] = (byte) time; 
        if (fp9Handshake) {
            // flash player version > 9.0.115.0
            handshakeBytes[4] = (byte) 0x80;
            handshakeBytes[5] = 0;
            handshakeBytes[6] = 7;
            handshakeBytes[7] = 2;
        } else {
            log.debug("Using pre-version 9.0.115.0 handshake");
            handshakeBytes[4] = 0;
            handshakeBytes[5] = 0;
            handshakeBytes[6] = 0;
            handshakeBytes[7] = 0;
        }
        if (log.isTraceEnabled()) {
            log.trace("Time and version handshake bytes: {}", Hex.encodeHexString(Arrays.copyOf(handshakeBytes, 8)));
        }
        // get the handshake digest
        if (fp9Handshake) {
            // handle encryption setup
            if (useEncryption()) {
                // create keypair
                KeyPair keys = generateKeyPair();
                // get public key
                outgoingPublicKey = getPublicKey(keys);
                log.debug("Client public key: {}", Hex.encodeHexString(outgoingPublicKey));
                // get the DH offset in the handshake bytes
                int clientDHOffset = getDHOffset(algorithm, handshakeBytes, 0);
                log.trace("Outgoing DH offset: {}", clientDHOffset);
                // adds the public key to handshake bytes
                System.arraycopy(outgoingPublicKey, 0, handshakeBytes, clientDHOffset, KEY_LENGTH);
                // perform special processing for each type if needed
                switch (handshakeType) {
                    case RTMPConnection.RTMP_ENCRYPTED:
                        
                        break;
                    case RTMPConnection.RTMP_ENCRYPTED_XTEA:
                        
                        break;
                    case RTMPConnection.RTMP_ENCRYPTED_BLOWFISH:
                        
                        break;
                }
            }
            digestPosClient = getDigestOffset(algorithm, handshakeBytes, 0);
            log.debug("Client digest position offset: {} algorithm: {}", digestPosClient, algorithm);
            System.arraycopy(handshakeBytes, 0, c1, 0, Constants.HANDSHAKE_SIZE);
            calculateDigest(digestPosClient, handshakeBytes, 0, GENUINE_FP_KEY, 30, c1, digestPosClient);
            // local storage of outgoing digest
            System.arraycopy(c1, digestPosClient, outgoingDigest, 0, DIGEST_LENGTH);
            log.debug("Client digest: {}", Hex.encodeHexString(outgoingDigest));
            log.debug("Digest is valid: {}", verifyDigest(digestPosClient, c1, RTMPHandshake.GENUINE_FP_KEY, 30));
        } else {
            c1 = new byte[Constants.HANDSHAKE_SIZE];
        }
        if (log.isTraceEnabled()) {
            log.trace("C1: {}", Hex.encodeHexString(c1));
        }
        // put the generated data into our request
        request.put(c1);
        request.flip();
        // clear original base bytes<commit_msg>Changed it back as I read it wrong<commit_after>/*
 * RED5 Open Source Flash Server - https://github.com/Red5/
 * 
 * Copyright 2006-2015 by respective authors (see below). All rights reserved.
 * 
 * Licensed under the Apache License, Version 2.0 (the "License");
 * you may not use this file except in compliance with the License.
 * You may obtain a copy of the License at
 * 
 * http://www.apache.org/licenses/LICENSE-2.0
 * 
 * Unless required by applicable law or agreed to in writing, software
 * distributed under the License is distributed on an "AS IS" BASIS,
 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
 * See the License for the specific language governing permissions and
 * limitations under the License.
 */

package org.red5.client.net.rtmp;

import java.io.File;
import java.math.BigInteger;
import java.nio.ByteBuffer;
import java.security.KeyPair;
import java.util.Arrays;

import org.apache.commons.codec.binary.Hex;
import org.apache.mina.core.buffer.IoBuffer;
import org.bouncycastle.util.BigIntegers;
import org.red5.server.net.rtmp.RTMPConnection;
import org.red5.server.net.rtmp.RTMPHandshake;
import org.red5.server.net.rtmp.message.Constants;
import org.red5.server.util.FileUtil;
import org.slf4j.LoggerFactory;

/**
 * Performs handshaking for client connections.
 * 
 * @author <NAME>
 */
public class OutboundHandshake extends RTMPHandshake {

    private byte[] outgoingDigest = new byte[DIGEST_LENGTH];

    private byte[] incomingDigest = new byte[DIGEST_LENGTH];

    private byte[] swfHash;

    private int digestPosClient;

    private int digestPosServer;

    // client initial request C1
    private byte[] c1 = null;

    // server initial response S1
    private byte[] s1 = null;

    public OutboundHandshake() {
        super(RTMPConnection.RTMP_NON_ENCRYPTED);
        log = LoggerFactory.getLogger(OutboundHandshake.class);
    }

    public OutboundHandshake(byte handshakeType) {
        super(handshakeType);
        log = LoggerFactory.getLogger(OutboundHandshake.class);
    }

    public OutboundHandshake(byte handshakeType, int algorithm) {
        this(handshakeType);
        this.algorithm = algorithm;
    }

    @Override
    public IoBuffer doHandshake(IoBuffer input) {
        throw new UnsupportedOperationException("Not used, call server response decoders directly");
    }

    /**
     * Creates the servers handshake bytes
     */
    @Override
    protected void createHandshakeBytes() {
        log.trace("createHandshakeBytes");
        BigInteger bi = new BigInteger((Constants.HANDSHAKE_SIZE * 8), random);
        handshakeBytes = BigIntegers.asUnsignedByteArray(bi);
        // prevent AOOB error that can occur, sometimes
        if (handshakeBytes.length < Constants.HANDSHAKE_SIZE) {
            // resize the handshake bytes
            ByteBuffer b = ByteBuffer.allocate(Constants.HANDSHAKE_SIZE);
            b.put(handshakeBytes);
            b.put((byte) 0x13);
            b.flip();
            handshakeBytes = b.array();
        }
    }

    /**
     * Create the first part of the outgoing connection request (C0 and C1).
     * <pre>
     * C0 = 0x03 (client handshake type - 0x03, 0x06, 0x08, or 0x09)
     * C1 = 1536 bytes from the client
     * </pre>
     * @return outgoing handshake C0+C1
     */
    public IoBuffer generateClientRequest1() {
        log.debug("generateClientRequest1");
        IoBuffer request = IoBuffer.allocate(Constants.HANDSHAKE_SIZE + 1);
        // set the handshake type byte
        request.put(handshakeType);
        if (useEncryption() || swfSize > 0) {
            fp9Handshake = true;
            algorithm = 1;
        } else {
            //fp9Handshake = false;
        }
        // timestamp
        int time = 5;
        handshakeBytes[0] = (byte) (time >>> 24);
        handshakeBytes[1] = (byte) (time >>> 16);
        handshakeBytes[2] = (byte) (time >>> 8);
        handshakeBytes[3] = (byte) time; 
        if (fp9Handshake) {
            // flash player version > 172.16.17.32
            handshakeBytes[4] = (byte) 0x80;
            handshakeBytes[5] = 0;
            handshakeBytes[6] = 7;
            handshakeBytes[7] = 2;
        } else {
            log.debug("Using pre-version 172.16.17.32 handshake");
            handshakeBytes[4] = 0;
            handshakeBytes[5] = 0;
            handshakeBytes[6] = 0;
            handshakeBytes[7] = 0;
        }
        if (log.isTraceEnabled()) {
            log.trace("Time and version handshake bytes: {}", Hex.encodeHexString(Arrays.copyOf(handshakeBytes, 8)));
        }
        // get the handshake digest
        c1 = new byte[Constants.HANDSHAKE_SIZE];        
        if (fp9Handshake) {
            // handle encryption setup
            if (useEncryption()) {
                // create keypair
                KeyPair keys = generateKeyPair();
                // get public key
                outgoingPublicKey = getPublicKey(keys);
                log.debug("Client public key: {}", Hex.encodeHexString(outgoingPublicKey));
                // get the DH offset in the handshake bytes
                int clientDHOffset = getDHOffset(algorithm, handshakeBytes, 0);
                log.trace("Outgoing DH offset: {}", clientDHOffset);
                // adds the public key to handshake bytes
                System.arraycopy(outgoingPublicKey, 0, handshakeBytes, clientDHOffset, KEY_LENGTH);
                // perform special processing for each type if needed
                switch (handshakeType) {
                    case RTMPConnection.RTMP_ENCRYPTED:
                        
                        break;
                    case RTMPConnection.RTMP_ENCRYPTED_XTEA:
                        
                        break;
                    case RTMPConnection.RTMP_ENCRYPTED_BLOWFISH:
                        
                        break;
                }
            }
            digestPosClient = getDigestOffset(algorithm, handshakeBytes, 0);
            log.debug("Client digest position offset: {} algorithm: {}", digestPosClient, algorithm);
            System.arraycopy(handshakeBytes, 0, c1, 0, Constants.HANDSHAKE_SIZE);
            calculateDigest(digestPosClient, handshakeBytes, 0, GENUINE_FP_KEY, 30, c1, digestPosClient);
            // local storage of outgoing digest
            System.arraycopy(c1, digestPosClient, outgoingDigest, 0, DIGEST_LENGTH);
            log.debug("Client digest: {}", Hex.encodeHexString(outgoingDigest));
            log.debug("Digest is valid: {}", verifyDigest(digestPosClient, c1, RTMPHandshake.GENUINE_FP_KEY, 30));
        }
        if (log.isTraceEnabled()) {
            log.trace("C1: {}", Hex.encodeHexString(c1));
        }
        // put the generated data into our request
        request.put(c1);
        request.flip();
        // clear original base bytes
{'fraction_non_alphanumeric': 0.09769612131816856, 'fraction_numerical': 0.018226888305628464, 'mean_word_length': 2.2237367802585193, 'pattern_counts': {'":': 0, '<': 11, '<?xml version=': 0, '>': 31, 'https://': 2, 'lorem ipsum': 0, 'www.': 2, 'xml': 0}, 'pii_count': 4, 'substrings_counts': 0, 'word_list_counts': {'cursed_substrings.json': 0, 'profanity_word_list.json': 0, 'sexual_word_list.json': 0, 'zh_pornsignals.json': 0}}
{'dir': 'git-commits-cleaned', 'id': '6887843', 'n_tokens_mistral': 4018, 'n_tokens_neox': 3757, 'n_words': 997}