nfqws: QUIC initial dissection support

This commit is contained in:
bol-van 2022-03-25 16:59:58 +03:00
parent f10d17469e
commit dce5b4c6f0
30 changed files with 3421 additions and 54 deletions

Binary file not shown.

Binary file not shown.

Binary file not shown.

Binary file not shown.

Binary file not shown.

Binary file not shown.

Binary file not shown.

Binary file not shown.

Binary file not shown.

View File

@ -422,7 +422,7 @@ UDP attacks are limited. Its not possible to fragment UDP on transport level, on
Only desync modes `fake`,`hopbyhop`,`destopt`,`ipfrag1` and `ipfrag2` are applicable.
`fake`,`hopbyhop`,`destopt` can be used in combo with `ipfrag2`.
QUIC initial packets are recognized. Decryption and hostname extraction is not supported so `--hostlist` parameter will not work.
QUIC initial packets are recognized. Decryption and hostname extraction is supported so `--hostlist` parameter will work.
For other protocols desync use `--dpi-desync-any-protocol`.
Conntrack supports udp. `--dpi-desync-cutoff` will work. UDP conntrack timeout can be set in the 4th parameter of `--ctrack-timeouts`.

View File

@ -455,8 +455,8 @@ window size итоговый размер окна стал максимальн
Атаки на udp более ограничены в возможностях. udp нельзя фрагментировать иначе, чем на уровне ip.
Для UDP действуют только режимы десинхронизации fake,hopbyhop,destopt,ipfrag1,ipfrag2.
Возможно сочетание fake,hopbyhop,destopt с ipfrag2.
Поддерживается определение пакетов QUIC Initial без расшифровки содержимого и имени хоста, то есть параметр
--hostlist не будет работать. Для десинхронизации других протоколов обязательно указывать --dpi-desync-any-protocol.
Поддерживается определение пакетов QUIC Initial с расшифровкой содержимого и имени хоста, то есть параметр
--hostlist будет работать. Для десинхронизации других протоколов обязательно указывать --dpi-desync-any-protocol.
Реализован conntrack для udp. Можно пользоваться --dpi-desync-cutoff. Таймаут conntrack для udp
можно изменить 4-м параметром в --ctrack-timeouts.
Атака fake полезна только для stateful DPI, она бесполезна для анализа на уровне отдельных пакетов.

View File

@ -1,7 +1,7 @@
CC ?= cc
CFLAGS += -std=gnu99 -s -O3 -Wno-address-of-packed-member -Wno-logical-op-parentheses -Wno-switch
LIBS = -lz
SRC_FILES = *.c
SRC_FILES = *.c crypto/*.c
all: dvtws

View File

@ -4,7 +4,7 @@ CFLAGS_BSD = -Wno-address-of-packed-member -Wno-switch
CFLAGS_MAC = -mmacosx-version-min=10.8
LIBS = -lnetfilter_queue -lnfnetlink -lz
LIBS_BSD = -lz
SRC_FILES = *.c
SRC_FILES = *.c crypto/*.c
all: nfqws

38
nfq/crypto/aes-gcm.c Normal file
View File

@ -0,0 +1,38 @@
#include "aes-gcm.h"
int aes_gcm_encrypt(unsigned char* output, const unsigned char* input, size_t input_length, const unsigned char* key, const size_t key_len, const unsigned char * iv, const size_t iv_len) {
int ret = 0; // our return value
gcm_context ctx; // includes the AES context structure
size_t tag_len = 0;
unsigned char * tag_buf = NULL;
gcm_setkey(&ctx, key, (const uint)key_len);
ret = gcm_crypt_and_tag(&ctx, ENCRYPT, iv, iv_len, NULL, 0,
input, output, input_length, tag_buf, tag_len);
gcm_zero_ctx(&ctx);
return(ret);
}
int aes_gcm_decrypt(unsigned char* output, const unsigned char* input, size_t input_length, const unsigned char* key, const size_t key_len, const unsigned char * iv, const size_t iv_len) {
int ret = 0; // our return value
gcm_context ctx; // includes the AES context structure
size_t tag_len = 0;
unsigned char * tag_buf = NULL;
gcm_setkey(&ctx, key, (const uint)key_len);
ret = gcm_crypt_and_tag(&ctx, DECRYPT, iv, iv_len, NULL, 0,
input, output, input_length, tag_buf, tag_len);
gcm_zero_ctx(&ctx);
return(ret);
}

6
nfq/crypto/aes-gcm.h Normal file
View File

@ -0,0 +1,6 @@
#pragma once
#include "gcm.h"
int aes_gcm_encrypt(unsigned char* output, const unsigned char* input, size_t input_length, const unsigned char* key, const size_t key_len, const unsigned char * iv, const size_t iv_len);
int aes_gcm_decrypt(unsigned char* output, const unsigned char* input, size_t input_length, const unsigned char* key, const size_t key_len, const unsigned char * iv, const size_t iv_len);

483
nfq/crypto/aes.c Normal file
View File

@ -0,0 +1,483 @@
/******************************************************************************
*
* THIS SOURCE CODE IS HEREBY PLACED INTO THE PUBLIC DOMAIN FOR THE GOOD OF ALL
*
* This is a simple and straightforward implementation of the AES Rijndael
* 128-bit block cipher designed by Vincent Rijmen and Joan Daemen. The focus
* of this work was correctness & accuracy. It is written in 'C' without any
* particular focus upon optimization or speed. It should be endian (memory
* byte order) neutral since the few places that care are handled explicitly.
*
* This implementation of Rijndael was created by Steven M. Gibson of GRC.com.
*
* It is intended for general purpose use, but was written in support of GRC's
* reference implementation of the SQRL (Secure Quick Reliable Login) client.
*
* See: http://csrc.nist.gov/archive/aes/rijndael/wsdindex.html
*
* NO COPYRIGHT IS CLAIMED IN THIS WORK, HOWEVER, NEITHER IS ANY WARRANTY MADE
* REGARDING ITS FITNESS FOR ANY PARTICULAR PURPOSE. USE IT AT YOUR OWN RISK.
*
*******************************************************************************/
#include "aes.h"
static int aes_tables_inited = 0; // run-once flag for performing key
// expasion table generation (see below)
/*
* The following static local tables must be filled-in before the first use of
* the GCM or AES ciphers. They are used for the AES key expansion/scheduling
* and once built are read-only and thread safe. The "gcm_initialize" function
* must be called once during system initialization to populate these arrays
* for subsequent use by the AES key scheduler. If they have not been built
* before attempted use, an error will be returned to the caller.
*
* NOTE: GCM Encryption/Decryption does NOT REQUIRE AES decryption. Since
* GCM uses AES in counter-mode, where the AES cipher output is XORed with
* the GCM input, we ONLY NEED AES encryption. Thus, to save space AES
* decryption is typically disabled by setting AES_DECRYPTION to 0 in aes.h.
*/
// We always need our forward tables
static uchar FSb[256]; // Forward substitution box (FSb)
static uint32_t FT0[256]; // Forward key schedule assembly tables
static uint32_t FT1[256];
static uint32_t FT2[256];
static uint32_t FT3[256];
#if AES_DECRYPTION // We ONLY need reverse for decryption
static uchar RSb[256]; // Reverse substitution box (RSb)
static uint32_t RT0[256]; // Reverse key schedule assembly tables
static uint32_t RT1[256];
static uint32_t RT2[256];
static uint32_t RT3[256];
#endif /* AES_DECRYPTION */
static uint32_t RCON[10]; // AES round constants
/*
* Platform Endianness Neutralizing Load and Store Macro definitions
* AES wants platform-neutral Little Endian (LE) byte ordering
*/
#define GET_UINT32_LE(n,b,i) { \
(n) = ( (uint32_t) (b)[(i) ] ) \
| ( (uint32_t) (b)[(i) + 1] << 8 ) \
| ( (uint32_t) (b)[(i) + 2] << 16 ) \
| ( (uint32_t) (b)[(i) + 3] << 24 ); }
#define PUT_UINT32_LE(n,b,i) { \
(b)[(i) ] = (uchar) ( (n) ); \
(b)[(i) + 1] = (uchar) ( (n) >> 8 ); \
(b)[(i) + 2] = (uchar) ( (n) >> 16 ); \
(b)[(i) + 3] = (uchar) ( (n) >> 24 ); }
/*
* AES forward and reverse encryption round processing macros
*/
#define AES_FROUND(X0,X1,X2,X3,Y0,Y1,Y2,Y3) \
{ \
X0 = *RK++ ^ FT0[ ( Y0 ) & 0xFF ] ^ \
FT1[ ( Y1 >> 8 ) & 0xFF ] ^ \
FT2[ ( Y2 >> 16 ) & 0xFF ] ^ \
FT3[ ( Y3 >> 24 ) & 0xFF ]; \
\
X1 = *RK++ ^ FT0[ ( Y1 ) & 0xFF ] ^ \
FT1[ ( Y2 >> 8 ) & 0xFF ] ^ \
FT2[ ( Y3 >> 16 ) & 0xFF ] ^ \
FT3[ ( Y0 >> 24 ) & 0xFF ]; \
\
X2 = *RK++ ^ FT0[ ( Y2 ) & 0xFF ] ^ \
FT1[ ( Y3 >> 8 ) & 0xFF ] ^ \
FT2[ ( Y0 >> 16 ) & 0xFF ] ^ \
FT3[ ( Y1 >> 24 ) & 0xFF ]; \
\
X3 = *RK++ ^ FT0[ ( Y3 ) & 0xFF ] ^ \
FT1[ ( Y0 >> 8 ) & 0xFF ] ^ \
FT2[ ( Y1 >> 16 ) & 0xFF ] ^ \
FT3[ ( Y2 >> 24 ) & 0xFF ]; \
}
#define AES_RROUND(X0,X1,X2,X3,Y0,Y1,Y2,Y3) \
{ \
X0 = *RK++ ^ RT0[ ( Y0 ) & 0xFF ] ^ \
RT1[ ( Y3 >> 8 ) & 0xFF ] ^ \
RT2[ ( Y2 >> 16 ) & 0xFF ] ^ \
RT3[ ( Y1 >> 24 ) & 0xFF ]; \
\
X1 = *RK++ ^ RT0[ ( Y1 ) & 0xFF ] ^ \
RT1[ ( Y0 >> 8 ) & 0xFF ] ^ \
RT2[ ( Y3 >> 16 ) & 0xFF ] ^ \
RT3[ ( Y2 >> 24 ) & 0xFF ]; \
\
X2 = *RK++ ^ RT0[ ( Y2 ) & 0xFF ] ^ \
RT1[ ( Y1 >> 8 ) & 0xFF ] ^ \
RT2[ ( Y0 >> 16 ) & 0xFF ] ^ \
RT3[ ( Y3 >> 24 ) & 0xFF ]; \
\
X3 = *RK++ ^ RT0[ ( Y3 ) & 0xFF ] ^ \
RT1[ ( Y2 >> 8 ) & 0xFF ] ^ \
RT2[ ( Y1 >> 16 ) & 0xFF ] ^ \
RT3[ ( Y0 >> 24 ) & 0xFF ]; \
}
/*
* These macros improve the readability of the key
* generation initialization code by collapsing
* repetitive common operations into logical pieces.
*/
#define ROTL8(x) ( ( x << 8 ) & 0xFFFFFFFF ) | ( x >> 24 )
#define XTIME(x) ( ( x << 1 ) ^ ( ( x & 0x80 ) ? 0x1B : 0x00 ) )
#define MUL(x,y) ( ( x && y ) ? pow[(log[x]+log[y]) % 255] : 0 )
#define MIX(x,y) { y = ( (y << 1) | (y >> 7) ) & 0xFF; x ^= y; }
#define CPY128 { *RK++ = *SK++; *RK++ = *SK++; \
*RK++ = *SK++; *RK++ = *SK++; }
/******************************************************************************
*
* AES_INIT_KEYGEN_TABLES
*
* Fills the AES key expansion tables allocated above with their static
* data. This is not "per key" data, but static system-wide read-only
* table data. THIS FUNCTION IS NOT THREAD SAFE. It must be called once
* at system initialization to setup the tables for all subsequent use.
*
******************************************************************************/
void aes_init_keygen_tables(void)
{
int i, x, y, z; // general purpose iteration and computation locals
int pow[256];
int log[256];
if (aes_tables_inited) return;
// fill the 'pow' and 'log' tables over GF(2^8)
for (i = 0, x = 1; i < 256; i++) {
pow[i] = x;
log[x] = i;
x = (x ^ XTIME(x)) & 0xFF;
}
// compute the round constants
for (i = 0, x = 1; i < 10; i++) {
RCON[i] = (uint32_t)x;
x = XTIME(x) & 0xFF;
}
// fill the forward and reverse substitution boxes
FSb[0x00] = 0x63;
#if AES_DECRYPTION // whether AES decryption is supported
RSb[0x63] = 0x00;
#endif /* AES_DECRYPTION */
for (i = 1; i < 256; i++) {
x = y = pow[255 - log[i]];
MIX(x, y);
MIX(x, y);
MIX(x, y);
MIX(x, y);
FSb[i] = (uchar)(x ^= 0x63);
#if AES_DECRYPTION // whether AES decryption is supported
RSb[x] = (uchar)i;
#endif /* AES_DECRYPTION */
}
// generate the forward and reverse key expansion tables
for (i = 0; i < 256; i++) {
x = FSb[i];
y = XTIME(x) & 0xFF;
z = (y ^ x) & 0xFF;
FT0[i] = ((uint32_t)y) ^ ((uint32_t)x << 8) ^
((uint32_t)x << 16) ^ ((uint32_t)z << 24);
FT1[i] = ROTL8(FT0[i]);
FT2[i] = ROTL8(FT1[i]);
FT3[i] = ROTL8(FT2[i]);
#if AES_DECRYPTION // whether AES decryption is supported
x = RSb[i];
RT0[i] = ((uint32_t)MUL(0x0E, x)) ^
((uint32_t)MUL(0x09, x) << 8) ^
((uint32_t)MUL(0x0D, x) << 16) ^
((uint32_t)MUL(0x0B, x) << 24);
RT1[i] = ROTL8(RT0[i]);
RT2[i] = ROTL8(RT1[i]);
RT3[i] = ROTL8(RT2[i]);
#endif /* AES_DECRYPTION */
}
aes_tables_inited = 1; // flag that the tables have been generated
} // to permit subsequent use of the AES cipher
/******************************************************************************
*
* AES_SET_ENCRYPTION_KEY
*
* This is called by 'aes_setkey' when we're establishing a key for
* subsequent encryption. We give it a pointer to the encryption
* context, a pointer to the key, and the key's length in bytes.
* Valid lengths are: 16, 24 or 32 bytes (128, 192, 256 bits).
*
******************************************************************************/
int aes_set_encryption_key(aes_context *ctx,
const uchar *key,
uint keysize)
{
uint i; // general purpose iteration local
uint32_t *RK = ctx->rk; // initialize our RoundKey buffer pointer
for (i = 0; i < (keysize >> 2); i++) {
GET_UINT32_LE(RK[i], key, i << 2);
}
switch (ctx->rounds)
{
case 10:
for (i = 0; i < 10; i++, RK += 4) {
RK[4] = RK[0] ^ RCON[i] ^
((uint32_t)FSb[(RK[3] >> 8) & 0xFF]) ^
((uint32_t)FSb[(RK[3] >> 16) & 0xFF] << 8) ^
((uint32_t)FSb[(RK[3] >> 24) & 0xFF] << 16) ^
((uint32_t)FSb[(RK[3]) & 0xFF] << 24);
RK[5] = RK[1] ^ RK[4];
RK[6] = RK[2] ^ RK[5];
RK[7] = RK[3] ^ RK[6];
}
break;
case 12:
for (i = 0; i < 8; i++, RK += 6) {
RK[6] = RK[0] ^ RCON[i] ^
((uint32_t)FSb[(RK[5] >> 8) & 0xFF]) ^
((uint32_t)FSb[(RK[5] >> 16) & 0xFF] << 8) ^
((uint32_t)FSb[(RK[5] >> 24) & 0xFF] << 16) ^
((uint32_t)FSb[(RK[5]) & 0xFF] << 24);
RK[7] = RK[1] ^ RK[6];
RK[8] = RK[2] ^ RK[7];
RK[9] = RK[3] ^ RK[8];
RK[10] = RK[4] ^ RK[9];
RK[11] = RK[5] ^ RK[10];
}
break;
case 14:
for (i = 0; i < 7; i++, RK += 8) {
RK[8] = RK[0] ^ RCON[i] ^
((uint32_t)FSb[(RK[7] >> 8) & 0xFF]) ^
((uint32_t)FSb[(RK[7] >> 16) & 0xFF] << 8) ^
((uint32_t)FSb[(RK[7] >> 24) & 0xFF] << 16) ^
((uint32_t)FSb[(RK[7]) & 0xFF] << 24);
RK[9] = RK[1] ^ RK[8];
RK[10] = RK[2] ^ RK[9];
RK[11] = RK[3] ^ RK[10];
RK[12] = RK[4] ^
((uint32_t)FSb[(RK[11]) & 0xFF]) ^
((uint32_t)FSb[(RK[11] >> 8) & 0xFF] << 8) ^
((uint32_t)FSb[(RK[11] >> 16) & 0xFF] << 16) ^
((uint32_t)FSb[(RK[11] >> 24) & 0xFF] << 24);
RK[13] = RK[5] ^ RK[12];
RK[14] = RK[6] ^ RK[13];
RK[15] = RK[7] ^ RK[14];
}
break;
default:
return -1;
}
return(0);
}
#if AES_DECRYPTION // whether AES decryption is supported
/******************************************************************************
*
* AES_SET_DECRYPTION_KEY
*
* This is called by 'aes_setkey' when we're establishing a
* key for subsequent decryption. We give it a pointer to
* the encryption context, a pointer to the key, and the key's
* length in bits. Valid lengths are: 128, 192, or 256 bits.
*
******************************************************************************/
int aes_set_decryption_key(aes_context *ctx,
const uchar *key,
uint keysize)
{
int i, j;
aes_context cty; // a calling aes context for set_encryption_key
uint32_t *RK = ctx->rk; // initialize our RoundKey buffer pointer
uint32_t *SK;
int ret;
cty.rounds = ctx->rounds; // initialize our local aes context
cty.rk = cty.buf; // round count and key buf pointer
if ((ret = aes_set_encryption_key(&cty, key, keysize)) != 0)
return(ret);
SK = cty.rk + cty.rounds * 4;
CPY128 // copy a 128-bit block from *SK to *RK
for (i = ctx->rounds - 1, SK -= 8; i > 0; i--, SK -= 8) {
for (j = 0; j < 4; j++, SK++) {
*RK++ = RT0[FSb[(*SK) & 0xFF]] ^
RT1[FSb[(*SK >> 8) & 0xFF]] ^
RT2[FSb[(*SK >> 16) & 0xFF]] ^
RT3[FSb[(*SK >> 24) & 0xFF]];
}
}
CPY128 // copy a 128-bit block from *SK to *RK
memset(&cty, 0, sizeof(aes_context)); // clear local aes context
return(0);
}
#endif /* AES_DECRYPTION */
/******************************************************************************
*
* AES_SETKEY
*
* Invoked to establish the key schedule for subsequent encryption/decryption
*
******************************************************************************/
int aes_setkey(aes_context *ctx, // AES context provided by our caller
int mode, // ENCRYPT or DECRYPT flag
const uchar *key, // pointer to the key
uint keysize) // key length in bytes
{
// since table initialization is not thread safe, we could either add
// system-specific mutexes and init the AES key generation tables on
// demand, or ask the developer to simply call "gcm_initialize" once during
// application startup before threading begins. That's what we choose.
if (!aes_tables_inited) return (-1); // fail the call when not inited.
ctx->mode = mode; // capture the key type we're creating
ctx->rk = ctx->buf; // initialize our round key pointer
switch (keysize) // set the rounds count based upon the keysize
{
case 16: ctx->rounds = 10; break; // 16-byte, 128-bit key
case 24: ctx->rounds = 12; break; // 24-byte, 192-bit key
case 32: ctx->rounds = 14; break; // 32-byte, 256-bit key
default: return(-1);
}
#if AES_DECRYPTION
if (mode == DECRYPT) // expand our key for encryption or decryption
return(aes_set_decryption_key(ctx, key, keysize));
else /* ENCRYPT */
#endif /* AES_DECRYPTION */
return(aes_set_encryption_key(ctx, key, keysize));
}
/******************************************************************************
*
* AES_CIPHER
*
* Perform AES encryption and decryption.
* The AES context will have been setup with the encryption mode
* and all keying information appropriate for the task.
*
******************************************************************************/
int aes_cipher(aes_context *ctx,
const uchar input[16],
uchar output[16])
{
int i;
uint32_t *RK, X0, X1, X2, X3, Y0, Y1, Y2, Y3; // general purpose locals
RK = ctx->rk;
GET_UINT32_LE(X0, input, 0); X0 ^= *RK++; // load our 128-bit
GET_UINT32_LE(X1, input, 4); X1 ^= *RK++; // input buffer in a storage
GET_UINT32_LE(X2, input, 8); X2 ^= *RK++; // memory endian-neutral way
GET_UINT32_LE(X3, input, 12); X3 ^= *RK++;
#if AES_DECRYPTION // whether AES decryption is supported
if (ctx->mode == DECRYPT)
{
for (i = (ctx->rounds >> 1) - 1; i > 0; i--)
{
AES_RROUND(Y0, Y1, Y2, Y3, X0, X1, X2, X3);
AES_RROUND(X0, X1, X2, X3, Y0, Y1, Y2, Y3);
}
AES_RROUND(Y0, Y1, Y2, Y3, X0, X1, X2, X3);
X0 = *RK++ ^ \
((uint32_t)RSb[(Y0) & 0xFF]) ^
((uint32_t)RSb[(Y3 >> 8) & 0xFF] << 8) ^
((uint32_t)RSb[(Y2 >> 16) & 0xFF] << 16) ^
((uint32_t)RSb[(Y1 >> 24) & 0xFF] << 24);
X1 = *RK++ ^ \
((uint32_t)RSb[(Y1) & 0xFF]) ^
((uint32_t)RSb[(Y0 >> 8) & 0xFF] << 8) ^
((uint32_t)RSb[(Y3 >> 16) & 0xFF] << 16) ^
((uint32_t)RSb[(Y2 >> 24) & 0xFF] << 24);
X2 = *RK++ ^ \
((uint32_t)RSb[(Y2) & 0xFF]) ^
((uint32_t)RSb[(Y1 >> 8) & 0xFF] << 8) ^
((uint32_t)RSb[(Y0 >> 16) & 0xFF] << 16) ^
((uint32_t)RSb[(Y3 >> 24) & 0xFF] << 24);
X3 = *RK++ ^ \
((uint32_t)RSb[(Y3) & 0xFF]) ^
((uint32_t)RSb[(Y2 >> 8) & 0xFF] << 8) ^
((uint32_t)RSb[(Y1 >> 16) & 0xFF] << 16) ^
((uint32_t)RSb[(Y0 >> 24) & 0xFF] << 24);
}
else /* ENCRYPT */
{
#endif /* AES_DECRYPTION */
for (i = (ctx->rounds >> 1) - 1; i > 0; i--)
{
AES_FROUND(Y0, Y1, Y2, Y3, X0, X1, X2, X3);
AES_FROUND(X0, X1, X2, X3, Y0, Y1, Y2, Y3);
}
AES_FROUND(Y0, Y1, Y2, Y3, X0, X1, X2, X3);
X0 = *RK++ ^ \
((uint32_t)FSb[(Y0) & 0xFF]) ^
((uint32_t)FSb[(Y1 >> 8) & 0xFF] << 8) ^
((uint32_t)FSb[(Y2 >> 16) & 0xFF] << 16) ^
((uint32_t)FSb[(Y3 >> 24) & 0xFF] << 24);
X1 = *RK++ ^ \
((uint32_t)FSb[(Y1) & 0xFF]) ^
((uint32_t)FSb[(Y2 >> 8) & 0xFF] << 8) ^
((uint32_t)FSb[(Y3 >> 16) & 0xFF] << 16) ^
((uint32_t)FSb[(Y0 >> 24) & 0xFF] << 24);
X2 = *RK++ ^ \
((uint32_t)FSb[(Y2) & 0xFF]) ^
((uint32_t)FSb[(Y3 >> 8) & 0xFF] << 8) ^
((uint32_t)FSb[(Y0 >> 16) & 0xFF] << 16) ^
((uint32_t)FSb[(Y1 >> 24) & 0xFF] << 24);
X3 = *RK++ ^ \
((uint32_t)FSb[(Y3) & 0xFF]) ^
((uint32_t)FSb[(Y0 >> 8) & 0xFF] << 8) ^
((uint32_t)FSb[(Y1 >> 16) & 0xFF] << 16) ^
((uint32_t)FSb[(Y2 >> 24) & 0xFF] << 24);
#if AES_DECRYPTION // whether AES decryption is supported
}
#endif /* AES_DECRYPTION */
PUT_UINT32_LE(X0, output, 0);
PUT_UINT32_LE(X1, output, 4);
PUT_UINT32_LE(X2, output, 8);
PUT_UINT32_LE(X3, output, 12);
return(0);
}
/* end of aes.c */

