luks

package module
v0.0.0-...-98c0a50 Latest Latest
Warning

This package is not in the latest version of its module.

Go to latest
Published: Mar 15, 2026 License: MIT Imports: 32 Imported by: 6

README

Pure Go library for LUKS volume management

luks.go is a pure-Go library for working with LUKS-encrypted volumes.

This library focuses on the read-only (unlocking) path — it reads LUKS metadata and recovers volume keys without modifying the LUKS header.

Installation

go get github.com/anatol/luks.go

Requires Linux (uses device-mapper via /dev/mapper).

Supported features

  • LUKS v1 and LUKS v2
  • Detached headers (OpenWithHeader)
  • KDFs: pbkdf2, argon2i, argon2id
  • Ciphers: AES, Camellia, Twofish (XTS mode)
  • Hash algorithms: SHA-1/224/256/384/512, SHA-3, RIPEMD-160, BLAKE2b, BLAKE2s, Whirlpool
  • dm-crypt flags: allow-discards, same-cpu-crypt, submit-from-crypt-cpus, no-read-workqueue, no-write-workqueue
  • Token metadata (clevis, systemd-fido2, etc.)
  • LUKS v2 keyslot priorities
  • Multi-segment LUKS2 layouts (e.g. integrity)

Usage

Open and unlock
dev, err := luks.Open("/dev/sda1")
if err != nil {
    log.Fatal(err)
}
defer dev.Close()

// Unlock a specific slot and create a device mapper entry.
// Equivalent to: cryptsetup open /dev/sda1 volumename
if err := dev.Unlock(0, []byte("password"), "volumename"); err == luks.ErrPassphraseDoesNotMatch {
    log.Fatal("wrong password")
} else if err != nil {
    log.Fatal(err)
}
// /dev/mapper/volumename is now available

// Close the mapper when done.
// Equivalent to: cryptsetup close volumename
if err := luks.Lock("volumename"); err != nil {
    log.Fatal(err)
}
Try all keyslots
if err := dev.UnlockAny([]byte("password"), "volumename"); err != nil {
    log.Fatal(err)
}
Detached header
dev, err := luks.OpenWithHeader("/dev/sda1", "/path/to/header.bin")
Flags (dm-crypt options)
// Set flags before unlocking
dev.FlagsAdd(luks.FlagAllowDiscards)
dev.FlagsAdd(luks.FlagNoReadWorkqueue, luks.FlagNoWriteWorkqueue)

// Get current flags
flags := dev.FlagsGet()

// Clear all flags
dev.FlagsClear()

Available flags:

  • FlagAllowDiscards — pass discard/TRIM requests through (SSD-friendly, reduces security)
  • FlagSameCPUCrypt — perform encryption on the same CPU as the IO
  • FlagSubmitFromCryptCPUs — submit IO from crypto CPUs
  • FlagNoReadWorkqueue — bypass read workqueue (Linux 5.9+)
  • FlagNoWriteWorkqueue — bypass write workqueue (Linux 5.9+)
Two-step unlock (advanced)

UnsealVolume recovers the key without activating the mapper, allowing inspection or custom dm-crypt setup:

volume, err := dev.UnsealVolume(0, []byte("password"))
if err == luks.ErrPassphraseDoesNotMatch {
    log.Fatal("wrong password")
} else if err != nil {
    log.Fatal(err)
}

// volume contains encryption parameters; activate when ready
if err := volume.SetupMapper("volumename"); err != nil {
    log.Fatal(err)
}
Token metadata

Tokens are LUKS v2 metadata entries (or luksmeta entries for LUKS v1) that carry supplementary information for unlocking tools like clevis or systemd-fido2.

tokens, err := dev.Tokens()
for _, t := range tokens {
    fmt.Printf("token %d: type=%s slots=%v\n", t.ID, t.Type, t.Slots)
    // t.Payload contains the raw JSON (LUKS v2) or binary (LUKS v1) token data
}
Device metadata
fmt.Println(dev.Version()) // 1 or 2
fmt.Println(dev.UUID())
fmt.Println(dev.Slots()) // active keyslot IDs, sorted by priority (LUKS v2)
fmt.Println(dev.Path())

How it works

