age-encryption

repository·main·Indexed 19 days ago

https://github.com/filosottile/typage

A TypeScript implementation of the age file encryption format. It supports asymmetric keys, passphrases, post-quantum hybrid keys, and WebAuthn-based symmetric encryption for browsers. The library provides Encrypter and Decrypter classes, support for the Streams API, and utilities for ASCII armor encoding. It is compatible with Node.js 20+, Bun, Deno, and modern browsers.

Tokens
8.6K
Snippets
38
Records
42
Agent score
66%

What's inside age-encryption

  1. Use age-plugin-fido2prf with CLI

    main

    If you encrypt files in the browser using a FIDO2 security key, you can decrypt them in the CLI using the age-plugin-fido2prf plugin. Since WebAuthn encryption is symmetric, use the -i flag with the identity string to encrypt/decrypt.

    go install filippo.io/typage/fido2prf/cmd/age-plugin-fido2prf@latest
    
    # Decrypt using an identity file
    age -d -i identity.txt << EOF
    -----BEGIN AGE ENCRYPTED FILE-----
    ...
    -----END AGE ENCRYPTED FILE-----
    EOF
  2. Bundle age-encryption for browser usage

    main

    To use age-encryption in a browser via a <script> tag, you can bundle it using esbuild to create a global age variable.

    cd "$(mktemp -d)" && npm init -y && npm install esbuild age-encryption
    npx esbuild --target=es2022 --bundle --minify --outfile=age.js --global-name=age age-encryption
  3. Use ScryptRecipient and ScryptIdentity for passphrase-based encryption

    main

    You can use a passphrase instead of a key pair by using the Scrypt implementations.

    ScryptRecipient (Encryption):

    • new ScryptRecipient(passphrase: string, logN: number): Requires a passphrase and a work factor logN.

    ScryptIdentity (Decryption):

    • new ScryptIdentity(passphrase: string): Requires the passphrase used during encryption.
    const recipient = new ScryptRecipient("my-password", 14);
    const identity = new ScryptIdentity("my-password");
  4. Implement custom Identity and Recipient interfaces

    main

    For advanced use cases like integrating with hardware security modules (HSMs), remote APIs, or secrets managers, you can implement the Identity and Recipient interfaces.

    Identity

    Used during decryption to unwrap the file key.

    • unwrapFileKey(stanzas: Stanza[]): Uint8Array | null | Promise<Uint8Array | null>
    • Behavior: Return the Uint8Array file key if the provided stanzas match your identity. Return null if they do not match. Throw an error only if the stanza matches but is malformed or decryption fails due to external factors (e.g., network error).

    Recipient

    Used during encryption to wrap the file key.

    • wrapFileKey(fileKey: Uint8Array): Stanza[] | Promise<Stanza[]>
    • Behavior: Return one or more Stanza objects that contain the encrypted file key for this recipient.
  5. Use TagRecipient and HybridTagRecipient

    main

    These classes provide support for tag-based recipients.

    • TagRecipient: Uses p256tag (P-256) and requires a recipient string starting with age1tag1....
    • HybridTagRecipient: Uses mlkem768p256tag (Hybrid P-256) and requires a recipient string starting with age1tagpq1....
  6. Use HybridRecipient and HybridIdentity for post-quantum security

    main

    For post-quantum resistance, use the hybrid implementations which utilize MLKEM768X25519.

    • HybridRecipient: Wrap file keys using a recipient string starting with age1pq1....
    • HybridIdentity: Unwrap file keys using an identity string starting with AGE-SECRET-KEY-PQ-1....
    const recipient = new HybridRecipient("age1pq1...");
    const identity = new HybridIdentity("AGE-SECRET-KEY-PQ-1...");
  7. Encrypt and decrypt using the Streams API

    main

    For large files or on-the-fly processing, age-encryption supports ReadableStream. The Encrypter.encrypt method can accept a stream and returns a stream. The Decrypter.decrypt method can accept an encrypted stream and returns a decrypted stream.

    import { Encrypter, Decrypter } from "age-encryption"
    
    const file = new File([new TextEncoder().encode("age")], "age.txt")
    
    const e = new Encrypter()
    e.setScryptWorkFactor(12)
    e.setPassphrase("your-passphrase")
    const encryptedStream = await e.encrypt(file.stream())
    
    const d = new Decrypter()
    d.addPassphrase("your-passphrase")
    const decryptedStream = await d.decrypt(encryptedStream)
    
    console.log(await new Response(decryptedStream).text())
  8. Encrypt and decrypt with a passphrase

    main

    For symmetric encryption using a passphrase, use setPassphrase on the Encrypter and addPassphrase on the Decrypter.

    import { Encrypter, Decrypter } from "age-encryption"
    
    const e = new Encrypter()
    e.setPassphrase("your-passphrase")
    const ciphertext = await e.encrypt("Hello, age!")
    
    const d = new Decrypter()
    d.addPassphrase("your-passphrase")
    const out = await d.decrypt(ciphertext, "text")
  9. Encrypt and decrypt with post-quantum hybrid keys

    main

    Use generateHybridIdentity() instead of generateIdentity() to create identities that support post-quantum hybrid key exchange.

    import * as age from "age-encryption"
    
    const identity = await age.generateHybridIdentity()
    const recipient = await age.identityToRecipient(identity)
    
    // ... use Encrypter and Decrypter as usual
  10. Encrypt and decrypt with identity/recipient pairs

    main

    To use standard age asymmetric encryption, generate an identity (private key) and convert it to a recipient (public key). Use the Encrypter class to add recipients and the Decrypter class to add identities.

    import * as age from "age-encryption"
    
    const identity = await age.generateIdentity()
    const recipient = await age.identityToRecipient(identity)
    
    const e = new age.Encrypter()
    e.addRecipient(recipient)
    const ciphertext = await e.encrypt("Hello, age!")
    
    const d = new age.Decrypter()
    d.addIdentity(identity)
    const out = await d.decrypt(ciphertext, "text")
    console.log(out)