78
nfq/crypto/aes.h Normal file
View File

@ -0,0 +1,78 @@
/******************************************************************************
*
* THIS SOURCE CODE IS HEREBY PLACED INTO THE PUBLIC DOMAIN FOR THE GOOD OF ALL
*
* This is a simple and straightforward implementation of the AES Rijndael
* 128-bit block cipher designed by Vincent Rijmen and Joan Daemen. The focus
* of this work was correctness & accuracy. It is written in 'C' without any
* particular focus upon optimization or speed. It should be endian (memory
* byte order) neutral since the few places that care are handled explicitly.
*
* This implementation of Rijndael was created by Steven M. Gibson of GRC.com.
*
* It is intended for general purpose use, but was written in support of GRC's
* reference implementation of the SQRL (Secure Quick Reliable Login) client.
*
* See: http://csrc.nist.gov/archive/aes/rijndael/wsdindex.html
*
* NO COPYRIGHT IS CLAIMED IN THIS WORK, HOWEVER, NEITHER IS ANY WARRANTY MADE
* REGARDING ITS FITNESS FOR ANY PARTICULAR PURPOSE. USE IT AT YOUR OWN RISK.
*
*******************************************************************************/
#pragma once
/******************************************************************************/
#define AES_DECRYPTION 0 // whether AES decryption is supported
/******************************************************************************/
#include <string.h>
#define ENCRYPT 1 // specify whether we're encrypting
#define DECRYPT 0 // or decrypting
#if defined(_MSC_VER)
#include <basetsd.h>
typedef UINT32 uint32_t;
#else
#include <inttypes.h>
#endif
typedef unsigned char uchar; // add some convienent shorter types
typedef unsigned int uint;
/******************************************************************************
* AES_INIT_KEYGEN_TABLES : MUST be called once before any AES use
******************************************************************************/
void aes_init_keygen_tables(void);
/******************************************************************************
* AES_CONTEXT : cipher context / holds inter-call data
******************************************************************************/
typedef struct {
int mode; // 1 for Encryption, 0 for Decryption
int rounds; // keysize-based rounds count
uint32_t *rk; // pointer to current round key
uint32_t buf[68]; // key expansion buffer
} aes_context;
/******************************************************************************
* AES_SETKEY : called to expand the key for encryption or decryption
******************************************************************************/
int aes_setkey(aes_context *ctx, // pointer to context
int mode, // 1 or 0 for Encrypt/Decrypt
const uchar *key, // AES input key
uint keysize); // size in bytes (must be 16, 24, 32 for
// 128, 192 or 256-bit keys respectively)
// returns 0 for success
/******************************************************************************
* AES_CIPHER : called to encrypt or decrypt ONE 128-bit block of data
******************************************************************************/
int aes_cipher(aes_context *ctx, // pointer to context
const uchar input[16], // 128-bit block to en/decipher
uchar output[16]); // 128-bit output result block
// returns 0 for success

511
nfq/crypto/gcm.c Normal file
View File

