Skip to content

Commit 31731ba

Browse files
madeyeclaude
andcommitted
Reduce memory copies in crypto encrypt/decrypt paths
Add bswap_data() to swap buffer pointers instead of copying, replace brealloc+memcpy with pointer swaps in aead and stream codecs, add in-place fast paths for non-Salsa20 stream ciphers, and use index tracking instead of memmove in aead_decrypt chunked reassembly. Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
1 parent 6423c8e commit 31731ba

3 files changed

Lines changed: 89 additions & 43 deletions

File tree

src/aead.c

Lines changed: 37 additions & 35 deletions
Original file line numberDiff line numberDiff line change
@@ -439,8 +439,7 @@ aead_encrypt_all(buffer_t *plaintext, cipher_t *cipher, size_t capacity)
439439

440440
assert(ciphertext->len == clen);
441441

442-
brealloc(plaintext, salt_len + ciphertext->len, capacity);
443-
memcpy(plaintext->data, ciphertext->data, salt_len + ciphertext->len);
442+
bswap_data(plaintext, ciphertext);
444443
plaintext->len = salt_len + ciphertext->len;
445444

446445
return CRYPTO_OK;
@@ -490,8 +489,7 @@ aead_decrypt_all(buffer_t *ciphertext, cipher_t *cipher, size_t capacity)
490489

491490
ppbloom_add((void *)salt, salt_len);
492491

493-
brealloc(ciphertext, plaintext->len, capacity);
494-
memcpy(ciphertext->data, plaintext->data, plaintext->len);
492+
bswap_data(ciphertext, plaintext);
495493
ciphertext->len = plaintext->len;
496494

497495
return CRYPTO_OK;
@@ -579,8 +577,7 @@ aead_encrypt(buffer_t *plaintext, cipher_ctx_t *cipher_ctx, size_t capacity)
579577
if (err)
580578
return err;
581579

582-
brealloc(plaintext, ciphertext->len, capacity);
583-
memcpy(plaintext->data, ciphertext->data, ciphertext->len);
580+
bswap_data(plaintext, ciphertext);
584581
plaintext->len = ciphertext->len;
585582

