@@ -745,8 +745,75 @@ std::string compute_message_hash(
745745 decoded_data);
746746}
747747
748+ std::vector<unsigned char > encrypt_xchacha20 (
749+ std::span<const unsigned char > plaintext, std::span<const unsigned char > enc_key) {
750+ if (enc_key.size () != 32 )
751+ throw std::invalid_argument{" Invalid enc_key: expected 32 bytes" };
752+
753+ std::vector<unsigned char > ciphertext;
754+ ciphertext.resize (
755+ crypto_aead_xchacha20poly1305_ietf_NPUBBYTES + plaintext.size () +
756+ crypto_aead_xchacha20poly1305_ietf_ABYTES);
757+
758+ // Generate random nonce, and stash it at the beginning of ciphertext:
759+ randombytes_buf (ciphertext.data (), crypto_aead_xchacha20poly1305_ietf_NPUBBYTES);
760+
761+ auto * c = reinterpret_cast <unsigned char *>(ciphertext.data ()) +
762+ crypto_aead_xchacha20poly1305_ietf_NPUBBYTES;
763+ unsigned long long clen;
764+
765+ crypto_aead_xchacha20poly1305_ietf_encrypt (
766+ c,
767+ &clen,
768+ plaintext.data (),
769+ plaintext.size (),
770+ nullptr ,
771+ 0 , // additional data
772+ nullptr , // nsec (always unused)
773+ reinterpret_cast <const unsigned char *>(ciphertext.data ()),
774+ enc_key.data ());
775+ assert (crypto_aead_xchacha20poly1305_ietf_NPUBBYTES + clen <= ciphertext.size ());
776+ ciphertext.resize (crypto_aead_xchacha20poly1305_ietf_NPUBBYTES + clen);
777+ return ciphertext;
778+ }
779+
780+ std::vector<unsigned char > decrypt_xchacha20 (
781+ std::span<const unsigned char > ciphertext, std::span<const unsigned char > enc_key) {
782+ if (ciphertext.size () <
783+ crypto_aead_xchacha20poly1305_ietf_NPUBBYTES + crypto_aead_xchacha20poly1305_ietf_ABYTES)
784+ throw std::invalid_argument{
785+ " Invalid ciphertext: too short to contain valid encrypted data" };
786+ if (enc_key.size () != 32 )
787+ throw std::invalid_argument{" Invalid enc_key: expected 32 bytes" };
788+
789+ // Extract nonce from the beginning of the ciphertext:
790+ auto nonce = ciphertext.subspan (0 , crypto_aead_xchacha20poly1305_ietf_NPUBBYTES);
791+ ciphertext = ciphertext.subspan (nonce.size ());
792+
793+ std::vector<unsigned char > plaintext;
794+ plaintext.resize (ciphertext.size () - crypto_aead_xchacha20poly1305_ietf_ABYTES);
795+ auto * m = reinterpret_cast <unsigned char *>(plaintext.data ());
796+ unsigned long long mlen;
797+ if (0 != crypto_aead_xchacha20poly1305_ietf_decrypt (
798+ m,
799+ &mlen,
800+ nullptr , // nsec (always unused)
801+ ciphertext.data (),
802+ ciphertext.size (),
803+ nullptr ,
804+ 0 , // additional data
805+ nonce.data (),
806+ enc_key.data ()))
807+ throw std::runtime_error{" Could not decrypt (XChaCha20-Poly1305)" };
808+ assert (mlen <= plaintext.size ());
809+ plaintext.resize (mlen);
810+ return plaintext;
811+ }
812+
748813} // namespace session
749814
815+ extern " C" {
816+
750817using namespace session ;
751818
752819LIBSESSION_C_API bool session_encrypt_for_recipient_deterministic (
@@ -925,3 +992,45 @@ LIBSESSION_C_API bool session_compute_message_hash(
925992 return false ;
926993 }
927994}
995+
996+ LIBSESSION_C_API bool session_encrypt_xchacha20 (
997+ const unsigned char * plaintext_in,
998+ size_t plaintext_len,
999+ const unsigned char * enc_key_in,
1000+ unsigned char ** ciphertext_out,
1001+ size_t * ciphertext_len) {
1002+ try {
1003+ auto ciphertext = session::encrypt_xchacha20 (
1004+ std::span<const unsigned char >{plaintext_in, plaintext_len},
1005+ std::span<const unsigned char >{enc_key_in, 32 });
1006+
1007+ *ciphertext_out = static_cast <unsigned char *>(malloc (ciphertext.size ()));
1008+ *ciphertext_len = ciphertext.size ();
1009+ std::memcpy (*ciphertext_out, ciphertext.data (), ciphertext.size ());
1010+ return true ;
1011+ } catch (...) {
1012+ return false ;
1013+ }
1014+ }
1015+
1016+ LIBSESSION_C_API bool session_decrypt_xchacha20 (
1017+ const unsigned char * ciphertext_in,
1018+ size_t ciphertext_len,
1019+ const unsigned char * enc_key_in,
1020+ unsigned char ** plaintext_out,
1021+ size_t * plaintext_len) {
1022+ try {
1023+ auto plaintext = session::decrypt_xchacha20 (
1024+ std::span<const unsigned char >{ciphertext_in, ciphertext_len},
1025+ std::span<const unsigned char >{enc_key_in, 32 });
1026+
1027+ *plaintext_out = static_cast <unsigned char *>(malloc (plaintext.size ()));
1028+ *plaintext_len = plaintext.size ();
1029+ std::memcpy (*plaintext_out, plaintext.data (), plaintext.size ());
1030+ return true ;
1031+ } catch (...) {
1032+ return false ;
1033+ }
1034+ }
1035+
1036+ } // extern "C"
0 commit comments