ascii-chat
Binary v0.15.83-53-gb711d8720
Library lib/v0.3.2-701-gb711d8720
Video chat in your terminal
Loading...
Searching...
No Matches
record.c File Reference

Go to the source code of this file.

Macros

#define CRYPTO_RECORD_PREFIX_SIZE   24
#define CRYPTO_RECORD_EPOCH_SIZE   8

Functions

asciichat_error_t crypto_record_init (crypto_record_t *state, crypto_context_t *crypto, bool server)
asciichat_error_t crypto_record_seal_into (crypto_record_t *state, unsigned traffic_class, unsigned delivery, const void *packet, size_t length, uint8_t *wire, size_t capacity, size_t *out_length)
asciichat_error_t crypto_record_seal_into_domain (crypto_record_t *state, unsigned traffic_class, unsigned delivery, const void *packet, size_t length, uint8_t *wire, size_t capacity, size_t *out_length, buffer_domain_t *requested_domain)
asciichat_error_t crypto_record_open_sequence (crypto_record_t *state, unsigned traffic_class, unsigned delivery, const void *packet, size_t length, uint8_t **out, size_t *out_length, uint64_t *sequence_out, crypto_record_validate_fn validate, void *context)
asciichat_error_t crypto_record_prepare_epoch (crypto_record_t *state)
asciichat_error_t crypto_record_commit_epoch (crypto_record_t *state)
asciichat_error_t crypto_record_retire_epoch_flow (crypto_record_t *state, unsigned traffic_class, uint64_t epoch)
void crypto_record_cleanup (crypto_record_t *state)

Macro Definition Documentation

◆ CRYPTO_RECORD_EPOCH_SIZE

#define CRYPTO_RECORD_EPOCH_SIZE   8

Definition at line 10 of file record.c.

Referenced by crypto_record_open_sequence(), and crypto_record_seal_into_domain().

◆ CRYPTO_RECORD_PREFIX_SIZE

#define CRYPTO_RECORD_PREFIX_SIZE   24

Definition at line 9 of file record.c.

Referenced by crypto_record_open_sequence(), and crypto_record_seal_into_domain().

Function Documentation

◆ crypto_record_cleanup()

void crypto_record_cleanup ( crypto_record_t * state)

Definition at line 248 of file record.c.

248 {
249 if (state)
250 sodium_memzero(state, sizeof(*state));
251}

Referenced by crypto_session_destroy().

◆ crypto_record_commit_epoch()

asciichat_error_t crypto_record_commit_epoch ( crypto_record_t * state)

Definition at line 211 of file record.c.

211 {
212 if (!ready(state) || !state->pending_prepared || state->previous_valid || !state->crypto->rekey_in_progress ||
213 !state->crypto->has_temp_key)
214 return SET_ERRNO(ERROR_INVALID_STATE, "record key epoch is not ready to commit");
215 crypto_context_t previous = *state->crypto;
216 previous.session = NULL;
217 previous.rekey_in_progress = previous.has_temp_key = false;
218 sodium_memzero(previous.temp_private_key, sizeof(previous.temp_private_key));
219 sodium_memzero(previous.temp_public_key, sizeof(previous.temp_public_key));
220 sodium_memzero(previous.temp_shared_key, sizeof(previous.temp_shared_key));
221 if (crypto_rekey_commit(state->crypto) != CRYPTO_OK) {
222 sodium_memzero(&previous, sizeof(previous));
223 return SET_ERRNO(ERROR_CRYPTO, "Cannot commit record key epoch");
224 }
225 state->previous_crypto = previous;
226 sodium_memzero(&previous, sizeof(previous));
227 state->previous_epoch = state->key_epoch;
228 state->key_epoch = state->crypto->rekey_count;
229 state->previous_valid = true;
230 state->pending_prepared = false;
231 for (unsigned i = 0; i < CRYPTO_RECORD_CHANNELS; ++i)
232 state->previous_allowed[i] = true;
233 return ASCIICHAT_OK;
234}
#define SET_ERRNO(code,...)
crypto_result_t crypto_rekey_commit(crypto_context_t *ctx)
Commit to new keys after successful REKEY_COMPLETE.
@ ERROR_INVALID_STATE
@ ASCIICHAT_OK
Definition error_codes.h:46
@ ERROR_CRYPTO
Definition error_codes.h:93
#define CRYPTO_RECORD_CHANNELS
Definition record.h:7
Endpoint cryptographic state for handshake, packet protection, authentication, and rekeying.
struct crypto_session * session
Endpoint cryptographic state for handshake, packet protection, authentication, and rekeying.