@ -0,0 +1,511 @@
/******************************************************************************
*
* THIS SOURCE CODE IS HEREBY PLACED INTO THE PUBLIC DOMAIN FOR THE GOOD OF ALL
*
* This is a simple and straightforward implementation of AES-GCM authenticated
* encryption. The focus of this work was correctness & accuracy. It is written
* in straight 'C' without any particular focus upon optimization or speed. It
* should be endian (memory byte order) neutral since the few places that care
* are handled explicitly.
*
* This implementation of AES-GCM was created by Steven M. Gibson of GRC.com.
*
* It is intended for general purpose use, but was written in support of GRC's
* reference implementation of the SQRL (Secure Quick Reliable Login) client.
*
* See: http://csrc.nist.gov/publications/nistpubs/800-38D/SP-800-38D.pdf
* http://csrc.nist.gov/groups/ST/toolkit/BCM/documents/proposedmodes/
* gcm/gcm-revised-spec.pdf
*
* NO COPYRIGHT IS CLAIMED IN THIS WORK, HOWEVER, NEITHER IS ANY WARRANTY MADE
* REGARDING ITS FITNESS FOR ANY PARTICULAR PURPOSE. USE IT AT YOUR OWN RISK.
*
*******************************************************************************/
#include "gcm.h"
#include "aes.h"
/******************************************************************************
* ==== IMPLEMENTATION WARNING ====
*
* This code was developed for use within SQRL's fixed environmnent. Thus, it
* is somewhat less "general purpose" than it would be if it were designed as
* a general purpose AES-GCM library. Specifically, it bothers with almost NO
* error checking on parameter limits, buffer bounds, etc. It assumes that it
* is being invoked by its author or by someone who understands the values it
* expects to receive. Its behavior will be undefined otherwise.
*
* All functions that might fail are defined to return 'ints' to indicate a
* problem. Most do not do so now. But this allows for error propagation out
* of internal functions if robust error checking should ever be desired.
*
******************************************************************************/
/* Calculating the "GHASH"
*
* There are many ways of calculating the so-called GHASH in software, each with
* a traditional size vs performance tradeoff. The GHASH (Galois field hash) is
* an intriguing construction which takes two 128-bit strings (also the cipher's
* block size and the fundamental operation size for the system) and hashes them
* into a third 128-bit result.
*
* Many implementation solutions have been worked out that use large precomputed
* table lookups in place of more time consuming bit fiddling, and this approach
* can be scaled easily upward or downward as needed to change the time/space
* tradeoff. It's been studied extensively and there's a solid body of theory and
* practice. For example, without using any lookup tables an implementation
* might obtain 119 cycles per byte throughput, whereas using a simple, though
* large, key-specific 64 kbyte 8-bit lookup table the performance jumps to 13
* cycles per byte.
*
* And Intel's processors have, since 2010, included an instruction which does
* the entire 128x128->128 bit job in just several 64x64->128 bit pieces.
*
* Since SQRL is interactive, and only processing a few 128-bit blocks, I've
* settled upon a relatively slower but appealing small-table compromise which
* folds a bunch of not only time consuming but also bit twiddling into a simple
* 16-entry table which is attributed to Victor Shoup's 1996 work while at
* Bellcore: "On Fast and Provably Secure MessageAuthentication Based on
* Universal Hashing." See: http://www.shoup.net/papers/macs.pdf
* See, also section 4.1 of the "gcm-revised-spec" cited above.
*/
/*
* This 16-entry table of pre-computed constants is used by the
* GHASH multiplier to improve over a strictly table-free but
* significantly slower 128x128 bit multiple within GF(2^128).
*/
static const uint64_t last4[16] = {
0x0000, 0x1c20, 0x3840, 0x2460, 0x7080, 0x6ca0, 0x48c0, 0x54e0,
0xe100, 0xfd20, 0xd940, 0xc560, 0x9180, 0x8da0, 0xa9c0, 0xb5e0 };
/*
* Platform Endianness Neutralizing Load and Store Macro definitions
* GCM wants platform-neutral Big Endian (BE) byte ordering
*/
#define GET_UINT32_BE(n,b,i) { \
(n) = ( (uint32_t) (b)[(i) ] << 24 ) \
| ( (uint32_t) (b)[(i) + 1] << 16 ) \
| ( (uint32_t) (b)[(i) + 2] << 8 ) \
| ( (uint32_t) (b)[(i) + 3] ); }
#define PUT_UINT32_BE(n,b,i) { \
(b)[(i) ] = (uchar) ( (n) >> 24 ); \
(b)[(i) + 1] = (uchar) ( (n) >> 16 ); \
(b)[(i) + 2] = (uchar) ( (n) >> 8 ); \
(b)[(i) + 3] = (uchar) ( (n) ); }
/******************************************************************************
*
* GCM_INITIALIZE
*
* Must be called once to initialize the GCM library.
*
* At present, this only calls the AES keygen table generator, which expands
* the AES keying tables for use. This is NOT A THREAD-SAFE function, so it
* MUST be called during system initialization before a multi-threading
* environment is running.
*
******************************************************************************/
int gcm_initialize(void)
{
aes_init_keygen_tables();
return(0);
}
/******************************************************************************
*
* GCM_MULT
*
* Performs a GHASH operation on the 128-bit input vector 'x', setting
* the 128-bit output vector to 'x' times H using our precomputed tables.
* 'x' and 'output' are seen as elements of GCM's GF(2^128) Galois field.
*
******************************************************************************/
static void gcm_mult(gcm_context *ctx, // pointer to established context
const uchar x[16], // pointer to 128-bit input vector
uchar output[16]) // pointer to 128-bit output vector
{
int i;
uchar lo, hi, rem;
uint64_t zh, zl;
lo = (uchar)(x[15] & 0x0f);
hi = (uchar)(x[15] >> 4);
zh = ctx->HH[lo];
zl = ctx->HL[lo];
for (i = 15; i >= 0; i--) {
lo = (uchar)(x[i] & 0x0f);
hi = (uchar)(x[i] >> 4);
if (i != 15) {
rem = (uchar)(zl & 0x0f);
zl = (zh << 60) | (zl >> 4);
zh = (zh >> 4);
zh ^= (uint64_t)last4[rem] << 48;
zh ^= ctx->HH[lo];
zl ^= ctx->HL[lo];
}
rem = (uchar)(zl & 0x0f);
zl = (zh << 60) | (zl >> 4);
zh = (zh >> 4);
zh ^= (uint64_t)last4[rem] << 48;
zh ^= ctx->HH[hi];
zl ^= ctx->HL[hi];
}
PUT_UINT32_BE(zh >> 32, output, 0);
PUT_UINT32_BE(zh, output, 4);
PUT_UINT32_BE(zl >> 32, output, 8);
PUT_UINT32_BE(zl, output, 12);
}
/******************************************************************************
*
* GCM_SETKEY
*
* This is called to set the AES-GCM key. It initializes the AES key
* and populates the gcm context's pre-calculated HTables.
*
******************************************************************************/
int gcm_setkey(gcm_context *ctx, // pointer to caller-provided gcm context
const uchar *key, // pointer to the AES encryption key
const uint keysize) // size in bytes (must be 16, 24, 32 for
// 128, 192 or 256-bit keys respectively)
{
int ret, i, j;
uint64_t hi, lo;
uint64_t vl, vh;
unsigned char h[16];
memset(ctx, 0, sizeof(gcm_context)); // zero caller-provided GCM context
memset(h, 0, 16); // initialize the block to encrypt
// encrypt the null 128-bit block to generate a key-based value
// which is then used to initialize our GHASH lookup tables
if ((ret = aes_setkey(&ctx->aes_ctx, ENCRYPT, key, keysize)) != 0)
return(ret);
if ((ret = aes_cipher(&ctx->aes_ctx, h, h)) != 0)
return(ret);
GET_UINT32_BE(hi, h, 0); // pack h as two 64-bit ints, big-endian
GET_UINT32_BE(lo, h, 4);
vh = (uint64_t)hi << 32 | lo;
GET_UINT32_BE(hi, h, 8);
GET_UINT32_BE(lo, h, 12);
vl = (uint64_t)hi << 32 | lo;
ctx->HL[8] = vl; // 8 = 1000 corresponds to 1 in GF(2^128)
ctx->HH[8] = vh;
ctx->HH[0] = 0; // 0 corresponds to 0 in GF(2^128)
ctx->HL[0] = 0;
for (i = 4; i > 0; i >>= 1) {
uint32_t T = (uint32_t)(vl & 1) * 0xe1000000U;
vl = (vh << 63) | (vl >> 1);
vh = (vh >> 1) ^ ((uint64_t)T << 32);
ctx->HL[i] = vl;
ctx->HH[i] = vh;
}
for (i = 2; i < 16; i <<= 1) {
uint64_t *HiL = ctx->HL + i, *HiH = ctx->HH + i;
vh = *HiH;
vl = *HiL;
for (j = 1; j < i; j++) {
HiH[j] = vh ^ ctx->HH[j];
HiL[j] = vl ^ ctx->HL[j];
}
}
return(0);
}
/******************************************************************************
*
* GCM processing occurs four phases: SETKEY, START, UPDATE and FINISH.
*
* SETKEY:
*
* START: Sets the Encryption/Decryption mode.
* Accepts the initialization vector and additional data.
*
* UPDATE: Encrypts or decrypts the plaintext or ciphertext.
*
* FINISH: Performs a final GHASH to generate the authentication tag.
*
******************************************************************************
*
* GCM_START
*
* Given a user-provided GCM context, this initializes it, sets the encryption
* mode, and preprocesses the initialization vector and additional AEAD data.
*
******************************************************************************/
int gcm_start(gcm_context *ctx, // pointer to user-provided GCM context
int mode, // GCM_ENCRYPT or GCM_DECRYPT
const uchar *iv, // pointer to initialization vector
size_t iv_len, // IV length in bytes (should == 12)
const uchar *add, // ptr to additional AEAD data (NULL if none)
size_t add_len) // length of additional AEAD data (bytes)
{
int ret; // our error return if the AES encrypt fails
uchar work_buf[16]; // XOR source built from provided IV if len != 16
const uchar *p; // general purpose array pointer
size_t use_len; // byte count to process, up to 16 bytes
size_t i; // local loop iterator
// since the context might be reused under the same key
// we zero the working buffers for this next new process
memset(ctx->y, 0x00, sizeof(ctx->y));
memset(ctx->buf, 0x00, sizeof(ctx->buf));
ctx->len = 0;
ctx->add_len = 0;
ctx->mode = mode; // set the GCM encryption/decryption mode
ctx->aes_ctx.mode = ENCRYPT; // GCM *always* runs AES in ENCRYPTION mode
if (iv_len == 12) { // GCM natively uses a 12-byte, 96-bit IV
memcpy(ctx->y, iv, iv_len); // copy the IV to the top of the 'y' buff
ctx->y[15] = 1; // start "counting" from 1 (not 0)
}
else // if we don't have a 12-byte IV, we GHASH whatever we've been given
{
memset(work_buf, 0x00, 16); // clear the working buffer
PUT_UINT32_BE(iv_len * 8, work_buf, 12); // place the IV into buffer
p = iv;
while (iv_len > 0) {
use_len = (iv_len < 16) ? iv_len : 16;
for (i = 0; i < use_len; i++) ctx->y[i] ^= p[i];
gcm_mult(ctx, ctx->y, ctx->y);
iv_len -= use_len;
p += use_len;
}
for (i = 0; i < 16; i++) ctx->y[i] ^= work_buf[i];
gcm_mult(ctx, ctx->y, ctx->y);
}
if ((ret = aes_cipher(&ctx->aes_ctx, ctx->y, ctx->base_ectr)) != 0)
return(ret);
ctx->add_len = add_len;
p = add;
while (add_len > 0) {
use_len = (add_len < 16) ? add_len : 16;
for (i = 0; i < use_len; i++) ctx->buf[i] ^= p[i];
gcm_mult(ctx, ctx->buf, ctx->buf);
add_len -= use_len;
p += use_len;
}
return(0);
}
/******************************************************************************
*
* GCM_UPDATE
*
* This is called once or more to process bulk plaintext or ciphertext data.
* We give this some number of bytes of input and it returns the same number
* of output bytes. If called multiple times (which is fine) all but the final
* invocation MUST be called with length mod 16 == 0. (Only the final call can
* have a partial block length of < 128 bits.)
*
******************************************************************************/
int gcm_update(gcm_context *ctx, // pointer to user-provided GCM context
size_t length, // length, in bytes, of data to process
const uchar *input, // pointer to source data
uchar *output) // pointer to destination data
{
int ret; // our error return if the AES encrypt fails
uchar ectr[16]; // counter-mode cipher output for XORing
size_t use_len; // byte count to process, up to 16 bytes
size_t i; // local loop iterator
ctx->len += length; // bump the GCM context's running length count
while (length > 0) {
// clamp the length to process at 16 bytes
use_len = (length < 16) ? length : 16;
// increment the context's 128-bit IV||Counter 'y' vector
for (i = 16; i > 12; i--) if (++ctx->y[i - 1] != 0) break;
// encrypt the context's 'y' vector under the established key
if ((ret = aes_cipher(&ctx->aes_ctx, ctx->y, ectr)) != 0)
return(ret);
// encrypt or decrypt the input to the output
if (ctx->mode == ENCRYPT)
{
for (i = 0; i < use_len; i++) {
// XOR the cipher's ouptut vector (ectr) with our input
output[i] = (uchar)(ectr[i] ^ input[i]);
// now we mix in our data into the authentication hash.
// if we're ENcrypting we XOR in the post-XOR (output)
// results, but if we're DEcrypting we XOR in the input
// data
ctx->buf[i] ^= output[i];
}
}
else
{
for (i = 0; i < use_len; i++) {
// but if we're DEcrypting we XOR in the input data first,
// i.e. before saving to ouput data, otherwise if the input
// and output buffer are the same (inplace decryption) we
// would not get the correct auth tag
ctx->buf[i] ^= input[i];
// XOR the cipher's ouptut vector (ectr) with our input
output[i] = (uchar)(ectr[i] ^ input[i]);
}
}
gcm_mult(ctx, ctx->buf, ctx->buf); // perform a GHASH operation
length -= use_len; // drop the remaining byte count to process
input += use_len; // bump our input pointer forward
output += use_len; // bump our output pointer forward
}
return(0);
}
/******************************************************************************
*
* GCM_FINISH
*
* This is called once after all calls to GCM_UPDATE to finalize the GCM.
* It performs the final GHASH to produce the resulting authentication TAG.
*
******************************************************************************/
int gcm_finish(gcm_context *ctx, // pointer to user-provided GCM context
uchar *tag, // pointer to buffer which receives the tag
size_t tag_len) // length, in bytes, of the tag-receiving buf
{
uchar work_buf[16];
uint64_t orig_len = ctx->len * 8;
uint64_t orig_add_len = ctx->add_len * 8;
size_t i;
if (tag_len != 0) memcpy(tag, ctx->base_ectr, tag_len);
if (orig_len || orig_add_len) {
memset(work_buf, 0x00, 16);
PUT_UINT32_BE((orig_add_len >> 32), work_buf, 0);
PUT_UINT32_BE((orig_add_len), work_buf, 4);
PUT_UINT32_BE((orig_len >> 32), work_buf, 8);
PUT_UINT32_BE((orig_len), work_buf, 12);
for (i = 0; i < 16; i++) ctx->buf[i] ^= work_buf[i];
gcm_mult(ctx, ctx->buf, ctx->buf);
for (i = 0; i < tag_len; i++) tag[i] ^= ctx->buf[i];
}
return(0);
}
/******************************************************************************
*
* GCM_CRYPT_AND_TAG
*
* This either encrypts or decrypts the user-provided data and, either
* way, generates an authentication tag of the requested length. It must be
* called with a GCM context whose key has already been set with GCM_SETKEY.
*
* The user would typically call this explicitly to ENCRYPT a buffer of data
* and optional associated data, and produce its an authentication tag.
*
* To reverse the process the user would typically call the companion
* GCM_AUTH_DECRYPT function to decrypt data and verify a user-provided
* authentication tag. The GCM_AUTH_DECRYPT function calls this function
* to perform its decryption and tag generation, which it then compares.
*
******************************************************************************/
int gcm_crypt_and_tag(
gcm_context *ctx, // gcm context with key already setup
int mode, // cipher direction: GCM_ENCRYPT or GCM_DECRYPT
const uchar *iv, // pointer to the 12-byte initialization vector
size_t iv_len, // byte length if the IV. should always be 12
const uchar *add, // pointer to the non-ciphered additional data
size_t add_len, // byte length of the additional AEAD data
const uchar *input, // pointer to the cipher data source
uchar *output, // pointer to the cipher data destination
size_t length, // byte length of the cipher data
uchar *tag, // pointer to the tag to be generated
size_t tag_len) // byte length of the tag to be generated
{ /*
assuming that the caller has already invoked gcm_setkey to
prepare the gcm context with the keying material, we simply
invoke each of the three GCM sub-functions in turn...
*/
gcm_start(ctx, mode, iv, iv_len, add, add_len);
gcm_update(ctx, length, input, output);
gcm_finish(ctx, tag, tag_len);
return(0);
}
/******************************************************************************
*
* GCM_AUTH_DECRYPT
*
* This DECRYPTS a user-provided data buffer with optional associated data.
* It then verifies a user-supplied authentication tag against the tag just
* re-created during decryption to verify that the data has not been altered.
*
* This function calls GCM_CRYPT_AND_TAG (above) to perform the decryption
* and authentication tag generation.
*
******************************************************************************/
int gcm_auth_decrypt(
gcm_context *ctx, // gcm context with key already setup
const uchar *iv, // pointer to the 12-byte initialization vector
size_t iv_len, // byte length if the IV. should always be 12
const uchar *add, // pointer to the non-ciphered additional data
size_t add_len, // byte length of the additional AEAD data
const uchar *input, // pointer to the cipher data source
uchar *output, // pointer to the cipher data destination
size_t length, // byte length of the cipher data
const uchar *tag, // pointer to the tag to be authenticated
size_t tag_len) // byte length of the tag <= 16
{
uchar check_tag[16]; // the tag generated and returned by decryption
int diff; // an ORed flag to detect authentication errors
size_t i; // our local iterator
/*
we use GCM_DECRYPT_AND_TAG (above) to perform our decryption
(which is an identical XORing to reverse the previous one)
and also to re-generate the matching authentication tag
*/
gcm_crypt_and_tag(ctx, DECRYPT, iv, iv_len, add, add_len,
input, output, length, check_tag, tag_len);
// now we verify the authentication tag in 'constant time'
for (diff = 0, i = 0; i < tag_len; i++)
diff |= tag[i] ^ check_tag[i];
if (diff != 0) { // see whether any bits differed?
memset(output, 0, length); // if so... wipe the output data
return(GCM_AUTH_FAILURE); // return GCM_AUTH_FAILURE
}
return(0);
}
/******************************************************************************
*
* GCM_ZERO_CTX
*
* The GCM context contains both the GCM context and the AES context.
* This includes keying and key-related material which is security-
* sensitive, so it MUST be zeroed after use. This function does that.
*
******************************************************************************/
void gcm_zero_ctx(gcm_context *ctx)
{
// zero the context originally provided to us
memset(ctx, 0, sizeof(gcm_context));
}

183
nfq/crypto/gcm.h Normal file
View File

