1
0
Fork 0
mirror of https://github.com/ossrs/srs.git synced 2025-03-09 15:49:59 +00:00

SRT: Build SRT from source by SRS. 4.0.115

This commit is contained in:
hondaxiao 2021-05-15 19:18:39 +08:00 committed by winlin
parent 262f0fc8c8
commit 90f1b482ab
115 changed files with 44513 additions and 19 deletions

View file

@ -0,0 +1,22 @@
#ifndef INC__CRYSPR_CONFIG_H
#define INC__CRYSPR_CONFIG_H
// Size of the single block for encryption.
// This might need tweaking for particular implementation library.
#define CRYSPR_AESBLKSZ 16 /* 128-bit */
#if defined(USE_OPENSSL)
#include "cryspr-openssl.h"
#define cryspr4SRT() crysprOpenSSL()
#elif defined(USE_GNUTLS)
#include "cryspr-gnutls.h"
#define cryspr4SRT() crysprGnuTLS()
#elif defined(USE_MBEDTLS)
#include "cryspr-mbedtls.h"
#define cryspr4SRT() crysprMbedtls()
#else
#error Cryspr implementation not selected. Please define USE_* + OPENSSL/GNUTLS/MBEDTLS.
#endif
#endif

View file

@ -0,0 +1,178 @@
/*
* SRT - Secure, Reliable, Transport
* Copyright (c) 2019 Haivision Systems Inc.
*
* This Source Code Form is subject to the terms of the Mozilla Public
* License, v. 2.0. If a copy of the MPL was not distributed with this
* file, You can obtain one at http://mozilla.org/MPL/2.0/.
*
*/
/*****************************************************************************
written by
Haivision Systems Inc.
2019-06-27 (jdube)
GnuTLS/Nettle CRYSPR/4SRT (CRYypto Service PRovider for SRT)
*****************************************************************************/
#include "hcrypt.h"
#include <string.h>
typedef struct tag_crysprGnuTLS_AES_cb {
CRYSPR_cb ccb; /* CRYSPR control block */
/* Add other cryptolib specific data here */
} crysprGnuTLS_cb;
int crysprGnuTLS_Prng(unsigned char *rn, int len)
{
return(gnutls_rnd(GNUTLS_RND_KEY,(rn),(len)) < 0 ? -1 : 0);
}
int crysprGnuTLS_AES_SetKey(
bool bEncrypt, /* true:encrypt key, false:decrypt key*/
const unsigned char *kstr, /* key string */
size_t kstr_len, /* kstr length in bytes (16, 24, or 32 bytes (for AES128,AES192, or AES256) */
CRYSPR_AESCTX *aes_key) /* Cryptolib Specific AES key context */
{
if (bEncrypt) { /* Encrypt key */
if (!(kstr_len == 16 || kstr_len == 24 || kstr_len == 32)) {
HCRYPT_LOG(LOG_ERR, "%s", "AES_set_encrypt_key(kek) bad length\n");
return -1;
}
aes_set_encrypt_key (aes_key, kstr_len, kstr);
} else { /* Decrypt key */
if (!(kstr_len == 16 || kstr_len == 24 || kstr_len == 32)) {
HCRYPT_LOG(LOG_ERR, "%s", "AES_set_decrypt_key(kek) bad length\n");
return -1;
}
aes_set_decrypt_key (aes_key, kstr_len, kstr);
}
return(0);
}
int crysprGnuTLS_AES_EcbCipher( /* AES Electronic Codebook cipher*/
bool bEncrypt, /* true:encrypt, false:decrypt */
CRYSPR_AESCTX *aes_key, /* CryptoLib AES context */
const unsigned char *indata,/* src (clear text)*/
size_t inlen, /* length */
unsigned char *out_txt, /* dst (cipher text) */
size_t *outlen) /* dst len */
{
int nblk = inlen/CRYSPR_AESBLKSZ;
int nmore = inlen%CRYSPR_AESBLKSZ;
int i;
if (bEncrypt) {
/* Encrypt packet payload, block by block, in output buffer */
for (i=0; i<nblk; i++){
aes_encrypt(aes_key, CRYSPR_AESBLKSZ, &out_txt[(i*CRYSPR_AESBLKSZ)], &indata[(i*CRYSPR_AESBLKSZ)]);
}
/* Encrypt last incomplete block */
if (0 < nmore) {
unsigned char intxt[CRYSPR_AESBLKSZ];
memcpy(intxt, &indata[(nblk*CRYSPR_AESBLKSZ)], nmore);
memset(intxt+nmore, 0, CRYSPR_AESBLKSZ-nmore);
aes_encrypt(aes_key, CRYSPR_AESBLKSZ, &out_txt[(nblk*CRYSPR_AESBLKSZ)], intxt);
nblk++;
}
if (outlen != NULL) *outlen = nblk*CRYSPR_AESBLKSZ;
} else { /* Decrypt */
for (i=0; i<nblk; i++){
aes_decrypt(aes_key, CRYSPR_AESBLKSZ, &out_txt[(i*CRYSPR_AESBLKSZ)], &indata[(i*CRYSPR_AESBLKSZ)]);
}
/* Encrypt last incomplete block */
if (0 < nmore) {
//shall not happens in decrypt
}
if (outlen != NULL) *outlen = nblk*CRYSPR_AESBLKSZ;
}
return 0;
}
int crysprGnuTLS_AES_CtrCipher( /* AES-CTR128 Encryption */
bool bEncrypt, /* true:encrypt, false:decrypt */
CRYSPR_AESCTX *aes_key, /* CryptoLib AES context */
unsigned char *iv, /* iv */
const unsigned char *indata,/* src */
size_t inlen, /* src length */
unsigned char *out_txt) /* dest buffer[inlen] */
{
(void)bEncrypt; /* CTR mode encrypt for both encryption and decryption */
ctr_crypt (aes_key, /* ctx */
(nettle_cipher_func*)aes_encrypt, /* nettle_cipher_func */
CRYSPR_AESBLKSZ, /* cipher blocksize */
iv, /* iv */
inlen, /* length */
out_txt, /* dest */
indata); /* src */
return 0;
}
#ifdef CRYSPR_HAS_PBKDF2
/*
* Password-based Key Derivation Function
*/
int crysprGnuTLS_KmPbkdf2(
CRYSPR_cb *cryspr_cb,
char *passwd, /* passphrase */
size_t passwd_len, /* passphrase len */
unsigned char *salt, /* salt */
size_t salt_len, /* salt_len */
int itr, /* iterations */
size_t key_len, /* key_len */
unsigned char *out) /* derived key buffer[key_len]*/
{
(void)cryspr_cb;
pbkdf2_hmac_sha1(passwd_len,(const uint8_t *)passwd,itr,salt_len,salt,key_len,out);
return(0);
}
#endif /* CRYSPR_HAS_PBKDF2 */
static CRYSPR_methods crysprGnuTLS_methods;
CRYSPR_methods *crysprGnuTLS(void)
{
if(NULL == crysprGnuTLS_methods.open) {
crysprInit(&crysprGnuTLS_methods); /* Set default methods */
/* CryptoLib Primitive API */
crysprGnuTLS_methods.prng = crysprGnuTLS_Prng;
crysprGnuTLS_methods.aes_set_key = crysprGnuTLS_AES_SetKey;
#if CRYSPR_HAS_AESCTR
crysprGnuTLS_methods.aes_ctr_cipher = crysprGnuTLS_AES_CtrCipher;
#endif
#if !(CRYSPR_HAS_AESCTR && CRYSPR_HAS_AESKWRAP)
/* AES-ECB only required if cryspr has no AES-CTR or no AES KeyWrap */
crysprGnuTLS_methods.aes_ecb_cipher = crysprGnuTLS_AES_EcbCipher;
#endif
#if !CRYSPR_HAS_PBKDF2
crysprGnuTLS_methods.sha1_msg_digest= crysprGnuTLS_SHA1_MsgDigest; //Onl required if using generic KmPbkdf2
#endif
//--Crypto Session (Top API)
// crysprGnuTLS_methods.open =
// crysprGnuTLS_methods.close =
//--Keying material (km) encryption
#if CRYSPR_HAS_PBKDF2
crysprGnuTLS_methods.km_pbkdf2 = crysprGnuTLS_KmPbkdf2;
#endif
// crysprGnuTLS_methods.km_setkey =
// crysprGnuTLS_methods.km_wrap =
// crysprGnuTLS_methods.km_unwrap =
//--Media stream (ms) encryption
// crysprGnuTLS_methods.ms_setkey =
// crysprGnuTLS_methods.ms_encrypt =
// crysprGnuTLS_methods.ms_decrypt =
}
return(&crysprGnuTLS_methods);
}

View file

@ -0,0 +1,61 @@
/*
* SRT - Secure, Reliable, Transport
* Copyright (c) 2019 Haivision Systems Inc.
*
* This Source Code Form is subject to the terms of the Mozilla Public
* License, v. 2.0. If a copy of the MPL was not distributed with this
* file, You can obtain one at http://mozilla.org/MPL/2.0/.
*
*/
/*****************************************************************************
written by
Haivision Systems Inc.
2019-06-27 (jdube)
GnuTLS/Nettle CRYSPR/4SRT (CRYypto Service PRovider for SRT)
*****************************************************************************/
#ifndef CRYSPR_GNUTLS_H
#define CRYSPR_GNUTLS_H
#include <gnutls/gnutls.h>
#include <gnutls/crypto.h> //gnutls_rnd()
#include <nettle/aes.h> //has AES cipher
#include <nettle/ctr.h> //has CTR cipher mode
#include <nettle/pbkdf2.h> //has Password-based Key Derivation Function 2
//#include <nettle/sha1.h> //No need for sha1 since we have pbkdf2
/* Define CRYSPR_HAS_AESCTR to 1 if this CRYSPR has AESCTR cipher mode
if not set it 0 to use enable CTR cipher mode implementation using ECB cipher mode
and provide the aes_ecb_cipher method.
*/
#define CRYSPR_HAS_AESCTR 1
/* Define CRYSPR_HAS_AESKWRAP to 1 if this CRYSPR has AES Key Wrap
if not set to 0 to enable default/fallback crysprFallback_AES_WrapKey/crysprFallback_AES_UnwrapKey methods
and provide the aes_ecb_cipher method .
*/
#define CRYSPR_HAS_AESKWRAP 0
/* Define CRYSPR_HAS_PBKDF2 to 1 if this CRYSPR has SHA1-HMAC Password-based Key Derivaion Function 2
if not set to 0 to enable not-yet-implemented/fallback crysprFallback.km_pbkdf2 method
and provide the sha1_msg_digest method.
*/
#define CRYSPR_HAS_PBKDF2 1
/*
#define CRYSPR_AESCTX to the CRYSPR specifix AES key context object.
This type reserves room in the CRYPSPR control block for Haicrypt KEK and SEK
It is set from hte keystring through CRYSPR_methods.aes_set_key and passed
to CRYSPR_methods.aes_XXX.
*/
typedef struct aes_ctx CRYSPR_AESCTX; /* CRYpto Service PRovider AES key context */
struct tag_CRYSPR_methods *crysprGnuTLS(void);
#endif /* CRYSPR_GNUTLS_H */

View file

@ -0,0 +1,235 @@
/*
* SRT - Secure, Reliable, Transport
* Copyright (c) 2019 Haivision Systems Inc.
*
* This Source Code Form is subject to the terms of the Mozilla Public
* License, v. 2.0. If a copy of the MPL was not distributed with this
* file, You can obtain one at http://mozilla.org/MPL/2.0/.
*
*/
/*****************************************************************************
written by
Haivision Systems Inc.
2019-06-27 (jdube)
GnuTLS/Nettle CRYSPR/4SRT (CRYypto Service PRovider for SRT)
*****************************************************************************/
#include "hcrypt.h"
#include <string.h>
#include <mbedtls/aes.h>
#include <mbedtls/md.h>
#include <mbedtls/pkcs5.h>
#include <mbedtls/entropy.h>
// Static members of cryspr::mbedtls class.
static mbedtls_ctr_drbg_context crysprMbedtls_ctr_drbg;
static mbedtls_entropy_context crysprMbedtls_entropy;
static mbedtls_md_context_t crysprMbedtls_mdctx;
typedef struct tag_crysprGnuTLS_AES_cb {
CRYSPR_cb ccb; /* CRYSPR control block */
/* Add other cryptolib specific data here */
} crysprMbedtls_cb;
int crysprMbedtls_Prng(unsigned char *rn, int len)
{
int ret = mbedtls_ctr_drbg_random( &crysprMbedtls_ctr_drbg, rn, len );
if (ret != 0)
{
return -1;
}
return 0;
}
int crysprMbedtls_AES_SetKey(
bool bEncrypt, /* true:encrypt key, false:decrypt key*/
const unsigned char *kstr, /* key string */
size_t kstr_len, /* kstr length in bytes (16, 24, or 32 bytes, for AES128,AES192, or AES256) */
CRYSPR_AESCTX *aes_key) /* Cryptolib Specific AES key context */
{
if (!(kstr_len == 16 || kstr_len == 24 || kstr_len == 32)) {
HCRYPT_LOG(LOG_ERR, "%s", "AES_set_encrypt_key(kek) bad length\n");
return -1;
}
int ret;
// mbedtls uses the "bits" convention (128, 192, 254), just like openssl.
// kstr_len is in "bytes" convention (16, 24, 32).
if (bEncrypt) { /* Encrypt key */
ret = mbedtls_aes_setkey_enc(aes_key, kstr, kstr_len*8);
} else { /* Decrypt key */
ret = mbedtls_aes_setkey_dec(aes_key, kstr, kstr_len*8);
}
return ret == 0 ? 0 : -1;
}
int crysprMbedtls_AES_EcbCipher( /* AES Electronic Codebook cipher*/
bool bEncrypt, /* true:encrypt, false:decrypt */
CRYSPR_AESCTX *aes_key, /* CryptoLib AES context */
const unsigned char *indata,/* src (clear text)*/
size_t inlen, /* length */
unsigned char *out_txt, /* dst (cipher text) */
size_t *outlen) /* dst len */
{
int nblk = inlen/CRYSPR_AESBLKSZ;
int nmore = inlen%CRYSPR_AESBLKSZ;
int i;
if (bEncrypt) {
/* Encrypt packet payload, block by block, in output buffer */
for (i = 0; i < nblk; i++) {
// NOTE: CRYSPR_AESBLKSZ is implicitly the ONLY POSSIBLE
// size of the block.
mbedtls_aes_crypt_ecb(aes_key, MBEDTLS_AES_ENCRYPT,
&indata[(i*CRYSPR_AESBLKSZ)],
&out_txt[(i*CRYSPR_AESBLKSZ)]);
}
/* Encrypt last incomplete block */
if (0 < nmore) {
unsigned char intxt[CRYSPR_AESBLKSZ];
memcpy(intxt, &indata[(nblk*CRYSPR_AESBLKSZ)], nmore);
memset(intxt+nmore, 0, CRYSPR_AESBLKSZ-nmore);
mbedtls_aes_crypt_ecb(aes_key, MBEDTLS_AES_ENCRYPT,
intxt,
&out_txt[(nblk*CRYSPR_AESBLKSZ)]);
nblk++;
}
if (outlen != NULL) *outlen = nblk*CRYSPR_AESBLKSZ;
} else { /* Decrypt */
for (i=0; i<nblk; i++){
mbedtls_aes_crypt_ecb(aes_key, MBEDTLS_AES_DECRYPT,
&indata[(i*CRYSPR_AESBLKSZ)],
&out_txt[(i*CRYSPR_AESBLKSZ)]);
}
/* Encrypt last incomplete block */
if (0 < nmore) {
//shall not happens in decrypt
}
if (outlen != NULL) *outlen = nblk*CRYSPR_AESBLKSZ;
}
return 0;
}
int crysprMbedtls_AES_CtrCipher( /* AES-CTR128 Encryption */
bool bEncrypt, /* true:encrypt, false:decrypt */
CRYSPR_AESCTX *aes_key, /* CryptoLib AES context */
unsigned char *iv, /* iv */
const unsigned char *indata,/* src */
size_t inlen, /* src length */
unsigned char *out_txt) /* dest buffer[inlen] */
{
unsigned char ctr[CRYSPR_AESBLKSZ];
size_t blk_ofs = 0;
(void)bEncrypt; /* CTR mode encrypt for both encryption and decryption */
memset(&ctr[0], 0, sizeof(ctr));
mbedtls_aes_crypt_ctr(aes_key,
inlen,
&blk_ofs,
iv,
ctr,
indata,
out_txt);
return 0;
}
/*
* Password-based Key Derivation Function
*/
int crysprMbedtls_KmPbkdf2(
CRYSPR_cb *cryspr_cb,
char *passwd, /* passphrase */
size_t passwd_len, /* passphrase len */
unsigned char *salt, /* salt */
size_t salt_len, /* salt_len */
int itr, /* iterations */
size_t key_len, /* key_len */
unsigned char *out) /* derived key buffer[key_len]*/
{
(void)cryspr_cb;
int ret = mbedtls_pkcs5_pbkdf2_hmac(&crysprMbedtls_mdctx,
(unsigned char*)passwd, passwd_len, salt, salt_len,
itr, key_len, out);
if (ret == 0)
return 0;
// XXX report error, log?
return -1;
}
static CRYSPR_methods crysprMbedtls_methods;
CRYSPR_methods *crysprMbedtls(void)
{
if (crysprMbedtls_methods.open)
return(&crysprMbedtls_methods);
crysprInit(&crysprMbedtls_methods); /* Set default methods */
/* CryptoLib Primitive API */
crysprMbedtls_methods.prng = crysprMbedtls_Prng;
crysprMbedtls_methods.aes_set_key = crysprMbedtls_AES_SetKey;
#if CRYSPR_HAS_AESCTR
crysprMbedtls_methods.aes_ctr_cipher = crysprMbedtls_AES_CtrCipher;
#endif
#if !(CRYSPR_HAS_AESCTR && CRYSPR_HAS_AESKWRAP)
/* AES-ECB only required if cryspr has no AES-CTR or no AES KeyWrap */
crysprMbedtls_methods.aes_ecb_cipher = crysprMbedtls_AES_EcbCipher;
#endif
#if !CRYSPR_HAS_PBKDF2
crysprMbedtls_methods.sha1_msg_digest= crysprMbedtls_SHA1_MsgDigest; //Onl required if using generic KmPbkdf2
#endif
//--Crypto Session (Top API)
// crysprMbedtls_methods.open =
// crysprMbedtls_methods.close =
//--Keying material (km) encryption
crysprMbedtls_methods.km_pbkdf2 = crysprMbedtls_KmPbkdf2;
// crysprMbedtls_methods.km_setkey =
// crysprMbedtls_methods.km_wrap =
// crysprMbedtls_methods.km_unwrap =
//--Media stream (ms) encryption
// crysprMbedtls_methods.ms_setkey =
// crysprMbedtls_methods.ms_encrypt =
// crysprMbedtls_methods.ms_decrypt =
// Initialize extra static data
mbedtls_entropy_init( &crysprMbedtls_entropy );
mbedtls_ctr_drbg_init( &crysprMbedtls_ctr_drbg );
int ret;
if ( (ret = mbedtls_ctr_drbg_seed( &crysprMbedtls_ctr_drbg, mbedtls_entropy_func,
&crysprMbedtls_entropy, NULL, 0)) != 0 )
{
HCRYPT_LOG(LOG_CRIT, "crysprMbedtls: STATIC INIT FAILED on mbedtls_ctr_drbg_init: -0x%04x", -ret);
return NULL;
}
// Ok, mbedtls with all flexibility you couldn't make it more complicated.
mbedtls_md_init(&crysprMbedtls_mdctx);
const mbedtls_md_info_t* ifo = mbedtls_md_info_from_type(MBEDTLS_MD_SHA1);
const int yes_use_hmac = 1;
mbedtls_md_setup(&crysprMbedtls_mdctx, ifo, yes_use_hmac);
return(&crysprMbedtls_methods);
}

View file

@ -0,0 +1,63 @@
/*
* SRT - Secure, Reliable, Transport
* Copyright (c) 2019 Haivision Systems Inc.
*
* This Source Code Form is subject to the terms of the Mozilla Public
* License, v. 2.0. If a copy of the MPL was not distributed with this
* file, You can obtain one at http://mozilla.org/MPL/2.0/.
*
*/
/*****************************************************************************
written by
Haivision Systems Inc.
2019-06-27 (jdube)
GnuTLS/Nettle CRYSPR/4SRT (CRYypto Service PRovider for SRT)
*****************************************************************************/
#ifndef CRYSPR_GNUTLS_H
#define CRYSPR_GNUTLS_H
#include <mbedtls/ctr_drbg.h>
#include <mbedtls/aes.h>
/* Define CRYSPR_HAS_AESCTR to 1 if this CRYSPR has AESCTR cipher mode
if not set it 0 to use enable CTR cipher mode implementation using ECB cipher mode
and provide the aes_ecb_cipher method.
*/
#define CRYSPR_HAS_AESCTR 1
/* Define CRYSPR_HAS_AESKWRAP to 1 if this CRYSPR has AES Key Wrap
if not set to 0 to enable default/fallback crysprFallback_AES_WrapKey/crysprFallback_AES_UnwrapKey methods
and provide the aes_ecb_cipher method .
*/
#define CRYSPR_HAS_AESKWRAP 0
/* Define CRYSPR_HAS_PBKDF2 to 1 if this CRYSPR has SHA1-HMAC Password-based Key Derivaion Function 2
if not set to 0 to enable not-yet-implemented/fallback crysprFallback.km_pbkdf2 method
and provide the sha1_msg_digest method.
*/
#define CRYSPR_HAS_PBKDF2 1
// mbedtls uses in the enc/dec functions 16-byte blocks
// for xcryption. This is not marked by any constant. See
// e.g. <mbedtls/aes.h>, mbedtls_aes_crypt_ecb signature.
#if CRYSPR_AESBLKSZ != 16
#error mbedtls requires AES single block size 16 bytes, implicitly.
#endif
/*
#define CRYSPR_AESCTX to the CRYSPR specifix AES key context object.
This type reserves room in the CRYPSPR control block for Haicrypt KEK and SEK
It is set from hte keystring through CRYSPR_methods.aes_set_key and passed
to CRYSPR_methods.aes_XXX.
*/
typedef struct mbedtls_aes_context CRYSPR_AESCTX; /* CRYpto Service PRovider AES key context */
struct tag_CRYSPR_methods *crysprMbedtls(void);
#endif /* CRYSPR_GNUTLS_H */

View file

