This commit is contained in:
Sergio R. Caprile
2026-03-31 17:38:04 -03:00
parent 1bb85799ce
commit 0d882f1b43
13 changed files with 528 additions and 252 deletions
+224 -119
View File
@@ -408,6 +408,7 @@ void mg_resolve(struct mg_connection *c, const char *url) {
}
}
// Response header length is 10 bytes
static const uint8_t mdns_answer[] = {
0, 1, // 2 bytes - record type, A
0, 1, // 2 bytes - address class, INET
@@ -415,6 +416,7 @@ static const uint8_t mdns_answer[] = {
0, 4 // 2 bytes - address length
};
// A name length is name->len + '.local' + 2 = name->len + 8
static uint8_t *build_name(struct mg_str *name, uint8_t *p) {
*p++ = (uint8_t) name->len; // label 1
memcpy(p, name->buf, name->len), p += name->len;
@@ -426,6 +428,7 @@ static uint8_t *build_name(struct mg_str *name, uint8_t *p) {
void mg_getlocaddr(struct mg_connection *, struct mg_addr *, struct mg_addr *);
// An A record length is 10 + 4 = 14 bytes
static uint8_t *build_a_record(struct mg_connection *c, uint8_t *p,
struct mg_addr *addr) {
memcpy(p, mdns_answer, sizeof(mdns_answer)), p += sizeof(mdns_answer);
@@ -447,6 +450,7 @@ static uint8_t *build_a_record(struct mg_connection *c, uint8_t *p,
return p;
}
// A srv name length is r->srvcproto.len + '.local' + 2 = r->srvcproto.len + 8
static uint8_t *build_srv_name(uint8_t *p, struct mg_dnssd_record *r) {
*p++ = (uint8_t) r->srvcproto.len - 5; // label 1, up to '._tcp'
memcpy(p, r->srvcproto.buf, r->srvcproto.len), p += r->srvcproto.len;
@@ -467,6 +471,7 @@ static uint8_t *build_mysrv_name(struct mg_str *name, uint8_t *p,
}
#endif
// A PTR record length is 10 + name->len + 3 = name->len + 13
static uint8_t *build_ptr_record(struct mg_str *name, uint8_t *p, uint16_t o) {
uint16_t offset = mg_htons(o);
memcpy(p, mdns_answer, sizeof(mdns_answer));
@@ -481,6 +486,7 @@ static uint8_t *build_ptr_record(struct mg_str *name, uint8_t *p, uint16_t o) {
return p;
}
// An SRV record length is 10 + name->len + 9 = name->len + 19
static uint8_t *build_srv_record(struct mg_str *name, uint8_t *p,
struct mg_dnssd_record *r, uint16_t o) {
uint16_t port = mg_htons(r->port);
@@ -501,6 +507,7 @@ static uint8_t *build_srv_record(struct mg_str *name, uint8_t *p,
return p;
}
// A TXT record length is r->txt.len (txt contents) + 10
static uint8_t *build_txt_record(uint8_t *p, struct mg_dnssd_record *r) {
uint16_t len = mg_htons((uint16_t) r->txt.len);
memcpy(p, mdns_answer, sizeof(mdns_answer));
@@ -511,6 +518,8 @@ static uint8_t *build_txt_record(uint8_t *p, struct mg_dnssd_record *r) {
return p;
}
// Each additional record has a 2-byte field pointing to the name label
// RFC-6762 16: case-insensitivity --> RFC-1034, 1035
static void handle_mdns_query(struct mg_connection *c) {
@@ -599,6 +608,10 @@ static void handle_mdns_query(struct mg_connection *c) {
uint8_t *o = p, *aux;
uint16_t offset;
if (respname->buf == NULL || respname->len == 0) return;
if ((sizeof(*h) + req.r->srvcproto.len + 8 + respname->len + 13 + 2 +
respname->len + 19 + 2 + req.r->txt.len + 10 + 2 + 14) >
sizeof(buf)) // srv name + PTR + 2 + SRV + 2 + TXT + 2 + A
return;
h->num_other_prs = mg_htons(3); // 3 additional records
p = build_srv_name(p, req.r);
aux = build_ptr_record(respname, p, (uint16_t) (o - buf));
@@ -619,12 +632,18 @@ static void handle_mdns_query(struct mg_connection *c) {
*p |= 0xC0, p += 2;
p = build_a_record(c, p, req.addr);
} else if (rr.atype == MG_DNS_RTYPE_TXT) {
if ((sizeof(*h) + req.r->srvcproto.len + 8 + req.r->txt.len + 10) >
sizeof(buf)) // srv name + TXT
return;
p = build_srv_name(p, req.r);
p = build_txt_record(p, req.r);
} else if (rr.atype == MG_DNS_RTYPE_SRV) { // serve SRV + A
uint8_t *o, *aux;
uint16_t offset;
if (respname->buf == NULL || respname->len == 0) return;
if ((sizeof(*h) + req.r->srvcproto.len + 8 + respname->len + 19 + 2 +
14) > sizeof(buf)) // srv name + SRV + 2 + A
return;
h->num_other_prs = mg_htons(1); // 1 additional record
p = build_srv_name(p, req.r);
o = p - 7; // point to '.local' label (\x05local\x00)
@@ -638,6 +657,8 @@ static void handle_mdns_query(struct mg_connection *c) {
} else { // A requested
// RFC-6762 6: 0 Auth, 0 Additional RRs
if (respname->buf == NULL || respname->len == 0) return;
if ((sizeof(*h) + respname->len + 8 + 14) > sizeof(buf)) // name + A
return;
p = build_name(respname, p);
p = build_a_record(c, p, req.addr);
}
@@ -1941,6 +1962,8 @@ static const char *skiptorn(const char *s, const char *end, struct mg_str *v) {
static bool mg_http_parse_headers(const char *s, const char *end,
struct mg_http_header *h, size_t max_hdrs) {
size_t i, n;
int cl_count = 0, te_count = 0, auth_count = 0;
int conn_count = 0, cookie_count = 0;
for (i = 0; i < max_hdrs; i++) {
struct mg_str k = {NULL, 0}, v = {NULL, 0};
if (s >= end) return false;
@@ -1956,6 +1979,13 @@ static bool mg_http_parse_headers(const char *s, const char *end,
while (v.len > 0 && (v.buf[v.len - 1] == ' ' || v.buf[v.len - 1] == '\t')) {
v.len--; // Trim spaces
}
// detect duplicated headers -> discard
if (((mg_strcasecmp(k, mg_str("Content-Length")) == 0) && (++cl_count > 1)) ||
((mg_strcasecmp(k, mg_str("Transfer-Encoding")) == 0) && (++te_count > 1)) ||
((mg_strcasecmp(k, mg_str("Authorization")) == 0) && (++auth_count > 1)) ||
((mg_strcasecmp(k, mg_str("Cookie")) == 0) && (++cookie_count > 1)) ||
((mg_strcasecmp(k, mg_str("Connection")) == 0) && (++conn_count > 1)))
return false;
// MG_INFO(("--HH [%.*s] [%.*s]", (int) k.len, k.buf, (int) v.len, v.buf));
h[i].name = k, h[i].value = v; // Success. Assign values
}
@@ -1990,6 +2020,8 @@ int mg_http_parse(const char *s, size_t len, struct mg_http_message *hm) {
// If we're given a version, check that it is HTTP/x.x
version_prefix_valid =
hm->proto.len > 5 && (mg_ncasecmp(hm->proto.buf, "HTTP/", 5) == 0);
if (!is_response && !version_prefix_valid)
return -1; // no version detected in request
if (!is_response && hm->proto.len > 0 &&
(!version_prefix_valid || hm->proto.len != 8 ||
(hm->proto.buf[5] < '0' || hm->proto.buf[5] > '9') ||
@@ -2739,7 +2771,11 @@ static void http_cb(struct mg_connection *c, int ev, void *ev_data) {
hm.message.len = c->recv.len - ofs; // and closes now, deliver MSG
hm.body.len = hm.message.len - (size_t) (hm.body.buf - hm.message.buf);
}
if ((te = mg_http_get_header(&hm, "Transfer-Encoding")) != NULL) {
bool is_http_1_0 =
hm.proto.len > 8 && mg_ncasecmp(hm.proto.buf, "HTTP/1.0", 8) == 0;
// HTTP/1.0 does not use "Transfer-Encoding: chunked"
if (!is_http_1_0 &&
(te = mg_http_get_header(&hm, "Transfer-Encoding")) != NULL) {
if (mg_strcasecmp(*te, mg_str("chunked")) == 0) {
is_chunked = true;
} else {
@@ -2963,15 +2999,28 @@ static double mg_atod(const char *p, int len, int *numlen) {
// Exponential
if (i < len && (p[i] == 'e' || p[i] == 'E')) {
int j, exp = 0, minus = 0;
int exp = 0, minus = 0;
i++;
if (i < len && p[i] == '-') minus = 1, i++;
if (i < len && p[i] == '+') i++;
while (i < len && p[i] >= '0' && p[i] <= '9' && exp < 308)
exp = exp * 10 + (p[i++] - '0');
if (minus) exp = -exp;
for (j = 0; j < exp; j++) d *= 10.0;
for (j = 0; j < -exp; j++) d /= 10.0;
// use fast exponentiation
// https://en.wikipedia.org/wiki/Exponentiation_by_squaring
if (exp != 0) {
double x = 10, y = 1;
if (exp > 308) exp = 308;
if (minus) x = 0.1;
while (exp > 1) {
if (exp & 1) {
y *= x;
--exp;
}
x *= x;
exp >>= 1;
}
d *= x * y;
}
}
if (numlen != NULL) *numlen = i;
@@ -5462,9 +5511,13 @@ static struct mg_connection *getpeer(struct mg_mgr *mgr, struct pkt *pkt,
!(c->loc.is_ip6 ^ (pkt->ip6 != NULL))) // IP or IPv6 to same dest
break;
if (!c->is_udp && pkt->tcp && c->loc.port == pkt->tcp->dport &&
!(c->loc.is_ip6 ^ (pkt->ip6 != NULL)) &&
lsn == (bool) c->is_listening &&
(lsn || c->rem.port == pkt->tcp->sport))
((lsn && c->is_listening && !(c->loc.is_ip6 ^ (pkt->ip6 != NULL))) ||
(!lsn && !c->is_listening && c->rem.port == pkt->tcp->sport &&
((!c->loc.is_ip6 && c->rem.addr.ip4 == pkt->ip->src)
#if MG_ENABLE_IPV6
|| (c->loc.is_ip6 && MG_IP6MATCH(c->rem.addr.ip6, pkt->ip6->src))
#endif
)))) // validate addr for established (not listening) conns
break;
}
return c;
@@ -6408,7 +6461,7 @@ static void backlog_poll(struct mg_mgr *mgr) {
}
// process options (MSS)
static void handle_opt(struct connstate *s, struct tcp *tcp, bool ip6) {
static bool handle_opt(struct connstate *s, struct tcp *tcp, bool ip6) {
uint8_t *opts = (uint8_t *) (tcp + 1);
int len = 4 * ((int) (tcp->off >> 4) - ((int) sizeof(*tcp) / 4));
s->dmss = ip6 ? 1220 : 536; // assume default, RFC-9293 3.7.1
@@ -6416,6 +6469,7 @@ static void handle_opt(struct connstate *s, struct tcp *tcp, bool ip6) {
uint8_t kind = opts[0], optlen = 1;
if (kind != 1) { // No-Operation
if (kind == 0) break; // End of Option List
if (len < 2 || opts[1] == 0) return false; // Malformed options
optlen = opts[1];
if (kind == 2 && optlen == 4) // set received MSS
s->dmss = (uint16_t) (((uint16_t) opts[2] << 8) + opts[3]);
@@ -6424,6 +6478,7 @@ static void handle_opt(struct connstate *s, struct tcp *tcp, bool ip6) {
opts += optlen;
len -= optlen;
}
return true;
}
static void rx_tcp(struct mg_tcpip_if *ifp, struct pkt *pkt) {
@@ -6438,7 +6493,7 @@ static void rx_tcp(struct mg_tcpip_if *ifp, struct pkt *pkt) {
// - check clients (Group 1) and established connections (Group 3)
if (c != NULL && c->is_connecting && pkt->tcp->flags == (TH_SYN | TH_ACK)) {
// client got a server connection accept
handle_opt(s, pkt->tcp, pkt->ip6 != NULL); // process options (MSS)
if (!handle_opt(s, pkt->tcp, pkt->ip6 != NULL)) return; // process options (MSS)
s->seq = mg_ntohl(pkt->tcp->ack), s->ack = mg_ntohl(pkt->tcp->seq) + 1;
tx_tcp_ctrlresp(ifp, pkt, TH_ACK, pkt->tcp->ack);
c->is_connecting = 0; // Client connected
@@ -6449,8 +6504,9 @@ static void rx_tcp(struct mg_tcpip_if *ifp, struct pkt *pkt) {
} else if (c != NULL && c->is_connecting && pkt->tcp->flags != TH_ACK) {
mg_error(c, "connection refused");
} else if (c != NULL && pkt->tcp->flags & TH_RST) {
// TODO(): validate RST is within window (and optional with proper ACK)
mg_error(c, "peer RST"); // RFC-1122 4.2.2.13
uint32_t seqno = mg_ntohl(pkt->tcp->seq);
if (seqno >= s->ack && seqno < (s->ack + MG_TCPIP_WIN)) // RFC-9293 3.5.3
mg_error(c, "peer RST"); // RFC-1122 4.2.2.13
} else if (c != NULL) {
// process segment
s->tmiss = 0; // Reset missed keep-alive counter
@@ -6472,7 +6528,7 @@ static void rx_tcp(struct mg_tcpip_if *ifp, struct pkt *pkt) {
int key;
uint32_t isn;
if (pkt->tcp->sport != 0) {
handle_opt(&cs, pkt->tcp, pkt->ip6 != NULL); // process options (MSS)
if (!handle_opt(&cs, pkt->tcp, pkt->ip6 != NULL)) return; // process options (MSS)
key = backlog_insert(c, pkt->tcp->sport,
cs.dmss); // backlog options (MSS)
if (key < 0) return; // no room in backlog, discard SYN, client retries
@@ -11365,7 +11421,9 @@ bool mg_match(struct mg_str s, struct mg_str p, struct mg_str *caps) {
} else if (i < p.len && (p.buf[i] == '*' || p.buf[i] == '#')) {
if (caps && !caps->buf) caps->len = 0, caps->buf = &s.buf[j]; // Init cap
ni = i++, nj = j + 1;
} else if (nj > 0 && nj <= s.len && ((ni < p.len && p.buf[ni] == '#') || s.buf[j] != '/')) {
} else if (nj > 0 && nj <= s.len &&
((ni < p.len && p.buf[ni] == '#') ||
(j < s.len && s.buf[j] != '/'))) {
i = ni, j = nj;
if (caps && caps->buf == NULL && caps->len == 0) {
caps--, caps->len = 0; // Restart previous cap
@@ -11446,8 +11504,7 @@ bool mg_str_to_num(struct mg_str str, int base, void *val, size_t val_len) {
i++, ndigits++;
}
break;
default:
return false;
default: return false;
}
if (ndigits == 0) return false;
if (i != str.len) return false;
@@ -11557,45 +11614,47 @@ static void aes_init_keygen_tables(void);
/******************************************************************************
* AES_SETKEY : called to expand the key for encryption or decryption
******************************************************************************/
static int aes_setkey(aes_context *ctx, // pointer to context
int mode, // 1 or 0 for Encrypt/Decrypt
const unsigned char *key, // AES input key
unsigned int keysize); // size in bytes (must be 16, 24, 32 for
// 128, 192 or 256-bit keys respectively)
// returns 0 for success
static int aes_setkey(
aes_context *ctx, // pointer to context
int mode, // 1 or 0 for Encrypt/Decrypt
const unsigned char *key, // AES input key
unsigned int 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
******************************************************************************/
static int aes_cipher(aes_context *ctx, // pointer to context
const unsigned char input[16], // 128-bit block to en/decipher
unsigned char output[16]); // 128-bit output result block
// returns 0 for success
static int aes_cipher(
aes_context *ctx, // pointer to context
const unsigned char input[16], // 128-bit block to en/decipher
unsigned char output[16]); // 128-bit output result block
// returns 0 for success
/******************************************************************************
* 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
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
unsigned char base_ectr[16]; // first counter-mode cipher output for tag
unsigned char y[16]; // the current cipher-input IV|Counter value
unsigned char buf[16]; // buf working value
aes_context aes_ctx; // cipher context used
aes_context aes_ctx; // cipher context used
} gcm_context;
/******************************************************************************
* GCM_SETKEY : sets the GCM (and AES) keying material for use
******************************************************************************/
static int gcm_setkey(
gcm_context *ctx, // caller-provided context ptr
gcm_context *ctx, // caller-provided context ptr
const unsigned char *key, // pointer to cipher key
const unsigned int keysize // size in bytes (must be 16, 24, 32 for
// 128, 192 or 256-bit keys respectively)
); // returns 0 for success
// 128, 192 or 256-bit keys respectively)
); // returns 0 for success
/******************************************************************************
*
@@ -11615,17 +11674,17 @@ static int gcm_setkey(
*
******************************************************************************/
static int gcm_crypt_and_tag(
gcm_context *ctx, // gcm context with key already setup
int mode, // cipher direction: MG_ENCRYPT (1) or MG_DECRYPT (0)
gcm_context *ctx, // gcm context with key already setup
int mode, // cipher direction: MG_ENCRYPT (1) or MG_DECRYPT (0)
const unsigned char *iv, // pointer to the 12-byte initialization vector
size_t iv_len, // byte length if the IV. should always be 12
size_t iv_len, // byte length if the IV. should always be 12
const unsigned char *add, // pointer to the non-ciphered additional data
size_t add_len, // byte length of the additional AEAD data
size_t add_len, // byte length of the additional AEAD data
const unsigned char *input, // pointer to the cipher data source
unsigned char *output, // pointer to the cipher data destination
size_t length, // byte length of the cipher data
size_t length, // byte length of the cipher data
unsigned char *tag, // pointer to the tag to be generated
size_t tag_len); // byte length of the tag to be generated
size_t tag_len); // byte length of the tag to be generated
/******************************************************************************
*
@@ -11636,12 +11695,12 @@ static int gcm_crypt_and_tag(
*
******************************************************************************/
static int gcm_start(
gcm_context *ctx, // pointer to user-provided GCM context
int mode, // MG_ENCRYPT (1) or MG_DECRYPT (0)
gcm_context *ctx, // pointer to user-provided GCM context
int mode, // MG_ENCRYPT (1) or MG_DECRYPT (0)
const unsigned char *iv, // pointer to initialization vector
size_t iv_len, // IV length in bytes (should == 12)
size_t iv_len, // IV length in bytes (should == 12)
const unsigned char *add, // pointer to additional AEAD data (NULL if none)
size_t add_len); // length of additional AEAD data (bytes)
size_t add_len); // length of additional AEAD data (bytes)
/******************************************************************************
*
@@ -11657,7 +11716,7 @@ static int gcm_start(
static int gcm_update(gcm_context *ctx, // pointer to user-provided GCM context
size_t length, // length, in bytes, of data to process
const unsigned char *input, // pointer to source data
unsigned char *output); // pointer to destination data
unsigned char *output); // pointer to destination data
/******************************************************************************
*
@@ -11668,9 +11727,9 @@ static int gcm_update(gcm_context *ctx, // pointer to user-provided GCM context
*
******************************************************************************/
static int gcm_finish(
gcm_context *ctx, // pointer to user-provided GCM context
unsigned char *tag, // ptr to tag buffer - NULL if tag_len = 0
size_t tag_len); // length, in bytes, of the tag-receiving buf
gcm_context *ctx, // pointer to user-provided GCM context
unsigned char *tag, // ptr to tag buffer - NULL if tag_len = 0
size_t tag_len); // length, in bytes, of the tag-receiving buf
/******************************************************************************
*
@@ -11721,15 +11780,15 @@ static int aes_tables_inited = 0; // run-once flag for performing key
* decryption is typically disabled by setting AES_DECRYPTION to 0 in aes.h.
*/
// We always need our forward tables
static unsigned char FSb[256]; // Forward substitution box (FSb)
static uint32_t FT0[256]; // Forward key schedule assembly tables
static unsigned char 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 unsigned char RSb[256]; // Reverse substitution box (RSb)
static uint32_t RT0[256]; // Reverse key schedule assembly tables
#if AES_DECRYPTION // We ONLY need reverse for decryption
static unsigned char 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];
@@ -11747,8 +11806,8 @@ static uint32_t RCON[10]; // AES round constants
((uint32_t) (b)[(i) + 2] << 16) | ((uint32_t) (b)[(i) + 3] << 24); \
}
#define PUT_UINT32_LE(n, b, i) \
{ \
#define PUT_UINT32_LE(n, b, i) \
{ \
(b)[(i)] = (unsigned char) ((n)); \
(b)[(i) + 1] = (unsigned char) ((n) >> 8); \
(b)[(i) + 2] = (unsigned char) ((n) >> 16); \
@@ -11893,7 +11952,7 @@ void aes_init_keygen_tables(void) {
******************************************************************************/
static int aes_set_encryption_key(aes_context *ctx, const unsigned char *key,
unsigned int keysize) {
unsigned int i; // general purpose iteration local
unsigned int i; // general purpose iteration local
uint32_t *RK = ctx->rk; // initialize our RoundKey buffer pointer
for (i = 0; i < (keysize >> 2); i++) {
@@ -12228,8 +12287,8 @@ static const uint64_t last4[16] = {
((uint32_t) (b)[(i) + 2] << 8) | ((uint32_t) (b)[(i) + 3]); \
}
#define PUT_UINT32_BE(n, b, i) \
{ \
#define PUT_UINT32_BE(n, b, i) \
{ \
(b)[(i)] = (unsigned char) ((n) >> 24); \
(b)[(i) + 1] = (unsigned char) ((n) >> 16); \
(b)[(i) + 2] = (unsigned char) ((n) >> 8); \
@@ -12262,9 +12321,10 @@ int mg_gcm_initialize(void) {
* '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 unsigned char x[16], // pointer to 128-bit input vector
unsigned char output[16]) // pointer to 128-bit output vector
static void gcm_mult(
gcm_context *ctx, // pointer to established context
const unsigned char x[16], // pointer to 128-bit input vector
unsigned char output[16]) // pointer to 128-bit output vector
{
int i;
unsigned char lo, hi, rem;
@@ -12309,10 +12369,10 @@ static void gcm_mult(gcm_context *ctx, // pointer to established context
*
******************************************************************************/
static int gcm_setkey(
gcm_context *ctx, // pointer to caller-provided gcm context
gcm_context *ctx, // pointer to caller-provided gcm context
const unsigned char *key, // pointer to the AES encryption key
const unsigned int keysize) // size in bytes (must be 16, 24, 32 for
// 128, 192 or 256-bit keys respectively)
// 128, 192 or 256-bit keys respectively)
{
int ret, i, j;
uint64_t hi, lo;
@@ -12381,18 +12441,19 @@ static int gcm_setkey(
* 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 unsigned char *iv, // pointer to initialization vector
size_t iv_len, // IV length in bytes (should == 12)
const unsigned char *add, // ptr to additional AEAD data (NULL if none)
size_t add_len) // length of additional AEAD data (bytes)
int gcm_start(
gcm_context *ctx, // pointer to user-provided GCM context
int mode, // GCM_ENCRYPT or GCM_DECRYPT
const unsigned char *iv, // pointer to initialization vector
size_t iv_len, // IV length in bytes (should == 12)
const unsigned char *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
int ret; // our error return if the AES encrypt fails
unsigned char work_buf[16]; // XOR source built from provided IV if len != 16
const unsigned char *p; // general purpose array pointer
size_t use_len; // byte count to process, up to 16 bytes
size_t i; // local loop iterator
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
@@ -12449,15 +12510,15 @@ int gcm_start(gcm_context *ctx, // pointer to user-provided GCM context
* 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
int gcm_update(gcm_context *ctx, // pointer to user-provided GCM context
size_t length, // length, in bytes, of data to process
const unsigned char *input, // pointer to source data
unsigned char *output) // pointer to destination data
{
int ret; // our error return if the AES encrypt fails
int ret; // our error return if the AES encrypt fails
unsigned char 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
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
@@ -12513,9 +12574,9 @@ int gcm_update(gcm_context *ctx, // pointer to user-provided GCM context
* It performs the final GHASH to produce the resulting authentication TAG.
*
******************************************************************************/
int gcm_finish(gcm_context *ctx, // pointer to user-provided GCM context
unsigned char *tag, // pointer to buffer which receives the tag
size_t tag_len) // length, in bytes, of the tag-receiving buf
int gcm_finish(gcm_context *ctx, // pointer to user-provided GCM context
unsigned char *tag, // pointer to buffer which receives the tag
size_t tag_len) // length, in bytes, of the tag-receiving buf
{
unsigned char work_buf[16];
uint64_t orig_len = ctx->len * 8;
@@ -12557,22 +12618,22 @@ int gcm_finish(gcm_context *ctx, // pointer to user-provided GCM context
*
******************************************************************************/
int gcm_crypt_and_tag(
gcm_context *ctx, // gcm context with key already setup
int mode, // cipher direction: GCM_ENCRYPT or GCM_DECRYPT
gcm_context *ctx, // gcm context with key already setup
int mode, // cipher direction: GCM_ENCRYPT or GCM_DECRYPT
const unsigned char *iv, // pointer to the 12-byte initialization vector
size_t iv_len, // byte length if the IV. should always be 12
size_t iv_len, // byte length if the IV. should always be 12
const unsigned char *add, // pointer to the non-ciphered additional data
size_t add_len, // byte length of the additional AEAD data
size_t add_len, // byte length of the additional AEAD data
const unsigned char *input, // pointer to the cipher data source
unsigned char *output, // pointer to the cipher data destination
size_t length, // byte length of the cipher data
size_t length, // byte length of the cipher data
unsigned char *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...
*/
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);
@@ -12622,20 +12683,28 @@ int mg_aes_gcm_encrypt(unsigned char *output, //
int mg_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) {
const size_t iv_len, unsigned char *aead,
size_t aead_len, const unsigned char *tag,
const size_t tag_len) {
int ret = 0; // our return value
gcm_context ctx; // includes the AES context structure
unsigned char computed_tag[16];
size_t i;
int diff = 0;
size_t tag_len = 0;
unsigned char *tag_buf = NULL;
if (tag_len > sizeof(computed_tag)) return -1;
gcm_setkey(&ctx, key, (unsigned int) key_len);
ret = gcm_crypt_and_tag(&ctx, MG_DECRYPT, iv, iv_len, NULL, 0, input, output,
input_length, tag_buf, tag_len);
ret = gcm_crypt_and_tag(&ctx, MG_DECRYPT, iv, iv_len, aead, aead_len, input,
output, input_length, computed_tag, tag_len);
gcm_zero_ctx(&ctx);
// compare tags
for (i = 0; i < tag_len; i++) diff |= computed_tag[i] ^ tag[i];
if (diff != 0) ret = -1;
return (ret);
}
#endif
@@ -13201,14 +13270,19 @@ static int mg_tls_recv_record(struct mg_connection *c) {
nonce[11] ^= (uint8_t) ((seq) & 255U);
#if MG_ENABLE_CHACHA20
{
uint8_t associated_data[5] = {MG_TLS_APP_DATA, 0x03, 0x03,
(uint8_t) ((msgsz >> 8) & 0xff),
(uint8_t) (msgsz & 0xff)};
uint8_t *dec = (uint8_t *) mg_calloc(1, msgsz);
size_t n;
if (dec == NULL) {
mg_error(c, "TLS OOM");
return -1;
}
n = mg_chacha20_poly1305_decrypt(dec, key, nonce, msg, msgsz);
n = mg_chacha20_poly1305_decrypt(dec, key, nonce, associated_data,
sizeof(associated_data), msg, msgsz);
if (n == (size_t) -1) {
mg_free(dec);
mg_error(c, "decryption error");
return -1;
}
@@ -13216,8 +13290,18 @@ static int mg_tls_recv_record(struct mg_connection *c) {
mg_free(dec);
}
#else
mg_gcm_initialize();
mg_aes_gcm_decrypt(msg, msg, msgsz - 16, key, 16, nonce, sizeof(nonce));
{
uint8_t associated_data[5] = {MG_TLS_APP_DATA, 0x03, 0x03,
(uint8_t) ((msgsz >> 8) & 0xff),
(uint8_t) (msgsz & 0xff)};
mg_gcm_initialize();
if (mg_aes_gcm_decrypt(msg, msg, msgsz - 16, key, 16, nonce, sizeof(nonce),
associated_data, sizeof(associated_data),
msg + msgsz - 16, 16) != 0) {
mg_error(c, "GCM tag verify failed");
return -1;
}
}
#endif
r = msgsz - 16 - 1;
@@ -14143,7 +14227,7 @@ static int mg_tls_verify_cert_san(const uint8_t *der, size_t dersz,
if (mg_match(mg_str(server_name), mg_str_n((char *) name.value, name.len),
NULL))
return 1; // and matches the host name
} // TODO(): add IPv6 comparison, more items ?
} // TODO(): add IPv6 comparison, more items ?
}
return -1;
}
@@ -14171,7 +14255,7 @@ static int mg_tls_verify_cert_signature(const struct mg_tls_cert *cert,
mg_uecc_secp256r1());
} else if (issuer->pubkey.len == 96) {
MG_VERBOSE(("ignore secp386 for now"));
return 1;
return 0;
} else {
MG_ERROR(("unsupported public key length: %d", issuer->pubkey.len));
return 0;
@@ -14288,6 +14372,14 @@ static int mg_tls_recv_cert(struct mg_connection *c, bool is_client) {
mg_error(c, "failed to verify hostname");
return -1;
}
if (ci->pubkey.len > sizeof(tls->pubkey)) {
mg_error(c, "invalid certificate length");
return -1;
}
if (ci->pubkey.len > sizeof(tls->pubkey)) {
mg_error(c, "peer public key too large");
return -1;
}
memmove(tls->pubkey, ci->pubkey.buf, ci->pubkey.len);
tls->pubkeysz = ci->pubkey.len;
} else {
@@ -14635,7 +14727,7 @@ static int mg_rsa_parse_der_int(const uint8_t **p, const uint8_t *end,
*p = value_end;
MG_VERBOSE(("DER INT: parsed %u bytes (skipped zero=%d)", len,
(size_t)(value_end - value_start) != (size_t) len ? 1 : 0));
(size_t) (value_end - value_start) != (size_t) len ? 1 : 0));
return 0;
}
@@ -15145,7 +15237,7 @@ long mg_tls_send(struct mg_connection *c, const void *buf, size_t len) {
if (!mg_tls_encrypt(c, (const uint8_t *) buf, len, MG_TLS_APP_DATA))
return 0; // returning 0 means an OOM condition (iobuf couldn't resize),
// yet this is so far recoverable, let the caller decide
} // else, resend outstanding encrypted data in tls->send
} // else, resend outstanding encrypted data in tls->send
while (tls->send.len > 0 &&
(n = mg_io_send(c, tls->send.buf, tls->send.len)) > 0) {
mg_iobuf_del(&tls->send, 0, (size_t) n);
@@ -15292,7 +15384,8 @@ static PORTABLE_8439_DECL void poly1305_finish(poly1305_context *ctx,
defined(__AVR__)
#define __HAVE_LITTLE_ENDIAN 1
#endif
// DO NOT test for LITTLE_ENDIAN, as it is defined as 1234 when including sys/types.h in GCC
// DO NOT test for LITTLE_ENDIAN, as it is defined as 1234 when including
// sys/types.h in GCC
#ifndef TEST_SLOW_PATH
#if defined(__HAVE_LITTLE_ENDIAN)
@@ -15388,7 +15481,7 @@ static void core_block(const uint32_t *restrict start,
TIMES16(__FIN)
}
#define U8(x) ((uint8_t) ((x) &0xFF))
#define U8(x) ((uint8_t) ((x) & 0xFF))
#ifdef FAST_PATH
#define xor32_le(dst, src, pad) \
@@ -15713,7 +15806,7 @@ static unsigned short U8TO16(const unsigned char *p) {
/* store a 16 bit unsigned integer as two 8 bit unsigned integers in little
* endian */
static void U16TO8(unsigned char *p, unsigned short v) {
p[0] = (v) &0xff;
p[0] = (v) & 0xff;
p[1] = (v >> 8) & 0xff;
}
@@ -15724,7 +15817,7 @@ static void poly1305_init(poly1305_context *ctx, const unsigned char key[32]) {
/* r &= 0xffffffc0ffffffc0ffffffc0fffffff */
t0 = U8TO16(&key[0]);
st->r[0] = (t0) &0x1fff;
st->r[0] = (t0) & 0x1fff;
t1 = U8TO16(&key[2]);
st->r[1] = ((t0 >> 13) | (t1 << 3)) & 0x1fff;
t2 = U8TO16(&key[4]);
@@ -15764,7 +15857,7 @@ static void poly1305_blocks(poly1305_state_internal_t *st,
/* h += m[i] */
t0 = U8TO16(&m[0]);
st->h[0] += (t0) &0x1fff;
st->h[0] += (t0) & 0x1fff;
t1 = U8TO16(&m[2]);
st->h[1] += ((t0 >> 13) | (t1 << 3)) & 0x1fff;
t2 = U8TO16(&m[4]);
@@ -15926,7 +16019,7 @@ static unsigned long U8TO32(const unsigned char *p) {
/* store a 32 bit unsigned integer as four 8 bit unsigned integers in little
* endian */
static void U32TO8(unsigned char *p, unsigned long v) {
p[0] = (unsigned char) ((v) &0xff);
p[0] = (unsigned char) ((v) & 0xff);
p[1] = (unsigned char) ((v >> 8) & 0xff);
p[2] = (unsigned char) ((v >> 16) & 0xff);
p[3] = (unsigned char) ((v >> 24) & 0xff);
@@ -16190,8 +16283,8 @@ typedef unsigned uint128_t __attribute__((mode(TI)));
#define MUL128(out, x, y) out = ((uint128_t) x * y)
#define ADD(out, in) out += in
#define ADDLO(out, in) out += in
#define SHR(in, shift) (uint64_t)(in >> (shift))
#define LO(in) (uint64_t)(in)
#define SHR(in, shift) (uint64_t) (in >> (shift))
#define LO(in) (uint64_t) (in)
#define POLY1305_NOINLINE __attribute__((noinline))
#endif
@@ -16220,7 +16313,7 @@ static uint64_t U8TO64(const unsigned char *p) {
/* store a 64 bit unsigned integer as eight 8 bit unsigned integers in little
* endian */
static void U64TO8(unsigned char *p, uint64_t v) {
p[0] = (unsigned char) ((v) &0xff);
p[0] = (unsigned char) ((v) & 0xff);
p[1] = (unsigned char) ((v >> 8) & 0xff);
p[2] = (unsigned char) ((v >> 16) & 0xff);
p[3] = (unsigned char) ((v >> 24) & 0xff);
@@ -16238,7 +16331,7 @@ static void poly1305_init(poly1305_context *ctx, const unsigned char key[32]) {
t0 = U8TO64(&key[0]);
t1 = U8TO64(&key[8]);
st->r[0] = (t0) &0xffc0fffffff;
st->r[0] = (t0) & 0xffc0fffffff;
st->r[1] = ((t0 >> 44) | (t1 << 20)) & 0xfffffc0ffff;
st->r[2] = ((t1 >> 24)) & 0x00ffffffc0f;
@@ -16282,7 +16375,7 @@ static void poly1305_blocks(poly1305_state_internal_t *st,
t0 = U8TO64(&m[0]);
t1 = U8TO64(&m[8]);
h0 += ((t0) &0xfffffffffff);
h0 += ((t0) & 0xfffffffffff);
h1 += (((t0 >> 44) | (t1 << 20)) & 0xfffffffffff);
h2 += (((t1 >> 24)) & 0x3ffffffffff) | hibit;
@@ -16389,7 +16482,7 @@ static POLY1305_NOINLINE void poly1305_finish(poly1305_context *ctx,
t0 = st->pad[0];
t1 = st->pad[1];
h0 += ((t0) &0xfffffffffff);
h0 += ((t0) & 0xfffffffffff);
c = (h0 >> 44);
h0 &= 0xfffffffffff;
h1 += (((t0 >> 44) | (t1 << 20)) & 0xfffffffffff) + c;
@@ -16466,7 +16559,7 @@ static PORTABLE_8439_DECL void pad_if_needed(poly1305_context *ctx,
}
}
#define __u8(v) ((uint8_t) ((v) &0xFF))
#define __u8(v) ((uint8_t) ((v) & 0xFF))
// TODO: make this depending on the unaligned/native read size possible
static PORTABLE_8439_DECL void write_64bit_int(poly1305_context *ctx,
@@ -16539,14 +16632,26 @@ PORTABLE_8439_DECL size_t mg_chacha20_poly1305_encrypt(
PORTABLE_8439_DECL size_t mg_chacha20_poly1305_decrypt(
uint8_t *restrict plain_text, const uint8_t key[RFC_8439_KEY_SIZE],
const uint8_t nonce[RFC_8439_NONCE_SIZE],
const uint8_t *restrict cipher_text, size_t cipher_text_size) {
const uint8_t nonce[RFC_8439_NONCE_SIZE], const uint8_t *restrict ad,
size_t ad_size, const uint8_t *restrict cipher_text,
size_t cipher_text_size) {
// first we calculate the mac and see if it lines up, only then do we decrypt
size_t actual_size = cipher_text_size - RFC_8439_TAG_SIZE;
uint8_t computed_mac[RFC_8439_TAG_SIZE];
int diff = 0;
size_t i;
if (MG_OVERLAPPING(plain_text, actual_size, cipher_text, cipher_text_size)) {
return (size_t) -1;
}
poly1305_calculate_mac(computed_mac, cipher_text, actual_size, key, nonce, ad,
ad_size);
// compare tags
for (i = 0; i < RFC_8439_TAG_SIZE; i++)
diff |= computed_mac[i] ^ cipher_text[actual_size + i];
if (diff != 0) return (size_t) -1;
chacha20_xor_stream(plain_text, cipher_text, actual_size, key, nonce, 1);
return actual_size;
}
+4 -1
View File
@@ -1961,7 +1961,9 @@ int mg_aes_gcm_encrypt(unsigned char *output, const unsigned char *input,
int mg_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);
const size_t iv_len, unsigned char *aead,
size_t aead_len, const unsigned char *tag,
const size_t tag_len);
#endif /* TLS_AES128_H */
@@ -2727,6 +2729,7 @@ PORTABLE_8439_DECL size_t mg_chacha20_poly1305_encrypt(
PORTABLE_8439_DECL size_t mg_chacha20_poly1305_decrypt(
uint8_t *restrict plain_text, const uint8_t key[RFC_8439_KEY_SIZE],
const uint8_t nonce[RFC_8439_NONCE_SIZE],
const uint8_t *restrict ad, size_t ad_size,
const uint8_t *restrict cipher_text, size_t cipher_text_size);
#if defined(__cplusplus)
}
+21
View File
@@ -287,6 +287,7 @@ void mg_resolve(struct mg_connection *c, const char *url) {
}
}
// Response header length is 10 bytes
static const uint8_t mdns_answer[] = {
0, 1, // 2 bytes - record type, A
0, 1, // 2 bytes - address class, INET
@@ -294,6 +295,7 @@ static const uint8_t mdns_answer[] = {
0, 4 // 2 bytes - address length
};
// A name length is name->len + '.local' + 2 = name->len + 8
static uint8_t *build_name(struct mg_str *name, uint8_t *p) {
*p++ = (uint8_t) name->len; // label 1
memcpy(p, name->buf, name->len), p += name->len;
@@ -305,6 +307,7 @@ static uint8_t *build_name(struct mg_str *name, uint8_t *p) {
void mg_getlocaddr(struct mg_connection *, struct mg_addr *, struct mg_addr *);
// An A record length is 10 + 4 = 14 bytes
static uint8_t *build_a_record(struct mg_connection *c, uint8_t *p,
struct mg_addr *addr) {
memcpy(p, mdns_answer, sizeof(mdns_answer)), p += sizeof(mdns_answer);
@@ -326,6 +329,7 @@ static uint8_t *build_a_record(struct mg_connection *c, uint8_t *p,
return p;
}
// A srv name length is r->srvcproto.len + '.local' + 2 = r->srvcproto.len + 8
static uint8_t *build_srv_name(uint8_t *p, struct mg_dnssd_record *r) {
*p++ = (uint8_t) r->srvcproto.len - 5; // label 1, up to '._tcp'
memcpy(p, r->srvcproto.buf, r->srvcproto.len), p += r->srvcproto.len;
@@ -346,6 +350,7 @@ static uint8_t *build_mysrv_name(struct mg_str *name, uint8_t *p,
}
#endif
// A PTR record length is 10 + name->len + 3 = name->len + 13
static uint8_t *build_ptr_record(struct mg_str *name, uint8_t *p, uint16_t o) {
uint16_t offset = mg_htons(o);
memcpy(p, mdns_answer, sizeof(mdns_answer));
@@ -360,6 +365,7 @@ static uint8_t *build_ptr_record(struct mg_str *name, uint8_t *p, uint16_t o) {
return p;
}
// An SRV record length is 10 + name->len + 9 = name->len + 19
static uint8_t *build_srv_record(struct mg_str *name, uint8_t *p,
struct mg_dnssd_record *r, uint16_t o) {
uint16_t port = mg_htons(r->port);
@@ -380,6 +386,7 @@ static uint8_t *build_srv_record(struct mg_str *name, uint8_t *p,
return p;
}
// A TXT record length is r->txt.len (txt contents) + 10
static uint8_t *build_txt_record(uint8_t *p, struct mg_dnssd_record *r) {
uint16_t len = mg_htons((uint16_t) r->txt.len);
memcpy(p, mdns_answer, sizeof(mdns_answer));
@@ -390,6 +397,8 @@ static uint8_t *build_txt_record(uint8_t *p, struct mg_dnssd_record *r) {
return p;
}
// Each additional record has a 2-byte field pointing to the name label
// RFC-6762 16: case-insensitivity --> RFC-1034, 1035
static void handle_mdns_query(struct mg_connection *c) {
@@ -478,6 +487,10 @@ static void handle_mdns_query(struct mg_connection *c) {
uint8_t *o = p, *aux;
uint16_t offset;
if (respname->buf == NULL || respname->len == 0) return;
if ((sizeof(*h) + req.r->srvcproto.len + 8 + respname->len + 13 + 2 +
respname->len + 19 + 2 + req.r->txt.len + 10 + 2 + 14) >
sizeof(buf)) // srv name + PTR + 2 + SRV + 2 + TXT + 2 + A
return;
h->num_other_prs = mg_htons(3); // 3 additional records
p = build_srv_name(p, req.r);
aux = build_ptr_record(respname, p, (uint16_t) (o - buf));
@@ -498,12 +511,18 @@ static void handle_mdns_query(struct mg_connection *c) {
*p |= 0xC0, p += 2;
p = build_a_record(c, p, req.addr);
} else if (rr.atype == MG_DNS_RTYPE_TXT) {
if ((sizeof(*h) + req.r->srvcproto.len + 8 + req.r->txt.len + 10) >
sizeof(buf)) // srv name + TXT
return;
p = build_srv_name(p, req.r);
p = build_txt_record(p, req.r);
} else if (rr.atype == MG_DNS_RTYPE_SRV) { // serve SRV + A
uint8_t *o, *aux;
uint16_t offset;
if (respname->buf == NULL || respname->len == 0) return;
if ((sizeof(*h) + req.r->srvcproto.len + 8 + respname->len + 19 + 2 +
14) > sizeof(buf)) // srv name + SRV + 2 + A
return;
h->num_other_prs = mg_htons(1); // 1 additional record
p = build_srv_name(p, req.r);
o = p - 7; // point to '.local' label (\x05local\x00)
@@ -517,6 +536,8 @@ static void handle_mdns_query(struct mg_connection *c) {
} else { // A requested
// RFC-6762 6: 0 Auth, 0 Additional RRs
if (respname->buf == NULL || respname->len == 0) return;
if ((sizeof(*h) + respname->len + 8 + 14) > sizeof(buf)) // name + A
return;
p = build_name(respname, p);
p = build_a_record(c, p, req.addr);
}
+16 -1
View File
@@ -237,6 +237,8 @@ static const char *skiptorn(const char *s, const char *end, struct mg_str *v) {
static bool mg_http_parse_headers(const char *s, const char *end,
struct mg_http_header *h, size_t max_hdrs) {
size_t i, n;
int cl_count = 0, te_count = 0, auth_count = 0;
int conn_count = 0, cookie_count = 0;
for (i = 0; i < max_hdrs; i++) {
struct mg_str k = {NULL, 0}, v = {NULL, 0};
if (s >= end) return false;
@@ -252,6 +254,13 @@ static bool mg_http_parse_headers(const char *s, const char *end,
while (v.len > 0 && (v.buf[v.len - 1] == ' ' || v.buf[v.len - 1] == '\t')) {
v.len--; // Trim spaces
}
// detect duplicated headers -> discard
if (((mg_strcasecmp(k, mg_str("Content-Length")) == 0) && (++cl_count > 1)) ||
((mg_strcasecmp(k, mg_str("Transfer-Encoding")) == 0) && (++te_count > 1)) ||
((mg_strcasecmp(k, mg_str("Authorization")) == 0) && (++auth_count > 1)) ||
((mg_strcasecmp(k, mg_str("Cookie")) == 0) && (++cookie_count > 1)) ||
((mg_strcasecmp(k, mg_str("Connection")) == 0) && (++conn_count > 1)))
return false;
// MG_INFO(("--HH [%.*s] [%.*s]", (int) k.len, k.buf, (int) v.len, v.buf));
h[i].name = k, h[i].value = v; // Success. Assign values
}
@@ -286,6 +295,8 @@ int mg_http_parse(const char *s, size_t len, struct mg_http_message *hm) {
// If we're given a version, check that it is HTTP/x.x
version_prefix_valid =
hm->proto.len > 5 && (mg_ncasecmp(hm->proto.buf, "HTTP/", 5) == 0);
if (!is_response && !version_prefix_valid)
return -1; // no version detected in request
if (!is_response && hm->proto.len > 0 &&
(!version_prefix_valid || hm->proto.len != 8 ||
(hm->proto.buf[5] < '0' || hm->proto.buf[5] > '9') ||
@@ -1035,7 +1046,11 @@ static void http_cb(struct mg_connection *c, int ev, void *ev_data) {
hm.message.len = c->recv.len - ofs; // and closes now, deliver MSG
hm.body.len = hm.message.len - (size_t) (hm.body.buf - hm.message.buf);
}
if ((te = mg_http_get_header(&hm, "Transfer-Encoding")) != NULL) {
bool is_http_1_0 =
hm.proto.len > 8 && mg_ncasecmp(hm.proto.buf, "HTTP/1.0", 8) == 0;
// HTTP/1.0 does not use "Transfer-Encoding: chunked"
if (!is_http_1_0 &&
(te = mg_http_get_header(&hm, "Transfer-Encoding")) != NULL) {
if (mg_strcasecmp(*te, mg_str("chunked")) == 0) {
is_chunked = true;
} else {
+17 -4
View File
@@ -60,15 +60,28 @@ static double mg_atod(const char *p, int len, int *numlen) {
// Exponential
if (i < len && (p[i] == 'e' || p[i] == 'E')) {
int j, exp = 0, minus = 0;
int exp = 0, minus = 0;
i++;
if (i < len && p[i] == '-') minus = 1, i++;
if (i < len && p[i] == '+') i++;
while (i < len && p[i] >= '0' && p[i] <= '9' && exp < 308)
exp = exp * 10 + (p[i++] - '0');
if (minus) exp = -exp;
for (j = 0; j < exp; j++) d *= 10.0;
for (j = 0; j < -exp; j++) d /= 10.0;
// use fast exponentiation
// https://en.wikipedia.org/wiki/Exponentiation_by_squaring
if (exp != 0) {
double x = 10, y = 1;
if (exp > 308) exp = 308;
if (minus) x = 0.1;
while (exp > 1) {
if (exp & 1) {
y *= x;
--exp;
}
x *= x;
exp >>= 1;
}
d *= x * y;
}
}
if (numlen != NULL) *numlen = i;
+15 -8
View File
@@ -551,9 +551,13 @@ static struct mg_connection *getpeer(struct mg_mgr *mgr, struct pkt *pkt,
!(c->loc.is_ip6 ^ (pkt->ip6 != NULL))) // IP or IPv6 to same dest
break;
if (!c->is_udp && pkt->tcp && c->loc.port == pkt->tcp->dport &&
!(c->loc.is_ip6 ^ (pkt->ip6 != NULL)) &&
lsn == (bool) c->is_listening &&
(lsn || c->rem.port == pkt->tcp->sport))
((lsn && c->is_listening && !(c->loc.is_ip6 ^ (pkt->ip6 != NULL))) ||
(!lsn && !c->is_listening && c->rem.port == pkt->tcp->sport &&
((!c->loc.is_ip6 && c->rem.addr.ip4 == pkt->ip->src)
#if MG_ENABLE_IPV6
|| (c->loc.is_ip6 && MG_IP6MATCH(c->rem.addr.ip6, pkt->ip6->src))
#endif
)))) // validate addr for established (not listening) conns
break;
}
return c;
@@ -1497,7 +1501,7 @@ static void backlog_poll(struct mg_mgr *mgr) {
}
// process options (MSS)
static void handle_opt(struct connstate *s, struct tcp *tcp, bool ip6) {
static bool handle_opt(struct connstate *s, struct tcp *tcp, bool ip6) {
uint8_t *opts = (uint8_t *) (tcp + 1);
int len = 4 * ((int) (tcp->off >> 4) - ((int) sizeof(*tcp) / 4));
s->dmss = ip6 ? 1220 : 536; // assume default, RFC-9293 3.7.1
@@ -1505,6 +1509,7 @@ static void handle_opt(struct connstate *s, struct tcp *tcp, bool ip6) {
uint8_t kind = opts[0], optlen = 1;
if (kind != 1) { // No-Operation
if (kind == 0) break; // End of Option List
if (len < 2 || opts[1] == 0) return false; // Malformed options
optlen = opts[1];
if (kind == 2 && optlen == 4) // set received MSS
s->dmss = (uint16_t) (((uint16_t) opts[2] << 8) + opts[3]);
@@ -1513,6 +1518,7 @@ static void handle_opt(struct connstate *s, struct tcp *tcp, bool ip6) {
opts += optlen;
len -= optlen;
}
return true;
}
static void rx_tcp(struct mg_tcpip_if *ifp, struct pkt *pkt) {
@@ -1527,7 +1533,7 @@ static void rx_tcp(struct mg_tcpip_if *ifp, struct pkt *pkt) {
// - check clients (Group 1) and established connections (Group 3)
if (c != NULL && c->is_connecting && pkt->tcp->flags == (TH_SYN | TH_ACK)) {
// client got a server connection accept
handle_opt(s, pkt->tcp, pkt->ip6 != NULL); // process options (MSS)
if (!handle_opt(s, pkt->tcp, pkt->ip6 != NULL)) return; // process options (MSS)
s->seq = mg_ntohl(pkt->tcp->ack), s->ack = mg_ntohl(pkt->tcp->seq) + 1;
tx_tcp_ctrlresp(ifp, pkt, TH_ACK, pkt->tcp->ack);
c->is_connecting = 0; // Client connected
@@ -1538,8 +1544,9 @@ static void rx_tcp(struct mg_tcpip_if *ifp, struct pkt *pkt) {
} else if (c != NULL && c->is_connecting && pkt->tcp->flags != TH_ACK) {
mg_error(c, "connection refused");
} else if (c != NULL && pkt->tcp->flags & TH_RST) {
// TODO(): validate RST is within window (and optional with proper ACK)
mg_error(c, "peer RST"); // RFC-1122 4.2.2.13
uint32_t seqno = mg_ntohl(pkt->tcp->seq);
if (seqno >= s->ack && seqno < (s->ack + MG_TCPIP_WIN)) // RFC-9293 3.5.3
mg_error(c, "peer RST"); // RFC-1122 4.2.2.13
} else if (c != NULL) {
// process segment
s->tmiss = 0; // Reset missed keep-alive counter
@@ -1561,7 +1568,7 @@ static void rx_tcp(struct mg_tcpip_if *ifp, struct pkt *pkt) {
int key;
uint32_t isn;
if (pkt->tcp->sport != 0) {
handle_opt(&cs, pkt->tcp, pkt->ip6 != NULL); // process options (MSS)
if (!handle_opt(&cs, pkt->tcp, pkt->ip6 != NULL)) return; // process options (MSS)
key = backlog_insert(c, pkt->tcp->sport,
cs.dmss); // backlog options (MSS)
if (key < 0) return; // no room in backlog, discard SYN, client retries
+4 -3
View File
@@ -87,7 +87,9 @@ bool mg_match(struct mg_str s, struct mg_str p, struct mg_str *caps) {
} else if (i < p.len && (p.buf[i] == '*' || p.buf[i] == '#')) {
if (caps && !caps->buf) caps->len = 0, caps->buf = &s.buf[j]; // Init cap
ni = i++, nj = j + 1;
} else if (nj > 0 && nj <= s.len && ((ni < p.len && p.buf[ni] == '#') || s.buf[j] != '/')) {
} else if (nj > 0 && nj <= s.len &&
((ni < p.len && p.buf[ni] == '#') ||
(j < s.len && s.buf[j] != '/'))) {
i = ni, j = nj;
if (caps && caps->buf == NULL && caps->len == 0) {
caps--, caps->len = 0; // Restart previous cap
@@ -168,8 +170,7 @@ bool mg_str_to_num(struct mg_str str, int base, void *val, size_t val_len) {
i++, ndigits++;
}
break;
default:
return false;
default: return false;
}
if (ndigits == 0) return false;
if (i != str.len) return false;
+93 -81
View File
@@ -40,45 +40,47 @@ static void aes_init_keygen_tables(void);
/******************************************************************************
* AES_SETKEY : called to expand the key for encryption or decryption
******************************************************************************/
static int aes_setkey(aes_context *ctx, // pointer to context
int mode, // 1 or 0 for Encrypt/Decrypt
const unsigned char *key, // AES input key
unsigned int keysize); // size in bytes (must be 16, 24, 32 for
// 128, 192 or 256-bit keys respectively)
// returns 0 for success
static int aes_setkey(
aes_context *ctx, // pointer to context
int mode, // 1 or 0 for Encrypt/Decrypt
const unsigned char *key, // AES input key
unsigned int 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
******************************************************************************/
static int aes_cipher(aes_context *ctx, // pointer to context
const unsigned char input[16], // 128-bit block to en/decipher
unsigned char output[16]); // 128-bit output result block
// returns 0 for success
static int aes_cipher(
aes_context *ctx, // pointer to context
const unsigned char input[16], // 128-bit block to en/decipher
unsigned char output[16]); // 128-bit output result block
// returns 0 for success
/******************************************************************************
* 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
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
unsigned char base_ectr[16]; // first counter-mode cipher output for tag
unsigned char y[16]; // the current cipher-input IV|Counter value
unsigned char buf[16]; // buf working value
aes_context aes_ctx; // cipher context used
aes_context aes_ctx; // cipher context used
} gcm_context;
/******************************************************************************
* GCM_SETKEY : sets the GCM (and AES) keying material for use
******************************************************************************/
static int gcm_setkey(
gcm_context *ctx, // caller-provided context ptr
gcm_context *ctx, // caller-provided context ptr
const unsigned char *key, // pointer to cipher key
const unsigned int keysize // size in bytes (must be 16, 24, 32 for
// 128, 192 or 256-bit keys respectively)
); // returns 0 for success
// 128, 192 or 256-bit keys respectively)
); // returns 0 for success
/******************************************************************************
*
@@ -98,17 +100,17 @@ static int gcm_setkey(
*
******************************************************************************/
static int gcm_crypt_and_tag(
gcm_context *ctx, // gcm context with key already setup
int mode, // cipher direction: MG_ENCRYPT (1) or MG_DECRYPT (0)
gcm_context *ctx, // gcm context with key already setup
int mode, // cipher direction: MG_ENCRYPT (1) or MG_DECRYPT (0)
const unsigned char *iv, // pointer to the 12-byte initialization vector
size_t iv_len, // byte length if the IV. should always be 12
size_t iv_len, // byte length if the IV. should always be 12
const unsigned char *add, // pointer to the non-ciphered additional data
size_t add_len, // byte length of the additional AEAD data
size_t add_len, // byte length of the additional AEAD data
const unsigned char *input, // pointer to the cipher data source
unsigned char *output, // pointer to the cipher data destination
size_t length, // byte length of the cipher data
size_t length, // byte length of the cipher data
unsigned char *tag, // pointer to the tag to be generated
size_t tag_len); // byte length of the tag to be generated
size_t tag_len); // byte length of the tag to be generated
/******************************************************************************
*
@@ -119,12 +121,12 @@ static int gcm_crypt_and_tag(
*
******************************************************************************/
static int gcm_start(
gcm_context *ctx, // pointer to user-provided GCM context
int mode, // MG_ENCRYPT (1) or MG_DECRYPT (0)
gcm_context *ctx, // pointer to user-provided GCM context
int mode, // MG_ENCRYPT (1) or MG_DECRYPT (0)
const unsigned char *iv, // pointer to initialization vector
size_t iv_len, // IV length in bytes (should == 12)
size_t iv_len, // IV length in bytes (should == 12)
const unsigned char *add, // pointer to additional AEAD data (NULL if none)
size_t add_len); // length of additional AEAD data (bytes)
size_t add_len); // length of additional AEAD data (bytes)
/******************************************************************************
*
@@ -140,7 +142,7 @@ static int gcm_start(
static int gcm_update(gcm_context *ctx, // pointer to user-provided GCM context
size_t length, // length, in bytes, of data to process
const unsigned char *input, // pointer to source data
unsigned char *output); // pointer to destination data
unsigned char *output); // pointer to destination data
/******************************************************************************
*
@@ -151,9 +153,9 @@ static int gcm_update(gcm_context *ctx, // pointer to user-provided GCM context
*
******************************************************************************/
static int gcm_finish(
gcm_context *ctx, // pointer to user-provided GCM context
unsigned char *tag, // ptr to tag buffer - NULL if tag_len = 0
size_t tag_len); // length, in bytes, of the tag-receiving buf
gcm_context *ctx, // pointer to user-provided GCM context
unsigned char *tag, // ptr to tag buffer - NULL if tag_len = 0
size_t tag_len); // length, in bytes, of the tag-receiving buf
/******************************************************************************
*
@@ -204,15 +206,15 @@ static int aes_tables_inited = 0; // run-once flag for performing key
* decryption is typically disabled by setting AES_DECRYPTION to 0 in aes.h.
*/
// We always need our forward tables
static unsigned char FSb[256]; // Forward substitution box (FSb)
static uint32_t FT0[256]; // Forward key schedule assembly tables
static unsigned char 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 unsigned char RSb[256]; // Reverse substitution box (RSb)
static uint32_t RT0[256]; // Reverse key schedule assembly tables
#if AES_DECRYPTION // We ONLY need reverse for decryption
static unsigned char 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];
@@ -230,8 +232,8 @@ static uint32_t RCON[10]; // AES round constants
((uint32_t) (b)[(i) + 2] << 16) | ((uint32_t) (b)[(i) + 3] << 24); \
}
#define PUT_UINT32_LE(n, b, i) \
{ \
#define PUT_UINT32_LE(n, b, i) \
{ \
(b)[(i)] = (unsigned char) ((n)); \
(b)[(i) + 1] = (unsigned char) ((n) >> 8); \
(b)[(i) + 2] = (unsigned char) ((n) >> 16); \
@@ -376,7 +378,7 @@ void aes_init_keygen_tables(void) {
******************************************************************************/
static int aes_set_encryption_key(aes_context *ctx, const unsigned char *key,
unsigned int keysize) {
unsigned int i; // general purpose iteration local
unsigned int i; // general purpose iteration local
uint32_t *RK = ctx->rk; // initialize our RoundKey buffer pointer
for (i = 0; i < (keysize >> 2); i++) {
@@ -711,8 +713,8 @@ static const uint64_t last4[16] = {
((uint32_t) (b)[(i) + 2] << 8) | ((uint32_t) (b)[(i) + 3]); \
}
#define PUT_UINT32_BE(n, b, i) \
{ \
#define PUT_UINT32_BE(n, b, i) \
{ \
(b)[(i)] = (unsigned char) ((n) >> 24); \
(b)[(i) + 1] = (unsigned char) ((n) >> 16); \
(b)[(i) + 2] = (unsigned char) ((n) >> 8); \
@@ -745,9 +747,10 @@ int mg_gcm_initialize(void) {
* '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 unsigned char x[16], // pointer to 128-bit input vector
unsigned char output[16]) // pointer to 128-bit output vector
static void gcm_mult(
gcm_context *ctx, // pointer to established context
const unsigned char x[16], // pointer to 128-bit input vector
unsigned char output[16]) // pointer to 128-bit output vector
{
int i;
unsigned char lo, hi, rem;
@@ -792,10 +795,10 @@ static void gcm_mult(gcm_context *ctx, // pointer to established context
*
******************************************************************************/
static int gcm_setkey(
gcm_context *ctx, // pointer to caller-provided gcm context
gcm_context *ctx, // pointer to caller-provided gcm context
const unsigned char *key, // pointer to the AES encryption key
const unsigned int keysize) // size in bytes (must be 16, 24, 32 for
// 128, 192 or 256-bit keys respectively)
// 128, 192 or 256-bit keys respectively)
{
int ret, i, j;
uint64_t hi, lo;
@@ -864,18 +867,19 @@ static int gcm_setkey(
* 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 unsigned char *iv, // pointer to initialization vector
size_t iv_len, // IV length in bytes (should == 12)
const unsigned char *add, // ptr to additional AEAD data (NULL if none)
size_t add_len) // length of additional AEAD data (bytes)
int gcm_start(
gcm_context *ctx, // pointer to user-provided GCM context
int mode, // GCM_ENCRYPT or GCM_DECRYPT
const unsigned char *iv, // pointer to initialization vector
size_t iv_len, // IV length in bytes (should == 12)
const unsigned char *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
int ret; // our error return if the AES encrypt fails
unsigned char work_buf[16]; // XOR source built from provided IV if len != 16
const unsigned char *p; // general purpose array pointer
size_t use_len; // byte count to process, up to 16 bytes
size_t i; // local loop iterator
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
@@ -932,15 +936,15 @@ int gcm_start(gcm_context *ctx, // pointer to user-provided GCM context
* 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
int gcm_update(gcm_context *ctx, // pointer to user-provided GCM context
size_t length, // length, in bytes, of data to process
const unsigned char *input, // pointer to source data
unsigned char *output) // pointer to destination data
{
int ret; // our error return if the AES encrypt fails
int ret; // our error return if the AES encrypt fails
unsigned char 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
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
@@ -996,9 +1000,9 @@ int gcm_update(gcm_context *ctx, // pointer to user-provided GCM context
* It performs the final GHASH to produce the resulting authentication TAG.
*
******************************************************************************/
int gcm_finish(gcm_context *ctx, // pointer to user-provided GCM context
unsigned char *tag, // pointer to buffer which receives the tag
size_t tag_len) // length, in bytes, of the tag-receiving buf
int gcm_finish(gcm_context *ctx, // pointer to user-provided GCM context
unsigned char *tag, // pointer to buffer which receives the tag
size_t tag_len) // length, in bytes, of the tag-receiving buf
{
unsigned char work_buf[16];
uint64_t orig_len = ctx->len * 8;
@@ -1040,22 +1044,22 @@ int gcm_finish(gcm_context *ctx, // pointer to user-provided GCM context
*
******************************************************************************/
int gcm_crypt_and_tag(
gcm_context *ctx, // gcm context with key already setup
int mode, // cipher direction: GCM_ENCRYPT or GCM_DECRYPT
gcm_context *ctx, // gcm context with key already setup
int mode, // cipher direction: GCM_ENCRYPT or GCM_DECRYPT
const unsigned char *iv, // pointer to the 12-byte initialization vector
size_t iv_len, // byte length if the IV. should always be 12
size_t iv_len, // byte length if the IV. should always be 12
const unsigned char *add, // pointer to the non-ciphered additional data
size_t add_len, // byte length of the additional AEAD data
size_t add_len, // byte length of the additional AEAD data
const unsigned char *input, // pointer to the cipher data source
unsigned char *output, // pointer to the cipher data destination
size_t length, // byte length of the cipher data
size_t length, // byte length of the cipher data
unsigned char *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...
*/
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);
@@ -1105,20 +1109,28 @@ int mg_aes_gcm_encrypt(unsigned char *output, //
int mg_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) {
const size_t iv_len, unsigned char *aead,
size_t aead_len, const unsigned char *tag,
const size_t tag_len) {
int ret = 0; // our return value
gcm_context ctx; // includes the AES context structure
unsigned char computed_tag[16];
size_t i;
int diff = 0;
size_t tag_len = 0;
unsigned char *tag_buf = NULL;
if (tag_len > sizeof(computed_tag)) return -1;
gcm_setkey(&ctx, key, (unsigned int) key_len);
ret = gcm_crypt_and_tag(&ctx, MG_DECRYPT, iv, iv_len, NULL, 0, input, output,
input_length, tag_buf, tag_len);
ret = gcm_crypt_and_tag(&ctx, MG_DECRYPT, iv, iv_len, aead, aead_len, input,
output, input_length, computed_tag, tag_len);
gcm_zero_ctx(&ctx);
// compare tags
for (i = 0; i < tag_len; i++) diff |= computed_tag[i] ^ tag[i];
if (diff != 0) ret = -1;
return (ret);
}
#endif
+3 -1
View File
@@ -59,7 +59,9 @@ int mg_aes_gcm_encrypt(unsigned char *output, const unsigned char *input,
int mg_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);
const size_t iv_len, unsigned char *aead,
size_t aead_len, const unsigned char *tag,
const size_t tag_len);
#endif /* TLS_AES128_H */
+30 -7
View File
@@ -555,14 +555,19 @@ static int mg_tls_recv_record(struct mg_connection *c) {
nonce[11] ^= (uint8_t) ((seq) & 255U);
#if MG_ENABLE_CHACHA20
{
uint8_t associated_data[5] = {MG_TLS_APP_DATA, 0x03, 0x03,
(uint8_t) ((msgsz >> 8) & 0xff),
(uint8_t) (msgsz & 0xff)};
uint8_t *dec = (uint8_t *) mg_calloc(1, msgsz);
size_t n;
if (dec == NULL) {
mg_error(c, "TLS OOM");
return -1;
}
n = mg_chacha20_poly1305_decrypt(dec, key, nonce, msg, msgsz);
n = mg_chacha20_poly1305_decrypt(dec, key, nonce, associated_data,
sizeof(associated_data), msg, msgsz);
if (n == (size_t) -1) {
mg_free(dec);
mg_error(c, "decryption error");
return -1;
}
@@ -570,8 +575,18 @@ static int mg_tls_recv_record(struct mg_connection *c) {
mg_free(dec);
}
#else
mg_gcm_initialize();
mg_aes_gcm_decrypt(msg, msg, msgsz - 16, key, 16, nonce, sizeof(nonce));
{
uint8_t associated_data[5] = {MG_TLS_APP_DATA, 0x03, 0x03,
(uint8_t) ((msgsz >> 8) & 0xff),
(uint8_t) (msgsz & 0xff)};
mg_gcm_initialize();
if (mg_aes_gcm_decrypt(msg, msg, msgsz - 16, key, 16, nonce, sizeof(nonce),
associated_data, sizeof(associated_data),
msg + msgsz - 16, 16) != 0) {
mg_error(c, "GCM tag verify failed");
return -1;
}
}
#endif
r = msgsz - 16 - 1;
@@ -1497,7 +1512,7 @@ static int mg_tls_verify_cert_san(const uint8_t *der, size_t dersz,
if (mg_match(mg_str(server_name), mg_str_n((char *) name.value, name.len),
NULL))
return 1; // and matches the host name
} // TODO(): add IPv6 comparison, more items ?
} // TODO(): add IPv6 comparison, more items ?
}
return -1;
}
@@ -1525,7 +1540,7 @@ static int mg_tls_verify_cert_signature(const struct mg_tls_cert *cert,
mg_uecc_secp256r1());
} else if (issuer->pubkey.len == 96) {
MG_VERBOSE(("ignore secp386 for now"));
return 1;
return 0;
} else {
MG_ERROR(("unsupported public key length: %d", issuer->pubkey.len));
return 0;
@@ -1642,6 +1657,14 @@ static int mg_tls_recv_cert(struct mg_connection *c, bool is_client) {
mg_error(c, "failed to verify hostname");
return -1;
}
if (ci->pubkey.len > sizeof(tls->pubkey)) {
mg_error(c, "invalid certificate length");
return -1;
}
if (ci->pubkey.len > sizeof(tls->pubkey)) {
mg_error(c, "peer public key too large");
return -1;
}
memmove(tls->pubkey, ci->pubkey.buf, ci->pubkey.len);
tls->pubkeysz = ci->pubkey.len;
} else {
@@ -1989,7 +2012,7 @@ static int mg_rsa_parse_der_int(const uint8_t **p, const uint8_t *end,
*p = value_end;
MG_VERBOSE(("DER INT: parsed %u bytes (skipped zero=%d)", len,
(size_t)(value_end - value_start) != (size_t) len ? 1 : 0));
(size_t) (value_end - value_start) != (size_t) len ? 1 : 0));
return 0;
}
@@ -2499,7 +2522,7 @@ long mg_tls_send(struct mg_connection *c, const void *buf, size_t len) {
if (!mg_tls_encrypt(c, (const uint8_t *) buf, len, MG_TLS_APP_DATA))
return 0; // returning 0 means an OOM condition (iobuf couldn't resize),
// yet this is so far recoverable, let the caller decide
} // else, resend outstanding encrypted data in tls->send
} // else, resend outstanding encrypted data in tls->send
while (tls->send.len > 0 &&
(n = mg_io_send(c, tls->send.buf, tls->send.len)) > 0) {
mg_iobuf_del(&tls->send, 0, (size_t) n);
+28 -15
View File
@@ -82,7 +82,8 @@ static PORTABLE_8439_DECL void poly1305_finish(poly1305_context *ctx,
defined(__AVR__)
#define __HAVE_LITTLE_ENDIAN 1
#endif
// DO NOT test for LITTLE_ENDIAN, as it is defined as 1234 when including sys/types.h in GCC
// DO NOT test for LITTLE_ENDIAN, as it is defined as 1234 when including
// sys/types.h in GCC
#ifndef TEST_SLOW_PATH
#if defined(__HAVE_LITTLE_ENDIAN)
@@ -178,7 +179,7 @@ static void core_block(const uint32_t *restrict start,
TIMES16(__FIN)
}
#define U8(x) ((uint8_t) ((x) &0xFF))
#define U8(x) ((uint8_t) ((x) & 0xFF))
#ifdef FAST_PATH
#define xor32_le(dst, src, pad) \
@@ -503,7 +504,7 @@ static unsigned short U8TO16(const unsigned char *p) {
/* store a 16 bit unsigned integer as two 8 bit unsigned integers in little
* endian */
static void U16TO8(unsigned char *p, unsigned short v) {
p[0] = (v) &0xff;
p[0] = (v) & 0xff;
p[1] = (v >> 8) & 0xff;
}
@@ -514,7 +515,7 @@ static void poly1305_init(poly1305_context *ctx, const unsigned char key[32]) {
/* r &= 0xffffffc0ffffffc0ffffffc0fffffff */
t0 = U8TO16(&key[0]);
st->r[0] = (t0) &0x1fff;
st->r[0] = (t0) & 0x1fff;
t1 = U8TO16(&key[2]);
st->r[1] = ((t0 >> 13) | (t1 << 3)) & 0x1fff;
t2 = U8TO16(&key[4]);
@@ -554,7 +555,7 @@ static void poly1305_blocks(poly1305_state_internal_t *st,
/* h += m[i] */
t0 = U8TO16(&m[0]);
st->h[0] += (t0) &0x1fff;
st->h[0] += (t0) & 0x1fff;
t1 = U8TO16(&m[2]);
st->h[1] += ((t0 >> 13) | (t1 << 3)) & 0x1fff;
t2 = U8TO16(&m[4]);
@@ -716,7 +717,7 @@ static unsigned long U8TO32(const unsigned char *p) {
/* store a 32 bit unsigned integer as four 8 bit unsigned integers in little
* endian */
static void U32TO8(unsigned char *p, unsigned long v) {
p[0] = (unsigned char) ((v) &0xff);
p[0] = (unsigned char) ((v) & 0xff);
p[1] = (unsigned char) ((v >> 8) & 0xff);
p[2] = (unsigned char) ((v >> 16) & 0xff);
p[3] = (unsigned char) ((v >> 24) & 0xff);
@@ -980,8 +981,8 @@ typedef unsigned uint128_t __attribute__((mode(TI)));
#define MUL128(out, x, y) out = ((uint128_t) x * y)
#define ADD(out, in) out += in
#define ADDLO(out, in) out += in
#define SHR(in, shift) (uint64_t)(in >> (shift))
#define LO(in) (uint64_t)(in)
#define SHR(in, shift) (uint64_t) (in >> (shift))
#define LO(in) (uint64_t) (in)
#define POLY1305_NOINLINE __attribute__((noinline))
#endif
@@ -1010,7 +1011,7 @@ static uint64_t U8TO64(const unsigned char *p) {
/* store a 64 bit unsigned integer as eight 8 bit unsigned integers in little
* endian */
static void U64TO8(unsigned char *p, uint64_t v) {
p[0] = (unsigned char) ((v) &0xff);
p[0] = (unsigned char) ((v) & 0xff);
p[1] = (unsigned char) ((v >> 8) & 0xff);
p[2] = (unsigned char) ((v >> 16) & 0xff);
p[3] = (unsigned char) ((v >> 24) & 0xff);
@@ -1028,7 +1029,7 @@ static void poly1305_init(poly1305_context *ctx, const unsigned char key[32]) {
t0 = U8TO64(&key[0]);
t1 = U8TO64(&key[8]);
st->r[0] = (t0) &0xffc0fffffff;
st->r[0] = (t0) & 0xffc0fffffff;
st->r[1] = ((t0 >> 44) | (t1 << 20)) & 0xfffffc0ffff;
st->r[2] = ((t1 >> 24)) & 0x00ffffffc0f;
@@ -1072,7 +1073,7 @@ static void poly1305_blocks(poly1305_state_internal_t *st,
t0 = U8TO64(&m[0]);
t1 = U8TO64(&m[8]);
h0 += ((t0) &0xfffffffffff);
h0 += ((t0) & 0xfffffffffff);
h1 += (((t0 >> 44) | (t1 << 20)) & 0xfffffffffff);
h2 += (((t1 >> 24)) & 0x3ffffffffff) | hibit;
@@ -1179,7 +1180,7 @@ static POLY1305_NOINLINE void poly1305_finish(poly1305_context *ctx,
t0 = st->pad[0];
t1 = st->pad[1];
h0 += ((t0) &0xfffffffffff);
h0 += ((t0) & 0xfffffffffff);
c = (h0 >> 44);
h0 &= 0xfffffffffff;
h1 += (((t0 >> 44) | (t1 << 20)) & 0xfffffffffff) + c;
@@ -1256,7 +1257,7 @@ static PORTABLE_8439_DECL void pad_if_needed(poly1305_context *ctx,
}
}
#define __u8(v) ((uint8_t) ((v) &0xFF))
#define __u8(v) ((uint8_t) ((v) & 0xFF))
// TODO: make this depending on the unaligned/native read size possible
static PORTABLE_8439_DECL void write_64bit_int(poly1305_context *ctx,
@@ -1329,14 +1330,26 @@ PORTABLE_8439_DECL size_t mg_chacha20_poly1305_encrypt(
PORTABLE_8439_DECL size_t mg_chacha20_poly1305_decrypt(
uint8_t *restrict plain_text, const uint8_t key[RFC_8439_KEY_SIZE],
const uint8_t nonce[RFC_8439_NONCE_SIZE],
const uint8_t *restrict cipher_text, size_t cipher_text_size) {
const uint8_t nonce[RFC_8439_NONCE_SIZE], const uint8_t *restrict ad,
size_t ad_size, const uint8_t *restrict cipher_text,
size_t cipher_text_size) {
// first we calculate the mac and see if it lines up, only then do we decrypt
size_t actual_size = cipher_text_size - RFC_8439_TAG_SIZE;
uint8_t computed_mac[RFC_8439_TAG_SIZE];
int diff = 0;
size_t i;
if (MG_OVERLAPPING(plain_text, actual_size, cipher_text, cipher_text_size)) {
return (size_t) -1;
}
poly1305_calculate_mac(computed_mac, cipher_text, actual_size, key, nonce, ad,
ad_size);
// compare tags
for (i = 0; i < RFC_8439_TAG_SIZE; i++)
diff |= computed_mac[i] ^ cipher_text[actual_size + i];
if (diff != 0) return (size_t) -1;
chacha20_xor_stream(plain_text, cipher_text, actual_size, key, nonce, 1);
return actual_size;
}
+1
View File
@@ -105,6 +105,7 @@ PORTABLE_8439_DECL size_t mg_chacha20_poly1305_encrypt(
PORTABLE_8439_DECL size_t mg_chacha20_poly1305_decrypt(
uint8_t *restrict plain_text, const uint8_t key[RFC_8439_KEY_SIZE],
const uint8_t nonce[RFC_8439_NONCE_SIZE],
const uint8_t *restrict ad, size_t ad_size,
const uint8_t *restrict cipher_text, size_t cipher_text_size);
#if defined(__cplusplus)
}
+72 -12
View File
@@ -72,6 +72,14 @@ static void test_match(void) {
ASSERT(mg_match(mg_str_n("caabxa", 6), mg_str_n("#aa#bb#", 7), NULL) == 0);
ASSERT(mg_match(mg_str_n("a__b_c", 6), mg_str_n("a*b*c", 5), NULL) == 1);
{
char *ae_value = malloc(1); // Exact size for ASAN
ae_value[0] = 'g';
struct mg_str input = {ae_value, 1};
mg_match(input, mg_str("*gzip*"), NULL); // Check for OOB
free(ae_value);
}
{
struct mg_str caps[3];
ASSERT(mg_match(mg_str("//a.c"), mg_str("#.c"), NULL) == true);
@@ -1796,15 +1804,15 @@ static void test_http_parse(void) {
}
// #2292: fail on stray \r inside the headers
ASSERT(mg_http_parse("a є\n\n", 6, &req) == 6);
ASSERT(mg_http_parse("a b\n\n", 5, &req) == 5);
ASSERT(mg_http_parse("a b\na:\n\n", 8, &req) > 0);
ASSERT(mg_http_parse("a b\na:\r\n\n", 9, &req) > 0);
ASSERT(mg_http_parse("a b\n\ra:\r\n\n", 10, &req) == -1);
ASSERT(mg_http_parse("a b\na:\r1\n\n", 10, &req) == -1);
ASSERT(mg_http_parse("a b\na: \r1\n\n", 11, &req) == -1);
ASSERT(mg_http_parse("a b\na: \rb:\n\n", 12, &req) == -1);
ASSERT(mg_http_parse("a b\na: \nb:\n\n", 12, &req) > 0);
ASSERT(mg_http_parse("a є HTTP/1.0\n\n", 15, &req) == 15);
ASSERT(mg_http_parse("a b HTTP/1.0\n\n", 14, &req) == 14);
ASSERT(mg_http_parse("a b HTTP/1.0\na:\n\n", 17, &req) > 0);
ASSERT(mg_http_parse("a b HTTP/1.0\na:\r\n\n", 18, &req) > 0);
ASSERT(mg_http_parse("a b HTTP/1.0\n\ra:\r\n\n", 19, &req) == -1);
ASSERT(mg_http_parse("a b HTTP/1.0\na:\r1\n\n", 19, &req) == -1);
ASSERT(mg_http_parse("a b HTTP/1.0\na: \r1\n\n", 20, &req) == -1);
ASSERT(mg_http_parse("a b HTTP/1.0\na: \rb:\n\n", 21, &req) == -1);
ASSERT(mg_http_parse("a b HTTP/1.0\na: \nb:\n\n", 21, &req) > 0);
{
const char *s = "ґєт /слеш HTTP/1.0\nмісто: кіїв \n\n";
@@ -1891,6 +1899,24 @@ static void test_http_parse(void) {
ASSERT((v = mg_http_get_header(&req, "h")) == NULL); // MG_MAX_HTTP_HEADERS
}
{
// no HTTP version
static char *s = "a b\na:1\nb:2\nc:2\n\n";
ASSERT(mg_http_parse(s, strlen(s), &req) == -1);
// HTTP version
s = "a b HTTP/1.0\na:1\nb:2\nc:2\n\n";
ASSERT(mg_http_parse(s, strlen(s), &req) == strlen(s));
// Content-Length
s = "a b HTTP/1.0\nContent-Length:10\nb:2\nc:2\n\n";
ASSERT(mg_http_parse(s, strlen(s), &req) == strlen(s));
// duplicated Content-Length
s = "a b HTTP/1.0\nContent-Length:10\nb:2\nContent-Length:20\n\n";
ASSERT(mg_http_parse(s, strlen(s), &req) == -1);
}
{
struct mg_connection c;
struct mg_str s,
@@ -1916,15 +1942,15 @@ static void test_http_parse(void) {
struct mg_http_message hm;
const char *s = "a b HTTP/1.0\n\n";
ASSERT(mg_http_parse(s, strlen(s), &hm) == (int) strlen(s));
s = "a b\nc:d\n\n";
s = "a b HTTP/1.0\nc:d\n\n";
ASSERT(mg_http_parse(s, strlen(s), &hm) == (int) strlen(s));
s = "a\nb:b\nc:c\n\n";
ASSERT(mg_http_parse(s, strlen(s), &hm) < 0);
s = "a b\nc: \xc0\n\n"; // Invalid UTF in the header value: accept
s = "a b HTTP/1.0\nc: \xc0\n\n"; // Invalid UTF in the header value: accept
ASSERT(mg_http_parse(s, strlen(s), &hm) == (int) strlen(s));
ASSERT((v = mg_http_get_header(&hm, "c")) != NULL);
ASSERT(vcmp(*v, "\xc0"));
s = "a b\n\xc0: 2\n\n"; // Invalid UTF in the header name: do NOT accept
s = "a b HTTP/1.0\n\xc0: 2\n\n"; // Invalid UTF in the header name: do NOT accept
ASSERT(mg_http_parse(s, strlen(s), &hm) == -1);
}
@@ -3625,6 +3651,40 @@ static void test_json(void) {
val = mg_json_get_tok(json, "$.a");
ASSERT(mg_strcmp(val, expected) == 0);
}
// mg_json_get_num: parsing exponential
{
double d = 0.0;
json = mg_str(
"{"
"\"i\":1e3,"
"\"n\":-2.5e2,"
"\"p\":3E+4,"
"\"m\":4.25E-1,"
"\"z\":0e0,"
"\"s\":-0.0e+0,"
"\"a\":[6.123456789e3,-1e-9],"
"\"bad\":\"1e3\""
"}");
double tolerance = 1e-12;
ASSERT(mg_json_get_num(json, "$.i", &d) == true);
ASSERT(fabs(d - 1000.0) < tolerance);
ASSERT(mg_json_get_num(json, "$.n", &d) == true);
ASSERT(fabs(d - -250.0) < tolerance);
ASSERT(mg_json_get_num(json, "$.p", &d) == true);
ASSERT(fabs(d - 30000.0) < tolerance);
ASSERT(mg_json_get_num(json, "$.m", &d) == true);
ASSERT(fabs(d - 0.425) < tolerance);
ASSERT(mg_json_get_num(json, "$.z", &d) == true);
ASSERT(fabs(d - 0.0) < tolerance);
ASSERT(mg_json_get_num(json, "$.s", &d) == true);
ASSERT(fabs(d - 0.0) < tolerance);
ASSERT(mg_json_get_num(json, "$.a[0]", &d) == true);
ASSERT(fabs(d - 6123.456789) < tolerance);
ASSERT(mg_json_get_num(json, "$.a[1]", &d) == true);
ASSERT(fabs(d - -1e-9) < tolerance);
ASSERT(mg_json_get_num(json, "$.bad", &d) == false);
}
}
static void resp_rpc(struct mg_rpc_req *r) {