Parcourir la source

Port cryptography providers onto Locksmith

SigningProvider and EncryptionProvider now hold Locksmith Signing/Sealing KeyPair objects instead of raw sodium byte buffers. Public signatures are unchanged except with_keys, which now throws Locksmith.Error because the keypairs are built from the SECRET key (Ed25519 sk is seed||pk, so the public half is derived deterministically); a configured public key that disagrees with the derived one prints a loud warning while the derived key is trusted. verify_with_key returns false on a malformed key instead of verifying nothing loudly. Random ids (channel id, slot id) come from Locksmith.Random.bytes. The Statum.vala factories catch Locksmith.Error and degrade gracefully to ephemeral keys with a distinct warning, consistent with the existing no-static-keys path.
saddle-ClankerSaddle il y a 1 jour
Parent
commit
c15b46a78c

+ 1 - 2
src/ChannelEndpoint.vala

@@ -175,8 +175,7 @@ namespace Statum {
         }
 
         private static string generate_channel_id() {
-            var bytes = new uint8[16];
-            Sodium.Random.random_bytes(bytes);
+            var bytes = Locksmith.Random.bytes(16).to_array();
             var builder = new StringBuilder();
             foreach (var b in bytes) {
                 builder.append_printf("%02x", b);

+ 29 - 26
src/Cryptography/EncryptionProvider.vala

@@ -22,7 +22,7 @@ namespace Statum.Cryptography {
      * {@link Statum.Model.ActionDto.private}).
      *
      * Data is "only readable by the application server": payloads are serialised
-     * to JSON, signed (Ed25519) and sealed (X25519-Seal) using libsodium, then
+     * to JSON, signed (Ed25519) and sealed (X25519-Seal) using Locksmith, then
      * returned as a base64 string suitable for embedding in a DTO. The reverse
      * operation unseals, verifies the signature and re-parses the JSON.
      *
@@ -36,32 +36,34 @@ namespace Statum.Cryptography {
         /** Default namespace used when none is supplied. */
         public const string DEFAULT_NAMESPACE = "statum";
 
-        private uint8[] signing_secret_key;
-        private uint8[] signing_public_key;
-        private uint8[] sealing_secret_key;
-        private uint8[] sealing_public_key;
+        private Locksmith.Signing.KeyPair signing;
+        private Locksmith.Sealing.KeyPair sealing;
 
         construct {
-            signing_secret_key = new uint8[Sodium.Asymmetric.Signing.SECRET_KEY_BYTES];
-            signing_public_key = new uint8[Sodium.Asymmetric.Signing.PUBLIC_KEY_BYTES];
-            Sodium.Asymmetric.Signing.generate_keypair(signing_public_key, signing_secret_key);
-
-            sealing_secret_key = new uint8[Sodium.Asymmetric.Sealing.SECRET_KEY_BYTES];
-            sealing_public_key = new uint8[Sodium.Asymmetric.Sealing.PUBLIC_KEY_BYTES];
-            Sodium.Asymmetric.Sealing.generate_keypair(sealing_public_key, sealing_secret_key);
+            signing = Locksmith.Signing.KeyPair.generate();
+            sealing = Locksmith.Sealing.KeyPair.generate();
         }
 
         /**
          * Constructs the provider with explicit key pairs (e.g. read from static
-         * configuration so private blobs survive a restart).
+         * configuration so private blobs survive a restart). Each pair is built
+         * from its secret key, so the public halves are derived
+         * deterministically; a configured public key that disagrees with the
+         * derived one triggers a loud warning (config error) while the derived
+         * key — the one that actually signs and unseals — is trusted.
+         *
+         * @throws Locksmith.Error.KEY_FORMAT if either secret key has the wrong
+         *         length.
          */
         public EncryptionProvider.with_keys(
                 uint8[] signing_secret_key, uint8[] signing_public_key,
-                uint8[] sealing_secret_key, uint8[] sealing_public_key) {
-            this.signing_secret_key = signing_secret_key;
-            this.signing_public_key = signing_public_key;
-            this.sealing_secret_key = sealing_secret_key;
-            this.sealing_public_key = sealing_public_key;
+                uint8[] sealing_secret_key, uint8[] sealing_public_key) throws Locksmith.Error {
+            signing = new Locksmith.Signing.KeyPair.from_byte_array(signing_secret_key);
+            warn_on_public_key_mismatch("encryption signing",
+                signing_public_key, signing.public_key.key.to_array());
+            sealing = new Locksmith.Sealing.KeyPair.from_byte_array(sealing_secret_key);
+            warn_on_public_key_mismatch("encryption sealing",
+                sealing_public_key, sealing.public_key.key.to_array());
         }
 
         /**
@@ -89,9 +91,9 @@ namespace Statum.Cryptography {
          */
         public string author_string(string namespace, string data) {
             var namespaced = @"$namespace:$data";
-            var signed = Sodium.Asymmetric.Signing.sign(namespaced.data, signing_secret_key);
-            var sealed = Sodium.Asymmetric.Sealing.seal(signed, sealing_public_key);
-            return Base64.encode(sealed);
+            var signed = signing.sign(new ByteBuffer.from_byte_array(namespaced.data));
+            var sealed = sealing.public_key.seal(signed);
+            return Base64.encode(sealed.to_array());
         }
 
         /**
@@ -131,18 +133,19 @@ namespace Statum.Cryptography {
          *         namespace validation fails.
          */
         public string read_string(string namespace, string blob) throws EncryptionError {
-            var bytes = Base64.decode(blob);
-            var signed = Sodium.Asymmetric.Sealing.unseal(bytes, sealing_public_key, sealing_secret_key);
-            if (signed == null) {
+            BinaryData signed;
+            try {
+                signed = sealing.unseal(new ByteBuffer.from_byte_array(Base64.decode(blob)));
+            } catch (Locksmith.Error e) {
                 throw new EncryptionError.DECRYPTION_FAILED("Could not decrypt private blob");
             }
 
-            var cleartext = Sodium.Asymmetric.Signing.verify((!)signed, signing_public_key);
+            var cleartext = signing.public_key.verify(signed);
             if (cleartext == null) {
                 throw new EncryptionError.SIGNATURE_INVALID("Signature verification failed for private blob");
             }
 
-            var namespaced = Wrap.byte_array((!)cleartext).to_raw_string();
+            var namespaced = ((!)cleartext).to_raw_string();
             var prefix = @"$namespace:";
             if (!namespaced.has_prefix(prefix)) {
                 throw new EncryptionError.NAMESPACE_MISMATCH(@"Expected namespace \"$namespace\" but the blob carries a different namespace");

+ 62 - 18
src/Cryptography/SigningProvider.vala

@@ -1,4 +1,5 @@
 using Statum.Model;
+using Invercargill.DataStructures;
 
 namespace Statum.Cryptography {
 
@@ -12,7 +13,7 @@ namespace Statum.Cryptography {
 
     /**
      * Signs and verifies the integrity of Statum {@link FrameDto}s using
-     * detached Ed25519 signatures (libsodium `crypto_sign_detached`).
+     * detached Ed25519 signatures (Locksmith `Signing.KeyPair.sign_detached`).
      *
      * A frame's {@link FrameDto.signature} is a base64-encoded detached
      * signature over its {@link FrameDto.content}, produced by the signer
@@ -32,8 +33,7 @@ namespace Statum.Cryptography {
      */
     public class SigningProvider : GLib.Object {
 
-        private uint8[] secret_key;
-        private uint8[] public_key;
+        private Locksmith.Signing.KeyPair keypair;
 
         /**
          * The signer id written into the {@link Frame.signer} field of frames
@@ -42,10 +42,8 @@ namespace Statum.Cryptography {
         public string signer_id { get; set; }
 
         construct {
-            secret_key = new uint8[Sodium.Asymmetric.Signing.SECRET_KEY_BYTES];
-            public_key = new uint8[Sodium.Asymmetric.Signing.PUBLIC_KEY_BYTES];
-            Sodium.Asymmetric.Signing.generate_keypair(public_key, secret_key);
-            signer_id = Base64.encode(public_key);
+            keypair = Locksmith.Signing.KeyPair.generate();
+            signer_id = keypair.public_key.to_base64();
         }
 
         /**
@@ -56,8 +54,8 @@ namespace Statum.Cryptography {
          *         {@link FrameDto.signature}.
          */
         public string sign(string content) {
-            var signature = Sodium.Asymmetric.Signing.sign_detached(content.data, secret_key);
-            return Base64.encode(signature);
+            var signature = keypair.sign_detached(new ByteBuffer.from_byte_array(content.data));
+            return Base64.encode(signature.to_array());
         }
 
         /**
@@ -67,18 +65,28 @@ namespace Statum.Cryptography {
          * @return `true` if the signature is valid for `content`.
          */
         public bool verify(string content, string signature_b64) {
-            return verify_with_key(content, signature_b64, public_key);
+            return verify_with_key(content, signature_b64, get_public_key());
         }
 
         /**
          * Verifies a base64-encoded detached signature against `content` using
          * an arbitrary Ed25519 public key (e.g. the key of a different signer).
          *
+         * A malformed verification key (wrong length) verifies nothing.
+         *
          * @return `true` if the signature is valid for `content`.
          */
         public bool verify_with_key(string content, string signature_b64, uint8[] verification_public_key) {
+            Locksmith.Signing.PublicKey verifier;
+            try {
+                verifier = new Locksmith.Signing.PublicKey.from_byte_array(verification_public_key);
+            } catch (Locksmith.Error e) {
+                return false;
+            }
             var signature = Base64.decode(signature_b64);
-            return Sodium.Asymmetric.Signing.verify_detached(signature, content.data, verification_public_key);
+            return verifier.verify_detached(
+                new ByteBuffer.from_byte_array(content.data),
+                new ByteBuffer.from_byte_array(signature));
         }
 
         /**
@@ -121,13 +129,20 @@ namespace Statum.Cryptography {
 
         /**
          * Constructs the provider with an explicit key pair (e.g. read from
-         * static configuration so frames survive a restart). The `signer_id` is
-         * derived from the public key.
+         * static configuration so frames survive a restart). The keypair is
+         * built from the secret key — an Ed25519 secret key is seed‖pk, so the
+         * public half is derived deterministically. If the supplied public key
+         * does not match the derived one, the pair disagrees with the secret
+         * (a config error) and a loud warning is printed; the *derived* key is
+         * what signs and verifies, so it is trusted.
+         *
+         * @throws Locksmith.Error.KEY_FORMAT if `secret_key` has the wrong
+         *         length for an Ed25519 secret key.
          */
-        public SigningProvider.with_keys(uint8[] secret_key, uint8[] public_key) {
-            this.secret_key = secret_key;
-            this.public_key = public_key;
-            signer_id = Base64.encode(public_key);
+        public SigningProvider.with_keys(uint8[] secret_key, uint8[] public_key) throws Locksmith.Error {
+            keypair = new Locksmith.Signing.KeyPair.from_byte_array(secret_key);
+            warn_on_public_key_mismatch("signing", public_key, keypair.public_key.key.to_array());
+            signer_id = keypair.public_key.to_base64();
         }
 
         /**
@@ -135,9 +150,38 @@ namespace Statum.Cryptography {
          * secret key, for distribution to verifiers.
          */
         public uint8[] get_public_key() {
-            return public_key;
+            return keypair.public_key.key.to_array();
         }
 
     }
 
+    /**
+     * Prints a loud warning when a configured public key does not match the
+     * one derived from the configured secret key: the `web-config.json`
+     * key pair is internally inconsistent, and the derived half is what
+     * the provider actually uses.
+     */
+    internal void warn_on_public_key_mismatch(string purpose, uint8[] configured_pk, uint8[] derived_pk) {
+        var differs = configured_pk.length != derived_pk.length;
+        if (!differs) {
+            for (int i = 0; i < derived_pk.length; i++) {
+                if (configured_pk[i] != derived_pk[i]) {
+                    differs = true;
+                    break;
+                }
+            }
+        }
+        if (!differs) {
+            return;
+        }
+        warning("[Statum] ────────────────────────────────────────────────────────────────────\n"
+            + @"[Statum]  Configured $purpose public key disagrees with the public key\n"
+            + "[Statum]  derived from the configured secret key:\n"
+            + @"[Statum]    configured: $(Base64.encode(configured_pk)) ($(configured_pk.length) bytes)\n"
+            + @"[Statum]    derived:    $(Base64.encode(derived_pk)) ($(derived_pk.length) bytes)\n"
+            + "[Statum]  The DERIVED public key is in use; fix web-config.json\n"
+            + "[Statum]  (regenerate with `statum-genkeys --out web-config.json`).\n"
+            + "[Statum] ────────────────────────────────────────────────────────────────────");
+    }
+
 }

+ 1 - 2
src/StateService.vala

@@ -37,8 +37,7 @@ namespace Statum {
         private Dictionary<string, Slot> slots = new Dictionary<string, Slot>();
 
         public Slot new_slot(Scope slot_scope, State initial_state) {
-            var id = new uint8[32];
-            Sodium.Random.random_bytes(id);
+            var id = Locksmith.Random.bytes(32).to_array();
 
             initial_state.last_touched = new DateTime.now_utc();
             var slot = new Slot() {

+ 26 - 4
src/Statum.vala

@@ -101,7 +101,11 @@ namespace Statum {
                 var sk = ((!)section).get_string("signing_secret_key");
                 var pk = ((!)section).get_string("signing_public_key");
                 if (sk.length > 0 && pk.length > 0) {
-                    return new Cryptography.SigningProvider.with_keys(Base64.decode(sk), Base64.decode(pk));
+                    try {
+                        return new Cryptography.SigningProvider.with_keys(Base64.decode(sk), Base64.decode(pk));
+                    } catch (Locksmith.Error e) {
+                        warn_on_malformed_keys("signing key", e);
+                    }
                 }
             }
             warning("[Statum] ────────────────────────────────────────────────────────────────────\n"
@@ -125,9 +129,13 @@ namespace Statum {
                 var esk = s.get_string("encryption_sealing_secret_key");
                 var epk = s.get_string("encryption_sealing_public_key");
                 if (ssk.length > 0 && spk.length > 0 && esk.length > 0 && epk.length > 0) {
-                    return new Cryptography.EncryptionProvider.with_keys(
-                        Base64.decode(ssk), Base64.decode(spk),
-                        Base64.decode(esk), Base64.decode(epk));
+                    try {
+                        return new Cryptography.EncryptionProvider.with_keys(
+                            Base64.decode(ssk), Base64.decode(spk),
+                            Base64.decode(esk), Base64.decode(epk));
+                    } catch (Locksmith.Error e) {
+                        warn_on_malformed_keys("encryption keys", e);
+                    }
                 }
             }
             warning("[Statum] ────────────────────────────────────────────────────────────────────\n"
@@ -142,6 +150,20 @@ namespace Statum {
             return new Cryptography.EncryptionProvider();
         }
 
+        /**
+         * Graceful degradation for malformed static keys (wrong length): the
+         * provider falls back to EPHEMERAL keys so the process still boots,
+         * loudly.
+         */
+        private static void warn_on_malformed_keys(string what, Locksmith.Error e) {
+            warning("[Statum] ────────────────────────────────────────────────────────────────────\n"
+                + @"[Statum]  Configured $what in web-config.json are malformed ($(e.message)).\n"
+                + "[Statum]  Falling back to EPHEMERAL keys — client-held frames and\n"
+                + "[Statum]  private blobs will NOT survive a restart.\n"
+                + "[Statum]  Run `statum-genkeys --out web-config.json` to regenerate.\n"
+                + "[Statum] ────────────────────────────────────────────────────────────────────");
+        }
+
         private static Astralis.WebConfigSection? read_statum_config(Inversion.Scope scope) {
             try {
                 var config = scope.resolve<Astralis.WebConfig>();

+ 0 - 335
vapi/libsodium.vapi

@@ -1,335 +0,0 @@
-/* Vala Bindings for LibSodium
- * Copyright (c) 2020 Billy Barrow <billyb@pcthingz.com>
- *
- * Permission to use, copy, modify, and/or distribute this software for any
- * purpose with or without fee is hereby granted, provided that the above
- * copyright notice and this permission notice appear in all copies.
- *
- * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES
- * WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF
- * MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR
- * ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES
- * WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN
- * ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF
- * OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
- */
-
-
- [CCode (cheader_filename = "sodium.h", lower_case_cprefix = "sodium_")]
- namespace Sodium {
- 
-   namespace Random {
-     [CCode (cname = "randombytes_SEEDBYTES")]
-     public const size_t SEED_BYTES;
-   
-     [CCode (cname = "randombytes_random")]
-     public uint32 random();
-   
-     [CCode (cname = "randombytes_uniform")]
-     public uint32 random_uniform(uint32 upper_bound);
-   
-     [CCode (cname = "randombytes_buf")]
-     public void random_bytes(uint8[] buffer);
-   
-     [CCode (cname = "randombytes_buf_deterministic")]
-     public void random_bytes_deterministic(uint8[] buffer, uint8[] seed);
-   }
- 
-   namespace Symmetric {
-     [CCode (cname = "crypto_secretbox_KEYBYTES")]
-     public const size_t KEY_BYTES;
- 
-     [CCode (cname = "crypto_secretbox_NONCEBYTES")]
-     public const size_t NONCE_BYTES;
- 
-     [CCode (cname = "crypto_secretbox_MACBYTES")]
-     public const size_t MAC_BYTES;
- 
-     [CCode (cname = "crypto_secretbox_keygen")]
-     private void key_gen([CCode (array_length = false)]uint8[] key);
- 
-     public uint8[] generate_key() {
-       uint8[] key = new uint8[KEY_BYTES];
-       key_gen(key);
-       return key;
-     }
- 
-     [CCode (cname = "crypto_secretbox_easy")]
-     private void secretbox(
-       [CCode (array_length = false)]uint8[] ciphertext,
-       uint8[] message,
-       [CCode (array_length = false)]uint8[] nonce,
-       [CCode (array_length = false)]uint8[] key
-     );
- 
-     public uint8[] encrypt(uint8[] message, uint8[] key, uint8[] nonce)
-       requires (key.length == KEY_BYTES) 
-       requires (nonce.length == NONCE_BYTES)
-     {
-       // Initialise array for ciphertext
-       size_t ciphertext_size = MAC_BYTES + message.length;
-       uint8[] ciphertext = new uint8[ciphertext_size];
- 
-       // Encrypt
-       secretbox(ciphertext, message, nonce, key);
- 
-       // Return ciphertext
-       return ciphertext;
-     }
- 
-     [CCode (cname = "crypto_secretbox_open_easy")]
-     private int secretbox_open(
-       [CCode (array_length = false)]uint8[] message,
-       uint8[] ciphertext,
-       [CCode (array_length = false)]uint8[] nonce,
-       [CCode (array_length = false)]uint8[] key
-     );
- 
-     public uint8[]? decrypt(uint8[] ciphertext, uint8[] key, uint8[] nonce)
-       requires (ciphertext.length > MAC_BYTES)
-       requires (key.length == KEY_BYTES) 
-       requires (nonce.length == NONCE_BYTES)
-     {
-       // Initialise array for message
-       size_t message_size = ciphertext.length - MAC_BYTES;
-       uint8[] message = new uint8[message_size];
- 
-       // Decrypt
-       int status = secretbox_open(message, ciphertext, nonce, key);
- 
-       // Did it work?
-       if(status != 0) {
-         // No, return null
-         return null;
-       }
- 
-       return message;
-     }
-   }
-   
-   namespace Asymmetric {
- 
-     namespace Signing {
- 
-         [CCode (cname = "crypto_sign_PUBLICKEYBYTES")]
-         public const size_t PUBLIC_KEY_BYTES;
- 
-         [CCode (cname = "crypto_sign_SECRETKEYBYTES")]
-         public const size_t SECRET_KEY_BYTES;
- 
-         [CCode (cname = "crypto_sign_BYTES")]
-         public const size_t MAX_HEADER_BYTES;
- 
-         [CCode (cname = "crypto_sign_keypair")]
-         public void generate_keypair(
-             [CCode (array_length = false)]uint8[] public_key,
-             [CCode (array_length = false)]uint8[] secret_key)
-             requires (public_key.length == PUBLIC_KEY_BYTES)
-             requires (secret_key.length == SECRET_KEY_BYTES);
-             
-         [CCode (cname = "crypto_sign")]
-         private void sign_message(
-             [CCode (array_length = false)] uint8[] signed_message,
-             out int signature_length,
-             uint8[] message,
-             [CCode (array_length = false)] uint8[] secret_key
-         );
- 
-         public uint8[] sign(
-             uint8[] message,
-             uint8[] secret_key)
-             requires (secret_key.length == SECRET_KEY_BYTES)
-         {
-             int signature_length;
-             uint8[] signed_message = new uint8[MAX_HEADER_BYTES + message.length];
-             sign_message(signed_message, out signature_length, message, secret_key);
-             signed_message.resize(signature_length);
- 
-             return signed_message;
-         }
- 
-         [CCode (cname = "crypto_sign_open")]
-         private int sign_open(
-             [CCode (array_length = false)] uint8[] message,
-             out int message_length,
-             uint8[] signed_message,
-             [CCode (array_length = false)] uint8[] public_key
-         );
- 
-          public uint8[]? verify(
-              uint8[] signed_message,
-              uint8[] public_key)
-              requires (public_key.length == PUBLIC_KEY_BYTES)
-          {
-              int message_length;
-              uint8[] message = new uint8[signed_message.length];
-              if(sign_open(message, out message_length, signed_message, public_key) != 0) {
-                  return null;
-              }
-              message.resize(message_length);
-
-              return message;
-          }
-
-          [CCode (cname = "crypto_sign_detached")]
-          private void sign_detached_message(
-              [CCode (array_length = false)] uint8[] signature,
-              out int signature_length,
-              uint8[] message,
-              [CCode (array_length = false)] uint8[] secret_key
-          );
-
-          /**
-           * Signs a message and returns only the detached signature.
-           *
-           * The signature is MAX_HEADER_BYTES long and does not include the
-           * message itself, allowing the message and signature to be
-           * transported separately (e.g. a Statum frame's content/signature).
-           */
-          public uint8[] sign_detached(
-              uint8[] message,
-              uint8[] secret_key)
-              requires (secret_key.length == SECRET_KEY_BYTES)
-          {
-              uint8[] signature = new uint8[MAX_HEADER_BYTES];
-              int signature_length;
-              sign_detached_message(signature, out signature_length, message, secret_key);
-              signature.resize(signature_length);
-
-              return signature;
-          }
-
-          [CCode (cname = "crypto_sign_verify_detached")]
-          private int verify_detached_message(
-              [CCode (array_length = false)] uint8[] signature,
-              [CCode (array_length = false)] uint8[] message,
-              size_t message_length,
-              [CCode (array_length = false)] uint8[] public_key
-          );
-
-          /**
-           * Verifies a detached signature against a message.
-           *
-           * @return true if the signature is valid for the message.
-           */
-          public bool verify_detached(
-              uint8[] signature,
-              uint8[] message,
-              uint8[] public_key)
-              requires (public_key.length == PUBLIC_KEY_BYTES)
-              requires (signature.length == MAX_HEADER_BYTES)
-          {
-              return verify_detached_message(signature, message, (size_t)message.length, public_key) == 0;
-          }
-  
-      }
- 
-     namespace Sealing {
- 
-         [CCode (cname = "crypto_box_PUBLICKEYBYTES")]
-         public const size_t PUBLIC_KEY_BYTES;
- 
-         [CCode (cname = "crypto_box_SECRETKEYBYTES")]
-         public const size_t SECRET_KEY_BYTES;
- 
-         [CCode (cname = "crypto_box_SEALBYTES")]
-         public const size_t HEADER_BYTES;
- 
-         [CCode (cname = "crypto_box_keypair")]
-         public void generate_keypair(
-             [CCode (array_length = false)]uint8[] public_key,
-             [CCode (array_length = false)]uint8[] secret_key)
-             requires (public_key.length == PUBLIC_KEY_BYTES)
-             requires (secret_key.length == SECRET_KEY_BYTES);
- 
-         [CCode (cname = "crypto_box_seal")]
-         private void seal_message(
-             [CCode (array_length = false)] uint8[] ciphertext,
-             uint8[] message,
-             [CCode (array_length = false)] uint8[] public_key
-         );
- 
-         public uint8[] seal(uint8[] message, uint8[] public_key)
-             requires (public_key.length == PUBLIC_KEY_BYTES)
-         {
-             uint8[] ciphertext = new uint8[HEADER_BYTES + message.length];
-             seal_message(ciphertext, message, public_key);
-             return ciphertext;
-         }
- 
-         [CCode (cname = "crypto_box_seal_open")]
-         private int seal_open(
-             [CCode (array_length = false)] uint8[] message,
-             uint8[] ciphertext,
-             [CCode (array_length = false)] uint8[] public_key,
-             [CCode (array_length = false)] uint8[] secret_key
-         );
- 
-         public uint8[]? unseal(
-             uint8[] ciphertext,
-             uint8[] public_key,
-             uint8[] secret_key) 
-             requires (public_key.length == PUBLIC_KEY_BYTES)
-             requires (secret_key.length == SECRET_KEY_BYTES)
-             requires (ciphertext.length > HEADER_BYTES)
-         {
-             uint8[] message = new uint8[ciphertext.length - HEADER_BYTES];
-             if(seal_open(message, ciphertext, public_key, secret_key) != 0){
-                 return null;
-             }
-              return message;
-          }
-          
-      }
-  
-    }
-    
-    namespace PasswordHashing {
-      [CCode (cname = "crypto_pwhash_STRBYTES")]
-      public const size_t STR_BYTES;
-    
-      [CCode (cname = "crypto_pwhash_OPSLIMIT_INTERACTIVE")]
-      public const size_t OPSLIMIT_INTERACTIVE;
-    
-      [CCode (cname = "crypto_pwhash_MEMLIMIT_INTERACTIVE")]
-      public const size_t MEMLIMIT_INTERACTIVE;
-    
-      [CCode (cname = "crypto_pwhash_OPSLIMIT_MODERATE")]
-      public const size_t OPSLIMIT_MODERATE;
-    
-      [CCode (cname = "crypto_pwhash_MEMLIMIT_MODERATE")]
-      public const size_t MEMLIMIT_MODERATE;
-    
-      [CCode (cname = "crypto_pwhash_OPSLIMIT_SENSITIVE")]
-      public const size_t OPSLIMIT_SENSITIVE;
-    
-      [CCode (cname = "crypto_pwhash_MEMLIMIT_SENSITIVE")]
-      public const size_t MEMLIMIT_SENSITIVE;
-    
-      [CCode (cname = "crypto_pwhash_str")]
-      private int pwhash_str(
-        [CCode (array_length = false)] uint8[] out,
-        string passwd,
-        size_t passwdlen,
-        ulong opslimit,
-        size_t memlimit
-      );
-    
-      public string? hash(string password, ulong opslimit = OPSLIMIT_MODERATE, size_t memlimit = MEMLIMIT_MODERATE) {
-        uint8[] out_buf = new uint8[STR_BYTES];
-        if (pwhash_str(out_buf, password, password.length, opslimit, memlimit) != 0) {
-          return null;
-        }
-        // Null-terminated string
-        return (string)out_buf;
-      }
-    
-      [CCode (cname = "crypto_pwhash_str_verify")]
-      private int pwhash_str_verify(string hash, string password, size_t passwdlen);
-    
-      public bool check(string hash, string password) {
-        return pwhash_str_verify(hash, password, password.length) == 0;
-      }
-    }
-  
-  }