@ -0,0 +1,218 @@
/*
* SRT - Secure, Reliable, Transport
* Copyright (c) 2019 Haivision Systems Inc.
*
* This Source Code Form is subject to the terms of the Mozilla Public
* License, v. 2.0. If a copy of the MPL was not distributed with this
* file, You can obtain one at http://mozilla.org/MPL/2.0/.
*
*/
/*****************************************************************************
written by
Haivision Systems Inc.
2019-06-26 (jdube)
OpenSSL CRYSPR/4SRT (CRYypto Service PRovider for SRT).
*****************************************************************************/
#include "hcrypt.h"
#include <string.h>
typedef struct tag_crysprOpenSSL_AES_cb {
CRYSPR_cb ccb;
/* Add cryptolib specific data here */
} crysprOpenSSL_cb;
int crysprOpenSSL_Prng(unsigned char *rn, int len)
{
return(RAND_bytes(rn, len) <= 0 ? -1 : 0);
}
int crysprOpenSSL_AES_SetKey(
bool bEncrypt, /* true Enxcrypt key, false: decrypt */
const unsigned char *kstr, /* key sttring*/
size_t kstr_len, /* kstr len in bytes (16, 24, or 32 bytes (for AES128,AES192, or AES256) */
CRYSPR_AESCTX *aes_key) /* CRYpto Service PRovider AES Key context */
{
if (bEncrypt) { /* Encrypt key */
if (AES_set_encrypt_key(kstr, kstr_len * 8, aes_key)) {
HCRYPT_LOG(LOG_ERR, "%s", "AES_set_encrypt_key(kek) failed\n");
return(-1);
}
} else { /* Decrypt key */
if (AES_set_decrypt_key(kstr, kstr_len * 8, aes_key)) {
HCRYPT_LOG(LOG_ERR, "%s", "AES_set_decrypt_key(kek) failed\n");
return(-1);
}
}
return(0);
}
#if !(CRYSPR_HAS_AESCTR && CRYSPR_HAS_AESKWRAP)
int crysprOpenSSL_AES_EcbCipher(
bool bEncrypt, /* true:encrypt, false:decrypt */
CRYSPR_AESCTX *aes_key, /* CRYpto Service PRovider AES Key context */
const unsigned char *indata,/* src (clear text if encrypt, cipher text otherwise)*/
size_t inlen, /* indata length */
unsigned char *out_txt, /* dst (cipher text if encrypt, clear text otherwise) */
size_t *outlen) /* in/out dst len */
{
int nblk = inlen/CRYSPR_AESBLKSZ;
int nmore = inlen%CRYSPR_AESBLKSZ;
size_t outsiz = (outlen ? *outlen : 0);
int i;
if (outsiz % CRYSPR_AESBLKSZ) return(-1); /* output buf size must be a multiple of AES block size (16) */
if (bEncrypt) {
if (outsiz > 16 && outsiz < (nblk+nmore)*CRYSPR_AESBLKSZ) return(-1); /* output buf size must have room for PKCS7 padding */
/* Encrypt packet payload, block by block, in output buffer */
for (i=0; i<nblk; i++){
AES_ecb_encrypt(&indata[(i*CRYSPR_AESBLKSZ)],
&out_txt[(i*CRYSPR_AESBLKSZ)], aes_key, AES_ENCRYPT);
}
/* Encrypt last incomplete block */
if (0 < nmore) {
unsigned char intxt[CRYSPR_AESBLKSZ];
/* PKCS7 padding: padding value is number of bytes padded */
memcpy(intxt, &indata[(nblk*CRYSPR_AESBLKSZ)], nmore);
memset(intxt+nmore, CRYSPR_AESBLKSZ-nmore, CRYSPR_AESBLKSZ-nmore);
AES_ecb_encrypt(intxt, &out_txt[(nblk*CRYSPR_AESBLKSZ)], aes_key, AES_ENCRYPT);
nblk++;
}
if (outlen != NULL) *outlen = nblk*CRYSPR_AESBLKSZ;
} else { /* Decrypt */
for (i=0; i<nblk; i++){
AES_ecb_encrypt(&indata[(i*CRYSPR_AESBLKSZ)],
&out_txt[(i*CRYSPR_AESBLKSZ)], aes_key, AES_DECRYPT);
}
/* Encrypt last incomplete block */
if (0 < nmore) {
//shall not happens in decrypt
}
if (outlen != NULL) *outlen = nblk*CRYSPR_AESBLKSZ;
}
return 0;
}
#endif /* !(CRYSPR_HAS_AESCTR && CRYSPR_HAS_AESKWRAP) */
int crysprOpenSSL_AES_CtrCipher(
bool bEncrypt, /* true:encrypt, false:decrypt */
CRYSPR_AESCTX *aes_key, /* CRYpto Service PRovider AES Key context */
unsigned char *iv, /* iv */
const unsigned char *indata,/* src */
size_t inlen, /* length */
unsigned char *out_txt) /* dest */
{
unsigned char ctr[CRYSPR_AESBLKSZ];
unsigned blk_ofs = 0;
(void)bEncrypt; /* CTR mode encrypt for both encryption and decryption */
memset(&ctr[0], 0, sizeof(ctr));
#if (OPENSSL_VERSION_NUMBER >= 0x10100000L && !defined(OPENSSL_IS_BORINGSSL))
CRYPTO_ctr128_encrypt(indata, out_txt,
inlen, aes_key, iv, ctr, &blk_ofs, (block128_f) AES_encrypt);
#else
AES_ctr128_encrypt(indata, out_txt,
inlen, aes_key, iv, ctr, &blk_ofs);
#endif
return 0;
}
/*
* Password-based Key Derivation Function
*/
int crysprOpenSSL_KmPbkdf2(
CRYSPR_cb *cryspr_cb,
char *passwd, /* passphrase */
size_t passwd_len, /* passphrase len */
unsigned char *salt, /* salt */
size_t salt_len, /* salt_len */
int itr, /* iterations */
size_t key_len, /* key_len */
unsigned char *out) /* derived key */
{
(void)cryspr_cb;
int rc = PKCS5_PBKDF2_HMAC_SHA1(passwd,passwd_len,salt,salt_len,itr,key_len,out);
return(rc == 1? 0 : -1);
}
#if CRYSPR_HAS_AESKWRAP
int crysprOpenSSL_KmWrap(CRYSPR_cb *cryspr_cb,
unsigned char *wrap,
const unsigned char *sek,
unsigned int seklen)
{
crysprOpenSSL_cb *aes_data = (crysprOpenSSL_cb *)cryspr_cb;
AES_KEY *kek = &aes_data->ccb.aes_kek; //key encrypting key
return(((seklen + HAICRYPT_WRAPKEY_SIGN_SZ) == (unsigned int)AES_wrap_key(kek, NULL, wrap, sek, seklen)) ? 0 : -1);
}
int crysprOpenSSL_KmUnwrap(
CRYSPR_cb *cryspr_cb,
unsigned char *sek, //Stream encrypting key
const unsigned char *wrap,
unsigned int wraplen)
{
crysprOpenSSL_cb *aes_data = (crysprOpenSSL_cb *)cryspr_cb;
AES_KEY *kek = &aes_data->ccb.aes_kek; //key encrypting key
return(((wraplen - HAICRYPT_WRAPKEY_SIGN_SZ) == (unsigned int)AES_unwrap_key(kek, NULL, sek, wrap, wraplen)) ? 0 : -1);
}
#endif /*CRYSPR_HAS_AESKWRAP*/
static CRYSPR_methods crysprOpenSSL_methods;
CRYSPR_methods *crysprOpenSSL(void)
{
if(NULL == crysprOpenSSL_methods.open) {
crysprInit(&crysprOpenSSL_methods); //Default/fallback methods
crysprOpenSSL_methods.prng = crysprOpenSSL_Prng;
//--CryptoLib Primitive API-----------------------------------------------
crysprOpenSSL_methods.aes_set_key = crysprOpenSSL_AES_SetKey;
#if CRYSPR_HAS_AESCTR
crysprOpenSSL_methods.aes_ctr_cipher = crysprOpenSSL_AES_CtrCipher;
#endif
#if !(CRYSPR_HAS_AESCTR && CRYSPR_HAS_AESKWRAP)
/* AES-ECB only required if cryspr has no AES-CTR and no AES KeyWrap */
/* OpenSSL has both AESCTR and AESKWRP and the AESECB wrapper is only used
to test the falback methods */
crysprOpenSSL_methods.aes_ecb_cipher = crysprOpenSSL_AES_EcbCipher;
#endif
#if !CRYSPR_HAS_PBKDF2
crysprOpenSSL_methods.sha1_msg_digest= NULL; //Required to use eventual default/fallback KmPbkdf2
#endif
//--Crypto Session API-----------------------------------------
// crysprOpenSSL_methods.open =
// crysprOpenSSL_methods.close =
//--Keying material (km) encryption
#if CRYSPR_HAS_PBKDF2
crysprOpenSSL_methods.km_pbkdf2 = crysprOpenSSL_KmPbkdf2;
#else
#error There is no default/fallback method for PBKDF2
#endif
// crysprOpenSSL_methods.km_setkey =
#if CRYSPR_HAS_AESKWRAP
crysprOpenSSL_methods.km_wrap = crysprOpenSSL_KmWrap;
crysprOpenSSL_methods.km_unwrap = crysprOpenSSL_KmUnwrap;
#endif
//--Media stream (ms) encryption
// crysprOpenSSL_methods.ms_setkey =
// crysprOpenSSL_methods.ms_encrypt =
// crysprOpenSSL_methods.ms_decrypt =
}
return(&crysprOpenSSL_methods);
}

View file

@ -0,0 +1,65 @@
/*
* SRT - Secure, Reliable, Transport
* Copyright (c) 2019 Haivision Systems Inc.
*
* This Source Code Form is subject to the terms of the Mozilla Public
* License, v. 2.0. If a copy of the MPL was not distributed with this
* file, You can obtain one at http://mozilla.org/MPL/2.0/.
*
*/
/*****************************************************************************
written by
Haivision Systems Inc.
2019-06-26 (jdube)
OpenSSL Direct AES CRYSPR/4SRT (CRYypto Service PRovider for SRT).
*****************************************************************************/
#ifndef CRYSPR_OPENSSL_H
#define CRYSPR_OPENSSL_H
#include <openssl/evp.h> /* PKCS5_xxx() */
#include <openssl/aes.h> /* AES_xxx() */
#if (OPENSSL_VERSION_NUMBER >= 0x10100000L && !defined(OPENSSL_IS_BORINGSSL))
# include <openssl/modes.h> /* CRYPTO_xxx() */
#endif
#include <openssl/rand.h>
#include <openssl/err.h>
#include <openssl/opensslv.h> /* OPENSSL_VERSION_NUMBER */
/* Define CRYSPR_HAS_AESCTR to 1 if this CRYSPR has AESCTR cipher mode
if not set it 0 to use enable CTR cipher mode implementation using ECB cipher mode
and provide the aes_ecb_cipher method.
*/
#define CRYSPR_HAS_AESCTR 1
/* Define CRYSPR_HAS_AESKWRAP to 1 if this CRYSPR has AES Key Wrap
if not set to 0 to enable default/fallback crysprFallback_AES_WrapKey/crysprFallback_AES_UnwrapKey methods
and provide the aes_ecb_cipher method .
*/
#if (OPENSSL_VERSION_NUMBER < 0x0090808fL) //0.9.8h
#define CRYSPR_HAS_AESKWRAP 0
#else
#define CRYSPR_HAS_AESKWRAP 1
#endif
/* Define CRYSPR_HAS_PBKDF2 to 1 if this CRYSPR has SHA1-HMAC Password-based Key Derivaion Function 2
if not set to 0 to enable not-yet-implemented/fallback crysprFallback.km_pbkdf2 method
and provide the sha1_msg_digest method.
*/
#define CRYSPR_HAS_PBKDF2 1 /* Define to 1 if CRYSPR has Password-based Key Derivaion Function 2 */
/*
#define CRYSPR_AESCTX to the CRYSPR specifix AES key context object.
This type reserves room in the CRYPSPR control block for Haicrypt KEK and SEK
It is set from hte keystring through CRYSPR_methods.aes_set_key and passed
to CRYSPR_methods.aes_*.
*/
typedef AES_KEY CRYSPR_AESCTX; /* CRYpto Service PRovider AES key context */
struct tag_CRYSPR_methods *crysprOpenSSL(void);
#endif /* CRYSPR_OPENSSL_H */

View file