References ASCIICHAT_OK, CRYPTO_OK, CRYPTO_RECORD_CHANNELS, crypto_rekey_commit(), ERROR_CRYPTO, ERROR_INVALID_STATE, crypto_context_t::session, and SET_ERRNO.

Referenced by crypto_record_open_sequence(), and crypto_rekey_session_receive().

◆ crypto_record_init()

asciichat_error_t crypto_record_init ( crypto_record_t * state,
crypto_context_t * crypto,
bool server )

Definition at line 23 of file record.c.

23 {
24 if (!state || state->crypto || !crypto || !crypto->initialized || !crypto->encrypt_data ||
25 !crypto->key_exchange_complete || !crypto->handshake_complete || crypto->nonce_size != CRYPTO_NONCE_SIZE ||
26 crypto->mac_size != CRYPTO_MAC_SIZE || crypto->rekey_in_progress)
27 return SET_ERRNO(ERROR_INVALID_STATE, "Session handshake and encryption must be ready");
28 *state = (crypto_record_t){.crypto = crypto, .key_epoch = crypto->rekey_count, .server = server};
29 return ASCIICHAT_OK;
30}
struct crypto_record crypto_record_t
When code needs to work with crypto record, it keeps crypto, key_epoch, last_receive_epoch,...
#define CRYPTO_NONCE_SIZE
Nonce size (XSalsa20).
Definition sizes.h:79
#define CRYPTO_MAC_SIZE
MAC size (Poly1305).
Definition sizes.h:85

References ASCIICHAT_OK, CRYPTO_MAC_SIZE, CRYPTO_NONCE_SIZE, crypto_context_t::encrypt_data, ERROR_INVALID_STATE, crypto_context_t::handshake_complete, crypto_context_t::initialized, crypto_context_t::key_exchange_complete, crypto_context_t::mac_size, crypto_context_t::nonce_size, crypto_context_t::rekey_count, crypto_context_t::rekey_in_progress, and SET_ERRNO.

Referenced by crypto_session_lock().

◆ crypto_record_open_sequence()

asciichat_error_t crypto_record_open_sequence ( crypto_record_t * state,
unsigned traffic_class,
unsigned delivery,
const void * packet,
size_t length,
uint8_t ** out,
size_t * out_length,
uint64_t * sequence_out,
crypto_record_validate_fn validate,
void * context )

Definition at line 88 of file record.c.

