Ironclad Cryptography Library

repository·master·Indexed 42 days ago

https://github.com/sharplispers/ironclad

A comprehensive cryptography library for Common Lisp providing implementations for ciphers, digests, MACs, authenticated encryption (AEAD), key derivation functions (KDF), and public-key cryptography. It includes support for algorithms such as AES, SHA-256, Argon2, and RSA, as well as low-level elliptic curve operations and PRNG management.

Tokens
5.9K
Snippets
18
Records
22
Agent score
3%

What's inside Ironclad

  1. Handle signature nonces for DSA, Elgamal, and ECDSA

    master

    Algorithms like DSA, Elgamal, and ECDSA require a nonce. Reusing a nonce with the same key for different messages will leak your private key.

    By default, Ironclad uses generate-signature-nonce to create random nonces. If you require deterministic nonces (e.g., RFC 6979), you must redefine the generate-signature-nonce method for your specific key type.

    ;; Example: Redefining for deterministic Secp256k1 nonces
    (defmethod generate-signature-nonce ((key secp256k1-private-key) message &optional parameters)
      (declare (ignore parameters))
      (compute-deterministic-nonce key message))
  2. Security Warnings and Thread Safety

    master

    Before using Ironclad, be aware of the following security and concurrency constraints:

    Side-Channel Attacks

    Ironclad should not be considered safe against side-channel attacks. While some implementations might be safe on specific architectures/runtimes, the general case cannot be guaranteed because Common Lisp integer and arithmetic functions are typically not side-channel resistant.

    Thread Safety

    • Objects: Digest, MAC, and cipher objects are not thread-safe. Do not use the same object in multiple threads simultaneously without external locking.
    • PRNG: Pseudo-random number generation is only thread-safe if you are using the bordeaux-threads library. If you are using implementation-specific threading directly, you must bind a unique PRNG to the *prng* special variable in each thread to avoid generating identical random numbers.
  3. Configure and use Pseudo-Random Number Generators (PRNG)

    master

    Ironclad uses a special variable *prng* to determine the default generator.

    PRNG Types

    • OS: The default. Uses /dev/urandom (Unix) or CryptGenRandom (Windows). Recommended for most users.
    • Fortuna: A cryptographically-secure generator. Use only if your OS does not provide a good PRNG.
    • Fortuna-generator: High-quality, deterministic-seeming numbers for simulations.

    Multi-threading Note

    In multi-threaded applications using bordeaux-threads or lparallel, the PRNG is handled automatically. If using implementation-specific threading, you must bind a unique PRNG to *prng* in each thread to avoid generating identical random data.

    ;; Manual thread setup for custom threading
    (make-thread (lambda ()
                   (let ((crypto:*prng* (crypto:make-prng :os)))
                     (forms-for-thread-1))))
    
    ;; Generating random data
    (random-data 16)           ; 16 bytes
    (random-bits 16)           ; integer with 16 bits
    (strong-random 16)        ; unbiased number between 0 and 15
  4. Install Ironclad

    master

    Ironclad is a cryptography library for Common Lisp. You can install it using ASDF or Quicklisp.

    Using ASDF

    If you have the source, use:

    (asdf:load-system "ironclad")

    To run the test suite, use:

    (asdf:test-system "ironclad")

    Using Quicklisp

    If you use Quicklisp, simply run:

    (ql:quickload "ironclad")

    Once loaded, functions are available in the ironclad package or via the crypto nickname.

  5. Load specific Ironclad subsystems via ASDF

    master

    Instead of loading the entire Ironclad library, you can optimize your application by loading only the specific algorithm subsystems you require using asdf:load-system. This is useful for reducing memory footprint and load times when you only need specific ciphers or digests.

    ;; Example: Loading only AES and SHA256
    (asdf:load-system "ironclad/cipher/aes")
    (asdf:load-system "ironclad/digest/sha256")
  6. Use Gray stream abstractions for crypto operations

    master

    Ironclad provides several stream wrappers to perform cryptographic operations on data as it flows through a stream.

    Octet Streams

    Use make-octet-input-stream and make-octet-output-stream to handle raw byte data similarly to standard Lisp string streams.

    Digest Streams

    Use make-digesting-stream to compute a hash (e.g., :sha1) of data written to the stream. Use produce-digest to retrieve the result.

    Cipher Streams

    Use make-encrypting-stream or make-decrypting-stream to wrap an existing stream.

    • Supported modes: Stream ciphers or block ciphers in CTR, CFB, CFB8, or OFB mode.
    • Direction: :input (data read from the stream is encrypted) or :output (data written to the stream is encrypted).

    MAC Streams

    Use make-authenticating-stream to compute a Message Authentication Code (MAC) as data is written. Use produce-mac to retrieve the result.

    ;; Example: Encrypt data and compute an HMAC of the ciphertext
    (let* ((data ...) 
           (output-stream ...) 
           (encryption-key ...) 
           (authentication-key ...) 
           (iv ...) 
           (mac-stream (make-authenticating-stream :hmac authentication-key :sha3)) 
           (stream (make-broadcast-stream output-stream mac-stream)) 
           (cipher-stream (make-encrypting-stream stream :chacha :stream encryption-key :initialization-vector iv))) 
      (write-sequence data cipher-stream) 
      (let ((mac (produce-mac mac-stream))) 
        ...)) 
  7. Destructure keys into plists

    master

    To serialize or store keys, use destructure-public-key and destructure-private-key. These return a property list (plist) where the keys match the arguments used in the make-public-key and make-private-key functions.

    (destructure-public-key my-public-key)
    ;; => (:y ...)
    
    (destructure-private-key my-private-key)
    ;; => (:x ... :y ...)
  8. PBKDF2 Password Hashing Convenience Functions

    master

    Ironclad provides high-level functions specifically for storing passwords using PBKDF2.

    Hash Password

    (pbkdf2-hash-password password &key salt digest iterations) => password

    Returns the derived hash and the original salt as byte vectors.

    (pbkdf2-hash-password-to-combined-string password &key salt digest iterations) => password

    Returns the derived hash as a single string encoding the salt and parameters.

    Verify Password

    (pbkdf2-check-password password combined-salt-and-digest) => boolean

    Checks a password byte vector against a combined string produced by pbkdf2-hash-password-to-combined-string.

    ;; Example workflow
    (let ((pass (make-array 8 :element-type '(unsigned-byte 8) :initial-element 0))) 
          (combined (ironclad:pbkdf2-hash-password-to-combined-string pass :salt my-salt :digest :sha256 :iterations 1000)))
      (ironclad:pbkdf2-check-password pass combined)) ; => T or NIL
  9. Use Elliptic Curve (EC) operations

    master

    Ironclad provides low-level primitives for elliptic curve math.

    • ec-make-point: Create a point on a curve using :x and :y (integers).
    • ec-point-on-curve: Verify if a point is valid for the curve.
    • ec-add / ec-double / ec-scalar-mult: Basic group operations.
    • ec-encode-point / ec-decode-point: Convert points to/from octet vectors.
    (ec-make-point :secp256k1 :key x y)
    (ec-scalar-mult point scalar)
    (ec-encode-point point)
  10. Encrypt and decrypt messages

    master

    Use encrypt-message and decrypt-message for symmetric or asymmetric encryption. The algorithm is determined by the class of the key provided.

    RSA Padding

    For RSA encryption, use the :oaep key to enable PKCS-1 OAEP padding. Pass a digest name or t (for SHA1).

    ;; RSA Encryption with OAEP
    (encrypt-message rsa-public-key message :oaep t)
    (decrypt-message rsa-private-key encrypted-message :oaep t)
  11. Key Derivation Functions (KDF)

    master

    Ironclad provides several KDFs for deriving keys from passwords or other secrets.

    Supported KDFs

    • argon2d, argon2i, argon2id (Note: only variants without parallelism are implemented)
    • bcrypt, bcrypt-pbkdf
    • pbkdf1, pbkdf2
    • scrypt-kdf
    • hmac-kdf

    Signature

    (derive-key kdf passphrase salt iteration-count key-length) => digest

    Specific Requirements

    • bcrypt: salt must be 16 bytes, iteration-count must be a power of 2 between $2^4$ and $2^{31}$, and key-length must be 24.
    • bcrypt-pbkdf: key-length must be between 1 and 1024.
    • Scrypt: Ignores iteration-count. Uses N, r, and p parameters (set via make-kdf). $N$ must be a power of 2. $r imes p ext{ must be } \le 2^{30}$.
    ;; Using the general derive-key API
    (let ((kdf (ironclad:make-kdf :pbkdf2 :digest :sha256))
          (pass (make-array 8 :element-type '(unsigned-byte 8) :initial-element 0))
          (salt (make-array 16 :element-type '(unsigned-byte 8) :initial-element 0)))
      (ironclad:derive-key kdf pass salt 1000 32))
  12. Create and use Ciphers

    master

    Use make-cipher to create an object for encryption and decryption.

    Signature

    (make-cipher name &key key mode initialization-vector padding tweak) => cipher

    Parameters

    • name: The algorithm (e.g., :aes, ironclad:arcfour). Use (list-all-ciphers) to see all supported names.
    • mode: The mode of operation (e.g., :ecb, :cbc, :ctr, :stream).
    • key: A (simple-array (unsigned-byte 8) (*)).
    • initialization-vector: A (simple-array (unsigned-byte 8) (*)) of the cipher's block length. For Chacha/Salsa20, use 8 or 12 bytes; for XChacha/XSalsa20, use 24 bytes.
    • padding: Supported for ECB/CBC modes: :pkcs7, :ansi-x923, :iso-7816-4.
    • tweak: Supported by certain ciphers like threefish.

    Encryption and Decryption

    Standard API

    (encrypt cipher plaintext ciphertext &key plaintext-start plaintext-end ciphertext-start handle-final-block)
    (decrypt cipher ciphertext plaintext &key ciphertext-start ciphertext-end plaintext-start handle-final-block)

    Note: handle-final-block is required if using padding.

    In-place API

    (encrypt-in-place cipher text &key start end)
    (decrypt-in-place cipher text &key start end)

    Note: In-place functions do not support handle-final-block.

    Message API

    (encrypt-message cipher message &key start end &allow-other-keys)
    (decrypt-message cipher message &key start end &allow-other-keys)
    (let ((key (make-array 32 :element-type '(unsigned-byte 8) :initial-element 0))
          (iv  (make-array 16 :element-type '(unsigned-byte 8) :initial-element 0))
          (p   (make-array 16 :element-type '(unsigned-byte 8) :initial-element 1))
          (c   (make-array 16 :element-type '(unsigned-byte 8))))
      (let ((cipher (make-cipher :aes :key key :mode :cbc :initialization-vector iv :padding :pkcs7)))
        (encrypt cipher p c :handle-final-block t)
        (decrypt cipher c p :handle-final-block t)))