@ -0,0 +1,709 @@
/*
* SRT - Secure, Reliable, Transport
* Copyright (c) 2019 Haivision Systems Inc.
*
* This Source Code Form is subject to the terms of the Mozilla Public
* License, v. 2.0. If a copy of the MPL was not distributed with this
* file, You can obtain one at http://mozilla.org/MPL/2.0/.
*
*/
/*****************************************************************************
written by
Haivision Systems Inc.
2019-06-28 (jdube)
CRYSPR/4SRT Initial implementation.
*****************************************************************************/
#include "hcrypt.h"
#include "cryspr.h"
#include <stdlib.h>
#include <string.h>
int crysprStub_Prng(unsigned char *rn, int len)
{
(void)rn;
(void)len;
return(0);
}
int crysprStub_AES_SetKey(
bool bEncrypt, /* true Enxcrypt key, false: decrypt */
const unsigned char *kstr, /* key sttring*/
size_t kstr_len, /* kstr len in bytes (16, 24, or 32 bytes (for AES128,AES192, or AES256) */
CRYSPR_AESCTX *aes_key) /* Cryptolib Specific AES key context */
{
(void)bEncrypt;
(void)kstr;
(void)kstr_len;
(void)aes_key;
return(0);
}
int crysprStub_AES_EcbCipher(
bool bEncrypt, /* true:encrypt, false:decrypt */
CRYSPR_AESCTX *aes_key, /* AES context */
const unsigned char *indata,/* src (clear text)*/
size_t inlen, /* length */
unsigned char *out_txt, /* dst (cipher text) */
size_t *outlen) /* dst len */
{
(void)bEncrypt;
(void)aes_key;
(void)indata;
(void)inlen;
(void)out_txt;
(void)outlen;
return -1;
}
int crysprStub_AES_CtrCipher(
bool bEncrypt, /* true:encrypt, false:decrypt */
CRYSPR_AESCTX *aes_key, /* AES context */
unsigned char *iv, /* iv */
const unsigned char *indata,/* src */
size_t inlen, /* length */
unsigned char *out_txt) /* dest */
{
(void)bEncrypt;
(void)aes_key;
(void)iv;
(void)indata;
(void)inlen;
(void)out_txt;
return(-1);
}
unsigned char *crysprStub_SHA1_MsgDigest(
const unsigned char *m, /* in: message */
size_t m_len, /* message length */
unsigned char *md) /* out: message digest buffer *160 bytes */
{
(void)m;
(void)m_len;
(void)md;
return(NULL);//return md;
}
/*
* Password-based Key Derivation Function
*/
int crysprStub_KmPbkdf2(
CRYSPR_cb *cryspr_cb,
char *passwd, /* passphrase */
size_t passwd_len, /* passphrase len */
unsigned char *salt, /* salt */
size_t salt_len, /* salt_len */
int itr, /* iterations */
size_t key_len, /* key_len */
unsigned char *out) /* derived key */
{
(void)cryspr_cb;
(void)passwd;
(void)passwd_len;
(void)salt;
(void)salt_len;
(void)itr;
(void)key_len;
(void)out;
/* >>Todo:
* develop PBKDF2 using SHA1 primitive cryspr_cb->cryspr->sha1_msg_digest() for cryptolibs not providing it
*/
return(-1);
}
static int crysprFallback_KmSetKey(CRYSPR_cb *cryspr_cb, bool bWrap, const unsigned char *kek, size_t kek_len)
{
CRYSPR_AESCTX *aes_kek = &cryspr_cb->aes_kek;
if (cryspr_cb->cryspr->aes_set_key(bWrap, kek, kek_len, aes_kek)) {
HCRYPT_LOG(LOG_ERR, "AES_set_%s_key(kek) failed\n", bWrap? "encrypt": "decrypt");
return(-1);
}
return(0);
}
/*
* AES_wrap_key()/AES_unwrap_key() introduced in openssl 0.9.8h
* Here is an implementation using AES native API for cryspr not providing it.
*/
static const unsigned char default_iv[] = {
0xA6, 0xA6, 0xA6, 0xA6, 0xA6, 0xA6, 0xA6, 0xA6,
};
int crysprFallback_AES_WrapKey(CRYSPR_cb *cryspr_cb,
unsigned char *out,
const unsigned char *in,
unsigned int inlen)
{
unsigned char *A, B[16], *R;
const unsigned char *iv = default_iv;
unsigned int i, j, t;
if ((inlen & 0x7) || (inlen < 8))
return -1;
A = B;
t = 1;
memcpy(out + 8, in, inlen);
memcpy(A, iv, 8);
for (j = 0; j < 6; j++)
{
R = out + 8;
for (i = 0; i < inlen; i += 8, t++, R += 8)
{
memcpy(B + 8, R, 8);
{
size_t outlen = 16;
cryspr_cb->cryspr->aes_ecb_cipher(true, &cryspr_cb->aes_kek, B, 16, B, &outlen);
}
A[7] ^= (unsigned char)(t & 0xff);
if (t > 0xff)
{
A[6] ^= (unsigned char)((t >> 8) & 0xff);
A[5] ^= (unsigned char)((t >> 16) & 0xff);
A[4] ^= (unsigned char)((t >> 24) & 0xff);
}
memcpy(R, B + 8, 8);
}
}
memcpy(out, A, 8);
return 0;
}
int crysprFallback_AES_UnwrapKey(CRYSPR_cb *cryspr_cb,
unsigned char *out,
const unsigned char *in,
unsigned int inlen)
{
unsigned char *A, B[16], *R;
const unsigned char *iv = default_iv;
unsigned int i, j, t;
inlen -= 8;
if (inlen & 0x7)
return -1;
if (inlen < 8)
return -1;
A = B;
t = 6 * (inlen >> 3);
memcpy(A, in, 8);
memcpy(out, in + 8, inlen);
for (j = 0; j < 6; j++)
{
R = out + inlen - 8;
for (i = 0; i < inlen; i += 8, t--, R -= 8)
{
A[7] ^= (unsigned char)(t & 0xff);
if (t > 0xff)
{
A[6] ^= (unsigned char)((t >> 8) & 0xff);
A[5] ^= (unsigned char)((t >> 16) & 0xff);
A[4] ^= (unsigned char)((t >> 24) & 0xff);
}
memcpy(B + 8, R, 8);
{
size_t outlen = 16;
cryspr_cb->cryspr->aes_ecb_cipher(false, &cryspr_cb->aes_kek, B, 16, B, &outlen);
}
memcpy(R, B + 8, 8);
}
}
if (memcmp(A, iv, 8))
{
memset(out, 0, inlen);
return -1;
}
return 0;
}
static unsigned char *_crysprFallback_GetOutbuf(CRYSPR_cb *cryspr_cb, size_t pfx_len, size_t out_len)
{
unsigned char *out_buf;
if ((pfx_len + out_len) > (cryspr_cb->outbuf_siz - cryspr_cb->outbuf_ofs)) {
/* Not enough room left, circle buffers */
cryspr_cb->outbuf_ofs = 0;
}
out_buf = &cryspr_cb->outbuf[cryspr_cb->outbuf_ofs];
cryspr_cb->outbuf_ofs += (pfx_len + out_len);
return(out_buf);
}
static CRYSPR_cb *crysprFallback_Open(CRYSPR_methods *cryspr, size_t max_len)
{
CRYSPR_cb *cryspr_cb;
unsigned char *membuf;
size_t memsiz, padded_len = hcryptMsg_PaddedLen(max_len, 128/8);
HCRYPT_LOG(LOG_DEBUG, "%s", "Using OpenSSL AES\n");
memsiz = sizeof(*cryspr_cb) + (CRYSPR_OUTMSGMAX * padded_len);
#if !CRYSPR_HAS_AESCTR
memsiz += HCRYPT_CTR_STREAM_SZ;
#endif /* !CRYSPR_HAS_AESCTR */
cryspr_cb = malloc(memsiz);
if (NULL == cryspr_cb) {
HCRYPT_LOG(LOG_ERR, "malloc(%zd) failed\n", memsiz);
return(NULL);
}
membuf = (unsigned char *)cryspr_cb;
membuf += sizeof(*cryspr_cb);
#if !CRYSPR_HAS_AESCTR
cryspr_cb->ctr_stream = membuf;
membuf += HCRYPT_CTR_STREAM_SZ;
cryspr_cb->ctr_stream_siz = HCRYPT_CTR_STREAM_SZ;
cryspr_cb->ctr_stream_len = 0;
#endif /* !CRYSPR_HAS_AESCTR */
cryspr_cb->outbuf = membuf;
cryspr_cb->outbuf_siz = CRYSPR_OUTMSGMAX * padded_len;
cryspr_cb->outbuf_ofs = 0;
// membuf += cryspr_cb->outbuf_siz;
cryspr_cb->cryspr=(CRYSPR_methods *)cryspr;
return(cryspr_cb);
}
static int crysprFallback_Close(CRYSPR_cb *cryspr_cb)
{
if (NULL != cryspr_cb) {
free(cryspr_cb);
}
return(0);
}
static int crysprFallback_MsSetKey(CRYSPR_cb *cryspr_cb, hcrypt_Ctx *ctx, const unsigned char *key, size_t key_len)
{
CRYSPR_AESCTX *aes_sek = &cryspr_cb->aes_sek[hcryptCtx_GetKeyIndex(ctx)]; /* Ctx tells if it's for odd or even key */
if ((ctx->flags & HCRYPT_CTX_F_ENCRYPT) /* Encrypt key */
|| (ctx->mode == HCRYPT_CTX_MODE_AESCTR)) { /* CTR mode decrypts using encryption methods */
if (cryspr_cb->cryspr->aes_set_key(true, key, key_len, aes_sek)) {
HCRYPT_LOG(LOG_ERR, "%s", "CRYSPR->set_encrypt_key(sek) failed\n");
return(-1);
}
} else { /* Decrypt key */
if (cryspr_cb->cryspr->aes_set_key(false, key, key_len, aes_sek)) {
HCRYPT_LOG(LOG_ERR, "%s", "CRYSPR->set_decrypt_key(sek) failed\n");
return(-1);
}
}
return(0);
}
#if !CRYSPR_HAS_AESCTR
static int _crysprFallback_AES_SetCtrStream(CRYSPR_cb *cryspr_cb, hcrypt_Ctx *ctx, size_t len, unsigned char *iv)
{
/* Counter stream:
* 0 1 2 3 4 5 nblk
* +---+---+---+---+---+---+---+---+
* |blk|blk|blk|blk|blk|blk|...|blk|
* +---+---+---+---+---+---+---+---+
*/
/* IV (128-bit):
* 0 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15
* +---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+
* | 0s | pki | ctr |
* +---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+
* XOR
* +---+---+---+---+---+---+---+---+---+---+---+---+---+---+
* | nonce +
* +---+---+---+---+---+---+---+---+---+---+---+---+---+---+
*
* pki (32-bit): packet index
* ctr (16-bit): block counter
* nonce (112-bit): number used once (salt)
*/
unsigned char ctr[HCRYPT_CTR_BLK_SZ];
unsigned nblk;
ASSERT(NULL != cryspr_cb);
ASSERT(NULL != ctx);
memcpy(ctr, iv, HCRYPT_CTR_BLK_SZ);
nblk = (len + (HCRYPT_CTR_BLK_SZ-1))/HCRYPT_CTR_BLK_SZ;
if ((nblk * HCRYPT_CTR_BLK_SZ) <= cryspr_cb->ctr_stream_siz) {
unsigned blk;
unsigned char *csp = &cryspr_cb->ctr_stream[0];
for(blk = 0; blk < nblk; blk++) {
memcpy(csp, ctr, HCRYPT_CTR_BLK_SZ);
csp += HCRYPT_CTR_BLK_SZ;
if (0 == ++(ctr[HCRYPT_CTR_BLK_SZ-1])) ++(ctr[HCRYPT_CTR_BLK_SZ-2]);
}
cryspr_cb->ctr_stream_len = nblk * HCRYPT_CTR_BLK_SZ;
} else {
HCRYPT_LOG(LOG_ERR, "packet too long(%zd)\n", len);
return(-1);
}
return(0);
}
#endif
static int crysprFallback_MsEncrypt(
CRYSPR_cb *cryspr_cb,
hcrypt_Ctx *ctx,
hcrypt_DataDesc *in_data, int nbin ATR_UNUSED,
void *out_p[], size_t out_len_p[], int *nbout_p)
{
unsigned char *out_msg;
size_t out_len = 0; //payload size
int pfx_len;
ASSERT(NULL != ctx);
ASSERT(NULL != cryspr_cb);
ASSERT((NULL != in_data) || (1 == nbin)); //Only one in_data[] supported
/*
* Get message prefix length
* to reserve room for unencrypted message header in output buffer
*/
pfx_len = ctx->msg_info->pfx_len;
/* Get buffer room from the internal circular output buffer */
out_msg = _crysprFallback_GetOutbuf(cryspr_cb, pfx_len, in_data[0].len);
if (NULL != out_msg) {
switch(ctx->mode) {
case HCRYPT_CTX_MODE_AESCTR: /* Counter mode */
{
#if CRYSPR_HAS_AESCTR
/* Get current key (odd|even) from context */
CRYSPR_AESCTX *aes_key = &cryspr_cb->aes_sek[hcryptCtx_GetKeyIndex(ctx)];
unsigned char iv[CRYSPR_AESBLKSZ];
/* Get input packet index (in network order) */
hcrypt_Pki pki = hcryptMsg_GetPki(ctx->msg_info, in_data[0].pfx, 1);
/*
* Compute the Initial Vector
* IV (128-bit):
* 0 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15
* +---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+
* | 0s | pki | ctr |
* +---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+
* XOR
* +---+---+---+---+---+---+---+---+---+---+---+---+---+---+
* | nonce +
* +---+---+---+---+---+---+---+---+---+---+---+---+---+---+
*
* pki (32-bit): packet index
* ctr (16-bit): block counter
* nonce (112-bit): number used once (salt)
*/
hcrypt_SetCtrIV((unsigned char *)&pki, ctx->salt, iv);
cryspr_cb->cryspr->aes_ctr_cipher(true, aes_key, iv, in_data[0].payload, in_data[0].len,
&out_msg[pfx_len]);
#else /*CRYSPR_HAS_AESCTR*/
/* Get current key (odd|even) from context */
CRYSPR_AESCTX *aes_key = &cryspr_cb->aes_sek[hcryptCtx_GetKeyIndex(ctx)];
unsigned char iv[CRYSPR_AESBLKSZ];
int iret = 0;
/* Get input packet index (in network order) */
hcrypt_Pki pki = hcryptMsg_GetPki(ctx->msg_info, in_data[0].pfx, 1);
/*
* Compute the Initial Vector
* IV (128-bit):
* 0 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15
* +---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+
* | 0s | pki | ctr |
* +---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+
* XOR
* +---+---+---+---+---+---+---+---+---+---+---+---+---+---+
* | nonce +
* +---+---+---+---+---+---+---+---+---+---+---+---+---+---+
*
* pki (32-bit): packet index
* ctr (16-bit): block counter
* nonce (112-bit): number used once (salt)
*/
hcrypt_SetCtrIV((unsigned char *)&pki, ctx->salt, iv);
/* Create CtrStream. May be longer than in_len (next cryspr block size boundary) */
iret = _crysprFallback_AES_SetCtrStream(cryspr_cb, ctx, in_data[0].len, iv);
if (iret) {
return(iret);
}
/* Reserve output buffer for cryspr */
out_msg = _crysprFallback_GetOutbuf(cryspr_cb, pfx_len, cryspr_cb->ctr_stream_len);
/* Create KeyStream (encrypt CtrStream) */
iret = cryspr_cb->cryspr->aes_ecb_cipher(true, aes_key,
cryspr_cb->ctr_stream, cryspr_cb->ctr_stream_len,
&out_msg[pfx_len], &out_len);
if (iret) {
HCRYPT_LOG(LOG_ERR, "%s", "hcOpenSSL_AES_ecb_cipher(encrypt, failed\n");
return(iret);
}
#endif/*CRYSPR_HAS_AESCTR*/
/* Prepend packet prefix (clear text) in output buffer */
memcpy(out_msg, in_data[0].pfx, pfx_len);
/* CTR mode output length is same as input, no padding */
out_len = in_data[0].len;
break;
}
case HCRYPT_CTX_MODE_CLRTXT: /* Clear text mode (transparent mode for tests) */
memcpy(&out_msg[pfx_len], in_data[0].payload, in_data[0].len);
memcpy(out_msg, in_data[0].pfx, pfx_len);
out_len = in_data[0].len;
break;
default:
/* Unsupported cipher mode */
return(-1);
}
} else {
/* input data too big */
return(-1);
}
if (out_len > 0) {
/* Encrypted messages have been produced */
if (NULL == out_p) {
/*
* Application did not provided output buffer,
* so copy encrypted message back in input buffer
*/
memcpy(in_data[0].pfx, out_msg, pfx_len);
#if !CRYSPR_HAS_AESCTR
if (ctx->mode == HCRYPT_CTX_MODE_AESCTR) {
/* XOR KeyStream with input text directly in input buffer */
hcrypt_XorStream(in_data[0].payload, &out_msg[pfx_len], out_len);
}else{
/* Copy output data back in input buffer */
memcpy(in_data[0].payload, &out_msg[pfx_len], out_len);
}
#else /* CRYSPR_HAS_AESCTR */
/* Copy output data back in input buffer */
memcpy(in_data[0].payload, &out_msg[pfx_len], out_len);
#endif /* CRYSPR_HAS_AESCTR */
} else {
/* Copy header in output buffer if needed */
if (pfx_len > 0) memcpy(out_msg, in_data[0].pfx, pfx_len);
#if !CRYSPR_HAS_AESCTR
if (ctx->mode == HCRYPT_CTX_MODE_AESCTR) {
hcrypt_XorStream(&out_msg[pfx_len], in_data[0].payload, out_len);
}
#endif /* CRYSPR_HAS_AESCTR */
out_p[0] = out_msg;
out_len_p[0] = pfx_len + out_len;
*nbout_p = 1;
}
} else {
/*
* Nothing out
* This is not an error for implementations using deferred/async processing
* with co-processor, DSP, crypto hardware, etc.
* Submitted input data could be returned encrypted in a next call.
*/
if (nbout_p != NULL) *nbout_p = 0;
return(-1);
}
return(0);
}
static int crysprFallback_MsDecrypt(CRYSPR_cb *cryspr_cb, hcrypt_Ctx *ctx,
hcrypt_DataDesc *in_data, int nbin ATR_UNUSED, void *out_p[], size_t out_len_p[], int *nbout_p)
{
unsigned char *out_txt;
size_t out_len;
int iret = 0;
ASSERT(NULL != cryspr_cb);
ASSERT(NULL != ctx);
ASSERT((NULL != in_data) || (1 == nbin)); //Only one in_data[] supported
/* Reserve output buffer (w/no header) */
out_txt = _crysprFallback_GetOutbuf(cryspr_cb, 0, in_data[0].len);
if (NULL != out_txt) {
switch(ctx->mode) {
case HCRYPT_CTX_MODE_AESCTR:
{
#if CRYSPR_HAS_AESCTR
/* Get current key (odd|even) from context */
CRYSPR_AESCTX *aes_key = &cryspr_cb->aes_sek[hcryptCtx_GetKeyIndex(ctx)];
unsigned char iv[CRYSPR_AESBLKSZ];
/* Get input packet index (in network order) */
hcrypt_Pki pki = hcryptMsg_GetPki(ctx->msg_info, in_data[0].pfx, 1);
/*
* Compute the Initial Vector
* IV (128-bit):
* 0 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15
* +---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+
* | 0s | pki | ctr |
* +---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+
* XOR
* +---+---+---+---+---+---+---+---+---+---+---+---+---+---+
* | nonce +
* +---+---+---+---+---+---+---+---+---+---+---+---+---+---+
*
* pki (32-bit): packet index
* ctr (16-bit): block counter
* nonce (112-bit): number used once (salt)
*/
hcrypt_SetCtrIV((unsigned char *)&pki, ctx->salt, iv);
cryspr_cb->cryspr->aes_ctr_cipher(false, aes_key, iv, in_data[0].payload, in_data[0].len,
out_txt);
out_len = in_data[0].len;
#else /*CRYSPR_HAS_AESCTR*/
/* Get current key (odd|even) from context */
CRYSPR_AESCTX *aes_key = &cryspr_cb->aes_sek[hcryptCtx_GetKeyIndex(ctx)];
unsigned char iv[CRYSPR_AESBLKSZ];
int iret = 0;
/* Get input packet index (in network order) */
hcrypt_Pki pki = hcryptMsg_GetPki(ctx->msg_info, in_data[0].pfx, 1);
/*
* Compute the Initial Vector
* IV (128-bit):
* 0 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15
* +---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+
* | 0s | pki | ctr |
* +---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+
* XOR
* +---+---+---+---+---+---+---+---+---+---+---+---+---+---+
* | nonce +
* +---+---+---+---+---+---+---+---+---+---+---+---+---+---+
*
* pki (32-bit): packet index
* ctr (16-bit): block counter
* nonce (112-bit): number used once (salt)
*/
hcrypt_SetCtrIV((unsigned char *)&pki, ctx->salt, iv);
/* Create CtrStream. May be longer than in_len (next cipher block size boundary) */
iret = _crysprFallback_AES_SetCtrStream(cryspr_cb, ctx, in_data[0].len, iv);
if (iret) {
return(iret);
}
/* Reserve output buffer for cryspr */
out_txt = _crysprFallback_GetOutbuf(cryspr_cb, 0, cryspr_cb->ctr_stream_len);
/* Create KeyStream (encrypt CtrStream) */
iret = cryspr_cb->cryspr->aes_ecb_cipher(true, aes_key,
cryspr_cb->ctr_stream, cryspr_cb->ctr_stream_len,
out_txt, &out_len);
if (iret) {
HCRYPT_LOG(LOG_ERR, "%s", "crysprNatural_AES_ecb_cipher(encrypt failed\n");
return(iret);
}
#endif /*CRYSPR_HAS_AESCTR*/
break;
}
case HCRYPT_CTX_MODE_CLRTXT:
memcpy(out_txt, in_data[0].payload, in_data[0].len);
out_len = in_data[0].len;
break;
default:
return(-1);
}
} else {
return(-1);
}
if (out_len > 0) {
if (NULL == out_p) {
/*
* Application did not provided output buffer,
* so copy encrypted message back in input buffer
*/
#if !CRYSPR_HAS_AESCTR
if (ctx->mode == HCRYPT_CTX_MODE_AESCTR) {
/* XOR KeyStream with input text directly in input buffer */
hcrypt_XorStream(in_data[0].payload, out_txt, out_len);
}else{
/* Copy output data back in input buffer */
memcpy(in_data[0].payload, out_txt, out_len);
}
#else /* CRYSPR_HAS_AESCTR */
/* Copy output data back in input buffer */
memcpy(in_data[0].payload, out_txt, out_len);
#endif /* CRYSPR_HAS_AESCTR */
} else {
/* Copy header in output buffer if needed */
#if !CRYSPR_HAS_AESCTR
if (ctx->mode == HCRYPT_CTX_MODE_AESCTR) {
hcrypt_XorStream(out_txt, in_data[0].payload, out_len);
}
#endif /* CRYSPR_HAS_AESCTR */
out_p[0] = out_txt;
out_len_p[0] = out_len;
*nbout_p = 1;
}
iret = 0;
} else {
if (NULL != nbout_p) *nbout_p = 0;
iret = -1;
}
#if 0
{ /* Debug decryption errors */
static int nberr = 0;
if (out_txt[0] != 0x47){
if ((++nberr == 1)
|| ((nberr > 500) && (0 == ((((unsigned char *)&MSmsg->pki)[2] & 0x0F)|((unsigned char *)&MSmsg->pki)[3])))) {
HCRYPT_LOG(LOG_DEBUG, "keyindex=%d\n", hcryptCtx_GetKeyIndex(ctx));
HCRYPT_PRINTKEY(ctx->sek, ctx->sek_len, "sek");
HCRYPT_PRINTKEY(ctx->salt, ctx->salt_len, "salt");
}
} else {
nberr = 0;
}
}
#endif
return(iret);
}
CRYSPR_methods *crysprInit(CRYSPR_methods *cryspr)
{
/* CryptoLib Primitive API */
cryspr->prng = crysprStub_Prng;
cryspr->aes_set_key = crysprStub_AES_SetKey;
cryspr->aes_ecb_cipher = crysprStub_AES_EcbCipher;
cryspr->aes_ctr_cipher = crysprStub_AES_CtrCipher;
cryspr->sha1_msg_digest = crysprStub_SHA1_MsgDigest;
/* Crypto Session API */
cryspr->open = crysprFallback_Open;
cryspr->close = crysprFallback_Close;
//Keying material (km) encryption
cryspr->km_pbkdf2 = crysprStub_KmPbkdf2;
cryspr->km_setkey = crysprFallback_KmSetKey;
cryspr->km_wrap = crysprFallback_AES_WrapKey;
cryspr->km_unwrap = crysprFallback_AES_UnwrapKey;
//Media stream (ms) encryption
cryspr->ms_setkey = crysprFallback_MsSetKey;
cryspr->ms_encrypt = crysprFallback_MsEncrypt;
cryspr->ms_decrypt = crysprFallback_MsDecrypt;
return(cryspr);
}
HaiCrypt_Cryspr HaiCryptCryspr_Get_Instance(void)
{
return((HaiCrypt_Cryspr)cryspr4SRT());
}

View file

@ -0,0 +1,203 @@
/*
* SRT - Secure, Reliable, Transport
* Copyright (c) 2019 Haivision Systems Inc.
*
* This Source Code Form is subject to the terms of the Mozilla Public
* License, v. 2.0. If a copy of the MPL was not distributed with this
* file, You can obtain one at http://mozilla.org/MPL/2.0/.
*
*/
/*****************************************************************************
written by
Haivision Systems Inc.
2019-06-28 (jdube)
CRYSPR/4SRT Initial implementation.
*****************************************************************************/
#ifndef CRYSPR_H
#define CRYSPR_H
#include <stdbool.h>
#include <sys/types.h>
#if !defined(HAISRT_VERSION_INT)
#include "haicrypt.h"
#include "hcrypt_msg.h"
#else
// Included by haisrt.h or similar
#include "haisrt/haicrypt.h"
#include "haisrt/hcrypt_msg.h"
#endif
#ifdef __cplusplus
extern "C" {
#endif
#include "cryspr-config.h"
typedef struct tag_CRYSPR_cb {
CRYSPR_AESCTX aes_kek; /* Key Encrypting Key (KEK) */
CRYSPR_AESCTX aes_sek[2]; /* even/odd Stream Encrypting Key (SEK) */
struct tag_CRYSPR_methods *cryspr;
#if !CRYSPR_HAS_AESCTR
/* Reserve room to build the counter stream ourself */
#define HCRYPT_CTR_BLK_SZ CRYSPR_AESBLKSZ
#define HCRYPT_CTR_STREAM_SZ 2048
unsigned char * ctr_stream;
size_t ctr_stream_len; /* Content size */
size_t ctr_stream_siz; /* Allocated length */
#endif /* !CRYSPR_HAS_AESCTR */
#define CRYSPR_OUTMSGMAX 6
uint8_t * outbuf; /* output circle buffer */
size_t outbuf_ofs; /* write offset in circle buffer */
size_t outbuf_siz; /* circle buffer size */
} CRYSPR_cb;
typedef struct tag_CRYSPR_methods {
/*
* prng:
* Pseudo-Random Number Generator
*/
int (*prng)(
unsigned char *rn, /* out: pseudo random number */
int rn_len);
int (*aes_set_key)(
bool bEncrypt, /* true Enxcrypt key, false: decrypt */
const unsigned char *kstr,/* key string*/
size_t kstr_len, /* kstr len in bytes (16, 24, or 32 bytes (for AES128,AES192, or AES256) */
CRYSPR_AESCTX *aeskey); /* Cryptolib Specific AES key context */
int (*aes_ecb_cipher)(
bool bEncrypt, /* true:encrypt false:decrypt */
CRYSPR_AESCTX *aes_key, /* ctx */
const unsigned char *indata, /* src (clear text)*/
size_t inlen, /* src length */
unsigned char *out_txt, /* dst (cipher text) */
size_t *outlen); /* dst length */
int (*aes_ctr_cipher)(
bool bEncrypt, /* true:encrypt false:decrypt (don't care with CTR) */
CRYSPR_AESCTX *aes_key, /* ctx */
unsigned char *iv, /* iv */
const unsigned char *indata, /* src (clear text) */
size_t inlen, /* src length */
unsigned char *out_txt);/* dest */
unsigned char *(*sha1_msg_digest)(
const unsigned char *m, /* in: message */
size_t m_len, /* message length */
unsigned char *md); /* out: message digest buffer *160 bytes */
/*
* open:
* Create a cipher instance
* Allocate output buffers
*/
CRYSPR_cb *(*open)(
struct tag_CRYSPR_methods *cryspr,
size_t max_len); /* Maximum packet length that will be encrypted/decrypted */
/*
* close:
* Release any cipher resources
*/
int (*close)(
CRYSPR_cb *cryspr_data); /* Cipher handle, internal data */
/*
* pbkdf2_hmac_sha1
* Password-based Key Derivation Function 2
*/
int (*km_pbkdf2)(
CRYSPR_cb *cryspr_cb, /* Cryspr Control Block */
char *passwd, /* passphrase */
size_t passwd_len, /* passphrase len */
unsigned char *salt, /* salt */
size_t salt_len, /* salt_len */
int itr, /* iterations */
size_t out_len, /* key_len */
unsigned char *out); /* derived key */
/*
* km_setkey:
* Set the Key Encypting Key for Wrap (Encryption) or UnWrap (Decryption).
* Context (ctx) tells if it's for Wrap or Unwrap
* A Context flags (ctx->flags) also tells if this is for wrap(encryption) or unwrap(decryption) context (HCRYPT_CTX_F_ENCRYPT)
*/
int (*km_setkey)(
CRYSPR_cb *cryspr_cb, /* Cryspr Control Block */
bool bWrap, /* True: Wrap KEK, False: Unwrap KEK */
const unsigned char *kek, size_t kek_len); /* KEK: Key Encrypting Key */
/*
* km_wrap:
* wrap media stream key
*/
int (*km_wrap)(CRYSPR_cb *cryspr_cb,
unsigned char *wrap,
const unsigned char *sek,
unsigned int seklen);
/*
* km_unwrap:
* wrap media stream key
*/
int (*km_unwrap)(CRYSPR_cb *cryspr_cb,
unsigned char *sek,
const unsigned char *wrap,
unsigned int wraplen);
/*
* setkey:
* Set the Odd or Even, Encryption or Decryption key.
* Context (ctx) tells if it's for Odd or Even key (hcryptCtx_GetKeyIndex(ctx))
* A Context flags (ctx->flags) also tells if this is an encryption or decryption context (HCRYPT_CTX_F_ENCRYPT)
*/
int (*ms_setkey)(
CRYSPR_cb *cryspr_cb, /* Cryspr Control Block */
hcrypt_Ctx *ctx, /* HaiCrypt Context (cipher, keys, Odd/Even, etc..) */
const unsigned char *key, size_t kwelen); /* New Key */
/*
* encrypt:
* Submit a list of nbin clear transport packets (hcrypt_DataDesc *in_data) to encryption
* returns *nbout encrypted data packets of length out_len_p[] into out_p[]
*
* If cipher implements deferred encryption (co-processor, async encryption),
* it may return no encrypted packets, or encrypted packets for clear text packets of a previous call.
*/
int (*ms_encrypt)(
CRYSPR_cb *cryspr_cb, /* Cryspr Control Block */
hcrypt_Ctx *ctx, /* HaiCrypt Context (cipher, keys, Odd/Even, etc..) */
hcrypt_DataDesc *in_data, int nbin, /* Clear text transport packets: header and payload */
void *out_p[], size_t out_len_p[], int *nbout); /* Encrypted packets */
/*
* decrypt:
* Submit a list of nbin encrypted transport packets (hcrypt_DataDesc *in_data) to decryption
* returns *nbout clear text data packets of length out_len_p[] into out_p[]
*
* If cipher implements deferred decryption (co-processor, async encryption),
* it may return no decrypted packets, or decrypted packets for encrypted packets of a previous call.
*/
int (*ms_decrypt)(
CRYSPR_cb *cryspr_cb, /* Cryspr Control Block */
hcrypt_Ctx *ctx, /* HaiCrypt Context (cipher, keys, Odd/Even, etc..) */
hcrypt_DataDesc *in_data, int nbin, /* Clear text transport packets: header and payload */
void *out_p[], size_t out_len_p[], int *nbout); /* Encrypted packets */
} CRYSPR_methods;
CRYSPR_methods *crysprInit(CRYSPR_methods *cryspr);
#ifdef __cplusplus
}
#endif
#endif /* CRYSPR_H */

View file

@ -0,0 +1,27 @@
# This file is currently reserved for future refactoring, when all headers
# are going to be moved here. This is the list of headers considered to be
# attached to the installation package. Once possible, please move the below
# header files from ../include back to this directory.
PUBLIC HEADERS
haicrypt.h
hcrypt_ctx.h
hcrypt_msg.h
PRIVATE HEADERS
hcrypt.h
cryspr.h
cryspr-gnutls.h
haicrypt_log.h
SOURCES
cryspr.c
cryspr-gnutls.c
hcrypt.c
hcrypt_ctx_rx.c
hcrypt_ctx_tx.c
hcrypt_rx.c
hcrypt_sa.c
hcrypt_tx.c
hcrypt_xpt_srt.c
hcrypt_xpt_sta.c
haicrypt_log.cpp

View file

@ -0,0 +1,25 @@
# HaiCrypt library contents
PUBLIC HEADERS
haicrypt.h
hcrypt_ctx.h
hcrypt_msg.h
PRIVATE HEADERS
hcrypt.h
cryspr.h
cryspr-mbedtls.h
haicrypt_log.h
SOURCES
cryspr.c
cryspr-mbedtls.c
hcrypt.c
hcrypt_ctx_rx.c
hcrypt_ctx_tx.c
hcrypt_rx.c
hcrypt_sa.c
hcrypt_tx.c
hcrypt_xpt_srt.c
hcrypt_xpt_sta.c
haicrypt_log.cpp

