diff --git a/include/polarssl/config.h b/include/polarssl/config.h index b13a4076f..20f104df3 100644 --- a/include/polarssl/config.h +++ b/include/polarssl/config.h @@ -913,6 +913,15 @@ */ #define POLARSSL_SSL_PROTO_DTLS +/** + * \def POLARSSL_SSL_DTLS_ANTI_REPLAY + * + * Enable support for the anti-replay mechanism in DTLS. + * + * Comment this to disable anti-replay in DTLS. + */ +#define POLARSSL_SSL_DTLS_ANTI_REPLAY + /** * \def POLARSSL_SSL_ALPN * diff --git a/include/polarssl/ssl.h b/include/polarssl/ssl.h index 63a75286e..59467994f 100644 --- a/include/polarssl/ssl.h +++ b/include/polarssl/ssl.h @@ -822,6 +822,10 @@ struct _ssl_context size_t next_record_offset; /*!< offset of the next record in datagram (equal to in_left if none) */ #endif +#if defined(POLARSSL_SSL_DTLS_ANTI_REPLAY) + uint64_t in_window_top; /*!< last validated record seq_num */ + uint64_t in_window; /*!< bitmask for replay detection */ +#endif size_t in_hslen; /*!< current handshake message length, including the handshake header */ @@ -2043,6 +2047,12 @@ void ssl_recv_flight_completed( ssl_context *ssl ); int ssl_resend( ssl_context *ssl ); #endif +/* Visible for testing purposes only */ +#if defined(POLARSSL_SSL_DTLS_ANTI_REPLAY) +int ssl_dtls_replay_check( ssl_context *ssl ); +void ssl_dtls_replay_update( ssl_context *ssl ); +#endif + /* constant-time buffer comparison */ static inline int safer_memcmp( const void *a, const void *b, size_t n ) { diff --git a/library/ssl_tls.c b/library/ssl_tls.c index 8546ed033..31be3ca3e 100644 --- a/library/ssl_tls.c +++ b/library/ssl_tls.c @@ -2694,6 +2694,90 @@ static int ssl_prepare_handshake_record( ssl_context *ssl ) return( 0 ); } +/* + * DTLS anti-replay: RFC 6347 4.1.2.6 + * + * - in_window_top is the highest record sequence number seen + * - the lsb of in_window is set iff in_window_top - 1 has been seen + * ... + * the msb of in_window is set iff in_window_top - 64 has been seen + */ +#if defined(POLARSSL_SSL_DTLS_ANTI_REPLAY) +static void ssl_dtls_replay_reset( ssl_context *ssl ) +{ + ssl->in_window_top = 0; + ssl->in_window = 0; +} + +static inline uint64_t ssl_load_six_bytes( unsigned char *buf ) +{ + return( ( (uint64_t) buf[0] << 40 ) | + ( (uint64_t) buf[1] << 32 ) | + ( (uint64_t) buf[2] << 24 ) | + ( (uint64_t) buf[3] << 16 ) | + ( (uint64_t) buf[4] << 8 ) | + ( (uint64_t) buf[5] ) ); +} + +/* + * Return 0 if sequence number is acceptable, -1 otherwise + */ +int ssl_dtls_replay_check( ssl_context *ssl ) +{ + uint64_t rec_seqnum = ssl_load_six_bytes( ssl->in_ctr + 2 ); + uint64_t bit; + + if( rec_seqnum > ssl->in_window_top ) + return( 0 ); + + if( rec_seqnum == ssl->in_window_top ) + return( -1 ); + + bit = ssl->in_window_top - rec_seqnum - 1; + + if( bit >= 64 ) + return( -1 ); + + if( ( ssl->in_window & ( (uint64_t) 1 << bit ) ) != 0 ) + return( -1 ); + + return( 0 ); +} + +/* + * Update replay window on new validated record + */ +void ssl_dtls_replay_update( ssl_context *ssl ) +{ + uint64_t rec_seqnum = ssl_load_six_bytes( ssl->in_ctr + 2 ); + + if( rec_seqnum > ssl->in_window_top ) + { + /* Update window_top and the contents of the window */ + uint64_t shift = rec_seqnum - ssl->in_window_top; + + if( shift >= 64 ) + ssl->in_window = 0; + else + ssl->in_window <<= shift; + + ssl->in_window_top = rec_seqnum; + } + else if( rec_seqnum == ssl->in_window_top ) + { + ; /* Can't happen, but anyway, nothing to do if it happened */ + } + else + { + /* Mark that number as seen in the current window */ + uint64_t bit = ssl->in_window_top - rec_seqnum - 1; + + if( bit < 64 ) /* Always true, but be extra sure */ + ssl->in_window |= (uint64_t) 1 << bit; + } +} +#endif /* POLARSSL_SSL_DTLS_ANTI_REPLAY */ + /* * ContentType type; * ProtocolVersion version;