HGRC Quarry Fossil Service
Simple tools for lasting software

Infrastructure as a Service / Nemoptera

A solid foundation to fork, tweak, and enhance.

Public | Owner: Infrastructure as a Service | Default branch: trunk | Last update: 2026-11-08 16:13

Artifact 3928bd6ff1cb797ecd9f954e0e93143b519e49393cbad6fdf3bbb40eabdda0e6:


(defmodule nemoptera_crypto
  (export all))

(defun key-path () "data/secret.key")
(defun iterations () 210000)

(defun hash-password (password)
  (let* ((salt (crypto:strong_rand_bytes 16))
         (hash (crypto:pbkdf2_hmac 'sha512 (to-bin password) salt (iterations) 64)))
    (lists:flatten
     (io_lib:format "pbkdf2$~p$~s$~s"
                    (list (iterations)
                          (base64:encode salt)
                          (base64:encode hash))))))

(defun verify-password (password stored)
  (case (parse-stored stored)
    (`#(ok ,iter ,salt ,expected)
     (let ((actual (crypto:pbkdf2_hmac 'sha512 (to-bin password) salt iter 64)))
       (crypto:hash_equals actual expected)))
    (_ 'false)))

(defun parse-stored (stored)
  (case (string:split (nemoptera_text:to-str stored) "$" 'all)
    (`("pbkdf2" ,iter-s ,salt-b64 ,hash-b64)
     (case (catch (tuple (list_to_integer iter-s)
                         (base64:decode salt-b64)
                         (base64:decode hash-b64)))
       (`#(EXIT ,_) 'error)
       (`#(,iter ,salt ,hash) `#(ok ,iter ,salt ,hash))))
    (_ 'error)))

(defun load-or-create-key ()
  (load-or-create-key (key-path)))

(defun load-or-create-key (path)
  (filelib:ensure_dir path)
  (case (file:read_file path)
    (`#(ok ,key)
     (if (=:= 32 (byte_size key))
         `#(ok ,key)
         `#(error bad-key-size)))
    (_
     (let ((key (crypto:strong_rand_bytes 32)))
       (case (file:write_file path key)
         ('ok
          (file:change_mode path 384)
          `#(ok ,key))
         (`#(error ,reason) `#(error ,reason)))))))

(defun secret ()
  (case (persistent_term:get 'nemoptera_secret 'error)
    ('error
     (case (load-or-create-key)
       (`#(ok ,key)
        (persistent_term:put 'nemoptera_secret key)
        key)
       (`#(error ,reason) (erlang:error reason))))
    (key key)))

(defun seal (plaintext)
  (let* ((key (secret))
         (iv (crypto:strong_rand_bytes 12))
         ((tuple ciphertext tag)
          (crypto:crypto_one_time_aead 'aes_256_gcm key iv (to-bin plaintext) #"" 'true))
         (packed (iolist_to_binary (list iv tag ciphertext))))
    (binary_to_list (base64:encode packed))))

(defun open-sealed (stored)
  (case (catch (base64:decode (to-bin stored)))
    (packed
     (if (and (is_binary packed) (>= (byte_size packed) 28))
         (let ((iv (binary:part packed 0 12))
               (tag (binary:part packed 12 16))
               (ciphertext (binary:part packed 28 (- (byte_size packed) 28))))
           (let ((result (catch (crypto:crypto_one_time_aead
                                 'aes_256_gcm (secret) iv ciphertext #"" tag 'false))))
             (if (is_binary result)
                 `#(ok ,(binary_to_list result))
                 'error)))
         'error))
    (_ 'error)))

(defun to-bin (value)
  (if (is_binary value)
      value
      (list_to_binary (nemoptera_text:to-str value))))

Clone

You can clone this repository with the following command:

fossil clone https://hgrc.dev/IaaS/Nemoptera Nemoptera.fossil

Contribute

The best way to contribute is through e-mail. See the project documentation for more details.