// Check datagram for valid HMAC bool sptps_verify_datagram(sptps_t *s, const void *data, size_t len) { if (!s->instate) return error(s, EIO, "SPTPS state not ready to verify this datagram"); if(len < 21) return error(s, EIO, "Received short packet in sptps_verify_datagram"); uint32_t seqno; memcpy(&seqno, data, 4); seqno = ntohl(seqno); // TODO: check whether seqno makes sense, to avoid CPU intensive decrypt char buffer[len]; size_t outlen; return chacha_poly1305_decrypt(s->incipher, seqno, data + 4, len - 4, buffer, &outlen); }
// Decrypts ctxt, stores as an encoded uint8 int decrypt_phrase(dheluks_ctx_t *ctx, dheluks_pkg_t *pkg, dheluks_kys_t *skr, dheluks_txt_t *txt) { uint8_t dig[DIGEST_SIZE]; chacha_poly1305_set_key(&ctx->ciph, skr->sharekey); //set key chacha_poly1305_set_nonce(&ctx->ciph, pkg->nonce); //set nonce chacha_poly1305_decrypt(&ctx->ciph, pkg->csize, txt->plntxt, pkg->cphtxt); //decrypt chacha_poly1305_digest(&ctx->ciph, DIGEST_SIZE, dig); if (not_equal(dig, pkg->digest, DIGEST_SIZE, "Message authentication failed.")) //if the digests aren't equal, return -1; //return an error return 0; }
// Receive incoming data. Check if it contains a complete record, if so, handle it. bool sptps_receive_data(sptps_t *s, const void *data, size_t len) { if(!s->state) return error(s, EIO, "Invalid session state zero"); if(s->datagram) return sptps_receive_data_datagram(s, data, len); while(len) { // First read the 2 length bytes. if(s->buflen < 2) { size_t toread = 2 - s->buflen; if(toread > len) toread = len; memcpy(s->inbuf + s->buflen, data, toread); s->buflen += toread; len -= toread; data += toread; // Exit early if we don't have the full length. if(s->buflen < 2) return true; // Get the length bytes memcpy(&s->reclen, s->inbuf, 2); s->reclen = ntohs(s->reclen); // If we have the length bytes, ensure our buffer can hold the whole request. s->inbuf = realloc(s->inbuf, s->reclen + 19UL); if(!s->inbuf) return error(s, errno, strerror(errno)); // Exit early if we have no more data to process. if(!len) return true; } // Read up to the end of the record. size_t toread = s->reclen + (s->instate ? 19UL : 3UL) - s->buflen; if(toread > len) toread = len; memcpy(s->inbuf + s->buflen, data, toread); s->buflen += toread; len -= toread; data += toread; // If we don't have a whole record, exit. if(s->buflen < s->reclen + (s->instate ? 19UL : 3UL)) return true; // Update sequence number. uint32_t seqno = s->inseqno++; // Check HMAC and decrypt. if(s->instate) { if(!chacha_poly1305_decrypt(s->incipher, seqno, s->inbuf + 2UL, s->reclen + 17UL, s->inbuf + 2UL, NULL)) return error(s, EINVAL, "Failed to decrypt and verify record"); } // Append a NULL byte for safety. s->inbuf[s->reclen + 3UL] = 0; uint8_t type = s->inbuf[2]; if(type < SPTPS_HANDSHAKE) { if(!s->instate) return error(s, EIO, "Application record received before handshake finished"); if(!s->receive_record(s->handle, type, s->inbuf + 3, s->reclen)) return false; } else if(type == SPTPS_HANDSHAKE) { if(!receive_handshake(s, s->inbuf + 3, s->reclen)) return false; } else { return error(s, EIO, "Invalid record type %d", type); } s->buflen = 0; } return true; }
// Receive incoming data, datagram version. static bool sptps_receive_data_datagram(sptps_t *s, const void *vdata, size_t len) { const char *data = vdata; if(len < (s->instate ? 21 : 5)) return error(s, EIO, "Received short packet in sptps_receive_data_datagram"); uint32_t seqno; memcpy(&seqno, data, 4); seqno = ntohl(seqno); if(!s->instate) { if(seqno != s->inseqno) return error(s, EIO, "Invalid packet seqno: %d != %d", seqno, s->inseqno); s->inseqno = seqno + 1; uint8_t type = data[4]; if(type != SPTPS_HANDSHAKE) return error(s, EIO, "Application record received before handshake finished"); return receive_handshake(s, data + 5, len - 5); } // Decrypt char buffer[len]; size_t outlen; if(!chacha_poly1305_decrypt(s->incipher, seqno, data + 4, len - 4, buffer, &outlen)) return error(s, EIO, "Failed to decrypt and verify packet"); // Replay protection using a sliding window of configurable size. // s->inseqno is expected sequence number // seqno is received sequence number // s->late[] is a circular buffer, a 1 bit means a packet has not been received yet // The circular buffer contains bits for sequence numbers from s->inseqno - s->replaywin * 8 to (but excluding) s->inseqno. if(s->replaywin) { if(seqno != s->inseqno) { if(seqno >= s->inseqno + s->replaywin * 8) { // TODO: Prevent packets that jump far ahead of the queue from causing many others to be dropped. warning(s, "Lost %d packets\n", seqno - s->inseqno); // Mark all packets in the replay window as being late. memset(s->late, 255, s->replaywin); } else if (seqno < s->inseqno) { // If the sequence number is farther in the past than the bitmap goes, or if the packet was already received, drop it. if((s->inseqno >= s->replaywin * 8 && seqno < s->inseqno - s->replaywin * 8) || !(s->late[(seqno / 8) % s->replaywin] & (1 << seqno % 8))) return error(s, EIO, "Received late or replayed packet, seqno %d, last received %d\n", seqno, s->inseqno); } else { // We missed some packets. Mark them in the bitmap as being late. for(int i = s->inseqno; i < seqno; i++) s->late[(i / 8) % s->replaywin] |= 1 << i % 8; } } // Mark the current packet as not being late. s->late[(seqno / 8) % s->replaywin] &= ~(1 << seqno % 8); } if(seqno >= s->inseqno) s->inseqno = seqno + 1; if(!s->inseqno) s->received = 0; else s->received++; // Append a NULL byte for safety. buffer[len - 20] = 0; uint8_t type = buffer[0]; if(type < SPTPS_HANDSHAKE) { if(!s->instate) return error(s, EIO, "Application record received before handshake finished"); if(!s->receive_record(s->handle, type, buffer + 1, len - 21)) abort(); } else if(type == SPTPS_HANDSHAKE) { if(!receive_handshake(s, buffer + 1, len - 21)) abort(); } else { return error(s, EIO, "Invalid record type %d", type); } return true; }