@hapi/iron

repository·master·Indexed 20 days ago

https://github.com/hapijs/iron

A Node.js library for creating encapsulated tokens by encrypting and MAC-ing objects to ensure confidentiality and integrity. It provides high-level methods like seal() and unseal() to convert JavaScript objects into URI-friendly strings and back, as well as low-level utilities for encryption, decryption, key generation, and HMAC calculation. While part of the hapi ecosystem, it is a standalone module compatible with any web framework or Node.js environment.

Tokens
4.5K
Snippets
11
Records
21
Agent score
71%

What's inside @hapi/iron

  1. Overview of @hapi/iron

    master

    @hapi/iron is a library for creating encapsulated tokens. These tokens are objects that have been both encrypted and protected with a Message Authentication Code (MAC) to ensure both confidentiality and integrity.

    While it is part of the hapi ecosystem and designed to work seamlessly with the hapi web framework, it is a standalone module that can be used with any other web framework or in any Node.js environment.

  2. Implement password rotation with Iron

    master

    To enable password rotation, instead of passing a single string or buffer as the password argument, pass an object where keys are password identifiers (id) and values are the passwords themselves.

    When calling seal(), the id included in the resulting protocol string allows unseal() to identify which password to use. To manage rotation effectively, combine this with the ttl (Time To Live) option so you only need to maintain passwords used within the current validity window.

  3. Seal and unseal objects with Iron

    master

    Use Iron.seal() to encrypt a JavaScript object into a URI-friendly string and Iron.unseal() to decrypt it back. This process ensures message integrity and prevents tampering.

    Important: You must explicitly pass an options object (like Iron.defaults) to these methods; they do not apply defaults automatically to ensure you are aware of the security properties being used.

    Note on Serialization: seal() uses JSON.stringify(). Properties with undefined values will be omitted from the sealed object.

    const obj = {
        a: 1,
        b: 2,
        c: [3, 4, 5],
        d: {
            e: 'f'
        }
    };
    
    const password = 'some_not_random_password_that_is_at_least_32_characters';
    
    try {
        // Seal the object
        const sealed = await Iron.seal(obj, password, Iron.defaults);
        
        // Unseal the object
        const unsealed = await Iron.unseal(sealed, password, Iron.defaults);
        console.log(unsealed);
    } catch (err) {
        console.log(err.message);
    }
  4. Configure Iron options

    master

    Iron methods accept an options object to customize key derivation, encryption, and integrity verification. The structure requires encryption and integrity sub-objects.

    Encryption & Integrity Options

    Both encryption and integrity objects support:

    • algorithm (required): Supported values are 'aes-256-cbc' and 'aes-128-ctr' for encryption, and 'sha256' for integrity.
    • iv (optional): An initialization vector buffer.

    Key Derivation Options

    When using a password string for key generation, you can configure:

    • salt (optional): A pre-generated salt buffer.
    • saltBits (required if salt is not provided): The size of the salt.
    • iterations (required): Number of iterations for key derivation (default is 1).
    • minPasswordlength (required): Minimum password string length (default is 32).

    Seal/Unseal Specific Options

    • ttl: Sealed object lifetime in milliseconds (0 for forever, default is 0).
    • timestampSkewSec: Permitted clock skew in seconds for expirations (default is 60).
    • localtimeOffsetMsec: Local clock offset in milliseconds (default is 0).
    const options = {
        encryption: {
            saltBits: 256,
            algorithm: 'aes-256-cbc',
            iterations: 1
        },
        integrity: {
            saltBits: 256,
            algorithm: 'sha256',
            iterations: 1
        },
        ttl: 0,
        timestampSkewSec: 60,
        localtimeOffsetMsec: 0
    };
  5. Use structured passwords with seal() and unseal()

    master

    Instead of a simple string or Buffer, you can use structured password objects to manage multiple keys or look up keys by ID.

    password.Secret

    Used in seal() to provide a single secret with an optional id.

    { id?: string, secret: Password }

    password.Specific

    Used in seal() to provide distinct passwords for encryption and integrity.

    { id?: string, encryption: Password, integrity: Password }

    password.Hash

    Used in unseal() to provide a map of password IDs to secrets. This allows unseal() to find the correct password if the sealed string contains a password ID.

    { [id: string]: Password | Secret | Specific }
  6. Configure sealing behavior with SealOptions

    master

    The SealOptions interface allows you to customize the security and lifecycle of the sealed object. It is divided into two main configuration blocks: encryption and integrity.

    Top-level options

    • encryption: A SealOptionsSub object defining encryption parameters.
    • integrity: A SealOptionsSub object defining HMAC/integrity parameters.
    • ttl: Sealed object lifetime in milliseconds (0 for forever).
    • timestampSkewSec: Permitted clock skew for incoming expirations in seconds (defaults to 60).
    • localtimeOffsetMsec: Local clock time offset in milliseconds.

    SealOptionsSub (used by both encryption and integrity)

    • algorithm: The algorithm used (e.g., 'aes-256-cbc' for encryption or 'sha256' for integrity). Defaults to 'aes-256-cbc' for encryption and 'sha256' for integrity.
    • saltBits: Length of the random salt (defaults to 256).
    • iterations: Number of iterations for key derivation (defaults to 1).
    • minPasswordlength: Minimum password size (defaults to 32).
    const options: SealOptions = {
        encryption: {
            algorithm: 'aes-256-cbc',
            iterations: 1000
        },
        integrity: {
            algorithm: 'sha256',
            iterations: 1000
        },
        ttl: 3600000 // 1 hour
    };
  7. Seal and unseal objects with seal() and unseal()

    master

    The primary way to use @hapi/iron is to serialize, encrypt, and sign data into a secure string using seal(), and later recover that data using unseal().

    seal() accepts an object, a password (which can be a string, Buffer, or a structured password object), and SealOptions to control encryption, integrity, and expiration.

    unseal() takes the resulting string, a password (or a password.Hash for looking up specific passwords by ID), and optional SealOptions to verify and decrypt the data.

    // Sealing an object
    const sealed = await seal({ foo: 'bar' }, 'my-password', defaults);
    
    // Unsealing the object
    const decrypted = await unseal(sealed, 'my-password', defaults);
  8. Encrypt and decrypt strings with encrypt() and decrypt()

    master

    If you only need to encrypt a raw string rather than a full object, use encrypt() and decrypt(). These methods require GenerateKeyOptions to define the key derivation parameters.

    encrypt() returns an object containing the encrypted Buffer and the key (an object containing the key, salt, and iv).

    decrypt() returns the original decrypted string.

    import { encrypt, decrypt, GenerateKeyOptions } from '@hapi/iron';
    
    const options: GenerateKeyOptions = {
        algorithm: 'aes-256-cbc',
        iterations: 1000
    };
    
    const { encrypted, key } = await encrypt('my-password', options, 'my-data');
    const decrypted = await decrypt('my-password', options, encrypted); // Note: decrypt expects the data to be the encrypted string/buffer
  9. Seal and unseal data with `seal()` and `unseal()`

    master

    Use seal() to encrypt and sign an object into a single string, and unseal() to verify the signature and decrypt it back into its original object. This is the primary high-level API for securing data.

    Password Formats

    To use seal and unseal, the password parameter can be:

    • A string or Buffer (used for both encryption and integrity).
    • An object with { id, secret } (where secret is used for both).
    • An object with { id, encryption, integrity } (allowing different secrets for encryption and HMAC).
    • An object used as a lookup table when unseal is called with a passwordId (e.g., { myId: { encryption: '...', integrity: '...' } }).

    Options

    Both functions accept an options object to override exports.defaults:

    • ttl: Time-to-live in milliseconds. If set, the seal will expire.
    • timestampSkewSec: Permitted clock skew in seconds for expiration checks.
    • localtimeOffsetMsec: Local clock offset in milliseconds.
    • encryption: Configuration for the encryption algorithm (e.g., algorithm, saltBits, iterations).
    • integrity: Configuration for the HMAC (e.g., algorithm, saltBits, iterations).
    const Iron = require('@hapi/iron');
    
    const password = 'my-secret-password';
    const object = { foo: 'bar' };
    
    (async () => {
        // Seal the object
        const sealed = await Iron.seal(object, password, { ttl: 1000 * 60 });
    
        // Unseal the object
        const unsealed = await Iron.unseal(sealed, password);
        console.log(unsealed); // { foo: 'bar' }
    })();
  10. Generate a key with generateKey()

    master

    The generateKey() function derives a cryptographic key from a password using the specified derivation parameters. This is useful for manual key management.

    Parameters:

    • password: A string or Buffer.
    • options: GenerateKeyOptions (includes algorithm, iterations, minPasswordlength, and optional saltBits, salt, or iv).

    Returns: A Promise<Key> where Key contains the key (Buffer), salt (string), and iv (Buffer).

    const keyObj = await generateKey('my-password', {
        algorithm: 'aes-256-cbc',
        iterations: 1000
    });
  11. Encrypt and decrypt data with `encrypt()` and `decrypt()`

    master

    encrypt(password, options, data) and decrypt(password, options, data) provide low-level encryption/decryption services using the derived key from a password.

    Parameters

    • password: A string or Buffer used to derive the key.
    • options: Configuration object (see generateKey for details). Must include an algorithm from exports.algorithms.
    • data: The data to encrypt (as a UTF-8 string) or decrypt (as a Buffer).

    Returns

    • encrypt: Resolves to { encrypted: Buffer, key: Object }.
    • decrypt: Resolves to the decrypted UTF-8 string.
    const Iron = require('@hapi/iron');
    
    const password = 'password';
    const data = 'hello world';
    const options = { algorithm: 'aes-256-cbc' };
    
    (async () => {
        const { encrypted, key } = await Iron.encrypt(password, options, data);
        const decrypted = await Iron.decrypt(password, options, encrypted);
        console.log(decrypted); // 'hello world'
    })();
  12. Calculate an HMAC with `hmacWithPassword()`

    master

    hmacWithPassword(password, options, data) generates a keyed hash (HMAC) of the provided data. This is useful for verifying data integrity independently of encryption.

    Parameters

    • password: A string or Buffer used to derive the HMAC key.
    • options: Configuration object (see generateKey for details). Must include an algorithm from exports.algorithms.
    • data: The data to hash.

    Returns

    A Promise that resolves to an object containing:

    • digest: A base64url-encoded HMAC digest.
    • salt: The salt used for key derivation.
    const Iron = require('@hapi/iron');
    
    const password = 'password';
    const data = 'some data';
    const options = { algorithm: 'sha256' };
    
    (async () => {
        const { digest, salt } = await Iron.hmacWithPassword(password, options, data);
        console.log(digest, salt);
    })();