Skip to content

Commit 76e91b6

Browse files
committed
FEAT: new native Chacha20Poly1305
1 parent ff94657 commit 76e91b6

File tree

5 files changed

+601
-68
lines changed

5 files changed

+601
-68
lines changed

src/core/n-crypt.c

+219-35
Original file line numberDiff line numberDiff line change
@@ -676,56 +676,71 @@ typedef struct {
676676
/*
677677
// chacha20: native [
678678
// "Encrypt/decrypt data using ChaCha20 algorithm. Returns stream cipher context handle or encrypted/decrypted data."
679-
// /key "Provided only for the first time to get stream HANDLE!"
680-
// crypt-key [binary!] "Crypt key (16 or 32 bytes)."
681-
// nonce [binary!] "Initialization nonce (8 bytes)."
679+
// ctx [handle! binary!] "ChaCha20 handle and or binary key for initialization (16 or 32 bytes)"
680+
// /init
681+
// nonce [binary!] "Initialization nonce (IV) - 8 or 12 bytes."
682682
// count [integer!] "A 32-bit block count parameter"
683+
// /aad sequence [integer!] "Sequence number used with /init to modify nonce"
683684
// /stream
684-
// ctx [handle!] "Stream cipher context."
685685
// data [binary!] "Data to encrypt/decrypt."
686686
// /into
687687
// out [binary!] "Output buffer (NOT YET IMPLEMENTED)"
688688
// ]
689689
***********************************************************************/
690690
{
691-
REBOOL ref_key = D_REF(1);
692-
REBVAL *val_crypt_key = D_ARG(2);
691+
REBVAL *val_ctx = D_ARG(1);
692+
REBOOL ref_init = D_REF(2);
693693
REBVAL *val_nonce = D_ARG(3);
694-
REBVAL *val_count = D_ARG(4);
695-
REBOOL ref_stream = D_REF(5);
696-
REBVAL *val_ctx = D_ARG(6);
697-
REBVAL *val_data = D_ARG(7);
698-
REBOOL ref_into = D_REF(8);
699-
700-
REBVAL *ret = D_RET;
694+
REBVAL *val_counter = D_ARG(4);
695+
REBOOL ref_aad = D_REF(5);
696+
REBVAL *val_sequence = D_ARG(6);
697+
REBOOL ref_stream = D_REF(7);
698+
REBVAL *val_data = D_ARG(8);
699+
REBOOL ref_into = D_REF(9);
700+
701701
REBSER *ctx_ser;
702702
REBINT len;
703+
REBU64 sequence;
703704

704-
if (ref_key) {
705-
//key defined - setup new context
706-
707-
len = VAL_LEN(val_crypt_key);
708-
709-
if (len != 16 && len != 32 && VAL_LEN(val_nonce) != 8) {
705+
if (IS_BINARY(val_ctx)) {
706+
len = VAL_LEN(val_ctx);
707+
if (!(len == 32 || len == 16)) {
708+
printf("ChaCha20 key must be of size 32 or 16 bytes! Is: %i\n", len);
709+
Trap1(RE_INVALID_DATA, val_ctx);
710710
return R_NONE;
711711
}
712-
713712
//making series from POOL so it will be GCed automaticaly
714713
ctx_ser = Make_Series(sizeof(chacha20_ctx), (REBCNT)1, FALSE);
714+
715+
chacha20_keysetup((chacha20_ctx*)ctx_ser->data, VAL_BIN_AT(val_ctx), len);
716+
715717
SERIES_TAIL(ctx_ser) = sizeof(chacha20_ctx);
718+
SET_HANDLE(val_ctx, ctx_ser, SYM_CHACHA20, HANDLE_SERIES);
719+
// the ctx_ser in the handle is released by GC once the handle is not referenced
720+
}
721+
else {
722+
ctx_ser = VAL_HANDLE_DATA(val_ctx);
723+
if (VAL_HANDLE_TYPE(val_ctx) != SYM_CHACHA20 || ctx_ser == NULL || SERIES_TAIL(ctx_ser) != sizeof(chacha20_ctx)){
724+
Trap0(RE_INVALID_HANDLE);
725+
}
726+
}
716727

717-
chacha20_setup(
718-
(chacha20_ctx*)ctx_ser->data,
719-
VAL_BIN_AT(val_crypt_key),
720-
len,
721-
VAL_BIN_AT(val_nonce)
722-
);
723-
chacha20_counter_set((chacha20_ctx*)ctx_ser->data, VAL_INT64(val_count));
728+
if (ref_init) {
729+
// initialize nonce with counter
730+
731+
len = VAL_LEN(val_nonce);
724732

725-
SET_HANDLE(ret, ctx_ser, SYM_CHACHA20, HANDLE_SERIES);
726-
// the ctx in the handle is released by GC once the handle is not referenced
733+
if (!(len == 12 || len == 8)) {
734+
Trap1(RE_INVALID_DATA, val_nonce);
735+
return R_NONE;
736+
}
727737

728-
} else if(ref_stream) {
738+
sequence = (ref_aad) ? VAL_INT64(val_sequence) : 0;
739+
chacha20_ivsetup((chacha20_ctx*)ctx_ser->data, VAL_BIN_AT(val_nonce), len, VAL_INT64(val_counter), (u8 *)&sequence);
740+
741+
}
742+
743+
if (ref_stream) {
729744

730745
ctx_ser = VAL_HANDLE_DATA(val_ctx);
731746

@@ -746,11 +761,11 @@ typedef struct {
746761
len
747762
);
748763

749-
SET_BINARY(ret, binaryOut);
750-
VAL_TAIL(ret) = len;
764+
SET_BINARY(val_ctx, binaryOut);
765+
VAL_TAIL(val_ctx) = len;
751766

752767
}
753-
return R_RET;
768+
return R_ARG1;
754769
}
755770

756771

@@ -777,7 +792,7 @@ typedef struct {
777792
REBVAL *val_mac = D_ARG(6);
778793

779794
REBVAL *ret = D_RET;
780-
REBSER *ctx_ser, *bin;
795+
REBSER *ctx_ser;
781796
REBINT len;
782797
REBCNT i;
783798
REBYTE mac[16];
@@ -824,4 +839,173 @@ typedef struct {
824839
}
825840

826841
return R_ARG1;
827-
}
842+
}
843+
844+
/***********************************************************************
845+
**
846+
*/ REBNATIVE(chacha20poly1305)
847+
/*
848+
// chacha20poly1305: native [
849+
// "..."
850+
// ctx [none! handle!]
851+
// /init
852+
// local-key [binary!]
853+
// local-iv [binary!]
854+
// remote-key [binary!]
855+
// remote-iv [binary!]
856+
// /encrypt
857+
// data-out [binary!]
858+
// aad-out [binary!]
859+
// /decrypt
860+
// data-in [binary!]
861+
// aad-in [binary!]
862+
// ]
863+
***********************************************************************/
864+
{
865+
REBVAL *val_ctx = D_ARG(1);
866+
REBOOL ref_init = D_REF(2);
867+
REBVAL *val_local_key = D_ARG(3);
868+
REBVAL *val_local_iv = D_ARG(4);
869+
REBVAL *val_remote_key = D_ARG(5);
870+
REBVAL *val_remote_iv = D_ARG(6);
871+
REBOOL ref_encrypt = D_REF(7);
872+
REBVAL *val_plain = D_ARG(8);
873+
REBVAL *val_local_aad = D_ARG(9);
874+
REBOOL ref_decrypt = D_REF(10);
875+
REBVAL *val_cipher = D_ARG(11);
876+
REBVAL *val_remote_aad = D_ARG(12);
877+
878+
REBSER *ctx_ser;
879+
REBINT len;
880+
chacha20poly1305_ctx *chacha;
881+
unsigned char poly1305_key[POLY1305_KEYLEN];
882+
size_t aad_size;
883+
884+
if (ref_init) {
885+
ctx_ser = Make_Series(sizeof(chacha20poly1305_ctx), (REBCNT)1, FALSE);
886+
SERIES_TAIL(ctx_ser) = sizeof(chacha20poly1305_ctx);
887+
SET_HANDLE(val_ctx, ctx_ser, SYM_CHACHA20POLY1305, HANDLE_SERIES);
888+
//SET_BINARY(val_ctx, ctx_ser);
889+
890+
chacha = (chacha20poly1305_ctx*)ctx_ser->data;
891+
len = VAL_LEN(val_local_key);
892+
if (!(len == 32 || len == 16))
893+
Trap1(RE_INVALID_DATA, val_local_key);
894+
chacha20_keysetup(&chacha->local_chacha, VAL_BIN_AT(val_local_key), len);
895+
len = VAL_LEN(val_remote_key);
896+
if (!(len == 32 || len == 16))
897+
Trap1(RE_INVALID_DATA, val_remote_key);
898+
chacha20_keysetup(&chacha->remote_chacha, VAL_BIN_AT(val_remote_key), len);
899+
900+
chacha->local_sequence = 0;
901+
chacha->remote_sequence = 0;
902+
903+
len = VAL_LEN(val_local_iv);
904+
if (!(len == 12 || len == 8))
905+
Trap1(RE_INVALID_DATA, val_local_iv);
906+
chacha20_ivsetup(&chacha->local_chacha, VAL_BIN_AT(val_local_iv), len, 1, (u8 *)&chacha->local_sequence);
907+
memcpy(chacha->local_iv, VAL_BIN_AT(val_local_iv), len);
908+
909+
len = VAL_LEN(val_remote_iv);
910+
if (!(len == 12 || len == 8))
911+
Trap1(RE_INVALID_DATA, val_remote_iv);
912+
chacha20_ivsetup(&chacha->remote_chacha, VAL_BIN_AT(val_remote_iv), len, 1, (u8 *)&chacha->remote_sequence);
913+
memcpy(chacha->remote_iv, VAL_BIN_AT(val_remote_iv), len);
914+
return R_ARG1;
915+
}
916+
917+
ctx_ser = VAL_HANDLE_DATA(val_ctx);
918+
if (VAL_HANDLE_TYPE(val_ctx) != SYM_CHACHA20POLY1305 || ctx_ser == NULL || SERIES_TAIL(ctx_ser) != sizeof(chacha20poly1305_ctx)){
919+
Trap0(RE_INVALID_HANDLE);
920+
return R_NONE;
921+
}
922+
chacha = (chacha20poly1305_ctx*)ctx_ser->data;
923+
924+
if (ref_encrypt) {
925+
chacha20_ivsetup(&chacha->local_chacha, chacha->local_iv, 12, 1, (u8 *)&chacha->local_sequence);
926+
chacha20_poly1305_key(&chacha->local_chacha, poly1305_key);
927+
//puts("poly1305_key:"); Dump_Bytes(poly1305_key, POLY1305_KEYLEN);
928+
929+
len = VAL_LEN(val_plain) + POLY1305_TAGLEN;
930+
ctx_ser = Make_Series(len, (REBCNT)1, FALSE);
931+
932+
// AEAD
933+
// sequence number (8 bytes)
934+
// content type (1 byte)
935+
// version (2 bytes)
936+
// length (2 bytes)
937+
unsigned char aad[13];
938+
aad_size = sizeof(aad);
939+
unsigned char *sequence = aad;
940+
941+
chacha20_poly1305_aead(&chacha->local_chacha, VAL_BIN_AT(val_plain), (REBCNT)len-POLY1305_TAGLEN, VAL_BIN_AT(val_local_aad), VAL_LEN(val_local_aad), poly1305_key, ctx_ser->data);
942+
943+
//chacha->local_sequence++;
944+
945+
SERIES_TAIL(ctx_ser) = len;
946+
SET_BINARY(val_ctx, ctx_ser);
947+
return R_ARG1;
948+
}
949+
950+
if (ref_decrypt) {
951+
static unsigned char zeropad[] = {0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0};
952+
unsigned char trail[16];
953+
unsigned char mac_tag[POLY1305_TAGLEN];
954+
955+
chacha20_ivsetup(&chacha->remote_chacha, chacha->remote_iv, 12, 1, VAL_BIN_AT(val_remote_aad));
956+
957+
len = VAL_LEN(val_cipher) - POLY1305_TAGLEN;
958+
if (len <= 0)
959+
return R_NONE;
960+
961+
ctx_ser = Make_Series(len, (REBCNT)1, FALSE);
962+
963+
//puts("\nDECRYPT:");
964+
965+
chacha20_encrypt(&chacha->remote_chacha, VAL_BIN_AT(val_cipher), ctx_ser->data, len);
966+
//chacha_encrypt_bytes(&chacha->remote_chacha, VAL_BIN_AT(val_cipher), ctx_ser->data, len);
967+
chacha20_poly1305_key(&chacha->remote_chacha, poly1305_key);
968+
//puts("poly1305_key:"); Dump_Bytes(poly1305_key, POLY1305_KEYLEN);
969+
970+
poly1305_context aead_ctx;
971+
poly1305_init(&aead_ctx, poly1305_key);
972+
973+
aad_size = VAL_LEN(val_remote_aad);
974+
poly1305_update(&aead_ctx, VAL_BIN_AT(val_remote_aad), aad_size);
975+
int rem = aad_size % 16;
976+
if (rem)
977+
poly1305_update(&aead_ctx, zeropad, 16 - rem);
978+
//puts("update:"); Dump_Bytes(VAL_BIN_AT(val_cipher), len);
979+
poly1305_update(&aead_ctx, VAL_BIN_AT(val_cipher), len);
980+
rem = len % 16;
981+
if (rem)
982+
poly1305_update(&aead_ctx, zeropad, 16 - rem);
983+
984+
U32TO8_LE(&trail[0], aad_size == 5 ? 5 : 13);
985+
*(int *)&trail[4] = 0;
986+
U32TO8_LE(&trail[8], len);
987+
*(int *)&trail[12] = 0;
988+
989+
//puts("trail:"); Dump_Bytes(trail, 16);
990+
991+
poly1305_update(&aead_ctx, trail, 16);
992+
poly1305_finish(&aead_ctx, mac_tag);
993+
994+
if (!poly1305_verify(mac_tag, VAL_BIN_TAIL(val_cipher) - POLY1305_TAGLEN)) {
995+
puts("MAC verification failed!");
996+
}
997+
else {
998+
puts("MAC OK!");
999+
}
1000+
1001+
//puts("mac result:"); Dump_Bytes(mac_tag, POLY1305_TAGLEN);
1002+
1003+
chacha->remote_sequence++;
1004+
1005+
1006+
1007+
SERIES_TAIL(ctx_ser) = len;
1008+
SET_BINARY(val_ctx, ctx_ser);
1009+
}
1010+
return R_ARG1;
1011+
}

0 commit comments

Comments
 (0)