91 {
92 if (!out || !out_length)
93 return SET_ERRNO(ERROR_INVALID_PARAM, "Missing record decrypted packet output");
94 *out = NULL;
95 *out_length = 0;
96 if (sequence_out)
97 *sequence_out = 0;
98 if (!ready(state) || delivery > CRYPTO_RECORD_DATAGRAM ||
99 (traffic_class != CRYPTO_RECORD_ANY_CHANNEL && !valid_flow(traffic_class, delivery)))
100 return SET_ERRNO(ERROR_INVALID_STATE, "Record decryption is not ready for this flow");
101 if (!packet || length < CRYPTO_RECORD_EPOCH_SIZE + state->crypto->nonce_size + state->crypto->mac_size ||
102 length >
103 CRYPTO_RECORD_EPOCH_SIZE + CRYPTO_MAX_PLAINTEXT_SIZE + state->crypto->nonce_size + state->crypto->mac_size)
104 return SET_ERRNO(ERROR_NETWORK_PROTOCOL, "Invalid Record encrypted packet size");
105 const uint8_t *cipher = packet;
106 size_t cipher_length = length;
107 uint64_t network_epoch;
108 memcpy(&network_epoch, cipher, sizeof(network_epoch));
109 uint64_t epoch = NET_TO_HOST_U64(network_epoch);
110 crypto_context_t pending_crypto;
111 crypto_context_t *decrypt_crypto = state->crypto;
112 bool pending = false;
113 if (epoch != state->key_epoch) {
114 if (state->previous_valid && epoch == state->previous_epoch) {
115 decrypt_crypto = &state->previous_crypto;
116 } else if (state->key_epoch != UINT64_MAX && epoch == state->key_epoch + 1 && state->pending_prepared &&
117 state->crypto->rekey_in_progress && state->crypto->has_temp_key) {
118 pending_crypto = *state->crypto;
119 pending_crypto.session = NULL;
120 memcpy(pending_crypto.shared_key, state->crypto->temp_shared_key, sizeof(pending_crypto.shared_key));
121 pending_crypto.rekey_in_progress = false;
122 pending_crypto.has_temp_key = false;
123 decrypt_crypto = &pending_crypto;
124 pending = true;
125 } else {
126 return SET_ERRNO(ERROR_CRYPTO, "Unknown or retired record key epoch");
127 }
128 }
129 cipher += CRYPTO_RECORD_EPOCH_SIZE;
130 cipher_length -= CRYPTO_RECORD_EPOCH_SIZE;
131 uint8_t *plain = buffer_alloc(NULL, cipher_length);
132 if (!plain) {
133 if (pending)
134 sodium_memzero(&pending_crypto, sizeof(pending_crypto));
135 return SET_ERRNO(ERROR_MEMORY, "Cannot allocate record decrypted packet");
136 }
137 defer(if (plain) buffer_release(plain));
138 size_t plain_length = 0;
139 crypto_result_t crypto_result =
140 crypto_decrypt(decrypt_crypto, cipher, cipher_length, plain, cipher_length, &plain_length);
141 if (pending)
142 sodium_memzero(&pending_crypto, sizeof(pending_crypto));
143 if (crypto_result != CRYPTO_OK) {
144 return SET_ERRNO(ERROR_CRYPTO, "Cannot authenticate Record packet");
145 }
146 if (plain_length < CRYPTO_RECORD_PREFIX_SIZE || memcmp(plain, "ACR1", 4) || plain[4] != (state->server ? 0 : 1) ||
147 (traffic_class != CRYPTO_RECORD_ANY_CHANNEL && plain[5] != traffic_class) || plain[5] >= CRYPTO_RECORD_CHANNELS ||
148 plain[6] != (uint8_t)delivery || plain[7] || memcmp(plain + 16, &network_epoch, sizeof(network_epoch))) {
149 return SET_ERRNO(ERROR_NETWORK_PROTOCOL, "Invalid authenticated record flow metadata");
150 }
151 traffic_class = plain[5];
152 if (epoch == state->previous_epoch && state->previous_valid && !state->previous_allowed[traffic_class]) {
153 return SET_ERRNO(ERROR_CRYPTO, "Retired record channel epoch");
154 }
155 uint64_t network_sequence;
156 memcpy(&network_sequence, plain + 8, sizeof(network_sequence));
157 uint64_t sequence = NET_TO_HOST_U64(network_sequence);
158 uint64_t high = state->receive_high[traffic_class][delivery];
159 uint64_t seen = state->datagram_seen[traffic_class];
160 if (!sequence || (delivery == CRYPTO_RECORD_RELIABLE && sequence <= high) ||
161 (delivery == CRYPTO_RECORD_DATAGRAM && sequence <= high &&
162 (high - sequence >= 64 || (seen & (UINT64_C(1) << (high - sequence)))))) {
163 return SET_ERRNO(ERROR_CRYPTO, "Replayed or stale Record packet");
164 }
165 size_t inner_length = plain_length - CRYPTO_RECORD_PREFIX_SIZE;
166 asciichat_error_t result =
167 validate ? validate(context, traffic_class, delivery, plain + CRYPTO_RECORD_PREFIX_SIZE, inner_length)
168 : ASCIICHAT_OK;
169 if (result != ASCIICHAT_OK) {
170 return result;
171 }
172 if (pending) {
173 result = crypto_record_commit_epoch(state);
174 if (result != ASCIICHAT_OK) {
175 return result;
176 }
177 }
178 if (delivery == CRYPTO_RECORD_DATAGRAM) {
179 if (sequence > high) {
180 uint64_t advance = sequence - high;
181 seen = advance >= 64 ? UINT64_C(1) : (seen << advance) | UINT64_C(1);
182 high = sequence;
183 } else {
184 seen |= UINT64_C(1) << (high - sequence);
185 }
186 state->datagram_seen[traffic_class] = seen;
187 } else {
188 high = sequence;
189 }
190 state->receive_high[traffic_class][delivery] = high;
191 state->last_receive_epoch = epoch;
192 state->last_receive_channel = traffic_class;
193 state->last_receive_delivery = delivery;
194 memmove(plain, plain + CRYPTO_RECORD_PREFIX_SIZE, inner_length);
195 *out = plain;
196 plain = NULL;
197 *out_length = inner_length;
198 if (sequence_out)
199 *sequence_out = sequence;
200 return ASCIICHAT_OK;
201}
void * buffer_alloc(buffer_domain_t *domain, size_t size)
Definition buffer.c:274
#define buffer_release(data)
Definition buffer.h:91
#define NET_TO_HOST_U64(val)
Definition endian.h:228
unsigned long long uint64_t
Definition common.h:63
unsigned char uint8_t
Definition common.h:60
crypto_result_t
Cryptographic operation result codes.
crypto_result_t crypto_decrypt(crypto_context_t *ctx, const uint8_t *ciphertext, size_t ciphertext_len, uint8_t *plaintext_out, size_t plaintext_out_size, size_t *plaintext_len_out)
Decrypt data using XSalsa20-Poly1305.
#define CRYPTO_MAX_PLAINTEXT_SIZE
Maximum plaintext size (1MB).
asciichat_error_t
Error and exit codes - unified status values (0-255).
Definition error_codes.h:44
@ ERROR_NETWORK_PROTOCOL
Definition error_codes.h:76
@ ERROR_MEMORY
Definition error_codes.h:51
@ ERROR_INVALID_PARAM
static bool size_t gotel_query_t * out
Definition queries.h:8
asciichat_error_t crypto_record_commit_epoch(crypto_record_t *state)
Definition record.c:211
#define CRYPTO_RECORD_PREFIX_SIZE
Definition record.c:9
#define CRYPTO_RECORD_EPOCH_SIZE
Definition record.c:10
#define CRYPTO_RECORD_RELIABLE
Definition record.h:9
#define CRYPTO_RECORD_DATAGRAM
Definition record.h:10
#define CRYPTO_RECORD_ANY_CHANNEL
Definition record.h:11