@ -0,0 +1,183 @@
/******************************************************************************
*
* THIS SOURCE CODE IS HEREBY PLACED INTO THE PUBLIC DOMAIN FOR THE GOOD OF ALL
*
* This is a simple and straightforward implementation of AES-GCM authenticated
* encryption. The focus of this work was correctness & accuracy. It is written
* in straight 'C' without any particular focus upon optimization or speed. It
* should be endian (memory byte order) neutral since the few places that care
* are handled explicitly.
*
* This implementation of AES-GCM was created by Steven M. Gibson of GRC.com.
*
* It is intended for general purpose use, but was written in support of GRC's
* reference implementation of the SQRL (Secure Quick Reliable Login) client.
*
* See: http://csrc.nist.gov/publications/nistpubs/800-38D/SP-800-38D.pdf
* http://csrc.nist.gov/groups/ST/toolkit/BCM/documents/proposedmodes/ \
* gcm/gcm-revised-spec.pdf
*
* NO COPYRIGHT IS CLAIMED IN THIS WORK, HOWEVER, NEITHER IS ANY WARRANTY MADE
* REGARDING ITS FITNESS FOR ANY PARTICULAR PURPOSE. USE IT AT YOUR OWN RISK.
*
*******************************************************************************/
#pragma once
#define GCM_AUTH_FAILURE 0x55555555 // authentication failure
#include "aes.h" // gcm_context includes aes_context
#if defined(_MSC_VER)
#include <basetsd.h>
typedef unsigned int size_t;// use the right type for length declarations
typedef UINT32 uint32_t;
typedef UINT64 uint64_t;
#else
#include <stdint.h>
#endif
/******************************************************************************
* GCM_CONTEXT : GCM context / holds keytables, instance data, and AES ctx
******************************************************************************/
typedef struct {
int mode; // cipher direction: encrypt/decrypt
uint64_t len; // cipher data length processed so far
uint64_t add_len; // total add data length
uint64_t HL[16]; // precalculated lo-half HTable
uint64_t HH[16]; // precalculated hi-half HTable
uchar base_ectr[16]; // first counter-mode cipher output for tag
uchar y[16]; // the current cipher-input IV|Counter value
uchar buf[16]; // buf working value
aes_context aes_ctx; // cipher context used
} gcm_context;
/******************************************************************************
* GCM_CONTEXT : MUST be called once before ANY use of this library
******************************************************************************/
int gcm_initialize(void);
/******************************************************************************
* GCM_SETKEY : sets the GCM (and AES) keying material for use
******************************************************************************/
int gcm_setkey(gcm_context *ctx, // caller-provided context ptr
const uchar *key, // pointer to cipher key
const uint keysize // size in bytes (must be 16, 24, 32 for
// 128, 192 or 256-bit keys respectively)
); // returns 0 for success
/******************************************************************************
*
* GCM_CRYPT_AND_TAG
*
* This either encrypts or decrypts the user-provided data and, either
* way, generates an authentication tag of the requested length. It must be
* called with a GCM context whose key has already been set with GCM_SETKEY.
*
* The user would typically call this explicitly to ENCRYPT a buffer of data
* and optional associated data, and produce its an authentication tag.
*
* To reverse the process the user would typically call the companion
* GCM_AUTH_DECRYPT function to decrypt data and verify a user-provided
* authentication tag. The GCM_AUTH_DECRYPT function calls this function
* to perform its decryption and tag generation, which it then compares.
*
******************************************************************************/
int gcm_crypt_and_tag(
gcm_context *ctx, // gcm context with key already setup
int mode, // cipher direction: ENCRYPT (1) or DECRYPT (0)
const uchar *iv, // pointer to the 12-byte initialization vector
size_t iv_len, // byte length if the IV. should always be 12
const uchar *add, // pointer to the non-ciphered additional data
size_t add_len, // byte length of the additional AEAD data
const uchar *input, // pointer to the cipher data source
uchar *output, // pointer to the cipher data destination
size_t length, // byte length of the cipher data
uchar *tag, // pointer to the tag to be generated
size_t tag_len); // byte length of the tag to be generated
/******************************************************************************
*
* GCM_AUTH_DECRYPT
*
* This DECRYPTS a user-provided data buffer with optional associated data.
* It then verifies a user-supplied authentication tag against the tag just
* re-created during decryption to verify that the data has not been altered.
*
* This function calls GCM_CRYPT_AND_TAG (above) to perform the decryption
* and authentication tag generation.
*
******************************************************************************/
int gcm_auth_decrypt(
gcm_context *ctx, // gcm context with key already setup
const uchar *iv, // pointer to the 12-byte initialization vector
size_t iv_len, // byte length if the IV. should always be 12
const uchar *add, // pointer to the non-ciphered additional data
size_t add_len, // byte length of the additional AEAD data
const uchar *input, // pointer to the cipher data source
uchar *output, // pointer to the cipher data destination
size_t length, // byte length of the cipher data
const uchar *tag, // pointer to the tag to be authenticated
size_t tag_len); // byte length of the tag <= 16
/******************************************************************************
*
* GCM_START
*
* Given a user-provided GCM context, this initializes it, sets the encryption
* mode, and preprocesses the initialization vector and additional AEAD data.
*
******************************************************************************/
int gcm_start(gcm_context *ctx, // pointer to user-provided GCM context
int mode, // ENCRYPT (1) or DECRYPT (0)
const uchar *iv, // pointer to initialization vector
size_t iv_len, // IV length in bytes (should == 12)
const uchar *add, // pointer to additional AEAD data (NULL if none)
size_t add_len); // length of additional AEAD data (bytes)
/******************************************************************************
*
* GCM_UPDATE
*
* This is called once or more to process bulk plaintext or ciphertext data.
* We give this some number of bytes of input and it returns the same number
* of output bytes. If called multiple times (which is fine) all but the final
* invocation MUST be called with length mod 16 == 0. (Only the final call can
* have a partial block length of < 128 bits.)
*
******************************************************************************/
int gcm_update(gcm_context *ctx, // pointer to user-provided GCM context
size_t length, // length, in bytes, of data to process
const uchar *input, // pointer to source data
uchar *output); // pointer to destination data
/******************************************************************************
*
* GCM_FINISH
*
* This is called once after all calls to GCM_UPDATE to finalize the GCM.
* It performs the final GHASH to produce the resulting authentication TAG.
*
******************************************************************************/
int gcm_finish(gcm_context *ctx, // pointer to user-provided GCM context
uchar *tag, // ptr to tag buffer - NULL if tag_len = 0
size_t tag_len); // length, in bytes, of the tag-receiving buf
/******************************************************************************
*
* GCM_ZERO_CTX
*
* The GCM context contains both the GCM context and the AES context.
* This includes keying and key-related material which is security-
* sensitive, so it MUST be zeroed after use. This function does that.
*
******************************************************************************/
void gcm_zero_ctx(gcm_context *ctx);

337
nfq/crypto/hkdf.c Normal file
View File

@ -0,0 +1,337 @@
/**************************** hkdf.c ***************************/
/***************** See RFC 6234 for details. *******************/
/* Copyright (c) 2011 IETF Trust and the persons identified as */
/* authors of the code. All rights reserved. */
/* See sha.h for terms of use and redistribution. */
/*
* Description:
* This file implements the HKDF algorithm (HMAC-based
* Extract-and-Expand Key Derivation Function, RFC 5869),
* expressed in terms of the various SHA algorithms.
*/
#include "sha.h"
#include <string.h>
#include <stdlib.h>
/*
* hkdf
*
* Description:
* This function will generate keying material using HKDF.
*
* Parameters:
* whichSha: [in]
* One of SHA1, SHA224, SHA256, SHA384, SHA512
* salt[ ]: [in]
* The optional salt value (a non-secret random value);
* if not provided (salt == NULL), it is set internally
* to a string of HashLen(whichSha) zeros.
* salt_len: [in]
* The length of the salt value. (Ignored if salt == NULL.)
* ikm[ ]: [in]
* Input keying material.
* ikm_len: [in]
* The length of the input keying material.
* info[ ]: [in]
* The optional context and application specific information.
* If info == NULL or a zero-length string, it is ignored.
* info_len: [in]
* The length of the optional context and application specific
* information. (Ignored if info == NULL.)
* okm[ ]: [out]
* Where the HKDF is to be stored.
* okm_len: [in]
* The length of the buffer to hold okm.
* okm_len must be <= 255 * USHABlockSize(whichSha)
*
* Notes:
* Calls hkdfExtract() and hkdfExpand().
*
* Returns:
* sha Error Code.
*
*/
int hkdf(SHAversion whichSha,
const unsigned char *salt, size_t salt_len,
const unsigned char *ikm, size_t ikm_len,
const unsigned char *info, size_t info_len,
uint8_t okm[], size_t okm_len)
{
uint8_t prk[USHAMaxHashSize];
return hkdfExtract(whichSha, salt, salt_len, ikm, ikm_len, prk) ||
hkdfExpand(whichSha, prk, USHAHashSize(whichSha), info,
info_len, okm, okm_len);
}
/*
* hkdfExtract
*
* Description:
* This function will perform HKDF extraction.
*
* Parameters:
* whichSha: [in]
* One of SHA1, SHA224, SHA256, SHA384, SHA512
* salt[ ]: [in]
* The optional salt value (a non-secret random value);
* if not provided (salt == NULL), it is set internally
* to a string of HashLen(whichSha) zeros.
* salt_len: [in]
* The length of the salt value. (Ignored if salt == NULL.)
* ikm[ ]: [in]
* Input keying material.
* ikm_len: [in]
* The length of the input keying material.
* prk[ ]: [out]
* Array where the HKDF extraction is to be stored.
* Must be larger than USHAHashSize(whichSha);
*
* Returns:
* sha Error Code.
*
*/
int hkdfExtract(SHAversion whichSha,
const unsigned char *salt, size_t salt_len,
const unsigned char *ikm, size_t ikm_len,
uint8_t prk[USHAMaxHashSize])
{
unsigned char nullSalt[USHAMaxHashSize];
if (salt == 0) {
salt = nullSalt;
salt_len = USHAHashSize(whichSha);
memset(nullSalt, '\0', salt_len);
}
else if (salt_len < 0) {
return shaBadParam;
}
return hmac(whichSha, ikm, ikm_len, salt, salt_len, prk);
}
/*
* hkdfExpand
*
* Description:
* This function will perform HKDF expansion.
*
* Parameters:
* whichSha: [in]
* One of SHA1, SHA224, SHA256, SHA384, SHA512
* prk[ ]: [in]
* The pseudo-random key to be expanded; either obtained
* directly from a cryptographically strong, uniformly
* distributed pseudo-random number generator, or as the
* output from hkdfExtract().
* prk_len: [in]
* The length of the pseudo-random key in prk;
* should at least be equal to USHAHashSize(whichSHA).
* info[ ]: [in]
* The optional context and application specific information.
* If info == NULL or a zero-length string, it is ignored.
* info_len: [in]
* The length of the optional context and application specific
* information. (Ignored if info == NULL.)
* okm[ ]: [out]
* Where the HKDF is to be stored.
* okm_len: [in]
* The length of the buffer to hold okm.
* okm_len must be <= 255 * USHABlockSize(whichSha)
*
* Returns:
* sha Error Code.
*
*/
int hkdfExpand(SHAversion whichSha, const uint8_t prk[], size_t prk_len,
const unsigned char *info, size_t info_len,
uint8_t okm[], size_t okm_len)
{
size_t hash_len, N;
unsigned char T[USHAMaxHashSize];
size_t Tlen, where, i;
if (info == 0) {
info = (const unsigned char *)"";
info_len = 0;
}
else if (info_len < 0) {
return shaBadParam;
}
if (okm_len <= 0) return shaBadParam;
if (!okm) return shaBadParam;
hash_len = USHAHashSize(whichSha);
if (prk_len < hash_len) return shaBadParam;
N = okm_len / hash_len;
if ((okm_len % hash_len) != 0) N++;
if (N > 255) return shaBadParam;
Tlen = 0;
where = 0;
for (i = 1; i <= N; i++) {
HMACContext context;
unsigned char c = i;
int ret = hmacReset(&context, whichSha, prk, prk_len) ||
hmacInput(&context, T, Tlen) ||
hmacInput(&context, info, info_len) ||
hmacInput(&context, &c, 1) ||
hmacResult(&context, T);
if (ret != shaSuccess) return ret;
memcpy(okm + where, T,
(i != N) ? hash_len : (okm_len - where));
where += hash_len;
Tlen = hash_len;
}
return shaSuccess;
}
/*
* hkdfReset
*
* Description:
* This function will initialize the hkdfContext in preparation
* for key derivation using the modular HKDF interface for
* arbitrary length inputs.
*
* Parameters:
* context: [in/out]
* The context to reset.
* whichSha: [in]
* One of SHA1, SHA224, SHA256, SHA384, SHA512
* salt[ ]: [in]
* The optional salt value (a non-secret random value);
* if not provided (salt == NULL), it is set internally
* to a string of HashLen(whichSha) zeros.
* salt_len: [in]
* The length of the salt value. (Ignored if salt == NULL.)
*
* Returns:
* sha Error Code.
*
*/
int hkdfReset(HKDFContext *context, enum SHAversion whichSha,
const unsigned char *salt, size_t salt_len)
{
unsigned char nullSalt[USHAMaxHashSize];
if (!context) return shaNull;
context->whichSha = whichSha;
context->hashSize = USHAHashSize(whichSha);
if (salt == 0) {
salt = nullSalt;
salt_len = context->hashSize;
memset(nullSalt, '\0', salt_len);
}
return hmacReset(&context->hmacContext, whichSha, salt, salt_len);
}
/*
* hkdfInput
*
* Description:
* This function accepts an array of octets as the next portion
* of the input keying material. It may be called multiple times.
*
* Parameters:
* context: [in/out]
* The HKDF context to update.
* ikm[ ]: [in]
* An array of octets representing the next portion of
* the input keying material.
* ikm_len: [in]
* The length of ikm.
*
* Returns:
* sha Error Code.
*
*/
int hkdfInput(HKDFContext *context, const unsigned char *ikm,
size_t ikm_len)
{
if (!context) return shaNull;
if (context->Corrupted) return context->Corrupted;
if (context->Computed) return context->Corrupted = shaStateError;
return hmacInput(&context->hmacContext, ikm, ikm_len);
}
/*
* hkdfFinalBits
*
* Description:
* This function will add in any final bits of the
* input keying material.
*
* Parameters:
* context: [in/out]
* The HKDF context to update
* ikm_bits: [in]
* The final bits of the input keying material, in the upper
* portion of the byte. (Use 0b###00000 instead of 0b00000###
* to input the three bits ###.)
* ikm_bit_count: [in]
* The number of bits in message_bits, between 1 and 7.
*
* Returns:
* sha Error Code.
*/
int hkdfFinalBits(HKDFContext *context, uint8_t ikm_bits,
unsigned int ikm_bit_count)
{
if (!context) return shaNull;
if (context->Corrupted) return context->Corrupted;
if (context->Computed) return context->Corrupted = shaStateError;
return hmacFinalBits(&context->hmacContext, ikm_bits, ikm_bit_count);
}
/*
* hkdfResult
*
* Description:
* This function will finish the HKDF extraction and perform the
* final HKDF expansion.
*
* Parameters:
* context: [in/out]
* The HKDF context to use to calculate the HKDF hash.
* prk[ ]: [out]
* An optional location to store the HKDF extraction.
* Either NULL, or pointer to a buffer that must be
* larger than USHAHashSize(whichSha);
* info[ ]: [in]
* The optional context and application specific information.
* If info == NULL or a zero-length string, it is ignored.
* info_len: [in]
* The length of the optional context and application specific
* information. (Ignored if info == NULL.)
* okm[ ]: [out]
* Where the HKDF is to be stored.
* okm_len: [in]
* The length of the buffer to hold okm.
* okm_len must be <= 255 * USHABlockSize(whichSha)
*
* Returns:
* sha Error Code.
*
*/
int hkdfResult(HKDFContext *context,
uint8_t prk[USHAMaxHashSize],
const unsigned char *info, size_t info_len,
uint8_t okm[], size_t okm_len)
{
uint8_t prkbuf[USHAMaxHashSize];
int ret;
if (!context) return shaNull;
if (context->Corrupted) return context->Corrupted;
if (context->Computed) return context->Corrupted = shaStateError;
if (!okm) return context->Corrupted = shaBadParam;
if (!prk) prk = prkbuf;
ret = hmacResult(&context->hmacContext, prk) ||
hkdfExpand(context->whichSha, prk, context->hashSize, info,
info_len, okm, okm_len);
context->Computed = 1;
return context->Corrupted = ret;
}

250
nfq/crypto/hmac.c Normal file
View File