View file

@ -0,0 +1,25 @@
# HaiCrypt library contents
PUBLIC HEADERS
haicrypt.h
hcrypt_ctx.h
hcrypt_msg.h
PRIVATE HEADERS
hcrypt.h
cryspr.h
cryspr-openssl.h
haicrypt_log.h
SOURCES
cryspr.c
cryspr-openssl.c
hcrypt.c
hcrypt_ctx_rx.c
hcrypt_ctx_tx.c
hcrypt_rx.c
hcrypt_sa.c
hcrypt_tx.c
hcrypt_xpt_srt.c
hcrypt_xpt_sta.c
haicrypt_log.cpp

View file

@ -0,0 +1,138 @@
/*
* SRT - Secure, Reliable, Transport
* Copyright (c) 2018 Haivision Systems Inc.
*
* This Source Code Form is subject to the terms of the Mozilla Public
* License, v. 2.0. If a copy of the MPL was not distributed with this
* file, You can obtain one at http://mozilla.org/MPL/2.0/.
*
*/
/*****************************************************************************
written by
Haivision Systems Inc.
2011-06-23 (jdube)
HaiCrypt initial implementation.
2014-03-11 (jdube)
Adaptation for SRT.
*****************************************************************************/
#ifndef HAICRYPT_H
#define HAICRYPT_H
#include <sys/types.h>
#include <stdint.h>
#ifdef __cplusplus
extern "C" {
#endif
// setup exports
#if defined _WIN32 && !defined __MINGW__
#ifdef HAICRYPT_DYNAMIC
#ifdef HAICRYPT_EXPORTS
#define HAICRYPT_API __declspec(dllexport)
#else
#define HAICRYPT_API __declspec(dllimport)
#endif
#else
#define HAICRYPT_API
#endif
#else
#define HAICRYPT_API
#endif
typedef void *HaiCrypt_Cryspr;
HAICRYPT_API HaiCrypt_Cryspr HaiCryptCryspr_Get_Instance (void); /* Return a default cryspr instance */
#define HAICRYPT_CIPHER_BLK_SZ 16 /* AES Block Size */
#define HAICRYPT_PWD_MAX_SZ 80 /* MAX password (for Password-based Key Derivation) */
#define HAICRYPT_KEY_MAX_SZ 32 /* MAX key */
#define HAICRYPT_SECRET_MAX_SZ (HAICRYPT_PWD_MAX_SZ > HAICRYPT_KEY_MAX_SZ ? HAICRYPT_PWD_MAX_SZ : HAICRYPT_KEY_MAX_SZ)
#define HAICRYPT_SALT_SZ 16
#define HAICRYPT_WRAPKEY_SIGN_SZ 8 /* RFC3394 AES KeyWrap signature size */
#define HAICRYPT_PBKDF2_SALT_LEN 8 /* PKCS#5 PBKDF2 Password based key derivation salt length */
#define HAICRYPT_PBKDF2_ITER_CNT 2048 /* PKCS#5 PBKDF2 Password based key derivation iteration count */
#define HAICRYPT_TS_PKT_SZ 188 /* Transport Stream packet size */
typedef struct {
#define HAICRYPT_SECTYP_UNDEF 0
#define HAICRYPT_SECTYP_PRESHARED 1 /* Preshared KEK */
#define HAICRYPT_SECTYP_PASSPHRASE 2 /* Password */
unsigned typ;
size_t len;
unsigned char str[HAICRYPT_SECRET_MAX_SZ];
}HaiCrypt_Secret;
typedef struct {
#define HAICRYPT_CFG_F_TX 0x01 /* !TX -> RX */
#define HAICRYPT_CFG_F_CRYPTO 0x02 /* Perform crypto Tx:Encrypt Rx:Decrypt */
#define HAICRYPT_CFG_F_FEC 0x04 /* Do Forward Error Correction */
unsigned flags;
HaiCrypt_Secret secret; /* Security Association */
HaiCrypt_Cryspr cryspr; /* CRYSPR implementation */
#define HAICRYPT_DEF_KEY_LENGTH 16 /* default key length (bytes) */
size_t key_len; /* SEK length (bytes) */
#define HAICRYPT_DEF_DATA_MAX_LENGTH 1500 /* default packet data length (bytes) */
size_t data_max_len; /* Maximum data_len passed to HaiCrypt (bytes) */
#define HAICRYPT_XPT_STANDALONE 0
#define HAICRYPT_XPT_SRT 1
int xport;
#define HAICRYPT_DEF_KM_TX_PERIOD 1000 /* Keying Material Default Tx Period (msec) */
unsigned int km_tx_period_ms; /* Keying Material Tx period (msec) */
#define HAICRYPT_DEF_KM_REFRESH_RATE 0x1000000 /* Keying Material Default Refresh Rate (pkts) */
unsigned int km_refresh_rate_pkt; /* Keying Material Refresh Rate (pkts) */
#define HAICRYPT_DEF_KM_PRE_ANNOUNCE 0x1000 /* Keying Material Default Pre/Post Announce (pkts) */
unsigned int km_pre_announce_pkt; /* Keying Material Pre/Post Announce (pkts) */
}HaiCrypt_Cfg;
typedef enum HaiCrypt_CryptoDir { HAICRYPT_CRYPTO_DIR_RX, HAICRYPT_CRYPTO_DIR_TX } HaiCrypt_CryptoDir;
//typedef void *HaiCrypt_Handle;
// internally it will be correctly interpreted,
// for the outsider it's just some kinda incomplete type
// but still if you use any kinda pointer instead, you'll get complaints
typedef struct hcrypt_Session_str* HaiCrypt_Handle;
HAICRYPT_API int HaiCrypt_SetLogLevel(int level, int logfa);
HAICRYPT_API int HaiCrypt_Create(const HaiCrypt_Cfg *cfg, HaiCrypt_Handle *phhc);
HAICRYPT_API int HaiCrypt_Clone(HaiCrypt_Handle hhcSrc, HaiCrypt_CryptoDir tx, HaiCrypt_Handle *phhc);
HAICRYPT_API int HaiCrypt_Close(HaiCrypt_Handle hhc);
HAICRYPT_API int HaiCrypt_Tx_GetBuf(HaiCrypt_Handle hhc, size_t data_len, unsigned char **in_p);
HAICRYPT_API int HaiCrypt_Tx_Process(HaiCrypt_Handle hhc, unsigned char *in, size_t in_len,
void *out_p[], size_t out_len_p[], int maxout);
HAICRYPT_API int HaiCrypt_Rx_Process(HaiCrypt_Handle hhc, unsigned char *in, size_t in_len,
void *out_p[], size_t out_len_p[], int maxout);
HAICRYPT_API int HaiCrypt_Tx_GetKeyFlags(HaiCrypt_Handle hhc);
HAICRYPT_API int HaiCrypt_Tx_ManageKeys(HaiCrypt_Handle hhc, void *out_p[], size_t out_len_p[], int maxout);
HAICRYPT_API int HaiCrypt_Tx_Data(HaiCrypt_Handle hhc, unsigned char *pfx, unsigned char *data, size_t data_len);
HAICRYPT_API int HaiCrypt_Rx_Data(HaiCrypt_Handle hhc, unsigned char *pfx, unsigned char *data, size_t data_len);
/* Status values */
#define HAICRYPT_ERROR -1
#define HAICRYPT_ERROR_WRONG_SECRET -2
#define HAICRYPT_OK 0
#ifdef __cplusplus
}
#endif
#endif /* HAICRYPT_H */

View file

@ -0,0 +1,120 @@
/*
* SRT - Secure, Reliable, Transport
* Copyright (c) 2018 Haivision Systems Inc.
*
* This Source Code Form is subject to the terms of the Mozilla Public
* License, v. 2.0. If a copy of the MPL was not distributed with this
* file, You can obtain one at http://mozilla.org/MPL/2.0/.
*
*/
#if ENABLE_HAICRYPT_LOGGING
#include "hcrypt.h"
#include "haicrypt.h"
#include "../srtcore/srt.h"
#include "../srtcore/logging.h"
extern srt_logging::LogConfig srt_logger_config;
// LOGFA symbol defined in srt.h
srt_logging::Logger hclog(SRT_LOGFA_HAICRYPT, srt_logger_config, "SRT.k");
extern "C" {
int HaiCrypt_SetLogLevel(int level, int logfa)
{
srt_setloglevel(level);
if (logfa != SRT_LOGFA_GENERAL) // General can't be turned on or off
{
srt_addlogfa(logfa);
}
return 0;
}
// HaiCrypt will be using its own FA, which will be turned off by default.
// Templates made C way.
// It's tempting to use the HAICRYPT_DEFINE_LOG_DISPATCHER macro here because it would provide the
// exact signature that is needed here, the problem is though that this would expand the LOGLEVEL
// parameter, which is also a macro, into the value that the macro designates, which would generate
// the HaiCrypt_LogF_0 instead of HaiCrypt_LogF_LOG_DEBUG, for example.
#define HAICRYPT_DEFINE_LOG_DISPATCHER(LOGLEVEL, dispatcher) \
int HaiCrypt_LogF_##LOGLEVEL ( const char* file, int line, const char* function, const char* format, ...) \
{ \
va_list ap; \
va_start(ap, format); \
srt_logging::LogDispatcher& lg = hclog.dispatcher; \
if (!lg.CheckEnabled()) return -1; \
lg().setloc(file, line, function).vform(format, ap); \
va_end(ap); \
return 0; \
}
HAICRYPT_DEFINE_LOG_DISPATCHER(LOG_DEBUG, Debug);
HAICRYPT_DEFINE_LOG_DISPATCHER(LOG_NOTICE, Note);
HAICRYPT_DEFINE_LOG_DISPATCHER(LOG_INFO, Note);
HAICRYPT_DEFINE_LOG_DISPATCHER(LOG_WARNING, Warn);
HAICRYPT_DEFINE_LOG_DISPATCHER(LOG_ERR, Error);
HAICRYPT_DEFINE_LOG_DISPATCHER(LOG_CRIT, Fatal);
HAICRYPT_DEFINE_LOG_DISPATCHER(LOG_ALERT, Fatal);
HAICRYPT_DEFINE_LOG_DISPATCHER(LOG_EMERG, Fatal);
static void DumpCfgFlags(int flags, std::ostream& out)
{
static struct { int flg; const char* desc; } flgtable [] = {
#define HCRYPTF(name) { HAICRYPT_CFG_F_##name, #name }
HCRYPTF(TX),
HCRYPTF(CRYPTO),
HCRYPTF(FEC)
#undef HCRYPTF
};
size_t flgtable_size = sizeof(flgtable)/sizeof(flgtable[0]);
size_t i;
out << "{";
const char* sep = "";
const char* sep_bar = " | ";
for (i = 0; i < flgtable_size; ++i)
{
if ( (flgtable[i].flg & flags) != 0 )
{
out << sep << flgtable[i].desc;
sep = sep_bar;
}
}
out << "}";
}
void HaiCrypt_DumpConfig(const HaiCrypt_Cfg* cfg)
{
std::ostringstream cfg_flags;
DumpCfgFlags(cfg->flags, cfg_flags);
LOGC(hclog.Debug, log << "CFG DUMP: flags=" << cfg_flags.str()
<< " xport=" << (cfg->xport == HAICRYPT_XPT_SRT ? "SRT" : "INVALID")
<< " cipher="
<< (cfg->cipher == HaiCryptCipher_OpenSSL_EVP_CTR() ? "OSSL-EVP-CTR":
cfg->cipher == HaiCryptCipher_OpenSSL_AES() ? "OSSL-AES":
// This below is used as the only one when Nettle is used. When OpenSSL
// is used, one of the above will trigger, and the one below will then never trigger.
cfg->cipher == HaiCryptCipher_Get_Instance() ? "Nettle-AES":
"UNKNOWN")
<< " key_len=" << cfg->key_len << " data_max_len=" << cfg->data_max_len);
LOGC(hclog.Debug, log << "CFG DUMP: txperiod="
<< cfg->km_tx_period_ms << "ms kmrefresh=" << cfg->km_refresh_rate_pkt
<< " kmpreannounce=" << cfg->km_pre_announce_pkt
<< " secret "
<< "{tp=" << (cfg->secret.typ == 1 ? "PSK" : cfg->secret.typ == 2 ? "PWD" : "???")
<< " len=" << cfg->secret.len << " pwd=" << cfg->secret.str << "}");
}
} // extern "C"
#endif // Block for the whole file

View file

@ -0,0 +1,34 @@
#ifndef IMC__HAICRYPT_LOG_H
#define IMC__HAICRYPT_LOG_H
#ifdef __cplusplus
extern "C" {
#endif
#define HAICRYPT_DECLARE_LOG_DISPATCHER(LOGLEVEL) \
int HaiCrypt_LogF_##LOGLEVEL ( const char* file, int line, const char* function, const char* format, ...)
// Now declare all dispatcher functions
HAICRYPT_DECLARE_LOG_DISPATCHER(LOG_DEBUG);
HAICRYPT_DECLARE_LOG_DISPATCHER(LOG_NOTICE);
HAICRYPT_DECLARE_LOG_DISPATCHER(LOG_INFO);
HAICRYPT_DECLARE_LOG_DISPATCHER(LOG_WARNING);
HAICRYPT_DECLARE_LOG_DISPATCHER(LOG_ERR);
HAICRYPT_DECLARE_LOG_DISPATCHER(LOG_CRIT);
HAICRYPT_DECLARE_LOG_DISPATCHER(LOG_ALERT);
HAICRYPT_DECLARE_LOG_DISPATCHER(LOG_EMERG);
#define HCRYPT_LOG_INIT()
#define HCRYPT_LOG_EXIT()
#define HCRYPT_LOG(lvl, fmt, ...) HaiCrypt_LogF_##lvl (__FILE__, __LINE__, __FUNCTION__, fmt, __VA_ARGS__)
#if ENABLE_HAICRYPT_LOGGING == 2
#define HCRYPT_DEV 1
#endif
#ifdef __cplusplus
}
#endif
#endif // macroguard

View file

@ -0,0 +1,342 @@
/*
* SRT - Secure, Reliable, Transport
* Copyright (c) 2018 Haivision Systems Inc.
*
* This Source Code Form is subject to the terms of the Mozilla Public
* License, v. 2.0. If a copy of the MPL was not distributed with this
* file, You can obtain one at http://mozilla.org/MPL/2.0/.
*
*/
/*****************************************************************************
written by
Haivision Systems Inc.
2011-06-23 (jdube)
HaiCrypt initial implementation.
2014-03-11 (jdube)
Adaptation for SRT.
*****************************************************************************/
#include <stdio.h> /* snprintf */
#include <stdlib.h> /* NULL, malloc, free */
#include <string.h> /* memcpy, memset */
#ifdef _WIN32
#include <winsock2.h>
#include <ws2tcpip.h>
#else
#include <sys/time.h> /* timerclear */
#endif
#include "hcrypt.h"
#if ENABLE_HAICRYPT_LOGGING
void HaiCrypt_DumpConfig(const HaiCrypt_Cfg* cfg);
#else
#define HaiCrypt_DumpConfig(x) (void)0
#endif
static hcrypt_Session* sHaiCrypt_PrepareHandle(const HaiCrypt_Cfg* cfg, HaiCrypt_CryptoDir tx)
{
hcrypt_Session *crypto;
unsigned char *mem_buf;
size_t mem_siz, inbuf_siz;
HaiCrypt_DumpConfig(cfg);
HCRYPT_PRINTKEY(cfg->secret.str, cfg->secret.len, "cfgkey");
inbuf_siz = 0;
inbuf_siz = hcryptMsg_PaddedLen(cfg->data_max_len, 128/8);
/* Allocate crypto session control struct */
mem_siz = sizeof(hcrypt_Session) // structure
+ inbuf_siz;
crypto = malloc(mem_siz);
if (NULL == crypto) {
HCRYPT_LOG(LOG_ERR, "%s\n", "malloc failed");
return NULL;
}
mem_buf = (unsigned char *)crypto;
mem_buf += sizeof(*crypto);
memset(crypto, 0, sizeof(*crypto));
if (inbuf_siz) {
crypto->inbuf = mem_buf;
crypto->inbuf_siz = inbuf_siz;
}
crypto->cryspr = cfg->cryspr;
crypto->cfg.data_max_len = cfg->data_max_len;
/* Setup transport packet info */
switch (cfg->xport) {
case HAICRYPT_XPT_STANDALONE:
crypto->se = HCRYPT_SE_TSUDP;
crypto->msg_info = hcryptMsg_STA_MsgInfo();
break;
case HAICRYPT_XPT_SRT:
crypto->se = HCRYPT_SE_TSSRT;
crypto->msg_info = hcryptMsg_SRT_MsgInfo();
break;
default:
HCRYPT_LOG(LOG_ERR, "invalid xport: %d\n", cfg->xport);
free(crypto);
return NULL;
}
timerclear(&crypto->km.tx_last);
crypto->km.tx_period.tv_sec = cfg->km_tx_period_ms / 1000;
crypto->km.tx_period.tv_usec = (cfg->km_tx_period_ms % 1000) * 1000;
crypto->km.refresh_rate = cfg->km_refresh_rate_pkt;
crypto->km.pre_announce = cfg->km_pre_announce_pkt;
/* Indentify each context */
crypto->ctx_pair[0].flags = HCRYPT_MSG_F_eSEK | (tx ? HCRYPT_CTX_F_ENCRYPT : 0);
crypto->ctx_pair[1].flags = HCRYPT_MSG_F_oSEK | (tx ? HCRYPT_CTX_F_ENCRYPT : 0);
/* Point to each other */
crypto->ctx_pair[0].alt = &crypto->ctx_pair[1];
crypto->ctx_pair[1].alt = &crypto->ctx_pair[0];
crypto->cryspr_cb = crypto->cryspr->open(crypto->cryspr, cfg->data_max_len);
if (NULL == crypto->cryspr_cb) {
free(crypto);
return NULL;
}
return crypto;
}
int HaiCrypt_Create(const HaiCrypt_Cfg *cfg, HaiCrypt_Handle *phhc)
{
ASSERT(cfg != NULL);
ASSERT(phhc != NULL);
hcrypt_Session *crypto;
HaiCrypt_CryptoDir tx = (HaiCrypt_CryptoDir)(HAICRYPT_CFG_F_TX & cfg->flags);
*phhc = NULL;
HCRYPT_LOG_INIT();
//Test log
HCRYPT_LOG(LOG_INFO, "creating crypto context(flags=0x%x)\n", cfg->flags);
if (!(HAICRYPT_CFG_F_CRYPTO & cfg->flags)) {
HCRYPT_LOG(LOG_INFO, "no supported flags set (0x%x)\n", cfg->flags);
return(-1);
} else if ((16 != cfg->key_len) /* SEK length */
&& (24 != cfg->key_len)
&& (32 != cfg->key_len)) {
HCRYPT_LOG(LOG_ERR, "invalid key length (%d). Expected: 16, 24, 32\n", (int)cfg->key_len);
return(-1);
} else if ((HAICRYPT_SECTYP_PASSPHRASE == cfg->secret.typ)
&& ((0 == cfg->secret.len) || (sizeof(cfg->secret.str) < cfg->secret.len))) { /* KEK length */
HCRYPT_LOG(LOG_ERR, "invalid secret passphrase length (%d)\n", (int)cfg->secret.len);
return(-1);
} else if ((HAICRYPT_SECTYP_PRESHARED == cfg->secret.typ)
&& (cfg->key_len > cfg->secret.len)) {
HCRYPT_LOG(LOG_ERR, "preshared secret length (%d) smaller than key length (%d)\n",
(int)cfg->secret.len, (int)cfg->key_len);
return(-1);
} else if (NULL == cfg->cryspr) {
HCRYPT_LOG(LOG_ERR, "%s\n", "no cryspr specified");
return(-1);
} else if (0 == cfg->data_max_len) {
HCRYPT_LOG(LOG_ERR, "%s\n", "no data_max_len specified");
return(-1);
}
crypto = sHaiCrypt_PrepareHandle(cfg, tx);
if (!crypto)
return -1;
if (tx) { /* Encoder */
/* Configure initial context */
if (hcryptCtx_Tx_Init(crypto, &crypto->ctx_pair[0], cfg)
|| hcryptCtx_Tx_Init(crypto, &crypto->ctx_pair[1], cfg)) {
free(crypto);
return(-1);
}
/* Generate keys for first (default) context */
if (hcryptCtx_Tx_Rekey(crypto, &crypto->ctx_pair[0])) {
free(crypto);
return(-1);
}
crypto->ctx = &crypto->ctx_pair[0];
crypto->ctx->flags |= (HCRYPT_CTX_F_ANNOUNCE | HCRYPT_CTX_F_TTSEND);
crypto->ctx->status = HCRYPT_CTX_S_ACTIVE;
} else { /* Decoder */
/* Configure contexts */
if (hcryptCtx_Rx_Init(crypto, &crypto->ctx_pair[0], cfg)
|| hcryptCtx_Rx_Init(crypto, &crypto->ctx_pair[1], cfg)) {
free(crypto);
return(-1);
}
}
*phhc = (void *)crypto;
return(0);
}
int HaiCrypt_ExtractConfig(HaiCrypt_Handle hhcSrc, HaiCrypt_Cfg* pcfg)
{
hcrypt_Session *crypto = (hcrypt_Session *)hhcSrc;
hcrypt_Ctx* ctx = crypto->ctx;
if (!ctx)
{
// Fall back to the first of the pair;
// Should this be not initialized, ignore it.
ctx = &crypto->ctx_pair[0];
// We assume that when ctx != NULL, it is active or keyed anyway.
if (ctx->status != HCRYPT_CTX_S_KEYED && ctx->status != HCRYPT_CTX_S_ACTIVE)
return -1;
}
pcfg->flags = HAICRYPT_CFG_F_CRYPTO;
if ((ctx->flags & HCRYPT_CTX_F_ENCRYPT) == HCRYPT_CTX_F_ENCRYPT)
pcfg->flags |= HAICRYPT_CFG_F_TX;
/* Set this explicitly - this use of this library is SRT only. */
pcfg->xport = HAICRYPT_XPT_SRT;
pcfg->cryspr = crypto->cryspr;
pcfg->key_len = ctx->cfg.key_len;
if (pcfg->key_len == 0) // not initialized - usual in RX
{
pcfg->key_len = ctx->sek_len;
}
pcfg->data_max_len = crypto->cfg.data_max_len;
pcfg->km_tx_period_ms = 0;//No HaiCrypt KM inject period, handled in SRT;
pcfg->km_refresh_rate_pkt = crypto->km.refresh_rate;
pcfg->km_pre_announce_pkt = crypto->km.pre_announce;
/* As SRT is using only the PASSPHRASE type, never PRESHARED,
* this is so assumed here, although there are completely no
* premises as to which is currently used by the hhcSrc.
*/
pcfg->secret.typ = HAICRYPT_SECTYP_PASSPHRASE;
pcfg->secret.len = ctx->cfg.pwd_len;
memcpy(pcfg->secret.str, ctx->cfg.pwd, pcfg->secret.len);
return 0;
}
int HaiCrypt_Clone(HaiCrypt_Handle hhcSrc, HaiCrypt_CryptoDir tx, HaiCrypt_Handle *phhc)
{
hcrypt_Session *cryptoSrc = (hcrypt_Session *)hhcSrc;
hcrypt_Session *cryptoClone;
unsigned char *mem_buf;
size_t mem_siz, inbuf_siz;
*phhc = NULL;
ASSERT(NULL != hhcSrc);
HCRYPT_LOG(LOG_INFO, "%s\n", "creating CLONED crypto context");
if (tx) {
HaiCrypt_Cfg crypto_config;
HaiCrypt_ExtractConfig(hhcSrc, &crypto_config);
/*
* Just invert the direction written in flags and use the
* standard way of creating the context, as you already have a config.
*/
crypto_config.flags |= HAICRYPT_CFG_F_TX;
cryptoClone = sHaiCrypt_PrepareHandle(&crypto_config, tx);
if (!cryptoClone)
return -1;
/* Configure initial context */
if (hcryptCtx_Tx_Init(cryptoClone, &cryptoClone->ctx_pair[0], &crypto_config)
|| hcryptCtx_Tx_Init(cryptoClone, &cryptoClone->ctx_pair[1], &crypto_config)) {
free(cryptoClone);
return(-1);
}
/* Clone keys for first (default) context from the source RX crypto */
if (hcryptCtx_Tx_CloneKey(cryptoClone, &cryptoClone->ctx_pair[0], cryptoSrc)) {
free(cryptoClone);
return(-1);
}
cryptoClone->ctx = &cryptoClone->ctx_pair[0];
cryptoClone->ctx->flags |= (HCRYPT_CTX_F_ANNOUNCE | HCRYPT_CTX_F_TTSEND);
cryptoClone->ctx->status = HCRYPT_CTX_S_ACTIVE;
} else { /* Receiver */
/*
* If cryspr has no special input buffer alignment requirement,
* handle it in the crypto session.
*/
inbuf_siz = cryptoSrc->inbuf_siz ;
/* Allocate crypto session control struct */
mem_siz = sizeof(hcrypt_Session) // structure
+ inbuf_siz;
cryptoClone = malloc(mem_siz);
if (NULL == cryptoClone) {
HCRYPT_LOG(LOG_ERR, "%s\n", "malloc failed");
return(-1);
}
mem_buf = (unsigned char *)cryptoClone;
mem_buf += sizeof(*cryptoClone);
memcpy(cryptoClone, cryptoSrc, sizeof(*cryptoClone));
if (inbuf_siz) {
cryptoClone->inbuf = mem_buf;
mem_buf += inbuf_siz;
}
timerclear(&cryptoClone->km.tx_last);
/* Adjust pointers pointing into cryproSrc after copy
msg_info and crysprs are extern statics so this is ok*/
cryptoClone->ctx_pair[0].alt = &cryptoClone->ctx_pair[1];
cryptoClone->ctx_pair[1].alt = &cryptoClone->ctx_pair[0];
/* create a new cryspr (OpenSSL) context */
cryptoClone->cryspr_cb = cryptoClone->cryspr->open(cryptoClone->cryspr, cryptoClone->cfg.data_max_len);
if (NULL == cryptoClone->cryspr_cb) {
//shred
free(cryptoClone);
return(-1);
}
/* Configure contexts */
if (hcryptCtx_Rx_Init(cryptoClone, &cryptoClone->ctx_pair[0], NULL)
|| hcryptCtx_Rx_Init(cryptoClone, &cryptoClone->ctx_pair[1], NULL)) {
free(cryptoClone);
return(-1);
}
/* Clear salt to force later regeneration of KEK as AES decrypting key,
copyed one is encrypting key */
cryptoClone->ctx_pair[0].flags &= ~HCRYPT_CTX_F_ENCRYPT;
cryptoClone->ctx_pair[1].flags &= ~HCRYPT_CTX_F_ENCRYPT;
memset(cryptoClone->ctx_pair[0].salt, 0, sizeof(cryptoClone->ctx_pair[0].salt));
cryptoClone->ctx_pair[0].salt_len = 0;
}
*phhc = (void *)cryptoClone;
return(0);
}
int HaiCrypt_Close(HaiCrypt_Handle hhc)
{
hcrypt_Session *crypto = (hcrypt_Session *)hhc;
int rc = -1;
if (crypto) {
if (crypto->cryspr && crypto->cryspr->close) crypto->cryspr->close(crypto->cryspr_cb);
free(crypto);
rc = 0;
}
HCRYPT_LOG_EXIT();
return rc;
}