The unlock process follows these steps for both LUKS v1 and v2:

  1. Open — read and validate the binary header (magic bytes, version, checksum). For LUKS v2, the JSON metadata area is also parsed.
  2. Key derivation — derive an intermediate key from the passphrase using the keyslot's KDF (PBKDF2 for v1; PBKDF2, Argon2i, or Argon2id for v2).
  3. Keyslot decryption — decrypt the keyslot area sector-by-sector using XTS-mode encryption with the derived key.
  4. Anti-forensic merge — recover the volume master key from 4000 stripes using the AFsplit/AFmerge algorithm (hash-based diffusion + XOR chain).
  5. Digest verification — re-derive a digest from the recovered key and compare it against the stored digest to confirm the passphrase is correct.
  6. Device mapper setup — pass the recovered key and encryption parameters to the Linux device mapper (dm-crypt) to create the decrypted block device.

References

License

See LICENSE.

Documentation

Index

Constants

View Source
const (
	FlagAllowDiscards       string = "allow-discards"
	FlagSameCPUCrypt        string = "same-cpu-crypt"
	FlagSubmitFromCryptCPUs string = "submit-from-crypt-cpus"
	FlagNoReadWorkqueue     string = "no-read-workqueue"  // supported at Linux 5.9 or newer
	FlagNoWriteWorkqueue    string = "no-write-workqueue" // supported at Linux 5.9 or newer
)

List of options handled by luks.go API. These names correspond to LUKSv2 persistent flags names (see persistent_flags[] array).

Variables

View Source
var ErrPassphraseDoesNotMatch = errors.New("passphrase does not match")

ErrPassphraseDoesNotMatch is an error that indicates provided passphrase does not match

Functions

func Lock

func Lock(name string) error

Lock closes device mapper partition with the given name

Types

type Device

type Device interface {
	io.Closer
	// Version returns version of LUKS disk
	Version() int
	// Path returns block device path
	Path() string
	// UUID returns UUID of the LUKS partition
	UUID() string
	// Slots returns list of all active slots for this device sorted by priority
	Slots() []int
	// Tokens returns list of available tokens (metadata) for slots
	Tokens() ([]Token, error)
	// FlagsGet get the list of LUKS flags (options) used during unlocking
	FlagsGet() []string
	// FlagsAdd adds LUKS flags used for the upcoming unlocking
	// Note that this method does not update LUKS v2 persistent flags
	FlagsAdd(flags ...string) error
	// FlagsClear clears flags
	// Note that this method does not update LUKS v2 persistent flags
	FlagsClear()

	// UnsealVolume recovers slot password and then populates Volume structure that contains information needed to
	// create a mapper device
	UnsealVolume(keyslot int, passphrase []byte) (*Volume, error)

	// Unlock is a shortcut for
	// “`go
	//   volume, err := dev.UnsealVolume(keyslot, passphrase)
	//   volume.SetupMapper(dmName)
	// “`
	Unlock(keyslot int, passphrase []byte, dmName string) error
	// UnlockAny iterates over all available slots and tries to unlock them until succeeds
	UnlockAny(passphrase []byte, dmName string) error
}

Device represents LUKS partition data

func Open

func Open(path string) (Device, error)

Open reads LUKS headers from the given partition and returns LUKS device object. This function internally handles LUKS v1 and v2 partitions metadata.

func OpenWithHeader

func OpenWithHeader(devicePath, headerPath string) (Device, error)

OpenWithHeader opens a LUKS device that uses a detached header. The LUKS metadata and keyslot material are read from headerPath, while the encrypted data resides on devicePath.

type Token

type Token struct {
	ID    int
	Slots []int
	// Type of the token e.g. "clevis", "systemd-fido2"
	Type    string
	Payload []byte
}

Token represents LUKS token metadata information

type Volume

type Volume struct {
	BackingDevice string
	Flags         []string // luks-named flags
	UUID          string

	LuksType          string
	StorageEncryption string
	StorageIvTweak    uint64
	StorageSectorSize uint64
	StorageOffset     uint64 // offset of underlying storage in bytes
	StorageSize       uint64 // length of underlying device in bytes, zero means that size should be calculated using `diskSize` function
	// contains filtered or unexported fields
}

Volume represents information provided by an unsealed (i.e. with recovered password) LUKS slot

func (*Volume) SetupMapper

func (v *Volume) SetupMapper(name string) error

SetupMapper creates a device mapper for the given LUKS volume

Directories

Path Synopsis

Jump to

Keyboard shortcuts

? : This menu
/ : Search site
f or F : Jump to
y or Y : Canonical URL