@ -0,0 +1,250 @@
/**************************** hmac.c ***************************/
/***************** See RFC 6234 for details. *******************/
/* Copyright (c) 2011 IETF Trust and the persons identified as */
/* authors of the code. All rights reserved. */
/* See sha.h for terms of use and redistribution. */
/*
* Description:
* This file implements the HMAC algorithm (Keyed-Hashing for
* Message Authentication, [RFC 2104]), expressed in terms of
* the various SHA algorithms.
*/
#include "sha.h"
#include <stddef.h>
/*
* hmac
*
* Description:
* This function will compute an HMAC message digest.
*
* Parameters:
* whichSha: [in]
* One of SHA1, SHA224, SHA256, SHA384, SHA512
* message_array[ ]: [in]
* An array of octets representing the message.
* Note: in RFC 2104, this parameter is known
* as 'text'.
* length: [in]
* The length of the message in message_array.
* key[ ]: [in]
* The secret shared key.
* key_len: [in]
* The length of the secret shared key.
* digest[ ]: [out]
* Where the digest is to be returned.
* NOTE: The length of the digest is determined by
* the value of whichSha.
*
* Returns:
* sha Error Code.
*
*/
int hmac(SHAversion whichSha,
const unsigned char *message_array, size_t length,
const unsigned char *key, size_t key_len,
uint8_t digest[USHAMaxHashSize])
{
HMACContext context;
return hmacReset(&context, whichSha, key, key_len) ||
hmacInput(&context, message_array, length) ||
hmacResult(&context, digest);
}
/*
* hmacReset
*
* Description:
* This function will initialize the hmacContext in preparation
* for computing a new HMAC message digest.
*
* Parameters:
* context: [in/out]
* The context to reset.
* whichSha: [in]
* One of SHA1, SHA224, SHA256, SHA384, SHA512
* key[ ]: [in]
* The secret shared key.
* key_len: [in]
* The length of the secret shared key.
*
* Returns:
* sha Error Code.
*
*/
int hmacReset(HMACContext *context, enum SHAversion whichSha,
const unsigned char *key, size_t key_len)
{
size_t i, blocksize, hashsize;
int ret;
/* inner padding - key XORd with ipad */
unsigned char k_ipad[USHA_Max_Message_Block_Size];
/* temporary buffer when keylen > blocksize */
unsigned char tempkey[USHAMaxHashSize];
if (!context) return shaNull;
context->Computed = 0;
context->Corrupted = shaSuccess;
blocksize = context->blockSize = USHABlockSize(whichSha);
hashsize = context->hashSize = USHAHashSize(whichSha);
context->whichSha = whichSha;
/*
* If key is longer than the hash blocksize,
* reset it to key = HASH(key).
*/
if (key_len > blocksize) {
USHAContext tcontext;
int err = USHAReset(&tcontext, whichSha) ||
USHAInput(&tcontext, key, key_len) ||
USHAResult(&tcontext, tempkey);
if (err != shaSuccess) return err;
key = tempkey;
key_len = hashsize;
}
/*
* The HMAC transform looks like:
*
* SHA(K XOR opad, SHA(K XOR ipad, text))
*
* where K is an n byte key, 0-padded to a total of blocksize bytes,
* ipad is the byte 0x36 repeated blocksize times,
* opad is the byte 0x5c repeated blocksize times,
* and text is the data being protected.
*/
/* store key into the pads, XOR'd with ipad and opad values */
for (i = 0; i < key_len; i++) {
k_ipad[i] = key[i] ^ 0x36;
context->k_opad[i] = key[i] ^ 0x5c;
}
/* remaining pad bytes are '\0' XOR'd with ipad and opad values */
for (; i < blocksize; i++) {
k_ipad[i] = 0x36;
context->k_opad[i] = 0x5c;
}
/* perform inner hash */
/* init context for 1st pass */
ret = USHAReset(&context->shaContext, whichSha) ||
/* and start with inner pad */
USHAInput(&context->shaContext, k_ipad, blocksize);
return context->Corrupted = ret;
}
/*
* hmacInput
*
* Description:
* This function accepts an array of octets as the next portion
* of the message. It may be called multiple times.
*
* Parameters:
* context: [in/out]
* The HMAC context to update.
* text[ ]: [in]
* An array of octets representing the next portion of
* the message.
* text_len: [in]
* The length of the message in text.
*
* Returns:
* sha Error Code.
*
*/
int hmacInput(HMACContext *context, const unsigned char *text,
size_t text_len)
{
if (!context) return shaNull;
if (context->Corrupted) return context->Corrupted;
if (context->Computed) return context->Corrupted = shaStateError;
/* then text of datagram */
return context->Corrupted =
USHAInput(&context->shaContext, text, text_len);
}
/*
* hmacFinalBits
*
* Description:
* This function will add in any final bits of the message.
*
* Parameters:
* context: [in/out]
* The HMAC context to update.
* message_bits: [in]
* The final bits of the message, in the upper portion of the
* byte. (Use 0b###00000 instead of 0b00000### to input the
* three bits ###.)
* length: [in]
* The number of bits in message_bits, between 1 and 7.
*
* Returns:
* sha Error Code.
*/
int hmacFinalBits(HMACContext *context,
uint8_t bits, unsigned int bit_count)
{
if (!context) return shaNull;
if (context->Corrupted) return context->Corrupted;
if (context->Computed) return context->Corrupted = shaStateError;
/* then final bits of datagram */
return context->Corrupted =
USHAFinalBits(&context->shaContext, bits, bit_count);
}
/*
* hmacResult
*
* Description:
* This function will return the N-byte message digest into the
* Message_Digest array provided by the caller.
*
* Parameters:
* context: [in/out]
* The context to use to calculate the HMAC hash.
* digest[ ]: [out]
* Where the digest is returned.
* NOTE 2: The length of the hash is determined by the value of
* whichSha that was passed to hmacReset().
*
* Returns:
* sha Error Code.
*
*/
int hmacResult(HMACContext *context, uint8_t *digest)
{
int ret;
if (!context) return shaNull;
if (context->Corrupted) return context->Corrupted;
if (context->Computed) return context->Corrupted = shaStateError;
/* finish up 1st pass */
/* (Use digest here as a temporary buffer.) */
ret =
USHAResult(&context->shaContext, digest) ||
/* perform outer SHA */
/* init context for 2nd pass */
USHAReset(&context->shaContext, context->whichSha) ||
/* start with outer pad */
USHAInput(&context->shaContext, context->k_opad,
context->blockSize) ||
/* then results of 1st hash */
USHAInput(&context->shaContext, digest, context->hashSize) ||
/* finish up 2nd pass */
USHAResult(&context->shaContext, digest);
context->Computed = 1;
return context->Corrupted = ret;
}

25
nfq/crypto/sha-private.h Normal file
View File

@ -0,0 +1,25 @@
/************************ sha-private.h ************************/
/***************** See RFC 6234 for details. *******************/
#pragma once
/*
* These definitions are defined in FIPS 180-3, section 4.1.
* Ch() and Maj() are defined identically in sections 4.1.1,
* 4.1.2, and 4.1.3.
*
* The definitions used in FIPS 180-3 are as follows:
*/
#ifndef USE_MODIFIED_MACROS
#define SHA_Ch(x,y,z) (((x) & (y)) ^ ((~(x)) & (z)))
#define SHA_Maj(x,y,z) (((x) & (y)) ^ ((x) & (z)) ^ ((y) & (z)))
#else /* USE_MODIFIED_MACROS */
/*
* The following definitions are equivalent and potentially faster.
*/
#define SHA_Ch(x, y, z) (((x) & ((y) ^ (z))) ^ (z))
#define SHA_Maj(x, y, z) (((x) & ((y) | (z))) | ((y) & (z)))
#endif /* USE_MODIFIED_MACROS */
#define SHA_Parity(x, y, z) ((x) ^ (y) ^ (z))

244
nfq/crypto/sha.h Normal file
View File

@ -0,0 +1,244 @@
#pragma once
/*
* Description:
* This file implements the Secure Hash Algorithms
* as defined in the U.S. National Institute of Standards
* and Technology Federal Information Processing Standards
* Publication (FIPS PUB) 180-3 published in October 2008
* and formerly defined in its predecessors, FIPS PUB 180-1
* and FIP PUB 180-2.
*
* A combined document showing all algorithms is available at
* http://csrc.nist.gov/publications/fips/
* fips180-3/fips180-3_final.pdf
*
* The five hashes are defined in these sizes:
* SHA-1 20 byte / 160 bit
* SHA-224 28 byte / 224 bit
* SHA-256 32 byte / 256 bit
* SHA-384 48 byte / 384 bit
* SHA-512 64 byte / 512 bit
*
* Compilation Note:
* These files may be compiled with two options:
* USE_32BIT_ONLY - use 32-bit arithmetic only, for systems
* without 64-bit integers
*
* USE_MODIFIED_MACROS - use alternate form of the SHA_Ch()
* and SHA_Maj() macros that are equivalent
* and potentially faster on many systems
*
*/
#include <stdint.h>
#include <stddef.h>
/*
* If you do not have the ISO standard stdint.h header file, then you
* must typedef the following:
* name meaning
* uint64_t unsigned 64-bit integer
* uint32_t unsigned 32-bit integer
* uint8_t unsigned 8-bit integer (i.e., unsigned char)
* int_least16_t integer of >= 16 bits
*
* See stdint-example.h
*/
#ifndef _SHA_enum_
#define _SHA_enum_
/*
* All SHA functions return one of these values.
*/
enum {
shaSuccess = 0,
shaNull, /* Null pointer parameter */
shaInputTooLong, /* input data too long */
shaStateError, /* called Input after FinalBits or Result */
shaBadParam /* passed a bad parameter */
};
#endif /* _SHA_enum_ */
/*
* These constants hold size information for each of the SHA
* hashing operations
*/
enum {
SHA1_Message_Block_Size = 64, SHA224_Message_Block_Size = 64,
SHA256_Message_Block_Size = 64,
USHA_Max_Message_Block_Size = SHA256_Message_Block_Size,
SHA1HashSize = 20, SHA224HashSize = 28, SHA256HashSize = 32,
USHAMaxHashSize = SHA256HashSize,
SHA1HashSizeBits = 160, SHA224HashSizeBits = 224,
SHA256HashSizeBits = 256, USHAMaxHashSizeBits = SHA256HashSizeBits
};
/*
* These constants are used in the USHA (Unified SHA) functions.
*/
typedef enum SHAversion {
SHA224, SHA256
} SHAversion;
/*
* This structure will hold context information for the SHA-256
* hashing operation.
*/
typedef struct SHA256Context {
uint32_t Intermediate_Hash[SHA256HashSize/4]; /* Message Digest */
uint32_t Length_High; /* Message length in bits */
uint32_t Length_Low; /* Message length in bits */
int_least16_t Message_Block_Index; /* Message_Block array index */
/* 512-bit message blocks */
uint8_t Message_Block[SHA256_Message_Block_Size];
int Computed; /* Is the hash computed? */
int Corrupted; /* Cumulative corruption code */
} SHA256Context;
/*
* This structure will hold context information for the SHA-224
* hashing operation. It uses the SHA-256 structure for computation.
*/
typedef struct SHA256Context SHA224Context;
/*
* This structure holds context information for all SHA
* hashing operations.
*/
typedef struct USHAContext {
int whichSha; /* which SHA is being used */
union {
SHA224Context sha224Context; SHA256Context sha256Context;
} ctx;
} USHAContext;
/*
* This structure will hold context information for the HMAC
* keyed-hashing operation.
*/
typedef struct HMACContext {
int whichSha; /* which SHA is being used */
int hashSize; /* hash size of SHA being used */
int blockSize; /* block size of SHA being used */
USHAContext shaContext; /* SHA context */
unsigned char k_opad[USHA_Max_Message_Block_Size];
/* outer padding - key XORd with opad */
int Computed; /* Is the MAC computed? */
int Corrupted; /* Cumulative corruption code */
} HMACContext;
/*
* This structure will hold context information for the HKDF
* extract-and-expand Key Derivation Functions.
*/
typedef struct HKDFContext {
int whichSha; /* which SHA is being used */
HMACContext hmacContext;
int hashSize; /* hash size of SHA being used */
unsigned char prk[USHAMaxHashSize];
/* pseudo-random key - output of hkdfInput */
int Computed; /* Is the key material computed? */
int Corrupted; /* Cumulative corruption code */
} HKDFContext;
/*
* Function Prototypes
*/
/* SHA-224 */
int SHA224Reset(SHA224Context *);
int SHA224Input(SHA224Context *, const uint8_t *bytes,
unsigned int bytecount);
int SHA224FinalBits(SHA224Context *, uint8_t bits,
unsigned int bit_count);
int SHA224Result(SHA224Context *,
uint8_t Message_Digest[SHA224HashSize]);
/* SHA-256 */
int SHA256Reset(SHA256Context *);
int SHA256Input(SHA256Context *, const uint8_t *bytes,
unsigned int bytecount);
int SHA256FinalBits(SHA256Context *, uint8_t bits,
unsigned int bit_count);
int SHA256Result(SHA256Context *,
uint8_t Message_Digest[SHA256HashSize]);
/* Unified SHA functions, chosen by whichSha */
int USHAReset(USHAContext *context, SHAversion whichSha);
int USHAInput(USHAContext *context,
const uint8_t *bytes, unsigned int bytecount);
int USHAFinalBits(USHAContext *context,
uint8_t bits, unsigned int bit_count);
int USHAResult(USHAContext *context,
uint8_t Message_Digest[USHAMaxHashSize]);
int USHABlockSize(enum SHAversion whichSha);
int USHAHashSize(enum SHAversion whichSha);
/*
* HMAC Keyed-Hashing for Message Authentication, RFC 2104,
* for all SHAs.
* This interface allows a fixed-length text input to be used.
*/
int hmac(SHAversion whichSha, /* which SHA algorithm to use */
const unsigned char *text, /* pointer to data stream */
size_t text_len, /* length of data stream */
const unsigned char *key, /* pointer to authentication key */
size_t key_len, /* length of authentication key */
uint8_t digest[USHAMaxHashSize]); /* caller digest to fill in */
/*
* HMAC Keyed-Hashing for Message Authentication, RFC 2104,
* for all SHAs.
* This interface allows any length of text input to be used.
*/
int hmacReset(HMACContext *context, enum SHAversion whichSha,
const unsigned char *key, size_t key_len);
int hmacInput(HMACContext *context, const unsigned char *text,
size_t text_len);
int hmacFinalBits(HMACContext *context, uint8_t bits,
unsigned int bit_count);
int hmacResult(HMACContext *context,
uint8_t digest[USHAMaxHashSize]);
/*
* HKDF HMAC-based Extract-and-Expand Key Derivation Function,
* RFC 5869, for all SHAs.
*/
int hkdf(SHAversion whichSha,
const unsigned char *salt, size_t salt_len,
const unsigned char *ikm, size_t ikm_len,
const unsigned char *info, size_t info_len,
uint8_t okm[ ], size_t okm_len);
int hkdfExtract(SHAversion whichSha, const unsigned char *salt,
size_t salt_len, const unsigned char *ikm,
size_t ikm_len, uint8_t prk[USHAMaxHashSize]);
int hkdfExpand(SHAversion whichSha, const uint8_t prk[ ],
size_t prk_len, const unsigned char *info,
size_t info_len, uint8_t okm[ ], size_t okm_len);
/*
* HKDF HMAC-based Extract-and-Expand Key Derivation Function,
* RFC 5869, for all SHAs.
* This interface allows any length of text input to be used.
*/
int hkdfReset(HKDFContext *context, enum SHAversion whichSha,
const unsigned char *salt, size_t salt_len);
int hkdfInput(HKDFContext *context, const unsigned char *ikm,
size_t ikm_len);
int hkdfFinalBits(HKDFContext *context, uint8_t ikm_bits,
unsigned int ikm_bit_count);
int hkdfResult(HKDFContext *context,
uint8_t prk[USHAMaxHashSize],
const unsigned char *info, size_t info_len,
uint8_t okm[USHAMaxHashSize], size_t okm_len);

581
nfq/crypto/sha224-256.c Normal file
View File