View file

@ -0,0 +1,169 @@
/*
* SRT - Secure, Reliable, Transport
* Copyright (c) 2018 Haivision Systems Inc.
*
* This Source Code Form is subject to the terms of the Mozilla Public
* License, v. 2.0. If a copy of the MPL was not distributed with this
* file, You can obtain one at http://mozilla.org/MPL/2.0/.
*
*/
/*****************************************************************************
written by
Haivision Systems Inc.
2011-06-23 (jdube)
HaiCrypt initial implementation.
2014-03-11 (jdube)
Adaptation for SRT.
2014-03-26 (jsantiago)
OS-X Build.
2014-03-27 (jdube)
Remove dependency on internal Crypto API.
2016-07-22 (jsantiago)
MINGW-W64 Build.
*****************************************************************************/
#ifndef HCRYPT_H
#define HCRYPT_H
#include <sys/types.h>
#ifdef _WIN32
#include <winsock2.h>
#include <ws2tcpip.h>
#if defined(_MSC_VER)
#pragma warning(disable:4267)
#pragma warning(disable:4018)
#endif
#else
#include <sys/time.h>
#endif
#ifdef __GNUC__
#define ATR_UNUSED __attribute__((unused))
#else
#define ATR_UNUSED
#endif
#include "haicrypt.h"
#include "hcrypt_msg.h"
#include "hcrypt_ctx.h"
#include "cryspr.h"
//#define HCRYPT_DEV 1 /* Development: should not be defined in committed code */
#ifdef HAICRYPT_SUPPORT_CRYPTO_API
/* See CRYPTOFEC_OBJECT in session structure */
#define CRYPTO_API_SERVER 1 /* Enable handler's structures */
#include "crypto_api.h"
#endif /* HAICRYPT_SUPPORT_CRYPTO_API */
typedef struct hcrypt_Session_str {
#ifdef HAICRYPT_SUPPORT_CRYPTO_API
/*
* Resv matches internal upper layer handle (crypto_api)
* They are not used in HaiCrypt.
* This make 3 layers using the same handle.
* To get rid of this dependency for a portable HaiCrypt,
* revise caller (crypto_hc.c) to allocate its own buffer.
*/
CRYPTOFEC_OBJECT resv; /* See above comment */
#endif /* HAICRYPT_SUPPORT_CRYPTO_API */
hcrypt_Ctx ctx_pair[2]; /* Even(0)/Odd(1) crypto contexts */
hcrypt_Ctx * ctx; /* Current context */
CRYSPR_methods * cryspr;
CRYSPR_cb * cryspr_cb;
unsigned char * inbuf; /* allocated if cipher has no getinbuf() func */
size_t inbuf_siz;
int se; /* Stream Encapsulation (HCRYPT_SE_xxx) */
hcrypt_MsgInfo * msg_info;
struct {
size_t data_max_len;
}cfg;
struct {
struct timeval tx_period; /* Keying Material tx period (milliseconds) */
struct timeval tx_last; /* Keying Material last tx time */
unsigned int refresh_rate; /* SEK use period */
unsigned int pre_announce; /* Pre/Post next/old SEK announce */
}km;
} hcrypt_Session;
#if ENABLE_HAICRYPT_LOGGING
#include "haicrypt_log.h"
#else
#define HCRYPT_LOG_INIT()
#define HCRYPT_LOG_EXIT()
#define HCRYPT_LOG(lvl, fmt, ...)
#endif
#ifdef HCRYPT_DEV
#define HCRYPT_PRINTKEY(key, len, tag) HCRYPT_LOG(LOG_DEBUG, \
"%s[%d]=0x%02x%02x..%02x%02x\n", tag, len, \
(key)[0], (key)[1], (key)[(len)-2], (key)[(len)-1])
#else /* HCRYPT_DEV */
#define HCRYPT_PRINTKEY(key,len,tag)
#endif /* HCRYPT_DEV */
#ifndef ASSERT
#include <assert.h>
#define ASSERT(c) assert(c)
#endif
/* HaiCrypt-TP CTR mode IV (128-bit):
* 0 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15
* +---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+
* | 0s | pki | ctr |
* +---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+
* XOR
* +---+---+---+---+---+---+---+---+---+---+---+---+---+---+
* | nonce +
* +---+---+---+---+---+---+---+---+---+---+---+---+---+---+
*
* pki (32-bit): packet index
* ctr (16-bit): block counter
* nonce (112-bit): number used once (salt)
*/
#define hcrypt_SetCtrIV(pki, nonce, iv) do { \
memset(&(iv)[0], 0, 128/8); \
memcpy(&(iv)[10], (pki), HCRYPT_PKI_SZ); \
hcrypt_XorStream(&(iv)[0], (nonce), 112/8); \
} while(0)
#define hcrypt_XorStream(dst, strm, len) do { \
int __XORSTREAMi; \
for (__XORSTREAMi = 0 \
;__XORSTREAMi < (int)(len) \
;__XORSTREAMi += 1) { \
(dst)[__XORSTREAMi] ^= (strm)[__XORSTREAMi]; \
} \
} while(0)
int hcryptCtx_SetSecret(hcrypt_Session *crypto, hcrypt_Ctx *ctx, const HaiCrypt_Secret *secret);
int hcryptCtx_GenSecret(hcrypt_Session *crypto, hcrypt_Ctx *ctx);
int hcryptCtx_Tx_Init(hcrypt_Session *crypto, hcrypt_Ctx *ctx, const HaiCrypt_Cfg *cfg);
int hcryptCtx_Tx_Rekey(hcrypt_Session *crypto, hcrypt_Ctx *ctx);
int hcryptCtx_Tx_CloneKey(hcrypt_Session *crypto, hcrypt_Ctx *ctx, const hcrypt_Session* cryptoSrc);
int hcryptCtx_Tx_Refresh(hcrypt_Session *crypto);
int hcryptCtx_Tx_PreSwitch(hcrypt_Session *crypto);
int hcryptCtx_Tx_Switch(hcrypt_Session *crypto);
int hcryptCtx_Tx_PostSwitch(hcrypt_Session *crypto);
int hcryptCtx_Tx_AsmKM(hcrypt_Session *crypto, hcrypt_Ctx *ctx, unsigned char *alt_sek);
int hcryptCtx_Tx_ManageKM(hcrypt_Session *crypto);
int hcryptCtx_Tx_InjectKM(hcrypt_Session *crypto, void *out_p[], size_t out_len_p[], int maxout);
int hcryptCtx_Rx_Init(hcrypt_Session *crypto, hcrypt_Ctx *ctx, const HaiCrypt_Cfg *cfg);
int hcryptCtx_Rx_ParseKM(hcrypt_Session *crypto, unsigned char *msg, size_t msg_len);
#endif /* HCRYPT_H */

View file

@ -0,0 +1,97 @@
/*
* SRT - Secure, Reliable, Transport
* Copyright (c) 2018 Haivision Systems Inc.
*
* This Source Code Form is subject to the terms of the Mozilla Public
* License, v. 2.0. If a copy of the MPL was not distributed with this
* file, You can obtain one at http://mozilla.org/MPL/2.0/.
*
*/
/*****************************************************************************
written by
Haivision Systems Inc.
2011-06-23 (jdube)
HaiCrypt initial implementation.
2014-03-11 (jdube)
Adaptation for SRT.
*****************************************************************************/
#ifndef HCRYPT_CTX_H
#define HCRYPT_CTX_H
#include <stdbool.h>
#include <sys/types.h>
#include "hcrypt.h"
#if !defined(HAISRT_VERSION_INT)
#include "haicrypt.h"
#include "hcrypt_msg.h"
#else
// Included by haisrt.h or similar
#include "haisrt/haicrypt.h"
#include "haisrt/hcrypt_msg.h"
#endif
typedef struct {
unsigned char *pfx; //Prefix described by transport msg info (in ctx)
unsigned char *payload;
size_t len; //Payload size
}hcrypt_DataDesc;
typedef struct tag_hcrypt_Ctx {
struct tag_hcrypt_Ctx * alt; /* Alternative ctx (even/odd) */
#define HCRYPT_CTX_F_MSG 0x00FF /* Aligned wiht message header flags */
#define HCRYPT_CTX_F_eSEK HCRYPT_MSG_F_eSEK
#define HCRYPT_CTX_F_oSEK HCRYPT_MSG_F_oSEK
#define HCRYPT_CTX_F_xSEK HCRYPT_MSG_F_xSEK
#define HCRYPT_CTX_F_ENCRYPT 0x0100 /* 0:decrypt 1:encrypt */
#define HCRYPT_CTX_F_ANNOUNCE 0x0200 /* Announce KM */
#define HCRYPT_CTX_F_TTSEND 0x0400 /* time to send */
unsigned flags;
#define hcryptCtx_GetKeyFlags(ctx) ((ctx)->flags & HCRYPT_CTX_F_xSEK)
#define hcryptCtx_GetKeyIndex(ctx) (((ctx)->flags & HCRYPT_CTX_F_xSEK)>>1)
#define HCRYPT_CTX_S_INIT 1
#define HCRYPT_CTX_S_SARDY 2 /* Security Association (KEK) ready */
#define HCRYPT_CTX_S_KEYED 3 /* Media Stream Encrypting Key (SEK) ready */
#define HCRYPT_CTX_S_ACTIVE 4 /* Announced and in use */
#define HCRYPT_CTX_S_DEPRECATED 5 /* Still announced but no longer used */
unsigned status;
#define HCRYPT_CTX_MODE_CLRTXT 0 /* NULL cipher (for tests) */
#define HCRYPT_CTX_MODE_AESECB 1 /* Electronic Code Book mode */
#define HCRYPT_CTX_MODE_AESCTR 2 /* Counter mode */
#define HCRYPT_CTX_MODE_AESCBC 3 /* Cipher-block chaining mode */
unsigned mode;
struct {
size_t key_len;
size_t pwd_len;
char pwd[HAICRYPT_PWD_MAX_SZ];
} cfg;
size_t salt_len;
unsigned char salt[HAICRYPT_SALT_SZ];
size_t sek_len;
unsigned char sek[HAICRYPT_KEY_MAX_SZ];
hcrypt_MsgInfo * msg_info; /* Transport message handler */
unsigned pkt_cnt; /* Key usage counter */
#define HCRYPT_CTX_MAX_KM_PFX_SZ 16
size_t KMmsg_len;
unsigned char KMmsg_cache[HCRYPT_CTX_MAX_KM_PFX_SZ + HCRYPT_MSG_KM_MAX_SZ];
#define HCRYPT_CTX_MAX_MS_PFX_SZ 16
unsigned char MSpfx_cache[HCRYPT_CTX_MAX_MS_PFX_SZ];
} hcrypt_Ctx;
#endif /* HCRYPT_CTX_H */

View file

@ -0,0 +1,199 @@
/*
* SRT - Secure, Reliable, Transport
* Copyright (c) 2018 Haivision Systems Inc.
*
* This Source Code Form is subject to the terms of the Mozilla Public
* License, v. 2.0. If a copy of the MPL was not distributed with this
* file, You can obtain one at http://mozilla.org/MPL/2.0/.
*
*/
/*****************************************************************************
written by
Haivision Systems Inc.
2011-06-23 (jdube)
HaiCrypt initial implementation.
2014-03-11 (jdube)
Adaptation for SRT.
*****************************************************************************/
#include <string.h> /* memcpy */
#include "hcrypt.h"
int hcryptCtx_Rx_Init(hcrypt_Session *crypto, hcrypt_Ctx *ctx, const HaiCrypt_Cfg *cfg)
{
ctx->mode = HCRYPT_CTX_MODE_AESCTR;
ctx->status = HCRYPT_CTX_S_INIT;
ctx->msg_info = crypto->msg_info;
if (cfg && hcryptCtx_SetSecret(crypto, ctx, &cfg->secret)) {
return(-1);
}
ctx->status = HCRYPT_CTX_S_SARDY;
return(0);
}
int hcryptCtx_Rx_Rekey(hcrypt_Session *crypto, hcrypt_Ctx *ctx, unsigned char *sek, size_t sek_len)
{
if (crypto->cryspr->ms_setkey(crypto->cryspr_cb, ctx, sek, sek_len)) {
HCRYPT_LOG(LOG_ERR, "cryspr setkey[%d](sek) failed\n", hcryptCtx_GetKeyIndex(ctx));
return(-1);
}
memcpy(ctx->sek, sek, sek_len);
ctx->sek_len = sek_len;
HCRYPT_LOG(LOG_INFO, "updated context[%d]\n", hcryptCtx_GetKeyIndex(ctx));
HCRYPT_PRINTKEY(ctx->sek, ctx->sek_len, "sek");
ctx->status = HCRYPT_CTX_S_KEYED;
return(0);
}
/* Parse Keying Material message */
int hcryptCtx_Rx_ParseKM(hcrypt_Session *crypto, unsigned char *km_msg, size_t msg_len)
{
size_t sek_len, salt_len;
unsigned char seks[HAICRYPT_KEY_MAX_SZ * 2];
int sek_cnt;
size_t kek_len = 0;
hcrypt_Ctx *ctx;
int do_pbkdf = 0;
if (NULL == crypto) {
HCRYPT_LOG(LOG_ERR, "Rx_ParseKM: invalid params: crypto=%p\n", crypto);
return(-1);
}
/* Validate message content */
{
if (msg_len <= HCRYPT_MSG_KM_OFS_SALT) {
HCRYPT_LOG(LOG_WARNING, "KMmsg length too small (%zd)\n", msg_len);
return(-1);
}
salt_len = hcryptMsg_KM_GetSaltLen(km_msg);
sek_len = hcryptMsg_KM_GetSekLen(km_msg);
if ((salt_len > HAICRYPT_SALT_SZ)
|| (sek_len > HAICRYPT_KEY_MAX_SZ)) {
HCRYPT_LOG(LOG_WARNING, "%s", "KMmsg unsupported salt/key length\n");
return(-1);
}
if ((16 != sek_len)
&& (24 != sek_len)
&& (32 != sek_len)) {
HCRYPT_LOG(LOG_WARNING, "%s", "KMmsg unsupported key length\n");
return(-1);
}
if (hcryptMsg_KM_HasBothSek(km_msg)) {
sek_cnt = 2;
} else {
sek_cnt = 1;
}
if (msg_len != (HCRYPT_MSG_KM_OFS_SALT + salt_len + (sek_cnt * sek_len) + HAICRYPT_WRAPKEY_SIGN_SZ)) {
HCRYPT_LOG(LOG_WARNING, "KMmsg length inconsistent (%zd,%zd,%zd)\n",
salt_len, sek_len, msg_len);
return(-1);
}
/* Check options support */
if ((HCRYPT_CIPHER_AES_CTR != km_msg[HCRYPT_MSG_KM_OFS_CIPHER])
|| (HCRYPT_AUTH_NONE != km_msg[HCRYPT_MSG_KM_OFS_AUTH])) {
HCRYPT_LOG(LOG_WARNING, "%s", "KMmsg unsupported option\n");
return(-1);
}
if (crypto->se != km_msg[HCRYPT_MSG_KM_OFS_SE]) {
HCRYPT_LOG(LOG_WARNING, "%s", "KMmsg invalid SE\n");
return(-1);
}
/* Check KEKI here and pick right key */
//>>todo
/*
* We support no key exchange,
* KEK is preshared or derived from a passphrase
*/
}
/* Pick the context updated by this KMmsg */
if (hcryptMsg_KM_HasBothSek(km_msg) && (NULL != crypto->ctx)) {
ctx = crypto->ctx->alt; /* 2 SEK KM, start with inactive ctx */
} else {
ctx = &crypto->ctx_pair[hcryptMsg_KM_GetKeyIndex(km_msg)];
}
if (NULL == ctx) {
HCRYPT_LOG(LOG_WARNING, "%s", "KMmsg invalid flags (no SEK)\n");
return(-1);
}
/* Check Salt and get if new */
if ((salt_len != ctx->salt_len)
|| (0 != memcmp(ctx->salt, &km_msg[HCRYPT_MSG_KM_OFS_SALT], salt_len))) {
/* Salt changed (or 1st KMmsg received) */
memcpy(ctx->salt, &km_msg[HCRYPT_MSG_KM_OFS_SALT], salt_len);
ctx->salt_len = salt_len;
do_pbkdf = 1; /* Impact on password derived kek */
}
/* Check SEK length and get if new */
if (sek_len != ctx->sek_len) {
/* Key length changed or 1st KMmsg received */
ctx->sek_len = sek_len;
do_pbkdf = 1; /* Impact on password derived kek */
}
/*
* Regenerate KEK if it is password derived
* and Salt or SEK length changed
*/
if (ctx->cfg.pwd_len && do_pbkdf) {
if (hcryptCtx_GenSecret(crypto, ctx)) {
return(-1);
}
ctx->status = HCRYPT_CTX_S_SARDY;
kek_len = sek_len; /* KEK changed */
}
/* Unwrap SEK(s) and set in context */
if (0 > crypto->cryspr->km_unwrap(crypto->cryspr_cb, seks,
&km_msg[HCRYPT_MSG_KM_OFS_SALT + salt_len],
(sek_cnt * sek_len) + HAICRYPT_WRAPKEY_SIGN_SZ)) {
HCRYPT_LOG(LOG_WARNING, "%s", "unwrap key failed\n");
return(-2); //Report unmatched shared secret
}
/*
* First SEK in KMmsg is eSEK if both SEK present
*/
hcryptCtx_Rx_Rekey(crypto, ctx,
((2 == sek_cnt) && (ctx->flags & HCRYPT_MSG_F_oSEK)) ? &seks[sek_len] : &seks[0],
sek_len);
/*
* Refresh KMmsg cache to detect Keying Material changes
*/
ctx->KMmsg_len = msg_len;
memcpy(ctx->KMmsg_cache, km_msg, msg_len);
/* update other (alternate) context if both SEK provided */
if (2 == sek_cnt) {
hcrypt_Ctx *alt = ctx->alt;
memcpy(alt->salt, &km_msg[HCRYPT_MSG_KM_OFS_SALT], salt_len);
alt->salt_len = salt_len;
if (kek_len) { /* New or changed KEK */
// memcpy(&alt->aes_kek, &ctx->aes_kek, sizeof(alt->aes_kek));
alt->status = HCRYPT_CTX_S_SARDY;
}
hcryptCtx_Rx_Rekey(crypto, alt,
((2 == sek_cnt) && (alt->flags & HCRYPT_MSG_F_oSEK)) ? &seks[sek_len] : &seks[0],
sek_len);
alt->KMmsg_len = msg_len;
memcpy(alt->KMmsg_cache, km_msg, msg_len);
}
return(0);
}