References ASCIICHAT_OK, buffer_alloc(), buffer_release, crypto_decrypt(), CRYPTO_MAX_PLAINTEXT_SIZE, CRYPTO_OK, CRYPTO_RECORD_ANY_CHANNEL, CRYPTO_RECORD_CHANNELS, crypto_record_commit_epoch(), CRYPTO_RECORD_DATAGRAM, CRYPTO_RECORD_EPOCH_SIZE, CRYPTO_RECORD_PREFIX_SIZE, CRYPTO_RECORD_RELIABLE, ERROR_CRYPTO, ERROR_INVALID_PARAM, ERROR_INVALID_STATE, ERROR_MEMORY, ERROR_NETWORK_PROTOCOL, crypto_context_t::has_temp_key, NET_TO_HOST_U64, out, crypto_context_t::rekey_in_progress, crypto_context_t::session, SET_ERRNO, and crypto_context_t::shared_key.

Referenced by acip_packet_decrypt_payload().

◆ crypto_record_prepare_epoch()

asciichat_error_t crypto_record_prepare_epoch ( crypto_record_t * state)

Definition at line 203 of file record.c.

203 {
204 if (!ready(state) || state->previous_valid || state->pending_prepared || state->key_epoch == UINT64_MAX ||
205 !state->crypto->rekey_in_progress || !state->crypto->has_temp_key)
206 return SET_ERRNO(ERROR_INVALID_STATE, "record key epoch is not ready to prepare");
207 state->pending_prepared = true;
208 return ASCIICHAT_OK;
209}