@ -0,0 +1,581 @@
/************************* sha224-256.c ************************/
/***************** See RFC 6234 for details. *******************/
/* Copyright (c) 2011 IETF Trust and the persons identified as */
/* authors of the code. All rights reserved. */
/* See sha.h for terms of use and redistribution. */
/*
* Description:
* This file implements the Secure Hash Algorithms SHA-224 and
* SHA-256 as defined in the U.S. National Institute of Standards
* and Technology Federal Information Processing Standards
* Publication (FIPS PUB) 180-3 published in October 2008
* and formerly defined in its predecessors, FIPS PUB 180-1
* and FIP PUB 180-2.
*
* A combined document showing all algorithms is available at
* http://csrc.nist.gov/publications/fips/
* fips180-3/fips180-3_final.pdf
*
* The SHA-224 and SHA-256 algorithms produce 224-bit and 256-bit
* message digests for a given data stream. It should take about
* 2**n steps to find a message with the same digest as a given
* message and 2**(n/2) to find any two messages with the same
* digest, when n is the digest size in bits. Therefore, this
* algorithm can serve as a means of providing a
* "fingerprint" for a message.
*
* Portability Issues:
* SHA-224 and SHA-256 are defined in terms of 32-bit "words".
* This code uses <stdint.h> (included via "sha.h") to define 32-
* and 8-bit unsigned integer types. If your C compiler does not
* support 32-bit unsigned integers, this code is not
* appropriate.
*
* Caveats:
* SHA-224 and SHA-256 are designed to work with messages less
* than 2^64 bits long. This implementation uses SHA224/256Input()
* to hash the bits that are a multiple of the size of an 8-bit
* octet, and then optionally uses SHA224/256FinalBits()
* to hash the final few bits of the input.
*/
#include "sha.h"
#include "sha-private.h"
/* Define the SHA shift, rotate left, and rotate right macros */
#define SHA256_SHR(bits,word) ((word) >> (bits))
#define SHA256_ROTL(bits,word) \
(((word) << (bits)) | ((word) >> (32-(bits))))
#define SHA256_ROTR(bits,word) \
(((word) >> (bits)) | ((word) << (32-(bits))))
/* Define the SHA SIGMA and sigma macros */
#define SHA256_SIGMA0(word) \
(SHA256_ROTR( 2,word) ^ SHA256_ROTR(13,word) ^ SHA256_ROTR(22,word))
#define SHA256_SIGMA1(word) \
(SHA256_ROTR( 6,word) ^ SHA256_ROTR(11,word) ^ SHA256_ROTR(25,word))
#define SHA256_sigma0(word) \
(SHA256_ROTR( 7,word) ^ SHA256_ROTR(18,word) ^ SHA256_SHR( 3,word))
#define SHA256_sigma1(word) \
(SHA256_ROTR(17,word) ^ SHA256_ROTR(19,word) ^ SHA256_SHR(10,word))
/*
* Add "length" to the length.
* Set Corrupted when overflow has occurred.
*/
static uint32_t addTemp;
#define SHA224_256AddLength(context, length) \
(addTemp = (context)->Length_Low, (context)->Corrupted = \
(((context)->Length_Low += (length)) < addTemp) && \
(++(context)->Length_High == 0) ? shaInputTooLong : \
(context)->Corrupted )
/* Local Function Prototypes */
static int SHA224_256Reset(SHA256Context *context, uint32_t *H0);
static void SHA224_256ProcessMessageBlock(SHA256Context *context);
static void SHA224_256Finalize(SHA256Context *context,
uint8_t Pad_Byte);
static void SHA224_256PadMessage(SHA256Context *context,
uint8_t Pad_Byte);
static int SHA224_256ResultN(SHA256Context *context,
uint8_t Message_Digest[ ], int HashSize);
/* Initial Hash Values: FIPS 180-3 section 5.3.2 */
static uint32_t SHA224_H0[SHA256HashSize/4] = {
0xC1059ED8, 0x367CD507, 0x3070DD17, 0xF70E5939,
0xFFC00B31, 0x68581511, 0x64F98FA7, 0xBEFA4FA4
};
/* Initial Hash Values: FIPS 180-3 section 5.3.3 */
static uint32_t SHA256_H0[SHA256HashSize/4] = {
0x6A09E667, 0xBB67AE85, 0x3C6EF372, 0xA54FF53A,
0x510E527F, 0x9B05688C, 0x1F83D9AB, 0x5BE0CD19
};
/*
* SHA224Reset
*
* Description:
* This function will initialize the SHA224Context in preparation
* for computing a new SHA224 message digest.
*
* Parameters:
* context: [in/out]
* The context to reset.
*
* Returns:
* sha Error Code.
*/
int SHA224Reset(SHA224Context *context)
{
return SHA224_256Reset(context, SHA224_H0);
}
/*
* SHA224Input
*
* Description:
* This function accepts an array of octets as the next portion
* of the message.
*
* Parameters:
* context: [in/out]
* The SHA context to update.
* message_array[ ]: [in]
* An array of octets representing the next portion of
* the message.
* length: [in]
* The length of the message in message_array.
*
* Returns:
* sha Error Code.
*
*/
int SHA224Input(SHA224Context *context, const uint8_t *message_array,
unsigned int length)
{
return SHA256Input(context, message_array, length);
}
/*
* SHA224FinalBits
*
* Description:
* This function will add in any final bits of the message.
*
* Parameters:
* context: [in/out]
* The SHA context to update.
* message_bits: [in]
* The final bits of the message, in the upper portion of the
* byte. (Use 0b###00000 instead of 0b00000### to input the
* three bits ###.)
* length: [in]
* The number of bits in message_bits, between 1 and 7.
*
* Returns:
* sha Error Code.
*/
int SHA224FinalBits(SHA224Context *context,
uint8_t message_bits, unsigned int length)
{
return SHA256FinalBits(context, message_bits, length);
}
/*
* SHA224Result
*
* Description:
* This function will return the 224-bit message digest
* into the Message_Digest array provided by the caller.
* NOTE:
* The first octet of hash is stored in the element with index 0,
* the last octet of hash in the element with index 27.
*
* Parameters:
* context: [in/out]
* The context to use to calculate the SHA hash.
* Message_Digest[ ]: [out]
* Where the digest is returned.
*
* Returns:
* sha Error Code.
*/
int SHA224Result(SHA224Context *context,
uint8_t Message_Digest[SHA224HashSize])
{
return SHA224_256ResultN(context, Message_Digest, SHA224HashSize);
}
/*
* SHA256Reset
*
* Description:
* This function will initialize the SHA256Context in preparation
* for computing a new SHA256 message digest.
*
* Parameters:
* context: [in/out]
* The context to reset.
*
* Returns:
* sha Error Code.
*/
int SHA256Reset(SHA256Context *context)
{
return SHA224_256Reset(context, SHA256_H0);
}
/*
* SHA256Input
*
* Description:
* This function accepts an array of octets as the next portion
* of the message.
*
* Parameters:
* context: [in/out]
* The SHA context to update.
* message_array[ ]: [in]
* An array of octets representing the next portion of
* the message.
* length: [in]
* The length of the message in message_array.
*
* Returns:
* sha Error Code.
*/
int SHA256Input(SHA256Context *context, const uint8_t *message_array,
unsigned int length)
{
if (!context) return shaNull;
if (!length) return shaSuccess;
if (!message_array) return shaNull;
if (context->Computed) return context->Corrupted = shaStateError;
if (context->Corrupted) return context->Corrupted;
while (length--) {
context->Message_Block[context->Message_Block_Index++] =
*message_array;
if ((SHA224_256AddLength(context, 8) == shaSuccess) &&
(context->Message_Block_Index == SHA256_Message_Block_Size))
SHA224_256ProcessMessageBlock(context);
message_array++;
}
return context->Corrupted;
}
/*
* SHA256FinalBits
*
* Description:
* This function will add in any final bits of the message.
*
* Parameters:
* context: [in/out]
* The SHA context to update.
* message_bits: [in]
* The final bits of the message, in the upper portion of the
* byte. (Use 0b###00000 instead of 0b00000### to input the
* three bits ###.)
* length: [in]
* The number of bits in message_bits, between 1 and 7.
*
* Returns:
* sha Error Code.
*/
int SHA256FinalBits(SHA256Context *context,
uint8_t message_bits, unsigned int length)
{
static uint8_t masks[8] = {
/* 0 0b00000000 */ 0x00, /* 1 0b10000000 */ 0x80,
/* 2 0b11000000 */ 0xC0, /* 3 0b11100000 */ 0xE0,
/* 4 0b11110000 */ 0xF0, /* 5 0b11111000 */ 0xF8,
/* 6 0b11111100 */ 0xFC, /* 7 0b11111110 */ 0xFE
};
static uint8_t markbit[8] = {
/* 0 0b10000000 */ 0x80, /* 1 0b01000000 */ 0x40,
/* 2 0b00100000 */ 0x20, /* 3 0b00010000 */ 0x10,
/* 4 0b00001000 */ 0x08, /* 5 0b00000100 */ 0x04,
/* 6 0b00000010 */ 0x02, /* 7 0b00000001 */ 0x01
};
if (!context) return shaNull;
if (!length) return shaSuccess;
if (context->Corrupted) return context->Corrupted;
if (context->Computed) return context->Corrupted = shaStateError;
if (length >= 8) return context->Corrupted = shaBadParam;
SHA224_256AddLength(context, length);
SHA224_256Finalize(context, (uint8_t)
((message_bits & masks[length]) | markbit[length]));
return context->Corrupted;
}
/*
* SHA256Result
*
* Description:
* This function will return the 256-bit message digest
* into the Message_Digest array provided by the caller.
* NOTE:
* The first octet of hash is stored in the element with index 0,
* the last octet of hash in the element with index 31.
*
* Parameters:
* context: [in/out]
* The context to use to calculate the SHA hash.
* Message_Digest[ ]: [out]
* Where the digest is returned.
*
* Returns:
* sha Error Code.
*/
int SHA256Result(SHA256Context *context,
uint8_t Message_Digest[SHA256HashSize])
{
return SHA224_256ResultN(context, Message_Digest, SHA256HashSize);
}
/*
* SHA224_256Reset
*
* Description:
* This helper function will initialize the SHA256Context in
* preparation for computing a new SHA-224 or SHA-256 message digest.
*
* Parameters:
* context: [in/out]
* The context to reset.
* H0[ ]: [in]
* The initial hash value array to use.
*
* Returns:
* sha Error Code.
*/
static int SHA224_256Reset(SHA256Context *context, uint32_t *H0)
{
if (!context) return shaNull;
context->Length_High = context->Length_Low = 0;
context->Message_Block_Index = 0;
context->Intermediate_Hash[0] = H0[0];
context->Intermediate_Hash[1] = H0[1];
context->Intermediate_Hash[2] = H0[2];
context->Intermediate_Hash[3] = H0[3];
context->Intermediate_Hash[4] = H0[4];
context->Intermediate_Hash[5] = H0[5];
context->Intermediate_Hash[6] = H0[6];
context->Intermediate_Hash[7] = H0[7];
context->Computed = 0;
context->Corrupted = shaSuccess;
return shaSuccess;
}
/*
* SHA224_256ProcessMessageBlock
*
* Description:
* This helper function will process the next 512 bits of the
* message stored in the Message_Block array.
*
* Parameters:
* context: [in/out]
* The SHA context to update.
*
* Returns:
* Nothing.
*
* Comments:
* Many of the variable names in this code, especially the
* single character names, were used because those were the
* names used in the Secure Hash Standard.
*/
static void SHA224_256ProcessMessageBlock(SHA256Context *context)
{
/* Constants defined in FIPS 180-3, section 4.2.2 */
static const uint32_t K[64] = {
0x428a2f98, 0x71374491, 0xb5c0fbcf, 0xe9b5dba5, 0x3956c25b,
0x59f111f1, 0x923f82a4, 0xab1c5ed5, 0xd807aa98, 0x12835b01,
0x243185be, 0x550c7dc3, 0x72be5d74, 0x80deb1fe, 0x9bdc06a7,
0xc19bf174, 0xe49b69c1, 0xefbe4786, 0x0fc19dc6, 0x240ca1cc,
0x2de92c6f, 0x4a7484aa, 0x5cb0a9dc, 0x76f988da, 0x983e5152,
0xa831c66d, 0xb00327c8, 0xbf597fc7, 0xc6e00bf3, 0xd5a79147,
0x06ca6351, 0x14292967, 0x27b70a85, 0x2e1b2138, 0x4d2c6dfc,
0x53380d13, 0x650a7354, 0x766a0abb, 0x81c2c92e, 0x92722c85,
0xa2bfe8a1, 0xa81a664b, 0xc24b8b70, 0xc76c51a3, 0xd192e819,
0xd6990624, 0xf40e3585, 0x106aa070, 0x19a4c116, 0x1e376c08,
0x2748774c, 0x34b0bcb5, 0x391c0cb3, 0x4ed8aa4a, 0x5b9cca4f,
0x682e6ff3, 0x748f82ee, 0x78a5636f, 0x84c87814, 0x8cc70208,
0x90befffa, 0xa4506ceb, 0xbef9a3f7, 0xc67178f2
};
int t, t4; /* Loop counter */
uint32_t temp1, temp2; /* Temporary word value */
uint32_t W[64]; /* Word sequence */
uint32_t A, B, C, D, E, F, G, H; /* Word buffers */
/*
* Initialize the first 16 words in the array W
*/
for (t = t4 = 0; t < 16; t++, t4 += 4)
W[t] = (((uint32_t)context->Message_Block[t4]) << 24) |
(((uint32_t)context->Message_Block[t4 + 1]) << 16) |
(((uint32_t)context->Message_Block[t4 + 2]) << 8) |
(((uint32_t)context->Message_Block[t4 + 3]));
for (t = 16; t < 64; t++)
W[t] = SHA256_sigma1(W[t-2]) + W[t-7] +
SHA256_sigma0(W[t-15]) + W[t-16];
A = context->Intermediate_Hash[0];
B = context->Intermediate_Hash[1];
C = context->Intermediate_Hash[2];
D = context->Intermediate_Hash[3];
E = context->Intermediate_Hash[4];
F = context->Intermediate_Hash[5];
G = context->Intermediate_Hash[6];
H = context->Intermediate_Hash[7];
for (t = 0; t < 64; t++) {
temp1 = H + SHA256_SIGMA1(E) + SHA_Ch(E,F,G) + K[t] + W[t];
temp2 = SHA256_SIGMA0(A) + SHA_Maj(A,B,C);
H = G;
G = F;
F = E;
E = D + temp1;
D = C;
C = B;
B = A;
A = temp1 + temp2;
}
context->Intermediate_Hash[0] += A;
context->Intermediate_Hash[1] += B;
context->Intermediate_Hash[2] += C;
context->Intermediate_Hash[3] += D;
context->Intermediate_Hash[4] += E;
context->Intermediate_Hash[5] += F;
context->Intermediate_Hash[6] += G;
context->Intermediate_Hash[7] += H;
context->Message_Block_Index = 0;
}
/*
* SHA224_256Finalize
*
* Description:
* This helper function finishes off the digest calculations.
*
* Parameters:
* context: [in/out]
* The SHA context to update.
* Pad_Byte: [in]
* The last byte to add to the message block before the 0-padding
* and length. This will contain the last bits of the message
* followed by another single bit. If the message was an
* exact multiple of 8-bits long, Pad_Byte will be 0x80.
*
* Returns:
* sha Error Code.
*/
static void SHA224_256Finalize(SHA256Context *context,
uint8_t Pad_Byte)
{
int i;
SHA224_256PadMessage(context, Pad_Byte);
/* message may be sensitive, so clear it out */
for (i = 0; i < SHA256_Message_Block_Size; ++i)
context->Message_Block[i] = 0;
context->Length_High = 0; /* and clear length */
context->Length_Low = 0;
context->Computed = 1;
}
/*
* SHA224_256PadMessage
*
* Description:
* According to the standard, the message must be padded to the next
* even multiple of 512 bits. The first padding bit must be a '1'.
* The last 64 bits represent the length of the original message.
* All bits in between should be 0. This helper function will pad
* the message according to those rules by filling the
* Message_Block array accordingly. When it returns, it can be
* assumed that the message digest has been computed.
*
* Parameters:
* context: [in/out]
* The context to pad.
* Pad_Byte: [in]
* The last byte to add to the message block before the 0-padding
* and length. This will contain the last bits of the message
* followed by another single bit. If the message was an
* exact multiple of 8-bits long, Pad_Byte will be 0x80.
*
* Returns:
* Nothing.
*/
static void SHA224_256PadMessage(SHA256Context *context,
uint8_t Pad_Byte)
{
/*
* Check to see if the current message block is too small to hold
* the initial padding bits and length. If so, we will pad the
* block, process it, and then continue padding into a second
* block.
*/
if (context->Message_Block_Index >= (SHA256_Message_Block_Size-8)) {
context->Message_Block[context->Message_Block_Index++] = Pad_Byte;
while (context->Message_Block_Index < SHA256_Message_Block_Size)
context->Message_Block[context->Message_Block_Index++] = 0;
SHA224_256ProcessMessageBlock(context);
} else
context->Message_Block[context->Message_Block_Index++] = Pad_Byte;
while (context->Message_Block_Index < (SHA256_Message_Block_Size-8))
context->Message_Block[context->Message_Block_Index++] = 0;
/*
* Store the message length as the last 8 octets
*/
context->Message_Block[56] = (uint8_t)(context->Length_High >> 24);
context->Message_Block[57] = (uint8_t)(context->Length_High >> 16);
context->Message_Block[58] = (uint8_t)(context->Length_High >> 8);
context->Message_Block[59] = (uint8_t)(context->Length_High);
context->Message_Block[60] = (uint8_t)(context->Length_Low >> 24);
context->Message_Block[61] = (uint8_t)(context->Length_Low >> 16);
context->Message_Block[62] = (uint8_t)(context->Length_Low >> 8);
context->Message_Block[63] = (uint8_t)(context->Length_Low);
SHA224_256ProcessMessageBlock(context);
}
/*
* SHA224_256ResultN
*
* Description:
* This helper function will return the 224-bit or 256-bit message
* digest into the Message_Digest array provided by the caller.
* NOTE:
* The first octet of hash is stored in the element with index 0,
* the last octet of hash in the element with index 27/31.
*
* Parameters:
* context: [in/out]
* The context to use to calculate the SHA hash.
* Message_Digest[ ]: [out]
* Where the digest is returned.
* HashSize: [in]
* The size of the hash, either 28 or 32.
*
* Returns:
* sha Error Code.
*/
static int SHA224_256ResultN(SHA256Context *context,
uint8_t Message_Digest[ ], int HashSize)
{
int i;
if (!context) return shaNull;
if (!Message_Digest) return shaNull;
if (context->Corrupted) return context->Corrupted;
if (!context->Computed)
SHA224_256Finalize(context, 0x80);
for (i = 0; i < HashSize; ++i)
Message_Digest[i] = (uint8_t)
(context->Intermediate_Hash[i>>2] >> 8 * ( 3 - ( i & 0x03 ) ));
return shaSuccess;
}

191
nfq/crypto/usha.c Normal file
View File