View file

@ -0,0 +1,420 @@
/*
* SRT - Secure, Reliable, Transport
* Copyright (c) 2018 Haivision Systems Inc.
*
* This Source Code Form is subject to the terms of the Mozilla Public
* License, v. 2.0. If a copy of the MPL was not distributed with this
* file, You can obtain one at http://mozilla.org/MPL/2.0/.
*
*/
/*****************************************************************************
written by
Haivision Systems Inc.
2011-06-23 (jdube)
HaiCrypt initial implementation.
2014-03-11 (jdube)
Adaptation for SRT.
*****************************************************************************/
#include <string.h> /* memcpy */
#ifdef _WIN32
#include <winsock2.h>
#include <ws2tcpip.h>
#include <win/wintime.h>
#else
#include <sys/time.h>
#endif
#include "hcrypt.h"
int hcryptCtx_Tx_Init(hcrypt_Session *crypto, hcrypt_Ctx *ctx, const HaiCrypt_Cfg *cfg)
{
ctx->cfg.key_len = cfg->key_len;
ctx->mode = HCRYPT_CTX_MODE_AESCTR;
ctx->status = HCRYPT_CTX_S_INIT;
ctx->msg_info = crypto->msg_info;
if (hcryptCtx_SetSecret(crypto, ctx, &cfg->secret)) {
return(-1);
}
return(0);
}
int hcryptCtx_Tx_Rekey(hcrypt_Session *crypto, hcrypt_Ctx *ctx)
{
int iret;
ASSERT(HCRYPT_CTX_S_SARDY <= ctx->status);
/* Generate Salt */
ctx->salt_len = HAICRYPT_SALT_SZ;
if (0 > (iret = crypto->cryspr->prng(ctx->salt, ctx->salt_len))) {
HCRYPT_LOG(LOG_ERR, "PRNG(salt[%zd]) failed\n", ctx->salt_len);
return(iret);
}
/* Generate SEK */
ctx->sek_len = ctx->cfg.key_len;
if (0 > (iret = crypto->cryspr->prng(ctx->sek, ctx->sek_len))) {
HCRYPT_LOG(LOG_ERR, "PRNG(sek[%zd] failed\n", ctx->sek_len);
return(iret);
}
/* Set SEK in cryspr */
if (crypto->cryspr->ms_setkey(crypto->cryspr_cb, ctx, ctx->sek, ctx->sek_len)) {
HCRYPT_LOG(LOG_ERR, "cryspr setkey(sek[%zd]) failed\n", ctx->sek_len);
return(-1);
}
HCRYPT_LOG(LOG_NOTICE, "rekeyed crypto context[%d]\n", (ctx->flags & HCRYPT_CTX_F_xSEK)/2);
HCRYPT_PRINTKEY(ctx->sek, ctx->sek_len, "sek");
/* Regenerate KEK if Password-based (uses newly generated salt and sek_len) */
if ((0 < ctx->cfg.pwd_len)
&& (0 > (iret = hcryptCtx_GenSecret(crypto, ctx)))) {
return(iret);
}
/* Assemble the new Keying Material message */
if (0 != (iret = hcryptCtx_Tx_AsmKM(crypto, ctx, NULL))) {
return(iret);
}
if ((HCRYPT_CTX_S_KEYED <= ctx->alt->status)
&& hcryptMsg_KM_HasBothSek(ctx->alt->KMmsg_cache)) {
/*
* previous context KM announced in alternate (odd/even) KM,
* reassemble it without our KM
*/
hcryptCtx_Tx_AsmKM(crypto, ctx->alt, NULL);
}
/* Initialize the Media Stream message prefix cache */
ctx->msg_info->resetCache(ctx->MSpfx_cache, HCRYPT_MSG_PT_MS, ctx->flags & HCRYPT_CTX_F_xSEK);
ctx->pkt_cnt = 1;
ctx->status = HCRYPT_CTX_S_KEYED;
return(0);
}
int hcryptCtx_Tx_CloneKey(hcrypt_Session *crypto, hcrypt_Ctx *ctx, const hcrypt_Session* cryptoSrc)
{
int iret;
ASSERT(HCRYPT_CTX_S_SARDY <= ctx->status);
const hcrypt_Ctx* ctxSrc = cryptoSrc->ctx;
if (!ctxSrc)
{
/* Probbly the context is not yet completely initialized, so
* use blindly the first context from the pair
*/
ctxSrc = &cryptoSrc->ctx_pair[0];
}
/* Copy SALT (instead of generating) */
ctx->salt_len = ctxSrc->salt_len;
memcpy(ctx->salt, ctxSrc->salt, ctx->salt_len);
/* Copy SEK */
ctx->sek_len = ctxSrc->sek_len;
memcpy(ctx->sek, ctxSrc->sek, ctx->sek_len);
/* Set SEK in cryspr */
if (crypto->cryspr->ms_setkey(crypto->cryspr_cb, ctx, ctx->sek, ctx->sek_len)) {
HCRYPT_LOG(LOG_ERR, "cryspr setkey(sek[%zd]) failed\n", ctx->sek_len);
return(-1);
}
HCRYPT_LOG(LOG_NOTICE, "clone-keyed crypto context[%d]\n", (ctx->flags & HCRYPT_CTX_F_xSEK)/2);
HCRYPT_PRINTKEY(ctx->sek, ctx->sek_len, "sek");
/* Regenerate KEK if Password-based (uses newly generated salt and sek_len) */
/* (note for CloneKey imp: it's expected that the same passphrase-salt pair
shall generate the same KEK. GenSecret also prints the KEK */
if ((0 < ctx->cfg.pwd_len)
&& (0 > (iret = hcryptCtx_GenSecret(crypto, ctx)))) {
return(iret);
}
/* Assemble the new Keying Material message */
if (0 != (iret = hcryptCtx_Tx_AsmKM(crypto, ctx, NULL))) {
return(iret);
}
if ((HCRYPT_CTX_S_KEYED <= ctx->alt->status)
&& hcryptMsg_KM_HasBothSek(ctx->alt->KMmsg_cache)) {
/*
* previous context KM announced in alternate (odd/even) KM,
* reassemble it without our KM
*/
hcryptCtx_Tx_AsmKM(crypto, ctx->alt, NULL);
}
/* Initialize the Media Stream message prefix cache */
ctx->msg_info->resetCache(ctx->MSpfx_cache, HCRYPT_MSG_PT_MS, ctx->flags & HCRYPT_CTX_F_xSEK);
ctx->pkt_cnt = 1;
ctx->status = HCRYPT_CTX_S_KEYED;
return(0);
}
/*
* Refresh the alternate context from the current.
* Regenerates the SEK but keep the salt, doing so also
* preserve the KEK generated from secret password and salt.
*/
int hcryptCtx_Tx_Refresh(hcrypt_Session *crypto)
{
hcrypt_Ctx *ctx = crypto->ctx;
hcrypt_Ctx *new_ctx;
int iret;
ASSERT(NULL != ctx);
ASSERT(HCRYPT_CTX_S_ACTIVE == ctx->status);
/* Pick the alternative (inactive) context */
new_ctx = ctx->alt;
ASSERT(HCRYPT_CTX_S_SARDY <= new_ctx->status);
/* Keep same KEK, configuration, and salt */
// memcpy(&new_ctx->aes_kek, &ctx->aes_kek, sizeof(new_ctx->aes_kek));
memcpy(&new_ctx->cfg, &ctx->cfg, sizeof(new_ctx->cfg));
new_ctx->salt_len = ctx->salt_len;
memcpy(new_ctx->salt, ctx->salt, HAICRYPT_SALT_SZ);
/* Generate new SEK */
new_ctx->sek_len = new_ctx->cfg.key_len;
HCRYPT_LOG(LOG_DEBUG, "refresh/generate SEK. salt_len=%d sek_len=%d\n", (int)new_ctx->salt_len, (int)new_ctx->sek_len);
if (0 > crypto->cryspr->prng(new_ctx->sek, new_ctx->sek_len)) {
HCRYPT_LOG(LOG_ERR, "PRNG(sek[%zd] failed\n", new_ctx->sek_len);
return(-1);
}
/* Cryspr's dependent key */
if (crypto->cryspr->ms_setkey(crypto->cryspr_cb, new_ctx, new_ctx->sek, new_ctx->sek_len)) {
HCRYPT_LOG(LOG_ERR, "refresh cryspr setkey(sek[%d]) failed\n", new_ctx->sek_len);
return(-1);
}
HCRYPT_PRINTKEY(new_ctx->sek, new_ctx->sek_len, "sek");
/* Assemble the new KMmsg with new and current SEK */
if (0 != (iret = hcryptCtx_Tx_AsmKM(crypto, new_ctx, ctx->sek))) {
return(iret);
}
/* Initialize the message prefix cache */
new_ctx->msg_info->resetCache(new_ctx->MSpfx_cache, HCRYPT_MSG_PT_MS, new_ctx->flags & HCRYPT_MSG_F_xSEK);
new_ctx->pkt_cnt = 0;
new_ctx->status = HCRYPT_CTX_S_KEYED;
return(0);
}
/*
* Prepare context switch
* both odd & even keys announced
*/
int hcryptCtx_Tx_PreSwitch(hcrypt_Session *crypto)
{
hcrypt_Ctx *ctx = crypto->ctx;
ASSERT(NULL != ctx);
ASSERT(HCRYPT_CTX_S_ACTIVE == ctx->status);
ASSERT(HCRYPT_CTX_S_KEYED == ctx->alt->status);
ctx->alt->flags |= HCRYPT_CTX_F_ANNOUNCE;
ctx->alt->flags |= HCRYPT_CTX_F_TTSEND; //Send now
/* Stop announcing current context if next one contains its key */
if (hcryptMsg_KM_HasBothSek(ctx->alt->KMmsg_cache)) {
ctx->flags &= ~HCRYPT_CTX_F_ANNOUNCE;
}
return(0);
}
int hcryptCtx_Tx_Switch(hcrypt_Session *crypto)
{
hcrypt_Ctx *ctx = crypto->ctx;
ASSERT(HCRYPT_CTX_S_KEYED <= ctx->alt->status);
ctx->status = HCRYPT_CTX_S_DEPRECATED;
ctx->alt->status = HCRYPT_CTX_S_ACTIVE;
ctx->alt->flags |= HCRYPT_CTX_F_ANNOUNCE; // Already cleared if new KM has both SEK
crypto->ctx = ctx->alt;
return(0);
}
int hcryptCtx_Tx_PostSwitch(hcrypt_Session *crypto)
{
hcrypt_Ctx *ctx = crypto->ctx;
hcrypt_Ctx *old_ctx = ctx->alt;
/* Stop announcing old context (if announced) */
old_ctx->flags &= ~HCRYPT_CTX_F_ANNOUNCE;
old_ctx->status = HCRYPT_CTX_S_SARDY;
/* If current context KM announce both, reassemble it */
if (hcryptMsg_KM_HasBothSek(ctx->KMmsg_cache)) {
hcryptCtx_Tx_AsmKM(crypto, ctx, NULL);
}
return(0);
}
/* Assemble Keying Material message */
int hcryptCtx_Tx_AsmKM(hcrypt_Session *crypto, hcrypt_Ctx *ctx, unsigned char *alt_sek)
{
unsigned char *km_msg;
size_t msg_len;
int sek_cnt = (NULL == alt_sek ? 1 : 2);
unsigned char sek_buf[HAICRYPT_KEY_MAX_SZ * 2];
unsigned char *seks;
if (NULL == ctx) {
HCRYPT_LOG(LOG_ERR, "%s", "crypto context undefined\n");
return(-1);
}
msg_len = HCRYPT_MSG_KM_OFS_SALT
+ ctx->salt_len
+ (ctx->sek_len * sek_cnt)
+ HAICRYPT_WRAPKEY_SIGN_SZ;
km_msg = &ctx->KMmsg_cache[0];
ctx->KMmsg_len = 0;
memset(km_msg, 0, msg_len);
ctx->msg_info->resetCache(km_msg, HCRYPT_MSG_PT_KM,
2 == sek_cnt ? HCRYPT_MSG_F_xSEK : (ctx->flags & HCRYPT_MSG_F_xSEK));
/* crypto->KMmsg_cache[4..7]: KEKI=0 */
km_msg[HCRYPT_MSG_KM_OFS_CIPHER] = HCRYPT_CIPHER_AES_CTR;
km_msg[HCRYPT_MSG_KM_OFS_AUTH] = HCRYPT_AUTH_NONE;
km_msg[HCRYPT_MSG_KM_OFS_SE] = crypto->se;
hcryptMsg_KM_SetSaltLen(km_msg, ctx->salt_len);
hcryptMsg_KM_SetSekLen(km_msg, ctx->sek_len);
memcpy(&km_msg[HCRYPT_MSG_KM_OFS_SALT], ctx->salt, ctx->salt_len);
if (2 == sek_cnt) {
/* Even SEK first in dual SEK KMmsg */
if (HCRYPT_MSG_F_eSEK & ctx->flags) {
memcpy(&sek_buf[0], ctx->sek, ctx->sek_len);
memcpy(&sek_buf[ctx->sek_len], alt_sek, ctx->sek_len);
} else {
memcpy(&sek_buf[0], alt_sek, ctx->sek_len);
memcpy(&sek_buf[ctx->sek_len], ctx->sek, ctx->sek_len);
}
seks = sek_buf;
} else {
seks = ctx->sek;
}
if (0 > crypto->cryspr->km_wrap(crypto->cryspr_cb,
&km_msg[HCRYPT_MSG_KM_OFS_SALT + ctx->salt_len],
seks, sek_cnt * ctx->sek_len)) {
HCRYPT_LOG(LOG_ERR, "%s", "wrap key failed\n");
return(-1);
}
ctx->KMmsg_len = msg_len;
return(0);
}
int hcryptCtx_Tx_ManageKM(hcrypt_Session *crypto)
{
hcrypt_Ctx *ctx = crypto->ctx;
ASSERT(NULL != ctx);
HCRYPT_LOG(LOG_DEBUG, "KM[%d] KEY STATUS: pkt_cnt=%u against ref.rate=%u and pre.announce=%u\n",
(ctx->alt->flags & HCRYPT_CTX_F_xSEK)/2,
ctx->pkt_cnt, crypto->km.refresh_rate, crypto->km.pre_announce);
if ((ctx->pkt_cnt > crypto->km.refresh_rate)
|| (ctx->pkt_cnt == 0)) { //rolled over
/*
* End of crypto period for current SEK,
* switch to other (even/odd) SEK
*/
HCRYPT_LOG(LOG_INFO, "KM[%d] Activated\n",
(ctx->alt->flags & HCRYPT_CTX_F_xSEK)/2);
hcryptCtx_Tx_Switch(crypto);
} else
if ((ctx->pkt_cnt > (crypto->km.refresh_rate - crypto->km.pre_announce))
&& !(ctx->alt->flags & HCRYPT_CTX_F_ANNOUNCE)) {
/*
* End of crypto period approach for this SEK,
* prepare next SEK for announcement
*/
hcryptCtx_Tx_Refresh(crypto);
HCRYPT_LOG(LOG_INFO, "KM[%d] Pre-announced\n",
(ctx->alt->flags & HCRYPT_CTX_F_xSEK)/2);
hcryptCtx_Tx_PreSwitch(crypto);
} else
if ((ctx->alt->status == HCRYPT_CTX_S_DEPRECATED)
&& (ctx->pkt_cnt > crypto->km.pre_announce)) {
/*
* Deprecated SEK is no longer needed (for late packets),
* decommission it
*/
HCRYPT_LOG(LOG_INFO, "KM[%d] Deprecated\n",
(ctx->alt->flags & HCRYPT_CTX_F_xSEK)/2);
hcryptCtx_Tx_PostSwitch(crypto);
}
/* Check if it is time to send Keying Material */
if (timerisset(&crypto->km.tx_period)) { /* tx_period=0.0 -> out-of-stream Keying Material distribution */
struct timeval now, nxt_tx;
gettimeofday(&now, NULL);
timeradd(&crypto->km.tx_last, &crypto->km.tx_period, &nxt_tx);
if (timercmp(&now, &nxt_tx, >)) {
if (crypto->ctx_pair[0].flags & HCRYPT_CTX_F_ANNOUNCE) crypto->ctx_pair[0].flags |= HCRYPT_CTX_F_TTSEND;
if (crypto->ctx_pair[1].flags & HCRYPT_CTX_F_ANNOUNCE) crypto->ctx_pair[1].flags |= HCRYPT_CTX_F_TTSEND;
}
}
return(0);
}
int hcryptCtx_Tx_InjectKM(hcrypt_Session *crypto,
void *out_p[], size_t out_len_p[], int maxout ATR_UNUSED)
{
int i, nbout = 0;
ASSERT(maxout >= 2);
for (i=0; i<2; i++) {
if (crypto->ctx_pair[i].flags & HCRYPT_CTX_F_TTSEND) { /* Time To Send */
HCRYPT_LOG(LOG_DEBUG, "Send KMmsg[%d] len=%zd\n", i,
crypto->ctx_pair[i].KMmsg_len);
/* Send Keying Material */
out_p[nbout] = crypto->ctx_pair[i].KMmsg_cache;
out_len_p[nbout] = crypto->ctx_pair[i].KMmsg_len;
nbout++;
crypto->ctx_pair[i].flags &= ~HCRYPT_CTX_F_TTSEND;
}
}
if (nbout) {
struct timeval now;
gettimeofday(&now, NULL);
crypto->km.tx_last = now;
}
return(nbout);
}

View file

@ -0,0 +1,155 @@
/*
* SRT - Secure, Reliable, Transport
* Copyright (c) 2018 Haivision Systems Inc.
*
* This Source Code Form is subject to the terms of the Mozilla Public
* License, v. 2.0. If a copy of the MPL was not distributed with this
* file, You can obtain one at http://mozilla.org/MPL/2.0/.
*
*/
/*****************************************************************************
written by
Haivision Systems Inc.
2011-06-23 (jdube)
HaiCrypt initial implementation.
2014-03-11 (jdube)
Adaptation for SRT.
*****************************************************************************/
#ifndef HCRYPT_MSG_H
#define HCRYPT_MSG_H
/*
* HaiCrypt Transport Message Header info
*/
#ifndef HCRYPT_DSP
#include <sys/types.h>
typedef uint32_t hcrypt_Pki;
#endif /* HCRYPT_DSP */
#define HCRYPT_MSG_VERSION 1 /* Current HaiCrypt version */
#define HCRYPT_MSG_SIGN (('H'-'@')<<10 | ('A'-'@')<<5 | ('I'-'@')) /* Haivision PnP Mfr ID 'HAI' */
#define HCRYPT_PKI_SZ 4 /* Packet Index size (CTR mode cipher) */
#define HCRYPT_MSG_PT_MS 1 /* Media stream */
#define HCRYPT_MSG_PT_KM 2 /* Keying Material */
#define HCRYPT_MSG_PT_RESV7 7 /* Reserved to dicriminate MPEG-TS packet (SyncByte=0x47) */
#define HCRYPT_MSG_F_eSEK 0x01 /* Even Stream Encrypting Key */
#define HCRYPT_MSG_F_oSEK 0x02 /* Odd Stream Encrypting Key */
#define HCRYPT_MSG_F_xSEK 0x03 /* Both Stream Encrypting Keys */
typedef struct {
int hdr_len; // data and control common prefix portion
int pfx_len; // Message Prefix len. Also payload offset
unsigned (*getKeyFlags)(unsigned char *msg);
hcrypt_Pki (*getPki)(unsigned char *msg, int nwko);
void (*setPki)(unsigned char *msg, hcrypt_Pki);
void (*resetCache)(unsigned char *pfx_cache, unsigned pkt_type, unsigned flags);
void (*indexMsg)(unsigned char *msg, unsigned char *pfx_cache);
int (*parseMsg)(unsigned char *msg);
}hcrypt_MsgInfo;
#define hcryptMsg_GetKeyIndex(mi,msg) ((mi)->getKeyFlags(msg)>>1)
#define hcryptMsg_GetPki(mi,msg,nwko) ((mi)->getPki(msg,nwko))
#define hcryptMsg_SetPki(mi,msg,pki) (mi)->setPki(msg, pki)
#define hcryptMsg_HasEvenSek(mi,msg) ((mi)->getKeyFlags(msg) & HCRYPT_MSG_F_eSEK)
#define hcryptMsg_HasOddSek(mi,msg) ((mi)->getKeyFlags(msg) & HCRYPT_MSG_F_oSEK)
#define hcryptMsg_HasBothSek(mi,msg) (HCRYPT_MSG_F_xSEK == ((mi)->getKeyFlags(msg) & HCRYPT_MSG_F_xSEK))
#define hcryptMsg_HasNoSek(mi,msg) (0 == ((mi)->getKeyFlags(msg) & HCRYPT_MSG_F_xSEK))
#define hcryptMsg_PaddedLen(len, fact) ((((len)+(fact)-1)/(fact))*(fact))
/*
* HaiCrypt KMmsg (Keying Material):
*
* 0 1 2 3
* 0 1 2 3 4 5 6 7 8 9 0 1 2 3 4 5 6 7 8 9 0 1 2 3 4 5 6 7 8 9 0 1
* +-+-+-+-+-+-+-+-|-+-+-+-+-+-+-+-|-+-+-+-+-+-+-+-|-+-+-+-+-+-+-+-+
*+0x00 |0|Vers | PT | Sign | resv |KF |
* +-+-+-+-+-+-+-+-|-+-+-+-+-+-+-+-|-+-+-+-+-+-+-+-|-+-+-+-+-+-+-+-+
*+0x04 | KEKI |
* +-+-+-+-+-+-+-+-|-+-+-+-+-+-+-+-|-+-+-+-+-+-+-+-|-+-+-+-+-+-+-+-+
*+0x08 | Cipher | Auth | SE | Resv1 |
* +-+-+-+-+-+-+-+-|-+-+-+-+-+-+-+-|-+-+-+-+-+-+-+-|-+-+-+-+-+-+-+-+
*+0x0C | Resv2 | Slen/4 | Klen/4 |
* +-+-+-+-+-+-+-+-|-+-+-+-+-+-+-+-|-+-+-+-+-+-+-+-|-+-+-+-+-+-+-+-+
*+0x10 | Salt |
* | ... |
* +-+-+-+-+-+-+-+-|-+-+-+-+-+-+-+-|-+-+-+-+-+-+-+-|-+-+-+-+-+-+-+-+
* | Wrap |
* | ... |
* +-+-+-+-+-+-+-+-|-+-+-+-+-+-+-+-|-+-+-+-+-+-+-+-|-+-+-+-+-+-+-+-+
*/
#define HCRYPT_MSG_KM_OFS_VERSION 0
#define HCRYPT_MSG_KM_OFS_PT 0
#define HCRYPT_MSG_KM_OFS_SIGN 1
#define HCRYPT_MSG_KM_OFS_KFLGS 3
#define HCRYPT_MSG_KM_RSH_KFLGS 0 /* Right shift (in byte) */
#define HCRYPT_MSG_KM_OFS_KEKI 4
#define HCRYPT_MSG_KM_OFS_CIPHER 8
#define HCRYPT_MSG_KM_OFS_AUTH 9
#define HCRYPT_MSG_KM_OFS_SE 10
#define HCRYPT_MSG_KM_OFS_RESV2 12
#define HCRYPT_MSG_KM_OFS_SLEN 14
#define HCRYPT_MSG_KM_OFS_KLEN 15
#define HCRYPT_MSG_KM_OFS_SALT 16
#define HCRYPT_MSG_KM_MAX_SZ (0 \
+ HCRYPT_MSG_KM_OFS_SALT \
+ HAICRYPT_SALT_SZ \
+ (HAICRYPT_KEY_MAX_SZ * 2) \
+ HAICRYPT_WRAPKEY_SIGN_SZ)
#define HCRYPT_CIPHER_NONE 0
#define HCRYPT_CIPHER_AES_ECB 1
#define HCRYPT_CIPHER_AES_CTR 2
#define HCRYPT_CIPHER_AES_CBC 3
#define HCRYPT_AUTH_NONE 0
#define HCRYPT_SE_TSUDP 1
hcrypt_MsgInfo * hcryptMsg_STA_MsgInfo(void);
#define HCRYPT_SE_TSSRT 2
hcrypt_MsgInfo * hcryptMsg_SRT_MsgInfo(void);
#define hcryptMsg_KM_GetVersion(msg) (((msg)[HCRYPT_MSG_KM_OFS_VERSION]>>4)& 0xF)
#define hcryptMsg_KM_GetPktType(msg) (((msg)[HCRYPT_MSG_KM_OFS_PT]) & 0xF)
#define hcryptMsg_KM_GetSign(msg) (((msg)[HCRYPT_MSG_KM_OFS_SIGN]<<8) | (msg)[HCRYPT_MSG_KM_OFS_SIGN+1])
#define hcryptMsg_KM_GetKeyIndex(msg) (((msg)[HCRYPT_MSG_KM_OFS_KFLGS] & HCRYPT_MSG_F_xSEK)>>1)
#define hcryptMsg_KM_HasEvenSek(msg) ((msg)[HCRYPT_MSG_KM_OFS_KFLGS] & HCRYPT_MSG_F_eSEK)
#define hcryptMsg_KM_HasOddSek(msg) ((msg)[HCRYPT_MSG_KM_OFS_KFLGS] & HCRYPT_MSG_F_oSEK)
#define hcryptMsg_KM_HasBothSek(msg) (HCRYPT_MSG_F_xSEK == ((msg)[HCRYPT_MSG_KM_OFS_KFLGS] & HCRYPT_MSG_F_xSEK))
#define hcryptMsg_KM_HasNoSek(msg) (0 == ((msg)[HCRYPT_MSG_KM_OFS_KFLGS] & HCRYPT_MSG_F_xSEK))
#define hcryptMsg_KM_GetCipher(msg) ((msg)[HCRYPT_MSG_KM_OFS_CIPHER])
#define hcryptMsg_KM_GetAuth(msg) ((msg)[HCRYPT_MSG_KM_OFS_AUTH])
#define hcryptMsg_KM_GetSE(msg) ((msg)[HCRYPT_MSG_KM_OFS_SE])
#define hcryptMsg_KM_GetSaltLen(msg) (size_t)((msg)[HCRYPT_MSG_KM_OFS_SLEN] * 4)
#define hcryptMsg_KM_GetSekLen(msg) (size_t)((msg)[HCRYPT_MSG_KM_OFS_KLEN] * 4)
#define hcryptMsg_KM_SetSaltLen(msg,len)do {(msg)[HCRYPT_MSG_KM_OFS_SLEN] = (len)/4;} while(0)
#define hcryptMsg_KM_SetSekLen(msg,len) do {(msg)[HCRYPT_MSG_KM_OFS_KLEN] = (len)/4;} while(0)
#endif /* HCRYPT_MSG_H */