References ASCIICHAT_OK, ERROR_INVALID_STATE, and SET_ERRNO.

Referenced by crypto_rekey_session_receive(), and crypto_rekey_session_tick().

◆ crypto_record_retire_epoch_flow()

asciichat_error_t crypto_record_retire_epoch_flow ( crypto_record_t * state,
unsigned traffic_class,
uint64_t epoch )

Definition at line 236 of file record.c.

236 {
237 if (!ready(state) || (unsigned)traffic_class > (CRYPTO_RECORD_CHANNELS - 1) || epoch != state->key_epoch ||
238 !state->previous_valid)
239 return SET_ERRNO(ERROR_INVALID_STATE, "Invalid record epoch retirement marker");
240 state->previous_allowed[traffic_class] = false;
241 if (!state->previous_allowed[0] && !state->previous_allowed[1] && !state->previous_allowed[2]) {
242 sodium_memzero(&state->previous_crypto, sizeof(state->previous_crypto));
243 state->previous_valid = false;
244 }
245 return ASCIICHAT_OK;
246}

References ASCIICHAT_OK, CRYPTO_RECORD_CHANNELS, ERROR_INVALID_STATE, and SET_ERRNO.

Referenced by crypto_rekey_session_receive().

◆ crypto_record_seal_into()

asciichat_error_t crypto_record_seal_into ( crypto_record_t * state,
unsigned channel,
unsigned delivery,
const void * packet,
size_t length,
uint8_t * wire,
size_t capacity,
size_t * out_length )

Output buffers belong to the buffer library. No packet headers or CRCs are added here.

Definition at line 32 of file record.c.

34 {
35 return crypto_record_seal_into_domain(state, traffic_class, delivery, packet, length, wire, capacity, out_length,
36 NULL);
37}
asciichat_error_t crypto_record_seal_into_domain(crypto_record_t *state, unsigned traffic_class, unsigned delivery, const void *packet, size_t length, uint8_t *wire, size_t capacity, size_t *out_length, buffer_domain_t *requested_domain)
Definition record.c:39

References crypto_record_seal_into_domain().

◆ crypto_record_seal_into_domain()

asciichat_error_t crypto_record_seal_into_domain ( crypto_record_t * state,
unsigned traffic_class,
unsigned delivery,
const void * packet,
size_t length,
uint8_t * wire,
size_t capacity,
size_t * out_length,
buffer_domain_t * requested_domain )

Definition at line 39 of file record.c.