@ -0,0 +1,191 @@
/**************************** usha.c ***************************/
/***************** See RFC 6234 for details. *******************/
/* Copyright (c) 2011 IETF Trust and the persons identified as */
/* authors of the code. All rights reserved. */
/* See sha.h for terms of use and redistribution. */
/*
* Description:
* This file implements a unified interface to the SHA algorithms.
*/
#include "sha.h"
/*
* USHAReset
*
* Description:
* This function will initialize the SHA Context in preparation
* for computing a new SHA message digest.
*
* Parameters:
* context: [in/out]
* The context to reset.
* whichSha: [in]
* Selects which SHA reset to call
*
* Returns:
* sha Error Code.
*
*/
int USHAReset(USHAContext *context, enum SHAversion whichSha)
{
if (!context) return shaNull;
context->whichSha = whichSha;
switch (whichSha) {
case SHA224: return SHA224Reset((SHA224Context*)&context->ctx);
case SHA256: return SHA256Reset((SHA256Context*)&context->ctx);
default: return shaBadParam;
}
}
/*
* USHAInput
*
* Description:
* This function accepts an array of octets as the next portion
* of the message.
*
* Parameters:
* context: [in/out]
* The SHA context to update.
* message_array: [in]
* An array of octets representing the next portion of
* the message.
* length: [in]
* The length of the message in message_array.
*
* Returns:
* sha Error Code.
*
*/
int USHAInput(USHAContext *context,
const uint8_t *bytes, unsigned int bytecount)
{
if (!context) return shaNull;
switch (context->whichSha) {
case SHA224:
return SHA224Input((SHA224Context*)&context->ctx, bytes,
bytecount);
case SHA256:
return SHA256Input((SHA256Context*)&context->ctx, bytes,
bytecount);
default: return shaBadParam;
}
}
/*
* USHAFinalBits
*
* Description:
* This function will add in any final bits of the message.
*
* Parameters:
* context: [in/out]
* The SHA context to update.
* message_bits: [in]
* The final bits of the message, in the upper portion of the
* byte. (Use 0b###00000 instead of 0b00000### to input the
* three bits ###.)
* length: [in]
* The number of bits in message_bits, between 1 and 7.
*
* Returns:
* sha Error Code.
*/
int USHAFinalBits(USHAContext *context,
uint8_t bits, unsigned int bit_count)
{
if (!context) return shaNull;
switch (context->whichSha) {
case SHA224:
return SHA224FinalBits((SHA224Context*)&context->ctx, bits,
bit_count);
case SHA256:
return SHA256FinalBits((SHA256Context*)&context->ctx, bits,
bit_count);
default: return shaBadParam;
}
}
/*
* USHAResult
*
* Description:
* This function will return the message digest of the appropriate
* bit size, as returned by USHAHashSizeBits(whichSHA) for the
* 'whichSHA' value used in the preceeding call to USHAReset,
* into the Message_Digest array provided by the caller.
*
* Parameters:
* context: [in/out]
* The context to use to calculate the SHA-1 hash.
* Message_Digest: [out]
* Where the digest is returned.
*
* Returns:
* sha Error Code.
*
*/
int USHAResult(USHAContext *context,
uint8_t Message_Digest[USHAMaxHashSize])
{
if (!context) return shaNull;
switch (context->whichSha) {
case SHA224:
return SHA224Result((SHA224Context*)&context->ctx,
Message_Digest);
case SHA256:
return SHA256Result((SHA256Context*)&context->ctx,
Message_Digest);
default: return shaBadParam;
}
}
/*
* USHABlockSize
*
* Description:
* This function will return the blocksize for the given SHA
* algorithm.
*
* Parameters:
* whichSha:
* which SHA algorithm to query
*
* Returns:
* block size
*
*/
int USHABlockSize(enum SHAversion whichSha)
{
switch (whichSha) {
case SHA224: return SHA224_Message_Block_Size;
default:
case SHA256: return SHA256_Message_Block_Size;
}
}
/*
* USHAHashSize
*
* Description:
* This function will return the hashsize for the given SHA
* algorithm.
*
* Parameters:
* whichSha:
* which SHA algorithm to query
*
* Returns:
* hash size
*
*/
int USHAHashSize(enum SHAversion whichSha)
{
switch (whichSha) {
case SHA224: return SHA224HashSize;
default:
case SHA256: return SHA256HashSize;
}
}

View File