586583
return 0;
@@ -634,9 +631,7 @@ aead_chunk_decrypt(cipher_ctx_t *ctx, uint8_t *p, uint8_t *c, uint8_t *n,
634631
int
635632
aead_decrypt(buffer_t *ciphertext, cipher_ctx_t *cipher_ctx, size_t capacity)
636633
{
637-
int err = CRYPTO_OK;
638-
static buffer_t tmp = { 0, 0, 0, NULL };
639-
634+
int err = CRYPTO_OK;
640635
cipher_t *cipher = cipher_ctx->cipher;
641636

642637
size_t salt_len = cipher->key_len;
@@ -647,20 +642,29 @@ aead_decrypt(buffer_t *ciphertext, cipher_ctx_t *cipher_ctx, size_t capacity)
647642
balloc(cipher_ctx->chunk, capacity);
648643
}
649644

650-
brealloc(cipher_ctx->chunk,
651-
cipher_ctx->chunk->len + ciphertext->len, capacity);
652-
memcpy(cipher_ctx->chunk->data + cipher_ctx->chunk->len,
653-
ciphertext->data, ciphertext->len);
654-
cipher_ctx->chunk->len += ciphertext->len;
645+
buffer_t *chunk = cipher_ctx->chunk;
655646

656-
brealloc(&tmp, cipher_ctx->chunk->len, capacity);
657-
buffer_t *plaintext = &tmp;
647+
if (chunk->len == 0) {
648+
bswap_data(chunk, ciphertext);
649+
chunk->len = ciphertext->len;
650+
chunk->idx = 0;
651+
ciphertext->len = 0;
652+
} else {
653+
brealloc(chunk,
654+
chunk->idx + chunk->len + ciphertext->len, capacity);
655+
memcpy(chunk->data + chunk->idx + chunk->len,
656+
ciphertext->data, ciphertext->len);
657+
chunk->len += ciphertext->len;
658+
}
659+
660+
// Write plaintext directly into the (now-free) ciphertext buffer.
661+
brealloc(ciphertext, chunk->len, capacity);
658662

659663
if (!cipher_ctx->init) {
660-
if (cipher_ctx->chunk->len <= salt_len)
664+
if (chunk->len <= salt_len)
661665
return CRYPTO_NEED_MORE;
662666

663-
memcpy(cipher_ctx->salt, cipher_ctx->chunk->data, salt_len);
667+
memcpy(cipher_ctx->salt, chunk->data + chunk->idx, salt_len);
664668

665669
if (ppbloom_check((void *)cipher_ctx->salt, salt_len) == 1) {
666670
LOGE("crypto: AEAD: repeat salt detected");
@@ -669,38 +673,40 @@ aead_decrypt(buffer_t *ciphertext, cipher_ctx_t *cipher_ctx, size_t capacity)
669673

670674
aead_cipher_ctx_set_key(cipher_ctx, 0);
671675

672-
memmove(cipher_ctx->chunk->data, cipher_ctx->chunk->data + salt_len,
673-
cipher_ctx->chunk->len - salt_len);
674-
cipher_ctx->chunk->len -= salt_len;
676+
chunk->idx += salt_len;
677+
chunk->len -= salt_len;
675678

676679
cipher_ctx->init = 1;
677680
}
678681

679682
size_t plen = 0;
680683
size_t cidx = 0;
681-
while (cipher_ctx->chunk->len > 0) {
682-
size_t chunk_clen = cipher_ctx->chunk->len;
684+
while (chunk->len > 0) {
685+
size_t chunk_clen = chunk->len;
683686
size_t chunk_plen = 0;
684687
err = aead_chunk_decrypt(cipher_ctx,
685-
(uint8_t *)plaintext->data + plen,
686-
(uint8_t *)cipher_ctx->chunk->data + cidx,
688+
(uint8_t *)ciphertext->data + plen,
689+
(uint8_t *)chunk->data + chunk->idx + cidx,
687690
cipher_ctx->nonce, &chunk_plen, &chunk_clen);
688691
if (err == CRYPTO_ERROR) {
689692
return err;
690693
} else if (err == CRYPTO_NEED_MORE) {
691694
if (plen == 0)
692695
return err;
693-
else{
694-
memmove((uint8_t *)cipher_ctx->chunk->data,
695-
(uint8_t *)cipher_ctx->chunk->data + cidx, chunk_clen);
696+
else {
697+
chunk->idx += cidx;
696698
break;
697699
}
698700
}
699-
cipher_ctx->chunk->len = chunk_clen;
701+
chunk->len = chunk_clen;
700702
cidx += cipher_ctx->cipher->tag_len * 2 + CHUNK_SIZE_LEN + chunk_plen;
701-
plen += chunk_plen;
703+
plen += chunk_plen;
702704
}
703-
plaintext->len = plen;
705+
706+
if (chunk->len == 0)
707+
chunk->idx = 0;
708+
709+
ciphertext->len = plen;
704710

705711
// Add the salt to bloom filter
706712
if (cipher_ctx->init == 1) {
@@ -712,10 +718,6 @@ aead_decrypt(buffer_t *ciphertext, cipher_ctx_t *cipher_ctx, size_t capacity)
712718
cipher_ctx->init = 2;
713719
}
714720

715-
brealloc(ciphertext, plaintext->len, capacity);
716-
memcpy(ciphertext->data, plaintext->data, plaintext->len);
717-
ciphertext->len = plaintext->len;
718-
719721
return CRYPTO_OK;
720722
}
721723

src/crypto.h

Lines changed: 12 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -131,6 +131,18 @@ int balloc(buffer_t *, size_t);
131131
int brealloc(buffer_t *, size_t, size_t);
132132
int bprepend(buffer_t *, buffer_t *, size_t);
133133
void bfree(buffer_t *);
134+
135+
static inline void
136+
bswap_data(buffer_t *a, buffer_t *b)
137+
{
138+
char *tmp_data = a->data;
139+
size_t tmp_capacity = a->capacity;
140+
a->data = b->data;
141+
a->capacity = b->capacity;
142+
b->data = tmp_data;
143+
b->capacity = tmp_capacity;
144+
}
145+
134146
int rand_bytes(void *, int);
135147

136148
crypto_t *crypto_init(const char *, const char *, const char *);

src/stream.c

Lines changed: 40 additions & 8 deletions
Original file line numberDiff line numberDiff line change
@@ -344,8 +344,7 @@ stream_encrypt_all(buffer_t *plaintext, cipher_t *cipher, size_t capacity)
344344
dump("NONCE", ciphertext->data, nonce_len);
345345
#endif
346346

347-
brealloc(plaintext, nonce_len + ciphertext->len, capacity);
348-
memcpy(plaintext->data, ciphertext->data, nonce_len + ciphertext->len);
347+
bswap_data(plaintext, ciphertext);
349348
plaintext->len = nonce_len + ciphertext->len;
350349

351350
return CRYPTO_OK;
@@ -359,6 +358,19 @@ stream_encrypt(buffer_t *plaintext, cipher_ctx_t *cipher_ctx, size_t capacity)
359358

360359
cipher_t *cipher = cipher_ctx->cipher;
361360

361+
// In-place fast path for non-Salsa20 ciphers after init.
362+
// mbedtls_cipher_update supports output == input for CFB/CTR stream modes.
363+
if (cipher_ctx->init && cipher->method < SALSA20) {
364+
size_t out_len = plaintext->len;
365+
int err = cipher_ctx_update(cipher_ctx,
366+
(uint8_t *)plaintext->data, &out_len,
367+
(const uint8_t *)plaintext->data, plaintext->len);
368+
if (err)
369+
return CRYPTO_ERROR;
370+
plaintext->len = out_len;
371+
return CRYPTO_OK;
372+
}
373+
362374
static buffer_t tmp = { 0, 0, 0, NULL };
363375

364376
int err = CRYPTO_OK;
@@ -416,8 +428,7 @@ stream_encrypt(buffer_t *plaintext, cipher_ctx_t *cipher_ctx, size_t capacity)
416428
dump("CIPHER", ciphertext->data + nonce_len, ciphertext->len);
417429
#endif
418430

419-
brealloc(plaintext, nonce_len + ciphertext->len, capacity);
420-
memcpy(plaintext->data, ciphertext->data, nonce_len + ciphertext->len);
431+
bswap_data(plaintext, ciphertext);
421432
plaintext->len = nonce_len + ciphertext->len;
422433

423434
return CRYPTO_OK;
@@ -475,8 +486,7 @@ stream_decrypt_all(buffer_t *ciphertext, cipher_t *cipher, size_t capacity)
475486

476487
ppbloom_add((void *)nonce, nonce_len);
477488

478-
brealloc(ciphertext, plaintext->len, capacity);
479-
memcpy(ciphertext->data, plaintext->data, plaintext->len);
489+
bswap_data(ciphertext, plaintext);
480490
ciphertext->len = plaintext->len;
481491

482492
return CRYPTO_OK;
@@ -490,6 +500,29 @@ stream_decrypt(buffer_t *ciphertext, cipher_ctx_t *cipher_ctx, size_t capacity)
490500

491501
cipher_t *cipher = cipher_ctx->cipher;
492502

503+
// In-place fast path for non-Salsa20 ciphers after init.
504+
// mbedtls_cipher_update supports output == input for CFB/CTR stream modes.
505+
if (cipher_ctx->init && cipher->method < SALSA20) {
506+
if (ciphertext->len <= 0)
507+
return CRYPTO_NEED_MORE;
508+
size_t out_len = ciphertext->len;
509+
int err = cipher_ctx_update(cipher_ctx,
510+
(uint8_t *)ciphertext->data, &out_len,
511+
(const uint8_t *)ciphertext->data, ciphertext->len);
512+
if (err)
513+
return CRYPTO_ERROR;
514+
ciphertext->len = out_len;
515+
if (cipher_ctx->init == 1 && cipher->method >= RC4_MD5) {
516+
if (ppbloom_check((void *)cipher_ctx->nonce, cipher->nonce_len) == 1) {
517+
LOGE("crypto: stream: repeat IV detected");
518+
return CRYPTO_ERROR;
519+
}
520+
ppbloom_add((void *)cipher_ctx->nonce, cipher->nonce_len);
521+
cipher_ctx->init = 2;
522+
}
523+
return CRYPTO_OK;
524+
}
525+
493526
static buffer_t tmp = { 0, 0, 0, NULL };
494527

495528
int err = CRYPTO_OK;
@@ -585,8 +618,7 @@ stream_decrypt(buffer_t *ciphertext, cipher_ctx_t *cipher_ctx, size_t capacity)
585618
}
586619
}
587620

588-
brealloc(ciphertext, plaintext->len, capacity);
589-
memcpy(ciphertext->data, plaintext->data, plaintext->len);
621+
bswap_data(ciphertext, plaintext);
590622
ciphertext->len = plaintext->len;
591623

592624
return CRYPTO_OK;

0 commit comments

Comments
 (0)