Browse Source

nfqws: QUIC initial dissection support

pull/98/head
bol-van 3 years ago
parent
commit
dce5b4c6f0
  1. BIN
      binaries/aarch64/nfqws
  2. BIN
      binaries/arm/nfqws
  3. BIN
      binaries/freebsd-x64/dvtws
  4. BIN
      binaries/mips32r1-lsb/nfqws
  5. BIN
      binaries/mips32r1-msb/nfqws
  6. BIN
      binaries/mips64r2-msb/nfqws
  7. BIN
      binaries/ppc/nfqws
  8. BIN
      binaries/x86/nfqws
  9. BIN
      binaries/x86_64/nfqws
  10. 2
      docs/readme.eng.md
  11. 4
      docs/readme.txt
  12. 2
      nfq/BSDmakefile
  13. 2
      nfq/Makefile
  14. 38
      nfq/crypto/aes-gcm.c
  15. 6
      nfq/crypto/aes-gcm.h
  16. 483
      nfq/crypto/aes.c
  17. 78
      nfq/crypto/aes.h
  18. 511
      nfq/crypto/gcm.c
  19. 183
      nfq/crypto/gcm.h
  20. 337
      nfq/crypto/hkdf.c
  21. 250
      nfq/crypto/hmac.c
  22. 25
      nfq/crypto/sha-private.h
  23. 244
      nfq/crypto/sha.h
  24. 581
      nfq/crypto/sha224-256.c
  25. 191
      nfq/crypto/usha.c
  26. 35
      nfq/desync.c
  27. 105
      nfq/helpers.c
  28. 4
      nfq/helpers.h
  29. 373
      nfq/protocol.c
  30. 21
      nfq/protocol.h

BIN
binaries/aarch64/nfqws

Binary file not shown.

BIN
binaries/arm/nfqws

Binary file not shown.

BIN
binaries/freebsd-x64/dvtws

Binary file not shown.

BIN
binaries/mips32r1-lsb/nfqws

Binary file not shown.

BIN
binaries/mips32r1-msb/nfqws

Binary file not shown.

BIN
binaries/mips64r2-msb/nfqws

Binary file not shown.

BIN
binaries/ppc/nfqws

Binary file not shown.

BIN
binaries/x86/nfqws

Binary file not shown.

BIN
binaries/x86_64/nfqws

Binary file not shown.

2
docs/readme.eng.md

@ -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`.

4
docs/readme.txt

@ -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, она бесполезна для анализа на уровне отдельных пакетов.

2
nfq/BSDmakefile

@ -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

2
nfq/Makefile

@ -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

@ -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

@ -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

@ -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

@ -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

@ -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

@ -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

@ -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

@ -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

@ -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

@ -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

@ -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

@ -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;
}
}

35
nfq/desync.c

@ -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;

105
nfq/helpers.c

@ -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)
{
perror("setsockopt (SO_RCVBUF)");
close(fd);
return false;
}
if (sndbuf && setsockopt(fd, SOL_SOCKET, SO_SNDBUF, &sndbuf, 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)
{
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);
}

4
nfq/helpers.h

@ -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);

373
nfq/protocol.c

@ -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;

21
nfq/protocol.h

@ -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);

Loading…
Cancel
Save