View file

@ -0,0 +1,146 @@
/*
* SRT - Secure, Reliable, Transport
* Copyright (c) 2018 Haivision Systems Inc.
*
* This Source Code Form is subject to the terms of the Mozilla Public
* License, v. 2.0. If a copy of the MPL was not distributed with this
* file, You can obtain one at http://mozilla.org/MPL/2.0/.
*
*/
/*****************************************************************************
written by
Haivision Systems Inc.
2011-06-23 (jdube)
HaiCrypt initial implementation.
2014-03-11 (jdube)
Adaptation for SRT.
*****************************************************************************/
#include <stdlib.h> /* NULL */
#include <string.h> /* memcmp */
#include "hcrypt.h"
int HaiCrypt_Rx_Data(HaiCrypt_Handle hhc,
unsigned char *in_pfx, unsigned char *data, size_t data_len)
{
hcrypt_Session *crypto = (hcrypt_Session *)hhc;
hcrypt_Ctx *ctx;
int nb = -1;
if ((NULL == crypto)
|| (NULL == data)) {
HCRYPT_LOG(LOG_ERR, "%s", "invalid parameters\n");
return(nb);
}
ctx = &crypto->ctx_pair[hcryptMsg_GetKeyIndex(crypto->msg_info, in_pfx)];
ASSERT(NULL != ctx); /* Header check should prevent this error */
ASSERT(NULL != crypto->cryspr); /* Header check should prevent this error */
crypto->ctx = ctx; /* Context of last received msg */
if (NULL == crypto->cryspr->ms_decrypt) {
HCRYPT_LOG(LOG_ERR, "%s", "cryspr had no decryptor\n");
} else if (ctx->status >= HCRYPT_CTX_S_KEYED) {
hcrypt_DataDesc indata;
indata.pfx = in_pfx;
indata.payload = data;
indata.len = data_len;
if (0 > (nb = crypto->cryspr->ms_decrypt(crypto->cryspr_cb, ctx, &indata, 1, NULL, NULL, NULL))) {
HCRYPT_LOG(LOG_ERR, "%s", "ms_decrypt failed\n");
} else {
nb = indata.len;
}
} else { /* No key received yet */
nb = 0;
}
return(nb);
}
int HaiCrypt_Rx_Process(HaiCrypt_Handle hhc,
unsigned char *in_msg, size_t in_len,
void *out_p[], size_t out_len_p[], int maxout)
{
hcrypt_Session *crypto = (hcrypt_Session *)hhc;
hcrypt_Ctx *ctx;
int nbout = maxout;
int msg_type;
if ((NULL == crypto)
|| (NULL == in_msg)) {
HCRYPT_LOG(LOG_ERR, "%s", "invalid parameters\n");
return(-1);
}
/* Validate HaiCrypt message */
if (0 > (msg_type = crypto->msg_info->parseMsg(in_msg))) {
return(-1);
}
switch(msg_type) {
case HCRYPT_MSG_PT_MS: /* MSmsg */
ctx = &crypto->ctx_pair[hcryptMsg_GetKeyIndex(crypto->msg_info, in_msg)];
if ((NULL == out_p)
|| (NULL == out_len_p)) {
HCRYPT_LOG(LOG_ERR, "%s", "invalid parameters\n");
return(-1);
}
ASSERT(NULL != ctx); /* Header check should prevent this error */
ASSERT(NULL != crypto->cryspr); /* Header check should prevent this error */
crypto->ctx = ctx; /* Context of last received msg */
if (NULL == crypto->cryspr->ms_decrypt) {
HCRYPT_LOG(LOG_ERR, "%s", "cryspr had no decryptor\n");
nbout = -1;
} else if (ctx->status >= HCRYPT_CTX_S_KEYED) {
hcrypt_DataDesc indata;
indata.pfx = in_msg;
indata.payload = &in_msg[crypto->msg_info->pfx_len];
indata.len = in_len - crypto->msg_info->pfx_len;
if (crypto->cryspr->ms_decrypt(crypto->cryspr_cb, ctx, &indata, 1, out_p, out_len_p, &nbout)) {
HCRYPT_LOG(LOG_ERR, "%s", "ms_decrypt failed\n");
nbout = -1;
}
} else { /* No key received yet */
nbout = 0;
}
break;
case HCRYPT_MSG_PT_KM: /* KMmsg */
/* Even or Both SEKs check with even context */
ctx = &crypto->ctx_pair[hcryptMsg_GetKeyIndex(crypto->msg_info, in_msg)];
ASSERT(NULL != ctx); /* Header check should prevent this error */
if ((ctx->status < HCRYPT_CTX_S_KEYED) /* No key deciphered yet */
|| (in_len != ctx->KMmsg_len) /* or not same size */
|| (0 != memcmp(ctx->KMmsg_cache, in_msg, in_len))) { /* or different */
nbout = hcryptCtx_Rx_ParseKM(crypto, in_msg, in_len);
//-2: unmatched shared secret
//-1: other failures
//0: success
} else {
nbout = 0;
}
if (NULL != out_p) out_p[0] = NULL;
if (NULL != out_len_p) out_len_p[0] = 0;
break;
default:
HCRYPT_LOG(LOG_WARNING, "%s", "unknown packet type\n");
nbout = 0;
break;
}
return(nbout);
}

View file

@ -0,0 +1,96 @@
/*
* SRT - Secure, Reliable, Transport
* Copyright (c) 2018 Haivision Systems Inc.
*
* This Source Code Form is subject to the terms of the Mozilla Public
* License, v. 2.0. If a copy of the MPL was not distributed with this
* file, You can obtain one at http://mozilla.org/MPL/2.0/.
*
*/
/*****************************************************************************
written by
Haivision Systems Inc.
2011-06-23 (jdube)
HaiCrypt initial implementation.
*****************************************************************************/
/*
* For now:
* Pre-shared or password derived KEK (Key Encrypting Key)
* Future:
* Certificate-based association
*/
#include <string.h> /* memcpy */
#include "hcrypt.h"
int hcryptCtx_SetSecret(hcrypt_Session *crypto, hcrypt_Ctx *ctx, const HaiCrypt_Secret *secret)
{
int iret;
(void)crypto;
switch(secret->typ) {
case HAICRYPT_SECTYP_PRESHARED:
ASSERT(secret->len <= HAICRYPT_KEY_MAX_SZ);
ctx->cfg.pwd_len = 0;
/* KEK: Key Encrypting Key */
if (0 > (iret = crypto->cryspr->km_setkey(crypto->cryspr_cb,
(HCRYPT_CTX_F_ENCRYPT & ctx->flags ? true : false),
secret->str, secret->len))) {
HCRYPT_LOG(LOG_ERR, "km_setkey(pdkek[%zd]) failed (rc=%d)\n", secret->len, iret);
return(-1);
}
ctx->status = HCRYPT_CTX_S_SARDY;
break;
case HAICRYPT_SECTYP_PASSPHRASE:
ASSERT(secret->len <= sizeof(ctx->cfg.pwd));
memcpy(ctx->cfg.pwd, secret->str, secret->len);
ctx->cfg.pwd_len = secret->len;
/* KEK will be derived from password with Salt */
ctx->status = HCRYPT_CTX_S_SARDY;
break;
default:
HCRYPT_LOG(LOG_ERR, "Unknown secret type %d\n",
secret->typ);
return(-1);
}
return(0);
}
int hcryptCtx_GenSecret(hcrypt_Session *crypto, hcrypt_Ctx *ctx)
{
/*
* KEK need same length as the key it protects (SEK)
* KEK = PBKDF2(Pwd, LSB(64, Salt), Iter, Klen)
*/
unsigned char kek[HAICRYPT_KEY_MAX_SZ];
size_t kek_len = ctx->sek_len;
size_t pbkdf_salt_len = (ctx->salt_len >= HAICRYPT_PBKDF2_SALT_LEN
? HAICRYPT_PBKDF2_SALT_LEN
: ctx->salt_len);
int iret = 0;
(void)crypto;
iret = crypto->cryspr->km_pbkdf2(crypto->cryspr_cb, ctx->cfg.pwd, ctx->cfg.pwd_len,
&ctx->salt[ctx->salt_len - pbkdf_salt_len], pbkdf_salt_len,
HAICRYPT_PBKDF2_ITER_CNT, kek_len, kek);
if(iret) {
HCRYPT_LOG(LOG_ERR, "km_pbkdf2() failed (rc=%d)\n", iret);
return(-1);
}
HCRYPT_PRINTKEY(ctx->cfg.pwd, ctx->cfg.pwd_len, "pwd");
HCRYPT_PRINTKEY(kek, kek_len, "kek");
/* KEK: Key Encrypting Key */
if (0 > (iret = crypto->cryspr->km_setkey(crypto->cryspr_cb, (HCRYPT_CTX_F_ENCRYPT & ctx->flags ? true : false), kek, kek_len))) {
HCRYPT_LOG(LOG_ERR, "km_setkey(pdkek[%zd]) failed (rc=%d)\n", kek_len, iret);
return(-1);
}
return(0);
}

View file

@ -0,0 +1,174 @@
/*
* SRT - Secure, Reliable, Transport
* Copyright (c) 2018 Haivision Systems Inc.
*
* This Source Code Form is subject to the terms of the Mozilla Public
* License, v. 2.0. If a copy of the MPL was not distributed with this
* file, You can obtain one at http://mozilla.org/MPL/2.0/.
*
*/
/*****************************************************************************
written by
Haivision Systems Inc.
2011-06-23 (jdube)
HaiCrypt initial implementation.
2014-03-11 (jdube)
Adaptation for SRT.
*****************************************************************************/
#include <sys/types.h>
#include <stdlib.h> /* NULL */
#include <string.h> /* memcpy */
#ifdef _WIN32
#include <winsock2.h>
#include <ws2tcpip.h>
#include <stdint.h>
#else
#include <arpa/inet.h> /* htonl */
#endif
#include "hcrypt.h"
int HaiCrypt_Tx_GetBuf(HaiCrypt_Handle hhc, size_t data_len, unsigned char **in_pp)
{
hcrypt_Session *crypto = (hcrypt_Session *)hhc;
ASSERT(NULL != crypto);
ASSERT(NULL != crypto->cryspr);
int pad_factor = (HCRYPT_CTX_MODE_AESECB == crypto->ctx->mode ? 128/8 : 1);
#ifndef _WIN32
ASSERT(crypto->inbuf != NULL);
#endif
size_t in_len = crypto->msg_info->pfx_len + hcryptMsg_PaddedLen(data_len, pad_factor);
*in_pp = crypto->inbuf;
if (in_len > crypto->inbuf_siz) {
*in_pp = NULL;
return(-1);
}
return(crypto->msg_info->pfx_len);
}
int HaiCrypt_Tx_ManageKeys(HaiCrypt_Handle hhc, void *out_p[], size_t out_len_p[], int maxout)
{
hcrypt_Session *crypto = (hcrypt_Session *)hhc;
hcrypt_Ctx *ctx = NULL;
int nbout = 0;
if ((NULL == crypto)
|| (NULL == (ctx = crypto->ctx))
|| (NULL == out_p)
|| (NULL == out_len_p)) {
HCRYPT_LOG(LOG_ERR, "ManageKeys: invalid params: crypto=%p crypto->ctx=%p\n", crypto, ctx);
return(-1);
}
/* Manage Key Material (refresh, announce, decommission) */
hcryptCtx_Tx_ManageKM(crypto);
if (NULL == (ctx = crypto->ctx)) {
HCRYPT_LOG(LOG_ERR, "%s", "crypto context not defined\n");
return(-1);
}
ASSERT(ctx->status == HCRYPT_CTX_S_ACTIVE);
nbout = hcryptCtx_Tx_InjectKM(crypto, out_p, out_len_p, maxout);
return(nbout);
}
int HaiCrypt_Tx_GetKeyFlags(HaiCrypt_Handle hhc)
{
hcrypt_Session *crypto = (hcrypt_Session *)hhc;
hcrypt_Ctx *ctx = NULL;
if ((NULL == crypto)
|| (NULL == (ctx = crypto->ctx))){
HCRYPT_LOG(LOG_ERR, "GetKeyFlags: invalid params: crypto=%p crypto->ctx=%p\n", crypto, ctx);
return(-1);
}
return(hcryptCtx_GetKeyFlags(ctx));
}
int HaiCrypt_Tx_Data(HaiCrypt_Handle hhc,
unsigned char *in_pfx, unsigned char *in_data, size_t in_len)
{
hcrypt_Session *crypto = (hcrypt_Session *)hhc;
hcrypt_Ctx *ctx = NULL;
int nbout = 0;
if ((NULL == crypto)
|| (NULL == (ctx = crypto->ctx))){
HCRYPT_LOG(LOG_ERR, "Tx_Data: invalid params: crypto=%p crypto->ctx=%p\n", crypto, ctx);
return(-1);
}
/* Get/Set packet index */
ctx->msg_info->indexMsg(in_pfx, ctx->MSpfx_cache);
/* Encrypt */
{
hcrypt_DataDesc indata;
indata.pfx = in_pfx;
indata.payload = in_data;
indata.len = in_len;
if (0 > (nbout = crypto->cryspr->ms_encrypt(crypto->cryspr_cb, ctx, &indata, 1, NULL, NULL, NULL))) {
HCRYPT_LOG(LOG_ERR, "%s", "ms_encrypt failed\n");
return(nbout);
}
}
ctx->pkt_cnt++;
return(nbout);
}
int HaiCrypt_Tx_Process(HaiCrypt_Handle hhc,
unsigned char *in_msg, size_t in_len,
void *out_p[], size_t out_len_p[], int maxout)
{
hcrypt_Session *crypto = (hcrypt_Session *)hhc;
hcrypt_Ctx *ctx = NULL;
int nb, nbout = 0;
if ((NULL == crypto)
|| (NULL == (ctx = crypto->ctx))
|| (NULL == out_p)
|| (NULL == out_len_p)) {
HCRYPT_LOG(LOG_ERR, "Tx_Process: invalid params: crypto=%p crypto->ctx=%p\n", crypto, ctx);
return(-1);
}
/* Manage Key Material (refresh, announce, decommission) */
hcryptCtx_Tx_ManageKM(crypto);
if (NULL == (ctx = crypto->ctx)) {
HCRYPT_LOG(LOG_ERR, "%s", "crypto context not defined\n");
return(-1);
}
ASSERT(ctx->status == HCRYPT_CTX_S_ACTIVE);
nbout += hcryptCtx_Tx_InjectKM(crypto, out_p, out_len_p, maxout);
/* Get packet index */
ctx->msg_info->indexMsg(in_msg, ctx->MSpfx_cache);
/* Encrypt */
nb = maxout - nbout;
{
hcrypt_DataDesc indata;
indata.pfx = in_msg;
indata.payload = &in_msg[ctx->msg_info->pfx_len];
indata.len = in_len - ctx->msg_info->pfx_len;
if (crypto->cryspr->ms_encrypt(crypto->cryspr_cb, ctx, &indata, 1, &out_p[nbout], &out_len_p[nbout], &nb)) {
HCRYPT_LOG(LOG_ERR, "%s", "ms_encrypt failed\n");
return(nbout);
}
}
nbout += nb;
ctx->pkt_cnt++;
return(nbout);
}

View file

