+ }
+
+ return true;
+
+ case SPTPS_ACK:
+
+ // We expect a handshake message to indicate transition to the new keys.
+ if(!receive_ack(s, data, len)) {
+ return false;
+ }
+
+ s->receive_record(s->handle, SPTPS_HANDSHAKE, NULL, 0);
+ s->state = SPTPS_SECONDARY_KEX;
+ return true;
+
+ // TODO: split ACK into a VERify and ACK?
+ default:
+ return error(s, EIO, "Invalid session state %d", s->state);
+ }
+}
+
+// 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, (const char *)data + 4, len - 4, buffer, &outlen);
+}
+
+// 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");