41 {
42 if (!wire || !out_length)
43 return SET_ERRNO(ERROR_INVALID_PARAM, "Missing Record encrypted packet output");
44 *out_length = 0;
45 if (!ready(state) || !valid_flow(traffic_class, delivery))
46 return SET_ERRNO(ERROR_INVALID_STATE, "Record encryption is not ready for this flow");
47 if (!packet || !length)
48 return SET_ERRNO(ERROR_INVALID_PARAM, "Missing record plaintext");
50 state->send_sequence[traffic_class][delivery] == UINT64_MAX)
51 return SET_ERRNO(ERROR_NETWORK_SIZE, "Record packet or sequence limit exceeded");
52 size_t plain_length = CRYPTO_RECORD_PREFIX_SIZE + length;
53 size_t cipher_capacity = plain_length + state->crypto->nonce_size + state->crypto->mac_size;
54 if (capacity < CRYPTO_RECORD_EPOCH_SIZE + cipher_capacity)
55 return SET_ERRNO(ERROR_NETWORK_SIZE, "Record ciphertext output is too small");
56 buffer_domain_t *record_buffer_domain =
57 requested_domain ? requested_domain
58 : (traffic_class == CRYPTO_RECORD_CONTROL_CHANNEL ? buffer_control_domain() : NULL);
59 uint8_t *plain = buffer_alloc(record_buffer_domain, plain_length);
60 if (!plain) {
61 return SET_ERRNO(ERROR_MEMORY, "Cannot allocate Record packet");
62 }
63 memcpy(plain, "ACR1", 4);
64 plain[4] = state->server ? 1 : 0;
65 plain[5] = (uint8_t)traffic_class;
66 plain[6] = (uint8_t)delivery;
67 plain[7] = 0;
68 uint64_t sequence = state->send_sequence[traffic_class][delivery] + 1;
69 uint64_t network_sequence = HOST_TO_NET_U64(sequence);
70 memcpy(plain + 8, &network_sequence, sizeof(network_sequence));
71 uint64_t network_epoch = HOST_TO_NET_U64(state->key_epoch);
72 memcpy(plain + 16, &network_epoch, sizeof(network_epoch));
73 memcpy(plain + CRYPTO_RECORD_PREFIX_SIZE, packet, length);
74 size_t cipher_length = 0;
75 crypto_result_t crypto_result = crypto_encrypt(state->crypto, plain, plain_length, wire + CRYPTO_RECORD_EPOCH_SIZE,
76 cipher_capacity, &cipher_length);
77 buffer_release(plain);
78 if (crypto_result != CRYPTO_OK) {
79 return SET_ERRNO(ERROR_CRYPTO, "Cannot encrypt Record packet");
80 }
81 memcpy(wire, &network_epoch, sizeof(network_epoch));
82 cipher_length += CRYPTO_RECORD_EPOCH_SIZE;
83 state->send_sequence[traffic_class][delivery] = sequence;
84 *out_length = cipher_length;
85 return ASCIICHAT_OK;
86}
buffer_domain_t * buffer_control_domain(void)
Definition buffer.c:436
struct buffer_domain buffer_domain_t
Definition buffer.h:24
#define HOST_TO_NET_U64(val)
Definition endian.h:213
crypto_result_t crypto_encrypt(crypto_context_t *ctx, const uint8_t *plaintext, size_t plaintext_len, uint8_t *ciphertext_out, size_t ciphertext_out_size, size_t *ciphertext_len_out)
Encrypt data using XSalsa20-Poly1305.
@ ERROR_NETWORK_SIZE
Definition error_codes.h:77
#define CRYPTO_RECORD_CONTROL_CHANNEL
Definition record.h:8

References ASCIICHAT_OK, buffer_alloc(), buffer_control_domain(), buffer_release, crypto_encrypt(), CRYPTO_MAX_PLAINTEXT_SIZE, CRYPTO_OK, CRYPTO_RECORD_CONTROL_CHANNEL, CRYPTO_RECORD_EPOCH_SIZE, CRYPTO_RECORD_PREFIX_SIZE, ERROR_CRYPTO, ERROR_INVALID_PARAM, ERROR_INVALID_STATE, ERROR_MEMORY, ERROR_NETWORK_SIZE, HOST_TO_NET_U64, and SET_ERRNO.

Referenced by acip_packet_encrypt(), and crypto_record_seal_into().