@ -0,0 +1,224 @@
/*
* SRT - Secure, Reliable, Transport
* Copyright (c) 2018 Haivision Systems Inc.
*
* This Source Code Form is subject to the terms of the Mozilla Public
* License, v. 2.0. If a copy of the MPL was not distributed with this
* file, You can obtain one at http://mozilla.org/MPL/2.0/.
*
*/
/*****************************************************************************
written by
Haivision Systems Inc.
2011-07-11 (jdube)
HaiCrypt initial implementation.
*****************************************************************************/
#include <string.h> /* memcpy */
#include <stdio.h>
#include <haicrypt.h>
#include "hcrypt.h"
#ifndef _WIN32
/* RFC6070 PBKDF2 Tests Vectors */
static struct TestVector {
size_t pwd_len;
const char *pwd;
size_t salt_len;
const unsigned char *salt;
int cnt;
size_t dk_len;
unsigned char dk[32];
} tv[] = {
{ /* 1 */
.pwd_len = 8, .pwd = "password",
.salt_len = 4, .salt = (unsigned char *)"salt",
.cnt = 1,
.dk_len = 20,
.dk = {
0x0c, 0x60, 0xc8, 0x0f, 0x96, 0x1f, 0x0e, 0x71,
0xf3, 0xa9, 0xb5, 0x24, 0xaf, 0x60, 0x12, 0x06,
0x2f, 0xe0, 0x37, 0xa6
}
},
{ /* 2 */
.pwd_len = 8, .pwd = "password",
.salt_len = 4, .salt = (unsigned char *)"salt",
.cnt = 2,
.dk_len = 20,
.dk = {
0xea, 0x6c, 0x01, 0x4d, 0xc7, 0x2d, 0x6f, 0x8c,
0xcd, 0x1e, 0xd9, 0x2a, 0xce, 0x1d, 0x41, 0xf0,
0xd8, 0xde, 0x89, 0x57
}
},
{ /* 3 */
.pwd_len = 8, .pwd = "password",
.salt_len = 4, .salt = (unsigned char *)"salt",
.cnt = 4096,
.dk_len = 20,
.dk = {
0x4b, 0x00, 0x79, 0x01, 0xb7, 0x65, 0x48, 0x9a,
0xbe, 0xad, 0x49, 0xd9, 0x26, 0xf7, 0x21, 0xd0,
0x65, 0xa4, 0x29, 0xc1
}
},
{ /* 4 */
.pwd_len = 8, .pwd = "password",
.salt_len = 4, .salt = (unsigned char *)"salt",
.cnt = 16777216,
.dk_len = 20,
.dk = {
0xee, 0xfe, 0x3d, 0x61, 0xcd, 0x4d, 0xa4, 0xe4,
0xe9, 0x94, 0x5b, 0x3d, 0x6b, 0xa2, 0x15, 0x8c,
0x26, 0x34, 0xe9, 0x84
}
},
{ /* 5 */
.pwd_len = 24, .pwd = "passwordPASSWORDpassword",
.salt_len = 36, .salt = (unsigned char *)"saltSALTsaltSALTsaltSALTsaltSALTsalt",
.cnt = 4096,
.dk_len = 25,
.dk = {
0x3d, 0x2e, 0xec, 0x4f, 0xe4, 0x1c, 0x84, 0x9b,
0x80, 0xc8, 0xd8, 0x36, 0x62, 0xc0, 0xe4, 0x4a,
0x8b, 0x29, 0x1a, 0x96, 0x4c, 0xf2, 0xf0, 0x70,
0x38
}
},
{ /* 6 */
.pwd_len = 9, .pwd = "pass\0word",
.salt_len = 5, .salt = (unsigned char *)"sa\0lt",
.cnt = 4096,
.dk_len = 16,
.dk = {
0x56, 0xfa, 0x6a, 0xa7, 0x55, 0x48, 0x09, 0x9d,
0xcc, 0x37, 0xd7, 0xf0, 0x34, 0x25, 0xe0, 0xc3
}
},
};
#include <sys/time.h>
static int hc_ut_pbkdf2(unsigned verbose)
{
int i;
int nbt = sizeof(tv)/sizeof(tv[0]);
int nbe = 0;
unsigned char dk[32];
struct timeval tstart, tstop, tdiff;
for (i=0; i<nbt; i++) {
if (verbose) {
printf("PBKDF2 test vector %d", i+1);
fflush(stdout);
gettimeofday(&tstart, NULL);
}
hcrypt_pbkdf2_hmac_sha1(tv[i].pwd, tv[i].pwd_len,
tv[i].salt, tv[i].salt_len,
tv[i].cnt, tv[i].dk_len, dk);
if (verbose) {
gettimeofday(&tstop, NULL);
timersub(&tstop, &tstart, &tdiff);
}
if(memcmp(dk, tv[i].dk, tv[i].dk_len)) {
if (verbose) {
printf(": failed in %lu.%06lu sec\n", tdiff.tv_sec, (unsigned long)tdiff.tv_usec);
} else {
printf("PBKDF2 test vector %d: failed\n", i+1);
}
nbe++;
} else if (verbose) {
printf(": passed in %lu.%06lu sec\n", tdiff.tv_sec, (unsigned long)tdiff.tv_usec);
}
}
return(nbe);
}
int hc_ut_encrypt_ctr_speed(void)
{
static HaiCrypt_Secret secret = {
.typ = HAICRYPT_SECTYP_PASSPHRASE,
.len = 12,
.str = "000000000000"
};
HaiCrypt_Cfg crypto_cfg;
HaiCrypt_Handle hcrypto;
struct timeval tstart, tstop, tdiff;
unsigned char pkt[1500];
int nbe = 0;
int i;
#ifdef HAICRYPT_USE_OPENSSL_EVP_CBC
HaiCrypt_Cipher HaiCryptCipher_OpenSSL_EVP_CBC(void); /* OpenSSL EVP interface CBC mode*/
#endif
memset(&crypto_cfg, 0, sizeof(crypto_cfg));
crypto_cfg.flags = HAICRYPT_CFG_F_CRYPTO | HAICRYPT_CFG_F_TX;
crypto_cfg.xport = HAICRYPT_XPT_SRT;
#ifdef HAICRYPT_USE_OPENSSL_EVP_CBC
crypto_cfg.cipher = HaiCryptCipher_OpenSSL_EVP_CBC();
#else
crypto_cfg.cipher = HaiCryptCipher_Get_Instance();
#endif
crypto_cfg.key_len = (size_t)128/8;
crypto_cfg.data_max_len = HAICRYPT_DEF_DATA_MAX_LENGTH; //MTU
crypto_cfg.km_tx_period_ms = 0;//No HaiCrypt KM inject period, handled in SRT;
crypto_cfg.km_refresh_rate_pkt = HAICRYPT_DEF_KM_REFRESH_RATE;
crypto_cfg.km_pre_announce_pkt = 0x10000; //HAICRYPT_DEF_KM_PRE_ANNOUNCE;
memcpy(&crypto_cfg.secret, &secret, sizeof(crypto_cfg.secret));
if (HaiCrypt_Create(&crypto_cfg, &hcrypto)) {
fprintf(stderr, "haicrypt: HaiCrypt_Create failed\n");
return(1);
}
for (i=0; i<1500; i++) {
pkt[i] = i & 0xff;
}
#define UT_NBPKTS 100000L
#define UT_PKTSZ (7*188)
gettimeofday(&tstart, NULL);
for (i=0; i<UT_NBPKTS; i++) {
if (0 > HaiCrypt_Tx_Data(hcrypto, &pkt[0], &pkt[16], UT_PKTSZ)) nbe++;
if (0 == (i % 1000)) {
printf("\b\b\b\b\b\b%6d", i);
fflush(stdout);
}
}
gettimeofday(&tstop, NULL);
timersub(&tstop, &tstart, &tdiff);
printf("\nhaicrypt: encrypted %ld packets in %lu.%06lu sec (%ld.%03ld kbps)\n",
UT_NBPKTS, tdiff.tv_sec, (unsigned long)tdiff.tv_usec,
(((UT_NBPKTS * UT_PKTSZ*10)/((tdiff.tv_sec*10) + (tdiff.tv_usec/100))) / 1000),
(((UT_NBPKTS * UT_PKTSZ*10)/((tdiff.tv_sec*10) + (tdiff.tv_usec/100))) % 1000));
HaiCrypt_Close(hcrypto);
return(nbe);
}
int main(int argc, char *argv[])
{
int nbe = 0;
(void)argc;
(void)argv;
nbe += hc_ut_encrypt_ctr_speed();
nbe += hc_ut_pbkdf2(1);
printf("haicrypt unit test %s: %d errors found\n", nbe ? "failed" : "passed", nbe);
return(nbe);
}
#endif // _WIN32

View file

@ -0,0 +1,171 @@
/*
* SRT - Secure, Reliable, Transport
* Copyright (c) 2018 Haivision Systems Inc.
*
* This Source Code Form is subject to the terms of the Mozilla Public
* License, v. 2.0. If a copy of the MPL was not distributed with this
* file, You can obtain one at http://mozilla.org/MPL/2.0/.
*
*/
/*****************************************************************************
written by
Haivision Systems Inc.
2014-03-11 (jdube)
Adaptation for SRT.
*****************************************************************************/
#include <string.h> /* memset, memcpy */
#ifdef _WIN32
#include <winsock2.h>
#include <ws2tcpip.h>
#else
#include <arpa/inet.h> /* htonl, ntohl */
#endif
#include "hcrypt.h"
/*
* HaiCrypt SRT (Secure Reliable Transport) Media Stream (MS) Msg Prefix:
* This is UDT data header with Crypto Key Flags (KF) added.
* Header is in 32bit host order words in the context of the functions of this handler.
*
* 0 1 2 3
* 0 1 2 3 4 5 6 7 8 9 0 1 2 3 4 5 6 7 8 9 0 1 2 3 4 5 6 7 8 9 0 1
* +-+-+-+-+-+-+-+-|-+-+-+-+-+-+-+-|-+-+-+-+-+-+-+-|-+-+-+-+-+-+-+-+
* 0x00 |0| Packet Sequence Number (pki) |
* +-+-+-+-+-+-+-+-|-+-+-+-+-+-+-+-|-+-+-+-+-+-+-+-|-+-+-+-+-+-+-+-+
* 0x04 |FF |o|KF | Message Number |
* +-+-+-+-+-+-+-+-|-+-+-+-+-+-+-+-|-+-+-+-+-+-+-+-|-+-+-+-+-+-+-+-+
* 0x08 | Time Stamp |
* +-+-+-+-+-+-+-+-|-+-+-+-+-+-+-+-|-+-+-+-+-+-+-+-|-+-+-+-+-+-+-+-+
* 0x0C | Destination Socket ID) |
* +-+-+-+-+-+-+-+-|-+-+-+-+-+-+-+-|-+-+-+-+-+-+-+-|-+-+-+-+-+-+-+-+
* | Payload... |
*/
/*
* HaiCrypt Standalone Transport Keying Material (KM) Msg header kept in SRT
* Message and cache maintained in network order
*
* 0 1 2 3
* 0 1 2 3 4 5 6 7 8 9 0 1 2 3 4 5 6 7 8 9 0 1 2 3 4 5 6 7 8 9 0 1
* +-+-+-+-+-+-+-+-|-+-+-+-+-+-+-+-|-+-+-+-+-+-+-+-|-+-+-+-+-+-+-+-+
* 0x00 |0|Vers | PT | Sign | resv |
* +-+-+-+-+-+-+-+-|-+-+-+-+-+-+-+-|-+-+-+-+-+-+-+-|-+-+-+-+-+-+-+-+
* ... .
*/
#define HCRYPT_MSG_SRT_HDR_SZ 16
#define HCRYPT_MSG_SRT_PFX_SZ 16
#define HCRYPT_MSG_SRT_OFS_PKI 0
#define HCRYPT_MSG_SRT_OFS_MSGNO 4
#define HCRYPT_MSG_SRT_SHF_KFLGS 27 //shift
static hcrypt_MsgInfo _hcMsg_SRT_MsgInfo;
static unsigned hcryptMsg_SRT_GetKeyFlags(unsigned char *msg)
{
uint32_t msgno;
memcpy(&msgno, &msg[HCRYPT_MSG_SRT_OFS_MSGNO], sizeof(msgno)); //header is in host order
return((unsigned)((msgno >> HCRYPT_MSG_SRT_SHF_KFLGS) & HCRYPT_MSG_F_xSEK));
}
static hcrypt_Pki hcryptMsg_SRT_GetPki(unsigned char *msg, int nwkorder)
{
hcrypt_Pki pki;
memcpy(&pki, &msg[HCRYPT_MSG_SRT_OFS_PKI], sizeof(pki)); //header is in host order
return (nwkorder ? htonl(pki) : pki);
}
static void hcryptMsg_SRT_SetPki(unsigned char *msg, hcrypt_Pki pki)
{
memcpy(&msg[HCRYPT_MSG_SRT_OFS_PKI], &pki, sizeof(pki)); //header is in host order
}
static void hcryptMsg_SRT_ResetCache(unsigned char *pfx_cache, unsigned pkt_type, unsigned kflgs)
{
switch(pkt_type) {
case HCRYPT_MSG_PT_MS: /* Media Stream */
/* Nothing to do, header filled by protocol */
break;
case HCRYPT_MSG_PT_KM: /* Keying Material */
pfx_cache[HCRYPT_MSG_KM_OFS_VERSION] = (unsigned char)((HCRYPT_MSG_VERSION << 4) | pkt_type); // version || PT
pfx_cache[HCRYPT_MSG_KM_OFS_SIGN] = (unsigned char)((HCRYPT_MSG_SIGN >> 8) & 0xFF); // Haivision PnP Mfr ID
pfx_cache[HCRYPT_MSG_KM_OFS_SIGN+1] = (unsigned char)(HCRYPT_MSG_SIGN & 0xFF);
pfx_cache[HCRYPT_MSG_KM_OFS_KFLGS] = (unsigned char)kflgs; //HCRYPT_MSG_F_xxx
break;
default:
break;
}
}
static void hcryptMsg_SRT_IndexMsg(unsigned char *msg, unsigned char *pfx_cache)
{
(void)msg;
(void)pfx_cache;
return; //nothing to do, header and index maintained by SRT
}
static int hcryptMsg_SRT_ParseMsg(unsigned char *msg)
{
int rc;
if ((HCRYPT_MSG_VERSION == hcryptMsg_KM_GetVersion(msg)) /* Version 1 */
&& (HCRYPT_MSG_PT_KM == hcryptMsg_KM_GetPktType(msg)) /* Keying Material */
&& (HCRYPT_MSG_SIGN == hcryptMsg_KM_GetSign(msg))) { /* 'HAI' PnP Mfr ID */
rc = HCRYPT_MSG_PT_KM;
} else {
//Assume it's data.
//SRT does not call this for MS msg
rc = HCRYPT_MSG_PT_MS;
}
switch(rc) {
case HCRYPT_MSG_PT_MS:
if (hcryptMsg_HasNoSek(&_hcMsg_SRT_MsgInfo, msg)
|| hcryptMsg_HasBothSek(&_hcMsg_SRT_MsgInfo, msg)) {
HCRYPT_LOG(LOG_ERR, "invalid MS msg flgs: %02x\n",
hcryptMsg_GetKeyIndex(&_hcMsg_SRT_MsgInfo, msg));
return(-1);
}
break;
case HCRYPT_MSG_PT_KM:
if (HCRYPT_SE_TSSRT != hcryptMsg_KM_GetSE(msg)) { //Check Stream Encapsulation (SE)
HCRYPT_LOG(LOG_ERR, "invalid KM msg SE: %d\n",
hcryptMsg_KM_GetSE(msg));
return(-1);
}
if (hcryptMsg_KM_HasNoSek(msg)) {
HCRYPT_LOG(LOG_ERR, "invalid KM msg flgs: %02x\n",
hcryptMsg_KM_GetKeyIndex(msg));
return(-1);
}
break;
default:
HCRYPT_LOG(LOG_ERR, "invalid pkt type: %d\n", rc);
rc = 0; /* unknown packet type */
break;
}
return(rc); /* -1: error, 0: unknown: >0: PT */
}
static hcrypt_MsgInfo _hcMsg_SRT_MsgInfo;
hcrypt_MsgInfo *hcryptMsg_SRT_MsgInfo(void)
{
_hcMsg_SRT_MsgInfo.hdr_len = HCRYPT_MSG_SRT_HDR_SZ;
_hcMsg_SRT_MsgInfo.pfx_len = HCRYPT_MSG_SRT_PFX_SZ;
_hcMsg_SRT_MsgInfo.getKeyFlags = hcryptMsg_SRT_GetKeyFlags;
_hcMsg_SRT_MsgInfo.getPki = hcryptMsg_SRT_GetPki;
_hcMsg_SRT_MsgInfo.setPki = hcryptMsg_SRT_SetPki;
_hcMsg_SRT_MsgInfo.resetCache = hcryptMsg_SRT_ResetCache;
_hcMsg_SRT_MsgInfo.indexMsg = hcryptMsg_SRT_IndexMsg;
_hcMsg_SRT_MsgInfo.parseMsg = hcryptMsg_SRT_ParseMsg;
return(&_hcMsg_SRT_MsgInfo);
}

View file

@ -0,0 +1,180 @@
/*
* SRT - Secure, Reliable, Transport
* Copyright (c) 2018 Haivision Systems Inc.
*
* This Source Code Form is subject to the terms of the Mozilla Public
* License, v. 2.0. If a copy of the MPL was not distributed with this
* file, You can obtain one at http://mozilla.org/MPL/2.0/.
*
*/
/*****************************************************************************
written by
Haivision Systems Inc.
2011-06-23 (jdube)
HaiCrypt initial implementation.
2014-03-11 (jdube)
Adaptation for SRT.
*****************************************************************************/
#include <string.h> /* memset, memcpy */
#include <time.h> /* time() */
#ifdef _WIN32
#include <winsock2.h>
#include <ws2tcpip.h>
#else
#include <arpa/inet.h> /* htonl, ntohl */
#endif
#include "hcrypt.h"
/*
* HaiCrypt Standalone Transport Media Stream (MS) Data Msg Prefix:
* Cache maintained in network order
*
* 0 1 2 3
* 0 1 2 3 4 5 6 7 8 9 0 1 2 3 4 5 6 7 8 9 0 1 2 3 4 5 6 7 8 9 0 1
* +-+-+-+-+-+-+-+-|-+-+-+-+-+-+-+-|-+-+-+-+-+-+-+-|-+-+-+-+-+-+-+-+
* 0x00 |0|Vers | PT | Sign | resv |KF |
* +-+-+-+-+-+-+-+-|-+-+-+-+-+-+-+-|-+-+-+-+-+-+-+-|-+-+-+-+-+-+-+-+
* 0x04 | pki |
* +-+-+-+-+-+-+-+-|-+-+-+-+-+-+-+-|-+-+-+-+-+-+-+-|-+-+-+-+-+-+-+-+
* | payload... |
*/
/*
* HaiCrypt Standalone Transport Keying Material (KM) Msg (no prefix, use KM Msg directly):
* Cache maintained in network order
*
* 0 1 2 3
* 0 1 2 3 4 5 6 7 8 9 0 1 2 3 4 5 6 7 8 9 0 1 2 3 4 5 6 7 8 9 0 1
* +-+-+-+-+-+-+-+-|-+-+-+-+-+-+-+-|-+-+-+-+-+-+-+-|-+-+-+-+-+-+-+-+
* 0x00 |0|Vers | PT | Sign | resv |
* +-+-+-+-+-+-+-+-|-+-+-+-+-+-+-+-|-+-+-+-+-+-+-+-|-+-+-+-+-+-+-+-+
* ... .
*/
#define HCRYPT_MSG_STA_HDR_SZ 4
#define HCRYPT_MSG_STA_PKI_SZ 4
#define HCRYPT_MSG_STA_PFX_SZ (HCRYPT_MSG_STA_HDR_SZ + HCRYPT_MSG_STA_PKI_SZ)
#define HCRYPT_MSG_STA_OFS_VERSION HCRYPT_MSG_KM_OFS_VERSION
#define HCRYPT_MSG_STA_OFS_PT HCRYPT_MSG_KM_OFS_PT
#define HCRYPT_MSG_STA_OFS_SIGN HCRYPT_MSG_KM_OFS_SIGN
#define HCRYPT_MSG_STA_OFS_KFLGS HCRYPT_MSG_KM_OFS_KFLGS
#define HCRYPT_MSG_STA_OFS_PKI HCRYPT_MSG_STA_HDR_SZ
#define hcryptMsg_STA_GetVersion(msg) (((msg)[HCRYPT_MSG_STA_OFS_VERSION]>>4)& 0xF)
#define hcryptMsg_STA_GetPktType(msg) (((msg)[HCRYPT_MSG_STA_OFS_PT]) & 0xF)
#define hcryptMsg_STA_GetSign(msg) (((msg)[HCRYPT_MSG_STA_OFS_SIGN]<<8) | (msg)[HCRYPT_MSG_STA_OFS_SIGN+1])
static hcrypt_MsgInfo _hcMsg_STA_MsgInfo;
static unsigned hcryptMsg_STA_GetKeyFlags(unsigned char *msg)
{
return((unsigned)(msg[HCRYPT_MSG_STA_OFS_KFLGS] & HCRYPT_MSG_F_xSEK));
}
static hcrypt_Pki hcryptMsg_STA_GetPki(unsigned char *msg, int nwkorder)
{
hcrypt_Pki pki;
memcpy(&pki, &msg[HCRYPT_MSG_STA_OFS_PKI], sizeof(pki)); //header is in host order
return (nwkorder ? pki : ntohl(pki));
}
static void hcryptMsg_STA_SetPki(unsigned char *msg, hcrypt_Pki pki)
{
hcrypt_Pki nwk_pki = htonl(pki);
memcpy(&msg[HCRYPT_MSG_STA_OFS_PKI], &nwk_pki, sizeof(nwk_pki)); //header is in host order
}
static void hcryptMsg_STA_ResetCache(unsigned char *pfx_cache, unsigned pkt_type, unsigned kflgs)
{
pfx_cache[HCRYPT_MSG_STA_OFS_VERSION] = (unsigned char)((HCRYPT_MSG_VERSION << 4) | pkt_type); // version || PT
pfx_cache[HCRYPT_MSG_STA_OFS_SIGN] = (unsigned char)((HCRYPT_MSG_SIGN >> 8) & 0xFF); // Haivision PnP Mfr ID
pfx_cache[HCRYPT_MSG_STA_OFS_SIGN+1] = (unsigned char)(HCRYPT_MSG_SIGN & 0xFF);
switch(pkt_type) {
case HCRYPT_MSG_PT_MS:
pfx_cache[HCRYPT_MSG_STA_OFS_KFLGS] = (unsigned char)kflgs; //HCRYPT_MSG_F_xxx
hcryptMsg_STA_SetPki(pfx_cache, 0);
break;
case HCRYPT_MSG_PT_KM:
pfx_cache[HCRYPT_MSG_KM_OFS_KFLGS] = (unsigned char)kflgs; //HCRYPT_MSG_F_xxx
break;
default:
break;
}
}
static void hcryptMsg_STA_IndexMsg(unsigned char *msg, unsigned char *pfx_cache)
{
hcrypt_Pki pki = hcryptMsg_STA_GetPki(pfx_cache, 0); //Get in host order
memcpy(msg, pfx_cache, HCRYPT_MSG_STA_PFX_SZ);
hcryptMsg_SetPki(&_hcMsg_STA_MsgInfo, pfx_cache, ++pki);
}
static time_t _tLastLogTime = 0;
static int hcryptMsg_STA_ParseMsg(unsigned char *msg)
{
int rc;
if ((HCRYPT_MSG_VERSION != hcryptMsg_STA_GetVersion(msg)) /* Version 1 */
|| (HCRYPT_MSG_SIGN != hcryptMsg_STA_GetSign(msg))) { /* 'HAI' PnP Mfr ID */
time_t tCurrentTime = time(NULL);
// invalid data
if ((tCurrentTime - _tLastLogTime) >= 2 || (0 == _tLastLogTime))
{
_tLastLogTime = tCurrentTime;
HCRYPT_LOG(LOG_ERR, "invalid msg hdr: 0x%02x %02x%02x %02x\n",
msg[0], msg[1], msg[2], msg[3]);
}
return(-1); /* Invalid packet */
}
rc = hcryptMsg_STA_GetPktType(msg);
switch(rc) {
case HCRYPT_MSG_PT_MS:
if (hcryptMsg_HasNoSek(&_hcMsg_STA_MsgInfo, msg)
|| hcryptMsg_HasBothSek(&_hcMsg_STA_MsgInfo, msg)) {
HCRYPT_LOG(LOG_ERR, "invalid MS msg flgs: %02x\n",
hcryptMsg_GetKeyIndex(&_hcMsg_STA_MsgInfo, msg));
return(-1);
}
break;
case HCRYPT_MSG_PT_KM:
if (HCRYPT_SE_TSUDP != hcryptMsg_KM_GetSE(msg)) {
HCRYPT_LOG(LOG_ERR, "invalid KM msg SE: %d\n",
hcryptMsg_KM_GetSE(msg));
} else if (hcryptMsg_KM_HasNoSek(msg)) {
HCRYPT_LOG(LOG_ERR, "invalid KM msg flgs: %02x\n",
hcryptMsg_KM_GetKeyIndex(msg));
return(-1);
}
break;
default:
HCRYPT_LOG(LOG_ERR, "invalid pkt type: %d\n", rc);
rc = 0; /* unknown packet type */
break;
}
return(rc); /* -1: error, 0: unknown: >0: PT */
}
static hcrypt_MsgInfo _hcMsg_STA_MsgInfo;
hcrypt_MsgInfo *hcryptMsg_STA_MsgInfo(void)
{
_hcMsg_STA_MsgInfo.hdr_len = HCRYPT_MSG_STA_HDR_SZ;
_hcMsg_STA_MsgInfo.pfx_len = HCRYPT_MSG_STA_PFX_SZ;
_hcMsg_STA_MsgInfo.getKeyFlags = hcryptMsg_STA_GetKeyFlags;
_hcMsg_STA_MsgInfo.getPki = hcryptMsg_STA_GetPki;
_hcMsg_STA_MsgInfo.setPki = hcryptMsg_STA_SetPki;
_hcMsg_STA_MsgInfo.resetCache = hcryptMsg_STA_ResetCache;
_hcMsg_STA_MsgInfo.indexMsg = hcryptMsg_STA_IndexMsg;
_hcMsg_STA_MsgInfo.parseMsg = hcryptMsg_STA_ParseMsg;
return(&_hcMsg_STA_MsgInfo);
}