mirror of
https://github.com/ossrs/srs.git
synced 2025-02-13 20:01:56 +00:00
1321 lines
38 KiB
C++
1321 lines
38 KiB
C++
//
|
|
// Copyright (c) 2013-2022 The SRS Authors
|
|
//
|
|
// SPDX-License-Identifier: MIT or MulanPSL-2.0
|
|
//
|
|
|
|
#include <srs_protocol_rtmp_handshake.hpp>
|
|
|
|
#include <time.h>
|
|
|
|
#include <srs_core_autofree.hpp>
|
|
#include <srs_kernel_error.hpp>
|
|
#include <srs_kernel_log.hpp>
|
|
#include <srs_protocol_io.hpp>
|
|
#include <srs_protocol_utility.hpp>
|
|
#include <srs_protocol_rtmp_stack.hpp>
|
|
#include <srs_kernel_buffer.hpp>
|
|
#include <srs_kernel_utility.hpp>
|
|
|
|
using namespace srs_internal;
|
|
|
|
// for openssl_HMACsha256
|
|
#include <openssl/evp.h>
|
|
#include <openssl/hmac.h>
|
|
// for openssl_generate_key
|
|
#include <openssl/dh.h>
|
|
|
|
// For randomly generate the handshake bytes.
|
|
#define RTMP_SIG_SRS_HANDSHAKE RTMP_SIG_SRS_KEY "(" RTMP_SIG_SRS_VERSION ")"
|
|
|
|
// @see https://wiki.openssl.org/index.php/OpenSSL_1.1.0_Changes
|
|
#if OPENSSL_VERSION_NUMBER < 0x10100000L
|
|
|
|
HMAC_CTX *HMAC_CTX_new(void)
|
|
{
|
|
HMAC_CTX *ctx = (HMAC_CTX *)malloc(sizeof(*ctx));
|
|
if (ctx != NULL) {
|
|
HMAC_CTX_init(ctx);
|
|
}
|
|
return ctx;
|
|
}
|
|
|
|
void HMAC_CTX_free(HMAC_CTX *ctx)
|
|
{
|
|
if (ctx != NULL) {
|
|
HMAC_CTX_cleanup(ctx);
|
|
free(ctx);
|
|
}
|
|
}
|
|
|
|
static void DH_get0_key(const DH *dh, const BIGNUM **pub_key, const BIGNUM **priv_key)
|
|
{
|
|
if (pub_key != NULL) {
|
|
*pub_key = dh->pub_key;
|
|
}
|
|
if (priv_key != NULL) {
|
|
*priv_key = dh->priv_key;
|
|
}
|
|
}
|
|
|
|
static int DH_set0_pqg(DH *dh, BIGNUM *p, BIGNUM *q, BIGNUM *g)
|
|
{
|
|
/* If the fields p and g in d are NULL, the corresponding input
|
|
* parameters MUST be non-NULL. q may remain NULL.
|
|
*/
|
|
if ((dh->p == NULL && p == NULL)
|
|
|| (dh->g == NULL && g == NULL))
|
|
return 0;
|
|
|
|
if (p != NULL) {
|
|
BN_free(dh->p);
|
|
dh->p = p;
|
|
}
|
|
if (q != NULL) {
|
|
BN_free(dh->q);
|
|
dh->q = q;
|
|
}
|
|
if (g != NULL) {
|
|
BN_free(dh->g);
|
|
dh->g = g;
|
|
}
|
|
|
|
if (q != NULL) {
|
|
dh->length = BN_num_bits(q);
|
|
}
|
|
|
|
return 1;
|
|
}
|
|
|
|
static int DH_set_length(DH *dh, long length)
|
|
{
|
|
dh->length = length;
|
|
return 1;
|
|
}
|
|
|
|
#endif
|
|
|
|
namespace srs_internal
|
|
{
|
|
// 68bytes FMS key which is used to sign the sever packet.
|
|
uint8_t SrsGenuineFMSKey[] = {
|
|
0x47, 0x65, 0x6e, 0x75, 0x69, 0x6e, 0x65, 0x20,
|
|
0x41, 0x64, 0x6f, 0x62, 0x65, 0x20, 0x46, 0x6c,
|
|
0x61, 0x73, 0x68, 0x20, 0x4d, 0x65, 0x64, 0x69,
|
|
0x61, 0x20, 0x53, 0x65, 0x72, 0x76, 0x65, 0x72,
|
|
0x20, 0x30, 0x30, 0x31, // Genuine Adobe Flash Media Server 001
|
|
0xf0, 0xee, 0xc2, 0x4a, 0x80, 0x68, 0xbe, 0xe8,
|
|
0x2e, 0x00, 0xd0, 0xd1, 0x02, 0x9e, 0x7e, 0x57,
|
|
0x6e, 0xec, 0x5d, 0x2d, 0x29, 0x80, 0x6f, 0xab,
|
|
0x93, 0xb8, 0xe6, 0x36, 0xcf, 0xeb, 0x31, 0xae
|
|
}; // 68
|
|
|
|
// 62bytes FP key which is used to sign the client packet.
|
|
uint8_t SrsGenuineFPKey[] = {
|
|
0x47, 0x65, 0x6E, 0x75, 0x69, 0x6E, 0x65, 0x20,
|
|
0x41, 0x64, 0x6F, 0x62, 0x65, 0x20, 0x46, 0x6C,
|
|
0x61, 0x73, 0x68, 0x20, 0x50, 0x6C, 0x61, 0x79,
|
|
0x65, 0x72, 0x20, 0x30, 0x30, 0x31, // Genuine Adobe Flash Player 001
|
|
0xF0, 0xEE, 0xC2, 0x4A, 0x80, 0x68, 0xBE, 0xE8,
|
|
0x2E, 0x00, 0xD0, 0xD1, 0x02, 0x9E, 0x7E, 0x57,
|
|
0x6E, 0xEC, 0x5D, 0x2D, 0x29, 0x80, 0x6F, 0xAB,
|
|
0x93, 0xB8, 0xE6, 0x36, 0xCF, 0xEB, 0x31, 0xAE
|
|
}; // 62
|
|
|
|
srs_error_t do_openssl_HMACsha256(HMAC_CTX* ctx, const void* data, int data_size, void* digest, unsigned int* digest_size)
|
|
{
|
|
srs_error_t err = srs_success;
|
|
|
|
if (HMAC_Update(ctx, (unsigned char *) data, data_size) < 0) {
|
|
return srs_error_new(ERROR_OpenSslSha256Update, "hmac update");
|
|
}
|
|
|
|
if (HMAC_Final(ctx, (unsigned char *) digest, digest_size) < 0) {
|
|
return srs_error_new(ERROR_OpenSslSha256Final, "hmac final");
|
|
}
|
|
|
|
return err;
|
|
}
|
|
|
|
/**
|
|
* sha256 digest algorithm.
|
|
* @param key the sha256 key, NULL to use EVP_Digest, for instance,
|
|
* hashlib.sha256(data).digest().
|
|
*/
|
|
srs_error_t openssl_HMACsha256(const void* key, int key_size, const void* data, int data_size, void* digest)
|
|
{
|
|
srs_error_t err = srs_success;
|
|
|
|
unsigned int digest_size = 0;
|
|
|
|
unsigned char* temp_key = (unsigned char*)key;
|
|
unsigned char* temp_digest = (unsigned char*)digest;
|
|
|
|
if (key == NULL) {
|
|
// use data to digest.
|
|
// @see ./crypto/sha/sha256t.c
|
|
// @see ./crypto/evp/digest.c
|
|
if (EVP_Digest(data, data_size, temp_digest, &digest_size, EVP_sha256(), NULL) < 0) {
|
|
return srs_error_new(ERROR_OpenSslSha256EvpDigest, "evp digest");
|
|
}
|
|
} else {
|
|
// use key-data to digest.
|
|
HMAC_CTX *ctx = HMAC_CTX_new();
|
|
if (ctx == NULL) {
|
|
return srs_error_new(ERROR_OpenSslCreateHMAC, "hmac new");
|
|
}
|
|
// @remark, if no key, use EVP_Digest to digest,
|
|
// for instance, in python, hashlib.sha256(data).digest().
|
|
if (HMAC_Init_ex(ctx, temp_key, key_size, EVP_sha256(), NULL) < 0) {
|
|
HMAC_CTX_free(ctx);
|
|
return srs_error_new(ERROR_OpenSslSha256Init, "hmac init");
|
|
}
|
|
|
|
err = do_openssl_HMACsha256(ctx, data, data_size, temp_digest, &digest_size);
|
|
HMAC_CTX_free(ctx);
|
|
|
|
if (err != srs_success) {
|
|
return srs_error_wrap(err, "hmac sha256");
|
|
}
|
|
}
|
|
|
|
if (digest_size != 32) {
|
|
return srs_error_new(ERROR_OpenSslSha256DigestSize, "digest size %d", digest_size);
|
|
}
|
|
|
|
return err;
|
|
}
|
|
|
|
#define RFC2409_PRIME_1024 \
|
|
"FFFFFFFFFFFFFFFFC90FDAA22168C234C4C6628B80DC1CD1" \
|
|
"29024E088A67CC74020BBEA63B139B22514A08798E3404DD" \
|
|
"EF9519B3CD3A431B302B0A6DF25F14374FE1356D6D51C245" \
|
|
"E485B576625E7EC6F44C42E9A637ED6B0BFF5CB6F406B7ED" \
|
|
"EE386BFB5A899FA5AE9F24117C4B1FE649286651ECE65381" \
|
|
"FFFFFFFFFFFFFFFF"
|
|
|
|
SrsDH::SrsDH()
|
|
{
|
|
pdh = NULL;
|
|
}
|
|
|
|
SrsDH::~SrsDH()
|
|
{
|
|
close();
|
|
}
|
|
|
|
void SrsDH::close()
|
|
{
|
|
if (pdh != NULL) {
|
|
DH_free(pdh);
|
|
pdh = NULL;
|
|
}
|
|
}
|
|
|
|
srs_error_t SrsDH::initialize(bool ensure_128bytes_public_key)
|
|
{
|
|
srs_error_t err = srs_success;
|
|
|
|
for (;;) {
|
|
if ((err = do_initialize()) != srs_success) {
|
|
return srs_error_wrap(err, "init");
|
|
}
|
|
|
|
if (ensure_128bytes_public_key) {
|
|
const BIGNUM *pub_key = NULL;
|
|
DH_get0_key(pdh, &pub_key, NULL);
|
|
int32_t key_size = BN_num_bytes(pub_key);
|
|
if (key_size != 128) {
|
|
srs_warn("regenerate 128B key, current=%dB", key_size);
|
|
continue;
|
|
}
|
|
}
|
|
|
|
break;
|
|
}
|
|
|
|
return err;
|
|
}
|
|
|
|
srs_error_t SrsDH::copy_public_key(char* pkey, int32_t& pkey_size)
|
|
{
|
|
srs_error_t err = srs_success;
|
|
|
|
// copy public key to bytes.
|
|
// sometimes, the key_size is 127, seems ok.
|
|
const BIGNUM *pub_key = NULL;
|
|
DH_get0_key(pdh, &pub_key, NULL);
|
|
int32_t key_size = BN_num_bytes(pub_key);
|
|
srs_assert(key_size > 0);
|
|
|
|
// maybe the key_size is 127, but dh will write all 128bytes pkey,
|
|
// so, donot need to set/initialize the pkey.
|
|
// @see https://github.com/ossrs/srs/issues/165
|
|
key_size = BN_bn2bin(pub_key, (unsigned char*)pkey);
|
|
srs_assert(key_size > 0);
|
|
|
|
// output the size of public key.
|
|
// @see https://github.com/ossrs/srs/issues/165
|
|
srs_assert(key_size <= pkey_size);
|
|
pkey_size = key_size;
|
|
|
|
return err;
|
|
}
|
|
|
|
srs_error_t SrsDH::copy_shared_key(const char* ppkey, int32_t ppkey_size, char* skey, int32_t& skey_size)
|
|
{
|
|
srs_error_t err = srs_success;
|
|
|
|
BIGNUM* ppk = NULL;
|
|
if ((ppk = BN_bin2bn((const unsigned char*)ppkey, ppkey_size, 0)) == NULL) {
|
|
return srs_error_new(ERROR_OpenSslGetPeerPublicKey, "bin2bn");
|
|
}
|
|
|
|
// if failed, donot return, do cleanup, @see ./test/dhtest.c:168
|
|
// maybe the key_size is 127, but dh will write all 128bytes skey,
|
|
// so, donot need to set/initialize the skey.
|
|
// @see https://github.com/ossrs/srs/issues/165
|
|
int32_t key_size = DH_compute_key((unsigned char*)skey, ppk, pdh);
|
|
|
|
if (key_size < ppkey_size) {
|
|
srs_warn("shared key size=%d, ppk_size=%d", key_size, ppkey_size);
|
|
}
|
|
|
|
if (key_size < 0 || key_size > skey_size) {
|
|
err = srs_error_new(ERROR_OpenSslComputeSharedKey, "key size %d", key_size);
|
|
} else {
|
|
skey_size = key_size;
|
|
}
|
|
|
|
if (ppk) {
|
|
BN_free(ppk);
|
|
}
|
|
|
|
return err;
|
|
}
|
|
|
|
srs_error_t SrsDH::do_initialize()
|
|
{
|
|
srs_error_t err = srs_success;
|
|
|
|
int32_t bits_count = 1024;
|
|
|
|
close();
|
|
|
|
//1. Create the DH
|
|
if ((pdh = DH_new()) == NULL) {
|
|
return srs_error_new(ERROR_OpenSslCreateDH, "dh new");
|
|
}
|
|
|
|
//2. Create his internal p and g
|
|
BIGNUM *p, *g;
|
|
if ((p = BN_new()) == NULL) {
|
|
return srs_error_new(ERROR_OpenSslCreateP, "dh new");
|
|
}
|
|
if ((g = BN_new()) == NULL) {
|
|
BN_free(p);
|
|
return srs_error_new(ERROR_OpenSslCreateG, "bn new");
|
|
}
|
|
DH_set0_pqg(pdh, p, NULL, g);
|
|
|
|
//3. initialize p and g, @see ./test/ectest.c:260
|
|
if (!BN_hex2bn(&p, RFC2409_PRIME_1024)) {
|
|
return srs_error_new(ERROR_OpenSslParseP1024, "hex2bn");
|
|
}
|
|
// @see ./test/bntest.c:1764
|
|
if (!BN_set_word(g, 2)) {
|
|
return srs_error_new(ERROR_OpenSslSetG, "set word");
|
|
}
|
|
|
|
// 4. Set the key length
|
|
DH_set_length(pdh, bits_count);
|
|
|
|
// 5. Generate private and public key
|
|
// @see ./test/dhtest.c:152
|
|
if (!DH_generate_key(pdh)) {
|
|
return srs_error_new(ERROR_OpenSslGenerateDHKeys, "dh generate key");
|
|
}
|
|
|
|
return err;
|
|
}
|
|
|
|
key_block::key_block()
|
|
{
|
|
offset = (int32_t)srs_random();
|
|
random0 = NULL;
|
|
random1 = NULL;
|
|
|
|
int valid_offset = calc_valid_offset();
|
|
srs_assert(valid_offset >= 0);
|
|
|
|
random0_size = valid_offset;
|
|
if (random0_size > 0) {
|
|
random0 = new char[random0_size];
|
|
srs_random_generate(random0, random0_size);
|
|
snprintf(random0, random0_size, "%s", RTMP_SIG_SRS_HANDSHAKE);
|
|
}
|
|
|
|
srs_random_generate(key, sizeof(key));
|
|
|
|
random1_size = 764 - valid_offset - 128 - 4;
|
|
if (random1_size > 0) {
|
|
random1 = new char[random1_size];
|
|
srs_random_generate(random1, random1_size);
|
|
snprintf(random1, random1_size, "%s", RTMP_SIG_SRS_HANDSHAKE);
|
|
}
|
|
}
|
|
|
|
key_block::~key_block()
|
|
{
|
|
srs_freepa(random0);
|
|
srs_freepa(random1);
|
|
}
|
|
|
|
srs_error_t key_block::parse(SrsBuffer* stream)
|
|
{
|
|
srs_error_t err = srs_success;
|
|
|
|
// the key must be 764 bytes.
|
|
srs_assert(stream->require(764));
|
|
|
|
// read the last offset first, 760-763
|
|
stream->skip(764 - sizeof(int32_t));
|
|
offset = stream->read_4bytes();
|
|
|
|
// reset stream to read others.
|
|
stream->skip(-764);
|
|
|
|
int valid_offset = calc_valid_offset();
|
|
srs_assert(valid_offset >= 0);
|
|
|
|
random0_size = valid_offset;
|
|
if (random0_size > 0) {
|
|
srs_freepa(random0);
|
|
random0 = new char[random0_size];
|
|
stream->read_bytes(random0, random0_size);
|
|
}
|
|
|
|
stream->read_bytes(key, 128);
|
|
|
|
random1_size = 764 - valid_offset - 128 - 4;
|
|
if (random1_size > 0) {
|
|
srs_freepa(random1);
|
|
random1 = new char[random1_size];
|
|
stream->read_bytes(random1, random1_size);
|
|
}
|
|
|
|
return err;
|
|
}
|
|
|
|
int key_block::calc_valid_offset()
|
|
{
|
|
int max_offset_size = 764 - 128 - 4;
|
|
|
|
int valid_offset = 0;
|
|
uint8_t* pp = (uint8_t*)&offset;
|
|
valid_offset += *pp++;
|
|
valid_offset += *pp++;
|
|
valid_offset += *pp++;
|
|
valid_offset += *pp++;
|
|
|
|
return valid_offset % max_offset_size;
|
|
}
|
|
|
|
digest_block::digest_block()
|
|
{
|
|
offset = (int32_t)srs_random();
|
|
random0 = NULL;
|
|
random1 = NULL;
|
|
|
|
int valid_offset = calc_valid_offset();
|
|
srs_assert(valid_offset >= 0);
|
|
|
|
random0_size = valid_offset;
|
|
if (random0_size > 0) {
|
|
random0 = new char[random0_size];
|
|
srs_random_generate(random0, random0_size);
|
|
snprintf(random0, random0_size, "%s", RTMP_SIG_SRS_HANDSHAKE);
|
|
}
|
|
|
|
srs_random_generate(digest, sizeof(digest));
|
|
|
|
random1_size = 764 - 4 - valid_offset - 32;
|
|
if (random1_size > 0) {
|
|
random1 = new char[random1_size];
|
|
srs_random_generate(random1, random1_size);
|
|
snprintf(random1, random1_size, "%s", RTMP_SIG_SRS_HANDSHAKE);
|
|
}
|
|
}
|
|
|
|
digest_block::~digest_block()
|
|
{
|
|
srs_freepa(random0);
|
|
srs_freepa(random1);
|
|
}
|
|
|
|
srs_error_t digest_block::parse(SrsBuffer* stream)
|
|
{
|
|
srs_error_t err = srs_success;
|
|
|
|
// the digest must be 764 bytes.
|
|
srs_assert(stream->require(764));
|
|
|
|
offset = stream->read_4bytes();
|
|
|
|
int valid_offset = calc_valid_offset();
|
|
srs_assert(valid_offset >= 0);
|
|
|
|
random0_size = valid_offset;
|
|
if (random0_size > 0) {
|
|
srs_freepa(random0);
|
|
random0 = new char[random0_size];
|
|
stream->read_bytes(random0, random0_size);
|
|
}
|
|
|
|
stream->read_bytes(digest, 32);
|
|
|
|
random1_size = 764 - 4 - valid_offset - 32;
|
|
if (random1_size > 0) {
|
|
srs_freepa(random1);
|
|
random1 = new char[random1_size];
|
|
stream->read_bytes(random1, random1_size);
|
|
}
|
|
|
|
return err;
|
|
}
|
|
|
|
int digest_block::calc_valid_offset()
|
|
{
|
|
int max_offset_size = 764 - 32 - 4;
|
|
|
|
int valid_offset = 0;
|
|
uint8_t* pp = (uint8_t*)&offset;
|
|
valid_offset += *pp++;
|
|
valid_offset += *pp++;
|
|
valid_offset += *pp++;
|
|
valid_offset += *pp++;
|
|
|
|
return valid_offset % max_offset_size;
|
|
}
|
|
|
|
c1s1_strategy::c1s1_strategy()
|
|
{
|
|
}
|
|
|
|
c1s1_strategy::~c1s1_strategy()
|
|
{
|
|
}
|
|
|
|
char* c1s1_strategy::get_digest()
|
|
{
|
|
return digest.digest;
|
|
}
|
|
|
|
char* c1s1_strategy::get_key()
|
|
{
|
|
return key.key;
|
|
}
|
|
|
|
srs_error_t c1s1_strategy::dump(c1s1* owner, char* _c1s1, int size)
|
|
{
|
|
srs_assert(size == 1536);
|
|
return copy_to(owner, _c1s1, size, true);
|
|
}
|
|
|
|
srs_error_t c1s1_strategy::c1_create(c1s1* owner)
|
|
{
|
|
srs_error_t err = srs_success;
|
|
|
|
// generate digest
|
|
char* c1_digest = NULL;
|
|
|
|
if ((err = calc_c1_digest(owner, c1_digest)) != srs_success) {
|
|
return srs_error_wrap(err, "sign c1");
|
|
}
|
|
|
|
srs_assert(c1_digest != NULL);
|
|
SrsAutoFreeA(char, c1_digest);
|
|
|
|
memcpy(digest.digest, c1_digest, 32);
|
|
|
|
return err;
|
|
}
|
|
|
|
srs_error_t c1s1_strategy::c1_validate_digest(c1s1* owner, bool& is_valid)
|
|
{
|
|
srs_error_t err = srs_success;
|
|
|
|
char* c1_digest = NULL;
|
|
|
|
if ((err = calc_c1_digest(owner, c1_digest)) != srs_success) {
|
|
return srs_error_wrap(err, "validate c1");
|
|
}
|
|
|
|
srs_assert(c1_digest != NULL);
|
|
SrsAutoFreeA(char, c1_digest);
|
|
|
|
is_valid = srs_bytes_equals(digest.digest, c1_digest, 32);
|
|
|
|
return err;
|
|
}
|
|
|
|
srs_error_t c1s1_strategy::s1_create(c1s1* owner, c1s1* c1)
|
|
{
|
|
srs_error_t err = srs_success;
|
|
|
|
SrsDH dh;
|
|
|
|
// ensure generate 128bytes public key.
|
|
if ((err = dh.initialize(true)) != srs_success) {
|
|
return srs_error_wrap(err, "dh init");
|
|
}
|
|
|
|
// directly generate the public key.
|
|
int pkey_size = 128;
|
|
if ((err = dh.copy_shared_key(c1->get_key(), 128, key.key, pkey_size)) != srs_success) {
|
|
return srs_error_wrap(err, "copy shared key");
|
|
}
|
|
|
|
// although the public key is always 128bytes, but the share key maybe not.
|
|
// we just ignore the actual key size, but if need to use the key, must use the actual size.
|
|
// TODO: FIXME: use the actual key size.
|
|
//srs_assert(pkey_size == 128);
|
|
|
|
char* s1_digest = NULL;
|
|
if ((err = calc_s1_digest(owner, s1_digest)) != srs_success) {
|
|
return srs_error_wrap(err, "calc s1 digest");
|
|
}
|
|
|
|
srs_assert(s1_digest != NULL);
|
|
SrsAutoFreeA(char, s1_digest);
|
|
|
|
memcpy(digest.digest, s1_digest, 32);
|
|
|
|
return err;
|
|
}
|
|
|
|
srs_error_t c1s1_strategy::s1_validate_digest(c1s1* owner, bool& is_valid)
|
|
{
|
|
srs_error_t err = srs_success;
|
|
|
|
char* s1_digest = NULL;
|
|
|
|
if ((err = calc_s1_digest(owner, s1_digest)) != srs_success) {
|
|
return srs_error_wrap(err, "validate s1");
|
|
}
|
|
|
|
srs_assert(s1_digest != NULL);
|
|
SrsAutoFreeA(char, s1_digest);
|
|
|
|
is_valid = srs_bytes_equals(digest.digest, s1_digest, 32);
|
|
|
|
return err;
|
|
}
|
|
|
|
srs_error_t c1s1_strategy::calc_c1_digest(c1s1* owner, char*& c1_digest)
|
|
{
|
|
srs_error_t err = srs_success;
|
|
|
|
/**
|
|
* c1s1 is splited by digest:
|
|
* c1s1-part1: n bytes (time, version, key and digest-part1).
|
|
* digest-data: 32bytes
|
|
* c1s1-part2: (1536-n-32)bytes (digest-part2)
|
|
* @return a new allocated bytes, user must free it.
|
|
*/
|
|
char* c1s1_joined_bytes = new char[1536 -32];
|
|
SrsAutoFreeA(char, c1s1_joined_bytes);
|
|
if ((err = copy_to(owner, c1s1_joined_bytes, 1536 - 32, false)) != srs_success) {
|
|
return srs_error_wrap(err, "copy bytes");
|
|
}
|
|
|
|
c1_digest = new char[SRS_OpensslHashSize];
|
|
if ((err = openssl_HMACsha256(SrsGenuineFPKey, 30, c1s1_joined_bytes, 1536 - 32, c1_digest)) != srs_success) {
|
|
srs_freepa(c1_digest);
|
|
return srs_error_wrap(err, "calc c1 digest");
|
|
}
|
|
|
|
return err;
|
|
}
|
|
|
|
srs_error_t c1s1_strategy::calc_s1_digest(c1s1* owner, char*& s1_digest)
|
|
{
|
|
srs_error_t err = srs_success;
|
|
|
|
/**
|
|
* c1s1 is splited by digest:
|
|
* c1s1-part1: n bytes (time, version, key and digest-part1).
|
|
* digest-data: 32bytes
|
|
* c1s1-part2: (1536-n-32)bytes (digest-part2)
|
|
* @return a new allocated bytes, user must free it.
|
|
*/
|
|
char* c1s1_joined_bytes = new char[1536 -32];
|
|
SrsAutoFreeA(char, c1s1_joined_bytes);
|
|
if ((err = copy_to(owner, c1s1_joined_bytes, 1536 - 32, false)) != srs_success) {
|
|
return srs_error_wrap(err, "copy bytes");
|
|
}
|
|
|
|
s1_digest = new char[SRS_OpensslHashSize];
|
|
if ((err = openssl_HMACsha256(SrsGenuineFMSKey, 36, c1s1_joined_bytes, 1536 - 32, s1_digest)) != srs_success) {
|
|
srs_freepa(s1_digest);
|
|
return srs_error_wrap(err, "calc s1 digest");
|
|
}
|
|
|
|
return err;
|
|
}
|
|
|
|
void c1s1_strategy::copy_time_version(SrsBuffer* stream, c1s1* owner)
|
|
{
|
|
srs_assert(stream->require(8));
|
|
|
|
// 4bytes time
|
|
stream->write_4bytes(owner->time);
|
|
|
|
// 4bytes version
|
|
stream->write_4bytes(owner->version);
|
|
}
|
|
void c1s1_strategy::copy_key(SrsBuffer* stream)
|
|
{
|
|
srs_assert(key.random0_size >= 0);
|
|
srs_assert(key.random1_size >= 0);
|
|
|
|
int total = key.random0_size + 128 + key.random1_size + 4;
|
|
srs_assert(stream->require(total));
|
|
|
|
// 764bytes key block
|
|
if (key.random0_size > 0) {
|
|
stream->write_bytes(key.random0, key.random0_size);
|
|
}
|
|
|
|
stream->write_bytes(key.key, 128);
|
|
|
|
if (key.random1_size > 0) {
|
|
stream->write_bytes(key.random1, key.random1_size);
|
|
}
|
|
|
|
stream->write_4bytes(key.offset);
|
|
}
|
|
void c1s1_strategy::copy_digest(SrsBuffer* stream, bool with_digest)
|
|
{
|
|
srs_assert(key.random0_size >= 0);
|
|
srs_assert(key.random1_size >= 0);
|
|
|
|
int total = 4 + digest.random0_size + digest.random1_size;
|
|
if (with_digest) {
|
|
total += 32;
|
|
}
|
|
srs_assert(stream->require(total));
|
|
|
|
// 732bytes digest block without the 32bytes digest-data
|
|
// nbytes digest block part1
|
|
stream->write_4bytes(digest.offset);
|
|
|
|
// digest random padding.
|
|
if (digest.random0_size > 0) {
|
|
stream->write_bytes(digest.random0, digest.random0_size);
|
|
}
|
|
|
|
// digest
|
|
if (with_digest) {
|
|
stream->write_bytes(digest.digest, 32);
|
|
}
|
|
|
|
// nbytes digest block part2
|
|
if (digest.random1_size > 0) {
|
|
stream->write_bytes(digest.random1, digest.random1_size);
|
|
}
|
|
}
|
|
|
|
c1s1_strategy_schema0::c1s1_strategy_schema0()
|
|
{
|
|
}
|
|
|
|
c1s1_strategy_schema0::~c1s1_strategy_schema0()
|
|
{
|
|
}
|
|
|
|
srs_schema_type c1s1_strategy_schema0::schema()
|
|
{
|
|
return srs_schema0;
|
|
}
|
|
|
|
srs_error_t c1s1_strategy_schema0::parse(char* _c1s1, int size)
|
|
{
|
|
srs_error_t err = srs_success;
|
|
|
|
srs_assert(size == 1536);
|
|
|
|
if (true) {
|
|
SrsBuffer stream(_c1s1 + 8, 764);
|
|
|
|
if ((err = key.parse(&stream)) != srs_success) {
|
|
return srs_error_wrap(err, "parse the c1 key");
|
|
}
|
|
}
|
|
|
|
if (true) {
|
|
SrsBuffer stream(_c1s1 + 8 + 764, 764);
|
|
|
|
if ((err = digest.parse(&stream)) != srs_success) {
|
|
return srs_error_wrap(err, "parse the c1 digest");
|
|
}
|
|
}
|
|
|
|
return err;
|
|
}
|
|
|
|
srs_error_t c1s1_strategy_schema0::copy_to(c1s1* owner, char* bytes, int size, bool with_digest)
|
|
{
|
|
srs_error_t err = srs_success;
|
|
|
|
if (with_digest) {
|
|
srs_assert(size == 1536);
|
|
} else {
|
|
srs_assert(size == 1504);
|
|
}
|
|
|
|
SrsBuffer stream(bytes, size);
|
|
|
|
copy_time_version(&stream, owner);
|
|
copy_key(&stream);
|
|
copy_digest(&stream, with_digest);
|
|
|
|
srs_assert(stream.empty());
|
|
|
|
return err;
|
|
}
|
|
|
|
c1s1_strategy_schema1::c1s1_strategy_schema1()
|
|
{
|
|
}
|
|
|
|
c1s1_strategy_schema1::~c1s1_strategy_schema1()
|
|
{
|
|
}
|
|
|
|
srs_schema_type c1s1_strategy_schema1::schema()
|
|
{
|
|
return srs_schema1;
|
|
}
|
|
|
|
srs_error_t c1s1_strategy_schema1::parse(char* _c1s1, int size)
|
|
{
|
|
srs_error_t err = srs_success;
|
|
|
|
srs_assert(size == 1536);
|
|
|
|
if (true) {
|
|
SrsBuffer stream(_c1s1 + 8, 764);
|
|
|
|
if ((err = digest.parse(&stream)) != srs_success) {
|
|
return srs_error_wrap(err, "parse c1 digest");
|
|
}
|
|
}
|
|
|
|
if (true) {
|
|
SrsBuffer stream(_c1s1 + 8 + 764, 764);
|
|
|
|
if ((err = key.parse(&stream)) != srs_success) {
|
|
return srs_error_wrap(err, "parse c1 key");
|
|
}
|
|
}
|
|
|
|
return err;
|
|
}
|
|
|
|
srs_error_t c1s1_strategy_schema1::copy_to(c1s1* owner, char* bytes, int size, bool with_digest)
|
|
{
|
|
srs_error_t err = srs_success;
|
|
|
|
if (with_digest) {
|
|
srs_assert(size == 1536);
|
|
} else {
|
|
srs_assert(size == 1504);
|
|
}
|
|
|
|
SrsBuffer stream(bytes, size);
|
|
|
|
copy_time_version(&stream, owner);
|
|
copy_digest(&stream, with_digest);
|
|
copy_key(&stream);
|
|
|
|
srs_assert(stream.empty());
|
|
|
|
return err;
|
|
}
|
|
|
|
c1s1::c1s1()
|
|
{
|
|
payload = NULL;
|
|
version = 0;
|
|
time = 0;
|
|
}
|
|
c1s1::~c1s1()
|
|
{
|
|
srs_freep(payload);
|
|
}
|
|
|
|
srs_schema_type c1s1::schema()
|
|
{
|
|
srs_assert(payload != NULL);
|
|
return payload->schema();
|
|
}
|
|
|
|
char* c1s1::get_digest()
|
|
{
|
|
srs_assert(payload != NULL);
|
|
return payload->get_digest();
|
|
}
|
|
|
|
char* c1s1::get_key()
|
|
{
|
|
srs_assert(payload != NULL);
|
|
return payload->get_key();
|
|
}
|
|
|
|
srs_error_t c1s1::dump(char* _c1s1, int size)
|
|
{
|
|
srs_assert(size == 1536);
|
|
srs_assert(payload != NULL);
|
|
return payload->dump(this, _c1s1, size);
|
|
}
|
|
|
|
srs_error_t c1s1::parse(char* _c1s1, int size, srs_schema_type schema)
|
|
{
|
|
srs_assert(size == 1536);
|
|
|
|
if (schema != srs_schema0 && schema != srs_schema1) {
|
|
return srs_error_new(ERROR_RTMP_CH_SCHEMA, "parse c1 failed. invalid schema=%d", schema);
|
|
}
|
|
|
|
SrsBuffer stream(_c1s1, size);
|
|
|
|
time = stream.read_4bytes();
|
|
version = stream.read_4bytes(); // client c1 version
|
|
|
|
srs_freep(payload);
|
|
if (schema == srs_schema0) {
|
|
payload = new c1s1_strategy_schema0();
|
|
} else {
|
|
payload = new c1s1_strategy_schema1();
|
|
}
|
|
|
|
return payload->parse(_c1s1, size);
|
|
}
|
|
|
|
srs_error_t c1s1::c1_create(srs_schema_type schema)
|
|
{
|
|
if (schema != srs_schema0 && schema != srs_schema1) {
|
|
return srs_error_new(ERROR_RTMP_CH_SCHEMA, "create c1 failed. invalid schema=%d", schema);
|
|
}
|
|
|
|
// client c1 time and version
|
|
time = (int32_t)::time(NULL);
|
|
version = 0x80000702; // client c1 version
|
|
|
|
// generate signature by schema
|
|
srs_freep(payload);
|
|
if (schema == srs_schema0) {
|
|
payload = new c1s1_strategy_schema0();
|
|
} else {
|
|
payload = new c1s1_strategy_schema1();
|
|
}
|
|
|
|
return payload->c1_create(this);
|
|
}
|
|
|
|
srs_error_t c1s1::c1_validate_digest(bool& is_valid)
|
|
{
|
|
is_valid = false;
|
|
srs_assert(payload);
|
|
return payload->c1_validate_digest(this, is_valid);
|
|
}
|
|
|
|
srs_error_t c1s1::s1_create(c1s1* c1)
|
|
{
|
|
if (c1->schema() != srs_schema0 && c1->schema() != srs_schema1) {
|
|
return srs_error_new(ERROR_RTMP_CH_SCHEMA, "create s1 failed. invalid schema=%d", c1->schema());
|
|
}
|
|
|
|
time = ::time(NULL);
|
|
version = 0x01000504; // server s1 version
|
|
|
|
srs_freep(payload);
|
|
if (c1->schema() == srs_schema0) {
|
|
payload = new c1s1_strategy_schema0();
|
|
} else {
|
|
payload = new c1s1_strategy_schema1();
|
|
}
|
|
|
|
return payload->s1_create(this, c1);
|
|
}
|
|
|
|
srs_error_t c1s1::s1_validate_digest(bool& is_valid)
|
|
{
|
|
is_valid = false;
|
|
srs_assert(payload);
|
|
return payload->s1_validate_digest(this, is_valid);
|
|
}
|
|
|
|
c2s2::c2s2()
|
|
{
|
|
srs_random_generate(random, 1504);
|
|
|
|
int size = snprintf(random, 1504, "%s", RTMP_SIG_SRS_HANDSHAKE);
|
|
srs_assert(size > 0 && size < 1504);
|
|
snprintf(random + 1504 - size, size, "%s", RTMP_SIG_SRS_HANDSHAKE);
|
|
|
|
srs_random_generate(digest, 32);
|
|
}
|
|
|
|
c2s2::~c2s2()
|
|
{
|
|
}
|
|
|
|
srs_error_t c2s2::dump(char* _c2s2, int size)
|
|
{
|
|
srs_assert(size == 1536);
|
|
|
|
memcpy(_c2s2, random, 1504);
|
|
memcpy(_c2s2 + 1504, digest, 32);
|
|
|
|
return srs_success;
|
|
}
|
|
|
|
srs_error_t c2s2::parse(char* _c2s2, int size)
|
|
{
|
|
srs_assert(size == 1536);
|
|
|
|
memcpy(random, _c2s2, 1504);
|
|
memcpy(digest, _c2s2 + 1504, 32);
|
|
|
|
return srs_success;
|
|
}
|
|
|
|
srs_error_t c2s2::c2_create(c1s1* s1)
|
|
{
|
|
srs_error_t err = srs_success;
|
|
|
|
char temp_key[SRS_OpensslHashSize];
|
|
if ((err = openssl_HMACsha256(SrsGenuineFPKey, 62, s1->get_digest(), 32, temp_key)) != srs_success) {
|
|
return srs_error_wrap(err, "create c2 temp key");
|
|
}
|
|
|
|
char _digest[SRS_OpensslHashSize];
|
|
if ((err = openssl_HMACsha256(temp_key, 32, random, 1504, _digest)) != srs_success) {
|
|
return srs_error_wrap(err, "create c2 digest");
|
|
}
|
|
|
|
memcpy(digest, _digest, 32);
|
|
|
|
return err;
|
|
}
|
|
|
|
srs_error_t c2s2::c2_validate(c1s1* s1, bool& is_valid)
|
|
{
|
|
is_valid = false;
|
|
srs_error_t err = srs_success;
|
|
|
|
char temp_key[SRS_OpensslHashSize];
|
|
if ((err = openssl_HMACsha256(SrsGenuineFPKey, 62, s1->get_digest(), 32, temp_key)) != srs_success) {
|
|
return srs_error_wrap(err, "create c2 temp key");
|
|
}
|
|
|
|
char _digest[SRS_OpensslHashSize];
|
|
if ((err = openssl_HMACsha256(temp_key, 32, random, 1504, _digest)) != srs_success) {
|
|
return srs_error_wrap(err, "create c2 digest");
|
|
}
|
|
|
|
is_valid = srs_bytes_equals(digest, _digest, 32);
|
|
|
|
return err;
|
|
}
|
|
|
|
srs_error_t c2s2::s2_create(c1s1* c1)
|
|
{
|
|
srs_error_t err = srs_success;
|
|
|
|
char temp_key[SRS_OpensslHashSize];
|
|
if ((err = openssl_HMACsha256(SrsGenuineFMSKey, 68, c1->get_digest(), 32, temp_key)) != srs_success) {
|
|
return srs_error_wrap(err, "create s2 temp key");
|
|
}
|
|
|
|
char _digest[SRS_OpensslHashSize];
|
|
if ((err = openssl_HMACsha256(temp_key, 32, random, 1504, _digest)) != srs_success) {
|
|
return srs_error_wrap(err, "create s2 digest");
|
|
}
|
|
|
|
memcpy(digest, _digest, 32);
|
|
|
|
return err;
|
|
}
|
|
|
|
srs_error_t c2s2::s2_validate(c1s1* c1, bool& is_valid)
|
|
{
|
|
is_valid = false;
|
|
srs_error_t err = srs_success;
|
|
|
|
char temp_key[SRS_OpensslHashSize];
|
|
if ((err = openssl_HMACsha256(SrsGenuineFMSKey, 68, c1->get_digest(), 32, temp_key)) != srs_success) {
|
|
return srs_error_wrap(err, "create s2 temp key");
|
|
}
|
|
|
|
char _digest[SRS_OpensslHashSize];
|
|
if ((err = openssl_HMACsha256(temp_key, 32, random, 1504, _digest)) != srs_success) {
|
|
return srs_error_wrap(err, "create s2 digest");
|
|
}
|
|
|
|
is_valid = srs_bytes_equals(digest, _digest, 32);
|
|
|
|
return err;
|
|
}
|
|
}
|
|
|
|
SrsSimpleHandshake::SrsSimpleHandshake()
|
|
{
|
|
}
|
|
|
|
SrsSimpleHandshake::~SrsSimpleHandshake()
|
|
{
|
|
}
|
|
|
|
srs_error_t SrsSimpleHandshake::handshake_with_client(SrsHandshakeBytes* hs_bytes, ISrsProtocolReadWriter* io)
|
|
{
|
|
srs_error_t err = srs_success;
|
|
|
|
ssize_t nsize;
|
|
|
|
if ((err = hs_bytes->read_c0c1(io)) != srs_success) {
|
|
return srs_error_wrap(err, "read c0c1");
|
|
}
|
|
|
|
// plain text required.
|
|
if (hs_bytes->c0c1[0] != 0x03) {
|
|
return srs_error_new(ERROR_RTMP_PLAIN_REQUIRED, "only support rtmp plain text, version=%X", (uint8_t)hs_bytes->c0c1[0]);
|
|
}
|
|
|
|
if ((err = hs_bytes->create_s0s1s2(hs_bytes->c0c1 + 1)) != srs_success) {
|
|
return srs_error_wrap(err, "create s0s1s2");
|
|
}
|
|
|
|
if ((err = io->write(hs_bytes->s0s1s2, 3073, &nsize)) != srs_success) {
|
|
return srs_error_wrap(err, "write s0s1s2");
|
|
}
|
|
|
|
if ((err = hs_bytes->read_c2(io)) != srs_success) {
|
|
return srs_error_wrap(err, "read c2");
|
|
}
|
|
|
|
srs_trace("simple handshake success.");
|
|
|
|
return err;
|
|
}
|
|
|
|
srs_error_t SrsSimpleHandshake::handshake_with_server(SrsHandshakeBytes* hs_bytes, ISrsProtocolReadWriter* io)
|
|
{
|
|
srs_error_t err = srs_success;
|
|
|
|
ssize_t nsize;
|
|
|
|
// simple handshake
|
|
if ((err = hs_bytes->create_c0c1()) != srs_success) {
|
|
return srs_error_wrap(err, "create c0c1");
|
|
}
|
|
|
|
if ((err = io->write(hs_bytes->c0c1, 1537, &nsize)) != srs_success) {
|
|
return srs_error_wrap(err, "write c0c1");
|
|
}
|
|
|
|
if ((err = hs_bytes->read_s0s1s2(io)) != srs_success) {
|
|
return srs_error_wrap(err, "read s0s1s2");
|
|
}
|
|
|
|
// plain text required.
|
|
if (hs_bytes->s0s1s2[0] != 0x03) {
|
|
return srs_error_new(ERROR_RTMP_HANDSHAKE, "handshake failed, plain text required, version=%X", (uint8_t)hs_bytes->s0s1s2[0]);
|
|
}
|
|
|
|
if ((err = hs_bytes->create_c2()) != srs_success) {
|
|
return srs_error_wrap(err, "create c2");
|
|
}
|
|
|
|
// for simple handshake, copy s1 to c2.
|
|
// @see https://github.com/ossrs/srs/issues/418
|
|
memcpy(hs_bytes->c2, hs_bytes->s0s1s2 + 1, 1536);
|
|
|
|
if ((err = io->write(hs_bytes->c2, 1536, &nsize)) != srs_success) {
|
|
return srs_error_wrap(err, "write c2");
|
|
}
|
|
|
|
srs_trace("simple handshake success.");
|
|
|
|
return err;
|
|
}
|
|
|
|
SrsComplexHandshake::SrsComplexHandshake()
|
|
{
|
|
}
|
|
|
|
SrsComplexHandshake::~SrsComplexHandshake()
|
|
{
|
|
}
|
|
|
|
srs_error_t SrsComplexHandshake::handshake_with_client(SrsHandshakeBytes* hs_bytes, ISrsProtocolReadWriter* io)
|
|
{
|
|
srs_error_t err = srs_success;
|
|
|
|
ssize_t nsize;
|
|
|
|
if ((err = hs_bytes->read_c0c1(io)) != srs_success) {
|
|
return srs_error_wrap(err, "read c0c1");
|
|
}
|
|
|
|
// decode c1
|
|
c1s1 c1;
|
|
// try schema0.
|
|
// @remark, use schema0 to make flash player happy.
|
|
if ((err = c1.parse(hs_bytes->c0c1 + 1, 1536, srs_schema0)) != srs_success) {
|
|
return srs_error_wrap(err, "parse c1, schema=%d", srs_schema0);
|
|
}
|
|
// try schema1
|
|
bool is_valid = false;
|
|
if ((err = c1.c1_validate_digest(is_valid)) != srs_success || !is_valid) {
|
|
srs_freep(err);
|
|
|
|
if ((err = c1.parse(hs_bytes->c0c1 + 1, 1536, srs_schema1)) != srs_success) {
|
|
return srs_error_wrap(err, "parse c0c1, schame=%d", srs_schema1);
|
|
}
|
|
|
|
if ((err = c1.c1_validate_digest(is_valid)) != srs_success || !is_valid) {
|
|
srs_freep(err);
|
|
return srs_error_new(ERROR_RTMP_TRY_SIMPLE_HS, "all schema valid failed, try simple handshake");
|
|
}
|
|
}
|
|
|
|
// encode s1
|
|
c1s1 s1;
|
|
if ((err = s1.s1_create(&c1)) != srs_success) {
|
|
return srs_error_wrap(err, "create s1 from c1");
|
|
}
|
|
// verify s1
|
|
if ((err = s1.s1_validate_digest(is_valid)) != srs_success || !is_valid) {
|
|
srs_freep(err);
|
|
return srs_error_new(ERROR_RTMP_TRY_SIMPLE_HS, "verify s1 failed, try simple handshake");
|
|
}
|
|
|
|
c2s2 s2;
|
|
if ((err = s2.s2_create(&c1)) != srs_success) {
|
|
return srs_error_wrap(err, "create s2 from c1");
|
|
}
|
|
// verify s2
|
|
if ((err = s2.s2_validate(&c1, is_valid)) != srs_success || !is_valid) {
|
|
srs_freep(err);
|
|
return srs_error_new(ERROR_RTMP_TRY_SIMPLE_HS, "verify s2 failed, try simple handshake");
|
|
}
|
|
|
|
// sendout s0s1s2
|
|
if ((err = hs_bytes->create_s0s1s2()) != srs_success) {
|
|
return srs_error_wrap(err, "create s0s1s2");
|
|
}
|
|
if ((err = s1.dump(hs_bytes->s0s1s2 + 1, 1536)) != srs_success) {
|
|
return srs_error_wrap(err, "dump s1");
|
|
}
|
|
if ((err = s2.dump(hs_bytes->s0s1s2 + 1537, 1536)) != srs_success) {
|
|
return srs_error_wrap(err, "dump s2");
|
|
}
|
|
if ((err = io->write(hs_bytes->s0s1s2, 3073, &nsize)) != srs_success) {
|
|
return srs_error_wrap(err, "write s0s1s2");
|
|
}
|
|
|
|
// recv c2
|
|
if ((err = hs_bytes->read_c2(io)) != srs_success) {
|
|
return srs_error_wrap(err, "read c2");
|
|
}
|
|
c2s2 c2;
|
|
if ((err = c2.parse(hs_bytes->c2, 1536)) != srs_success) {
|
|
return srs_error_wrap(err, "parse c2");
|
|
}
|
|
|
|
// verify c2
|
|
// never verify c2, for ffmpeg will failed.
|
|
// it's ok for flash.
|
|
|
|
srs_trace("complex handshake success");
|
|
|
|
return err;
|
|
}
|
|
|
|
srs_error_t SrsComplexHandshake::handshake_with_server(SrsHandshakeBytes* hs_bytes, ISrsProtocolReadWriter* io)
|
|
{
|
|
srs_error_t err = srs_success;
|
|
|
|
ssize_t nsize;
|
|
|
|
// complex handshake
|
|
if ((err = hs_bytes->create_c0c1()) != srs_success) {
|
|
return srs_error_wrap(err, "create c0c1");
|
|
}
|
|
|
|
// sign c1
|
|
c1s1 c1;
|
|
// @remark, FMS requires the schema1(digest-key), or connect failed.
|
|
if ((err = c1.c1_create(srs_schema1)) != srs_success) {
|
|
return srs_error_wrap(err, "create c1");
|
|
}
|
|
if ((err = c1.dump(hs_bytes->c0c1 + 1, 1536)) != srs_success) {
|
|
return srs_error_wrap(err, "dump c1");
|
|
}
|
|
|
|
// verify c1
|
|
bool is_valid;
|
|
if ((err = c1.c1_validate_digest(is_valid)) != srs_success || !is_valid) {
|
|
srs_freep(err);
|
|
return srs_error_new(ERROR_RTMP_TRY_SIMPLE_HS, "try simple handshake");
|
|
}
|
|
|
|
if ((err = io->write(hs_bytes->c0c1, 1537, &nsize)) != srs_success) {
|
|
return srs_error_wrap(err, "write c0c1");
|
|
}
|
|
|
|
// s0s1s2
|
|
if ((err = hs_bytes->read_s0s1s2(io)) != srs_success) {
|
|
return srs_error_wrap(err, "read s0s1s2");
|
|
}
|
|
|
|
// plain text required.
|
|
if (hs_bytes->s0s1s2[0] != 0x03) {
|
|
return srs_error_new(ERROR_RTMP_HANDSHAKE, "handshake failed, plain text required, version=%X", (uint8_t)hs_bytes->s0s1s2[0]);
|
|
}
|
|
|
|
// verify s1s2
|
|
c1s1 s1;
|
|
if ((err = s1.parse(hs_bytes->s0s1s2 + 1, 1536, c1.schema())) != srs_success) {
|
|
return srs_error_wrap(err, "parse s1");
|
|
}
|
|
|
|
// never verify the s1,
|
|
// for if forward to nginx-rtmp, verify s1 will failed,
|
|
// TODO: FIXME: find the handshake schema of nginx-rtmp.
|
|
|
|
// c2
|
|
if ((err = hs_bytes->create_c2()) != srs_success) {
|
|
return srs_error_wrap(err, "create c2");
|
|
}
|
|
|
|
c2s2 c2;
|
|
if ((err = c2.c2_create(&s1)) != srs_success) {
|
|
return srs_error_wrap(err, "create c2");
|
|
}
|
|
|
|
if ((err = c2.dump(hs_bytes->c2, 1536)) != srs_success) {
|
|
return srs_error_wrap(err, "dump c2");
|
|
}
|
|
if ((err = io->write(hs_bytes->c2, 1536, &nsize)) != srs_success) {
|
|
return srs_error_wrap(err, "write c2");
|
|
}
|
|
|
|
srs_trace("complex handshake success.");
|
|
|
|
return err;
|
|
}
|
|
|