@ -655,12 +655,37 @@ packet_process_result dpi_desync_udp_packet(uint8_t *data_pkt, size_t len_pkt, s
const uint8_t *fake;
size_t fake_size;
bool b;
char host[256];
bool bHaveHost=false;
if (IsQUICInitial(data_payload,len_payload))
{
DLOG("packet contains QUIC initial\n")
fake = params.fake_quic;
fake_size = params.fake_quic_size;
bool bIsCryptoHello;
bHaveHost=QUICExtractHostFromInitial(data_payload,len_payload,host,sizeof(host),&bIsCryptoHello);
if (bIsCryptoHello)
{
if (params.desync_skip_nosni && !bHaveHost)
{
DLOG("not applying tampering to QUIC ClientHello without hostname in the SNI\n")
return res;
}
}
else
{
if (params.desync_any_proto)
{
DLOG("QUIC initial without CRYPTO frame. applying tampering because desync_any_proto is set\n")
}
else
{
DLOG("not applying tampering to QUIC initial without CRYPTO frame\n")
return res;
}
}
}
else
{
@ -670,6 +695,16 @@ packet_process_result dpi_desync_udp_packet(uint8_t *data_pkt, size_t len_pkt, s
fake_size = params.fake_unknown_udp_size;
}
if (bHaveHost)
{
DLOG("hostname: %s\n",host)
if (params.hostlist && !SearchHostList(params.hostlist,host,params.debug))
{
DLOG("not applying tampering to this request\n")
return res;
}
}
enum dpi_desync_mode desync_mode = params.desync_mode;
uint8_t fooling_orig = FOOL_NONE;

View File

@ -9,20 +9,20 @@
void hexdump_limited_dlog(const uint8_t *data, size_t size, size_t limit)
{
size_t k;
bool bcut=false;
if (size>limit)
bool bcut = false;
if (size > limit)
{
size=limit;
size = limit;
bcut = true;
}
if (!size) return;
for (k=0;k<size;k++) DLOG("%02X ",data[k]);
for (k = 0; k < size; k++) DLOG("%02X ", data[k]);
DLOG(bcut ? "... : " : ": ");
for (k=0;k<size;k++) DLOG("%c",data[k]>=0x20 && data[k]<=0x7F ? (char)data[k] : '.');
for (k = 0; k < size; k++) DLOG("%c", data[k] >= 0x20 && data[k] <= 0x7F ? (char)data[k] : '.');
if (bcut) DLOG(" ...");
}
char *strncasestr(const char *s,const char *find, size_t slen)
char *strncasestr(const char *s, const char *find, size_t slen)
{
char c, sc;
size_t len;
@ -43,14 +43,14 @@ char *strncasestr(const char *s,const char *find, size_t slen)
return (char *)s;
}
bool load_file(const char *filename,void *buffer,size_t *buffer_size)
bool load_file(const char *filename, void *buffer, size_t *buffer_size)
{
FILE *F;
F = fopen(filename,"rb");
F = fopen(filename, "rb");
if (!F) return false;
*buffer_size = fread(buffer,1,*buffer_size,F);
*buffer_size = fread(buffer, 1, *buffer_size, F);
if (ferror(F))
{
fclose(F);
@ -60,18 +60,34 @@ bool load_file(const char *filename,void *buffer,size_t *buffer_size)
fclose(F);
return true;
}
bool load_file_nonempty(const char *filename,void *buffer,size_t *buffer_size)
bool load_file_nonempty(const char *filename, void *buffer, size_t *buffer_size)
{
bool b = load_file(filename,buffer,buffer_size);
bool b = load_file(filename, buffer, buffer_size);
return b && *buffer_size;
}
bool save_file(const char *filename, const void *buffer, size_t buffer_size)
{
FILE *F;
F = fopen(filename, "wb");
if (!F) return false;
fwrite(buffer, 1, buffer_size, F);
if (ferror(F))
{
fclose(F);
return false;
}
fclose(F);
return true;
}
void ntop46(const struct sockaddr *sa, char *str, size_t len)
{
if (!len) return;
*str=0;
*str = 0;
switch (sa->sa_family)
{
case AF_INET:
@ -81,31 +97,31 @@ void ntop46(const struct sockaddr *sa, char *str, size_t len)
inet_ntop(sa->sa_family, &((struct sockaddr_in6*)sa)->sin6_addr, str, len);
break;
default:
snprintf(str,len,"UNKNOWN_FAMILY_%d",sa->sa_family);
snprintf(str, len, "UNKNOWN_FAMILY_%d", sa->sa_family);
}
}
void ntop46_port(const struct sockaddr *sa, char *str, size_t len)
{
char ip[40];
ntop46(sa,ip,sizeof(ip));
ntop46(sa, ip, sizeof(ip));
switch (sa->sa_family)
{
case AF_INET:
snprintf(str,len,"%s:%u",ip,ntohs(((struct sockaddr_in*)sa)->sin_port));
snprintf(str, len, "%s:%u", ip, ntohs(((struct sockaddr_in*)sa)->sin_port));
break;
case AF_INET6:
snprintf(str,len,"[%s]:%u",ip,ntohs(((struct sockaddr_in6*)sa)->sin6_port));
snprintf(str, len, "[%s]:%u", ip, ntohs(((struct sockaddr_in6*)sa)->sin6_port));
break;
default:
snprintf(str,len,"%s",ip);
snprintf(str, len, "%s", ip);
}
}
void print_sockaddr(const struct sockaddr *sa)
{
char ip_port[48];
ntop46_port(sa,ip_port,sizeof(ip_port));
printf("%s",ip_port);
ntop46_port(sa, ip_port, sizeof(ip_port));
printf("%s", ip_port);
}
void dbgprint_socket_buffers(int fd)
@ -114,24 +130,24 @@ void dbgprint_socket_buffers(int fd)
{
int v;
socklen_t sz;
sz=sizeof(int);
if (!getsockopt(fd,SOL_SOCKET,SO_RCVBUF,&v,&sz))
DLOG("fd=%d SO_RCVBUF=%d\n",fd,v)
sz=sizeof(int);
if (!getsockopt(fd,SOL_SOCKET,SO_SNDBUF,&v,&sz))
DLOG("fd=%d SO_SNDBUF=%d\n",fd,v)
sz = sizeof(int);
if (!getsockopt(fd, SOL_SOCKET, SO_RCVBUF, &v, &sz))
DLOG("fd=%d SO_RCVBUF=%d\n", fd, v)
sz = sizeof(int);
if (!getsockopt(fd, SOL_SOCKET, SO_SNDBUF, &v, &sz))
DLOG("fd=%d SO_SNDBUF=%d\n", fd, v)
}
}
bool set_socket_buffers(int fd, int rcvbuf, int sndbuf)
{
DLOG("set_socket_buffers fd=%d rcvbuf=%d sndbuf=%d\n",fd,rcvbuf,sndbuf)
if (rcvbuf && setsockopt(fd, SOL_SOCKET, SO_RCVBUF, &rcvbuf, sizeof(int)) <0)
DLOG("set_socket_buffers fd=%d rcvbuf=%d sndbuf=%d\n", fd, rcvbuf, sndbuf)
if (rcvbuf && setsockopt(fd, SOL_SOCKET, SO_RCVBUF, &rcvbuf, sizeof(int)) < 0)
{
perror("setsockopt (SO_RCVBUF)");
close(fd);
return false;
}
if (sndbuf && setsockopt(fd, SOL_SOCKET, SO_SNDBUF, &sndbuf, sizeof(int)) <0)
if (sndbuf && setsockopt(fd, SOL_SOCKET, SO_SNDBUF, &sndbuf, sizeof(int)) < 0)
{
perror("setsockopt (SO_SNDBUF)");
close(fd);
@ -140,3 +156,26 @@ bool set_socket_buffers(int fd, int rcvbuf, int sndbuf)
dbgprint_socket_buffers(fd);
return true;
}
uint64_t pntoh64(const void *p)
{
return (uint64_t)*((const uint8_t *)(p)+0) << 56 |
(uint64_t)*((const uint8_t *)(p)+1) << 48 |
(uint64_t)*((const uint8_t *)(p)+2) << 40 |
(uint64_t)*((const uint8_t *)(p)+3) << 32 |
(uint64_t)*((const uint8_t *)(p)+4) << 24 |
(uint64_t)*((const uint8_t *)(p)+5) << 16 |
(uint64_t)*((const uint8_t *)(p)+6) << 8 |
(uint64_t)*((const uint8_t *)(p)+7) << 0;
}
void phton64(uint8_t *p, uint64_t v)
{
p[0] = (uint8_t)(v >> 56);
p[1] = (uint8_t)(v >> 48);
p[2] = (uint8_t)(v >> 40);
p[3] = (uint8_t)(v >> 32);
p[4] = (uint8_t)(v >> 24);
p[5] = (uint8_t)(v >> 16);
p[6] = (uint8_t)(v >> 8);
p[7] = (uint8_t)(v >> 0);
}

View File

@ -12,6 +12,7 @@ void hexdump_limited_dlog(const uint8_t *data, size_t size, size_t limit);
char *strncasestr(const char *s,const char *find, size_t slen);
bool load_file(const char *filename,void *buffer,size_t *buffer_size);
bool load_file_nonempty(const char *filename,void *buffer,size_t *buffer_size);
bool save_file(const char *filename, const void *buffer, size_t buffer_size);
void print_sockaddr(const struct sockaddr *sa);
void ntop46(const struct sockaddr *sa, char *str, size_t len);
@ -19,3 +20,6 @@ void ntop46_port(const struct sockaddr *sa, char *str, size_t len);
void dbgprint_socket_buffers(int fd);
bool set_socket_buffers(int fd, int rcvbuf, int sndbuf);
uint64_t pntoh64(const void *p);
void phton64(uint8_t *p, uint64_t v);

View File

@ -43,17 +43,64 @@ bool HttpExtractHost(const uint8_t *data, size_t len, char *host, size_t len_hos
}
return false;
}
static uint8_t tvb_get_varint(const uint8_t *tvb, uint64_t *value)
{
switch (*tvb >> 6)
{
case 0: /* 0b00 => 1 byte length (6 bits Usable) */
if (value) *value = *tvb & 0x3F;
return 1;
case 1: /* 0b01 => 2 bytes length (14 bits Usable) */
if (value) *value = ntohs(*(uint16_t*)tvb) & 0x3FFF;
return 2;
case 2: /* 0b10 => 4 bytes length (30 bits Usable) */
if (value) *value = ntohl(*(uint32_t*)tvb) & 0x3FFFFFFF;
return 4;
case 3: /* 0b11 => 8 bytes length (62 bits Usable) */
if (value) *value = pntoh64(tvb) & 0x3FFFFFFFFFFFFFFF;
return 8;
}
return 0;
}
static uint8_t tvb_get_size(uint8_t tvb)
{
switch(tvb >> 6)
{
case 0: /* 0b00 => 1 byte length (6 bits Usable) */
return 1;
case 1: /* 0b01 => 2 bytes length (14 bits Usable) */
return 2;
case 2: /* 0b10 => 4 bytes length (30 bits Usable) */
return 4;
case 3: /* 0b11 => 8 bytes length (62 bits Usable) */
return 8;
}
return 0;
}
bool IsQUICCryptoHello(const uint8_t *data, size_t len, size_t *hello_offset, size_t *hello_len)
{
size_t offset = 1;
uint64_t coff, clen;
if (len < 3 || *data != 6) return false;
offset += tvb_get_varint(data + offset, &coff);
if (offset >= len) return false;
offset += tvb_get_varint(data + offset, &clen);
if (offset >= len || data[offset] != 0x01 || (offset + coff + clen) > len) return false;
if (hello_offset) *hello_offset = offset + coff;
if (hello_len) *hello_len = (size_t)clen;
return true;
}
bool IsTLSClientHello(const uint8_t *data, size_t len)
{
return len >= 6 && data[0] == 0x16 && data[1] == 0x03 && data[2] >= 0x01 && data[2] <= 0x03 && data[5] == 0x01 && (ntohs(*(uint16_t*)(data + 3)) + 5) <= len;
}
bool TLSFindExt(const uint8_t *data, size_t len, uint16_t type, const uint8_t **ext, size_t *len_ext)
bool TLSFindExtInHandshake(const uint8_t *data, size_t len, uint16_t type, const uint8_t **ext, size_t *len_ext)
{
// +0
// u8 ContentType: Handshake
// u16 Version: TLS1.0
// u16 Length
// +5
// u8 HandshakeType: ClientHello
// u24 Length
// u16 Version
@ -68,11 +115,11 @@ bool TLSFindExt(const uint8_t *data, size_t len, uint16_t type, const uint8_t **
size_t l, ll;
l = 1 + 2 + 2 + 1 + 3 + 2 + 32;
l = 1 + 3 + 2 + 32;
// SessionIDLength
if (len < (l + 1)) return false;
ll = data[6] << 16 | data[7] << 8 | data[8]; // HandshakeProtocol length
if (len < (ll + 9)) return false;
ll = data[1] << 16 | data[2] << 8 | data[3]; // HandshakeProtocol length
if (len < (ll + 4)) return false;
l += data[l] + 1;
// CipherSuitesLength
if (len < (l + 2)) return false;
@ -109,12 +156,17 @@ bool TLSFindExt(const uint8_t *data, size_t len, uint16_t type, const uint8_t **
return false;
}
bool TLSHelloExtractHost(const uint8_t *data, size_t len, char *host, size_t len_host)
bool TLSFindExt(const uint8_t *data, size_t len, uint16_t type, const uint8_t **ext, size_t *len_ext)
{
// +0
// u8 ContentType: Handshake
// u16 Version: TLS1.0
// u16 Length
if (!IsTLSClientHello(data, len)) return false;
return TLSFindExtInHandshake(data + 5, len - 5, type, ext, len_ext);
}
static bool TLSExtractHostFromExt(const uint8_t *ext, size_t elen, char *host, size_t len_host)
{
const uint8_t *ext;
size_t elen;
if (!TLSFindExt(data, len, 0, &ext, &elen)) return false;
// u16 data+0 - name list length
// u8 data+2 - server name type. 0=host_name
// u16 data+3 - server name length
@ -130,11 +182,27 @@ bool TLSHelloExtractHost(const uint8_t *data, size_t len, char *host, size_t len
}
return true;
}
bool TLSHelloExtractHost(const uint8_t *data, size_t len, char *host, size_t len_host)
{
const uint8_t *ext;
size_t elen;
if (!TLSFindExt(data, len, 0, &ext, &elen)) return false;
return TLSExtractHostFromExt(ext, elen, host, len_host);
}
bool TLSHelloExtractHostFromHandshake(const uint8_t *data, size_t len, char *host, size_t len_host)
{
const uint8_t *ext;
size_t elen;
if (!TLSFindExtInHandshake(data, len, 0, &ext, &elen)) return false;
return TLSExtractHostFromExt(ext, elen, host, len_host);
}
#define QUIC_MAX_CID_LENGTH 20
/* Returns the QUIC draft version or 0 if not applicable. */
static inline uint8_t quic_draft_version(uint32_t version) {
uint8_t QUICDraftVersion(uint32_t version)
{
/* IETF Draft versions */
if ((version >> 8) == 0xff0000) {
return (uint8_t)version;
@ -176,13 +244,286 @@ static inline uint8_t quic_draft_version(uint32_t version) {
}
return 0;
}
static inline bool is_quic_draft_max(uint32_t draft_version, uint8_t max_version)
{
return draft_version && draft_version <= max_version;
}
static bool is_quic_ver_less_than(uint32_t version, uint8_t max_version)
{
return is_quic_draft_max(QUICDraftVersion(version), max_version);
}
static bool is_version_with_v1_labels(uint32_t version)
{
if (((version & 0xFFFFFF00) == 0x51303500) /* Q05X */ ||
((version & 0xFFFFFF00) == 0x54303500)) /* T05X */
return true;
return is_quic_ver_less_than(version, 34);
}
static bool quic_hkdf_expand_label(uint8_t *secret, uint8_t secret_len, const char *label, uint8_t *out, size_t out_len)
{
uint8_t hkdflabel[64];
size_t label_size = strlen(label);
if (label_size > 255) return false;
size_t hkdflabel_size = 2 + 1 + label_size + 1;
if (hkdflabel_size > sizeof(hkdflabel)) return false;
*(uint16_t*)hkdflabel = htons(out_len);
hkdflabel[2] = (uint8_t)label_size;
memcpy(hkdflabel + 3, label, label_size);
hkdflabel[3 + label_size] = 0;
return !hkdfExpand(SHA256, secret, secret_len, hkdflabel, hkdflabel_size, out, out_len);
}
static bool quic_derive_initial_secret(const quic_cid_t *cid, uint8_t *client_initial_secret, uint32_t version)
{
/*
* https://tools.ietf.org/html/draft-ietf-quic-tls-29#section-5.2
*
* initial_salt = 0xafbfec289993d24c9e9786f19c6111e04390a899
* initial_secret = HKDF-Extract(initial_salt, client_dst_connection_id)
*
* client_initial_secret = HKDF-Expand-Label(initial_secret,
* "client in", "", Hash.length)
* server_initial_secret = HKDF-Expand-Label(initial_secret,
* "server in", "", Hash.length)
*
* Hash for handshake packets is SHA-256 (output size 32).
*/
static const uint8_t handshake_salt_draft_22[20] = {
0x7f, 0xbc, 0xdb, 0x0e, 0x7c, 0x66, 0xbb, 0xe9, 0x19, 0x3a,
0x96, 0xcd, 0x21, 0x51, 0x9e, 0xbd, 0x7a, 0x02, 0x64, 0x4a
};
static const uint8_t handshake_salt_draft_23[20] = {
0xc3, 0xee, 0xf7, 0x12, 0xc7, 0x2e, 0xbb, 0x5a, 0x11, 0xa7,
0xd2, 0x43, 0x2b, 0xb4, 0x63, 0x65, 0xbe, 0xf9, 0xf5, 0x02,
};
static const uint8_t handshake_salt_draft_29[20] = {
0xaf, 0xbf, 0xec, 0x28, 0x99, 0x93, 0xd2, 0x4c, 0x9e, 0x97,
0x86, 0xf1, 0x9c, 0x61, 0x11, 0xe0, 0x43, 0x90, 0xa8, 0x99
};
static const uint8_t handshake_salt_v1[20] = {
0x38, 0x76, 0x2c, 0xf7, 0xf5, 0x59, 0x34, 0xb3, 0x4d, 0x17,
0x9a, 0xe6, 0xa4, 0xc8, 0x0c, 0xad, 0xcc, 0xbb, 0x7f, 0x0a
};
static const uint8_t hanshake_salt_draft_q50[20] = {
0x50, 0x45, 0x74, 0xEF, 0xD0, 0x66, 0xFE, 0x2F, 0x9D, 0x94,
0x5C, 0xFC, 0xDB, 0xD3, 0xA7, 0xF0, 0xD3, 0xB5, 0x6B, 0x45
};
static const uint8_t hanshake_salt_draft_t50[20] = {
0x7f, 0xf5, 0x79, 0xe5, 0xac, 0xd0, 0x72, 0x91, 0x55, 0x80,
0x30, 0x4c, 0x43, 0xa2, 0x36, 0x7c, 0x60, 0x48, 0x83, 0x10
};
static const uint8_t hanshake_salt_draft_t51[20] = {
0x7a, 0x4e, 0xde, 0xf4, 0xe7, 0xcc, 0xee, 0x5f, 0xa4, 0x50,
0x6c, 0x19, 0x12, 0x4f, 0xc8, 0xcc, 0xda, 0x6e, 0x03, 0x3d
};
static const uint8_t handshake_salt_v2_draft_00[20] = {
0xa7, 0x07, 0xc2, 0x03, 0xa5, 0x9b, 0x47, 0x18, 0x4a, 0x1d,
0x62, 0xca, 0x57, 0x04, 0x06, 0xea, 0x7a, 0xe3, 0xe5, 0xd3
};
int err;
const uint8_t *salt;
uint8_t secret[USHAMaxHashSize];
uint8_t draft_version = QUICDraftVersion(version);
if (version == 0x51303530) {
salt = hanshake_salt_draft_q50;
}
else if (version == 0x54303530) {
salt = hanshake_salt_draft_t50;
}
else if (version == 0x54303531) {
salt = hanshake_salt_draft_t51;
}
else if (is_quic_draft_max(draft_version, 22)) {
salt = handshake_salt_draft_22;
}
else if (is_quic_draft_max(draft_version, 28)) {
salt = handshake_salt_draft_23;
}
else if (is_quic_draft_max(draft_version, 32)) {
salt = handshake_salt_draft_29;
}
else if (is_quic_draft_max(draft_version, 34)) {
salt = handshake_salt_v1;
}
else {
salt = handshake_salt_v2_draft_00;
}
err = hkdfExtract(SHA256, salt, 20, cid->cid, cid->len, secret);
if (err) return false;
if (client_initial_secret && !quic_hkdf_expand_label(secret, SHA256HashSize, "tls13 client in", client_initial_secret, SHA256HashSize))
return false;
return true;
}
bool QUICIsLongHeader(const uint8_t *data, size_t len)
{
return len>=9 && !!(*data & 0x80);
}
uint32_t QUICExtractVersion(const uint8_t *data, size_t len)
{
// long header, fixed bit, type=initial
return QUICIsLongHeader(data, len) ? ntohl(*(uint32_t*)(data + 1)) : 0;
}
bool QUICExtractDCID(const uint8_t *data, size_t len, quic_cid_t *cid)
{
if (!QUICIsLongHeader(data,len) || !data[5] || data[5] > QUIC_MAX_CID_LENGTH || (6+data[5])>len) return false;
cid->len = data[5];
memcpy(&cid->cid, data + 6, data[5]);
return true;
}
bool QUICDecryptInitial(const uint8_t *data, size_t data_len, uint8_t *clean, size_t *clean_len)
{
uint32_t ver = QUICExtractVersion(data, data_len);
if (!ver) return false;
quic_cid_t dcid;
if (!QUICExtractDCID(data, data_len, &dcid)) return false;
uint8_t client_initial_secret[SHA256HashSize];
if (!quic_derive_initial_secret(&dcid, client_initial_secret, ver)) return false;
uint8_t aeskey[16], aesiv[12], aeshp[16];
bool v1_label = is_version_with_v1_labels(ver);
if (!quic_hkdf_expand_label(client_initial_secret, SHA256HashSize, v1_label ? "tls13 quic key" : "tls13 quicv2 key", aeskey, sizeof(aeskey)) ||
!quic_hkdf_expand_label(client_initial_secret, SHA256HashSize, v1_label ? "tls13 quic iv" : "tls13 quicv2 iv", aesiv, sizeof(aesiv)) ||
!quic_hkdf_expand_label(client_initial_secret, SHA256HashSize, v1_label ? "tls13 quic hp" : "tls13 quicv2 hp", aeshp, sizeof(aeshp)))
{
return false;
}
uint64_t payload_len,token_len;
size_t pn_offset;
pn_offset = 1 + 4 + 1 + data[5];
if (pn_offset >= data_len) return false;
pn_offset += 1 + data[pn_offset];
if ((pn_offset + 8) > data_len) return false;
pn_offset += tvb_get_varint(data + pn_offset, &token_len);
pn_offset += token_len;
if ((pn_offset + 8) > data_len) return false;
pn_offset += tvb_get_varint(data + pn_offset, &payload_len);
if (payload_len<20 || (pn_offset + payload_len)>data_len) return false;
aes_init_keygen_tables();
uint8_t sample_enc[16];
aes_context ctx;
if (aes_setkey(&ctx, 1, aeshp, sizeof(aeshp)) || aes_cipher(&ctx, data + pn_offset + 4, sample_enc)) return false;
uint8_t mask[5];
memcpy(mask, sample_enc, sizeof(mask));
uint8_t packet0 = data[0] ^ (mask[0] & 0x0f);
uint32_t pkn_len = (packet0 & 0x03) + 1;
uint8_t pkn_bytes[4];
memcpy(pkn_bytes, data + pn_offset, pkn_len);
uint32_t pkn = 0;
for (uint32_t i = 0; i < pkn_len; i++) pkn |= (uint32_t)(pkn_bytes[i] ^ mask[1 + i]) << (8 * (pkn_len - 1 - i));
phton64(aesiv + sizeof(aesiv) - 8, pntoh64(aesiv + sizeof(aesiv) - 8) ^ pkn);
size_t cryptlen = payload_len - pkn_len - 16;
if (cryptlen > *clean_len) return false;
*clean_len = cryptlen;
const uint8_t *decrypt_begin = data + pn_offset + pkn_len;
return !aes_gcm_decrypt(clean, decrypt_begin, cryptlen, aeskey, sizeof(aeskey), aesiv, sizeof(aesiv));
}
bool QUICDefragCrypto(const uint8_t *clean,size_t clean_len, uint8_t *defrag,size_t *defrag_len)
{
if (*defrag_len<10) return false;
uint8_t *defrag_data = defrag+10;
size_t defrag_data_len = *defrag_len-10;
uint8_t ft;
uint64_t offset,sz,szmax=0,zeropos=0,pos=0;
bool found=false;
while(pos<clean_len)
{
ft = clean[pos];
pos++;
if (ft>1) // 00 - padding, 01 - ping
{
if (ft!=6) return false; // dont want to know all possible frame type formats
if (pos>=clean_len) return false;
if ((pos+tvb_get_size(clean[pos])>clean_len)) return false;
pos += tvb_get_varint(clean+pos, &offset);
if ((pos+tvb_get_size(clean[pos])>clean_len)) return false;
pos += tvb_get_varint(clean+pos, &sz);
if ((pos+sz)>clean_len) return false;
if (ft==6)
{
if ((offset+sz)>defrag_data_len) return false;
if (zeropos < offset)
// make sure no uninitialized gaps exist in case of not full fragment coverage
memset(defrag_data+zeropos,0,offset-zeropos);
if ((offset+sz) > zeropos)
zeropos=offset+sz;
memcpy(defrag_data+offset,clean+pos,sz);
if ((offset+sz) > szmax) szmax = offset+sz;
found=true;
}
pos+=sz;
}
}
if (found)
{
defrag[0] = 6;
defrag[1] = 0; // offset
// 2..9 - length 64 bit
// +10 - data start
phton64(defrag+2,szmax);
defrag[2] |= 0xC0; // 64 bit value
*defrag_len = (size_t)(szmax+10);
}
return found;
}
bool QUICExtractHostFromInitial(const uint8_t *data, size_t data_len, char *host, size_t len_host, bool *bIsCryptoHello)
{
if (bIsCryptoHello) *bIsCryptoHello=false;
uint8_t clean[1500];
size_t clean_len = sizeof(clean);
if (!QUICDecryptInitial(data,data_len,clean,&clean_len)) return false;
uint8_t defrag[1500];
size_t defrag_len = sizeof(defrag);
if (!QUICDefragCrypto(clean,clean_len,defrag,&defrag_len)) return false;
size_t hello_offset, hello_len;
if (!IsQUICCryptoHello(defrag, defrag_len, &hello_offset, &hello_len)) return false;
if (bIsCryptoHello) *bIsCryptoHello=true;
return TLSHelloExtractHostFromHandshake(defrag + hello_offset, hello_len, host, len_host);
}
bool IsQUICInitial(uint8_t *data, size_t len)
{
// long header, fixed bit, type=initial
if (len < 512 || (data[0] & 0xF0) != 0xC0) return false;
uint8_t *p = data + 1;
uint32_t ver = ntohl(*(uint32_t*)p);
if (quic_draft_version(ver) < 11) return false;
if (QUICDraftVersion(ver) < 11) return false;
p += 4;
if (!*p || *p > QUIC_MAX_CID_LENGTH) return false;
return true;

View File

@ -3,10 +3,31 @@
#include <stddef.h>
#include <stdint.h>
#include <stdbool.h>
#include "crypto/sha.h"
#include "crypto/aes-gcm.h"
bool IsHttp(const uint8_t *data, size_t len);
bool HttpExtractHost(const uint8_t *data, size_t len, char *host, size_t len_host);
bool IsTLSClientHello(const uint8_t *data, size_t len);
bool TLSFindExt(const uint8_t *data, size_t len, uint16_t type, const uint8_t **ext, size_t *len_ext);
bool TLSFindExtInHandshake(const uint8_t *data, size_t len, uint16_t type, const uint8_t **ext, size_t *len_ext);
bool TLSHelloExtractHost(const uint8_t *data, size_t len, char *host, size_t len_host);
bool TLSHelloExtractHostFromHandshake(const uint8_t *data, size_t len, char *host, size_t len_host);
#define QUIC_MAX_CID_LENGTH 20
typedef struct quic_cid {
uint8_t len;
uint8_t cid[QUIC_MAX_CID_LENGTH];
} quic_cid_t;
bool IsQUICInitial(uint8_t *data, size_t len);
bool IsQUICCryptoHello(const uint8_t *data, size_t len, size_t *hello_offset, size_t *hello_len);
bool QUICIsLongHeader(const uint8_t *data, size_t len);
uint32_t QUICExtractVersion(const uint8_t *data, size_t len);
uint8_t QUICDraftVersion(uint32_t version);
bool QUICExtractDCID(const uint8_t *data, size_t len, quic_cid_t *cid);
bool QUICDecryptInitial(const uint8_t *data, size_t data_len, uint8_t *clean, size_t *clean_len);
bool QUICDefragCrypto(const uint8_t *clean,size_t clean_len, uint8_t *defrag,size_t *defrag_len);
bool QUICExtractHostFromInitial(const uint8_t *data, size_t data_len, char *host, size_t len_host, bool *bIsCryptoHello);