Skip to content
Gert-Jan Timmer edited this page Jul 24, 2018 · 1 revision

Go-SQLite3 now supports full database encryption.

Full database encryption can be enabled by building with the tag sqlite_codec.

The cipher and master key can be configured with the DSN key key or with the attribute Key within the *Config

CREDITS

Full database encryption has been adopted from github.com/mxk/go-sqlite/ and modified for this repository this documentation is also from github.com/mxk/go-sqlite/.

Overview

Quick Start

cfg := NewConfig()
cfg.Key = "aes-hmac:256:password"

db := OpenDB(cfg)

Usage

Key Format

<CIPHER>:<CIPHER OPTIONS>:<KEY>

Available Codecs

AES-HMAC

The aes-hmac codec provides authenticated encryption using the Advanced Encryption Standard (AES) cipher and the Hash-based Message Authentication Code (HMAC) in Encrypt-then-MAC mode. Each page has an independent, pseudorandom IV, which is regenerated every time the page is encrypted, and an authentication tag, which is verified before the page is decrypted. The codec requires 32 bytes per page to store this information.

The key format is "aes-hmac::", where is a comma-separated list of codec options described below, and is the key from which separate encryption and authentication keys are derived. SECURITY WARNING: The master key is called a "key" and not a "password" for a reason. It is not passed through pbkdf2, bcrypt, scrypt, or any other key stretching function. The application is expected to ensure that this key is sufficiently resistant to brute-force attacks. Ideally, it should be obtained from a cryptographically secure pseudorandom number generator (CSPRNG), such as the one provided by the crypto/rand package.

The encryption and authentication keys are derived from the master key using the HMAC-based Key Derivation Function (HKDF), as described in RFC 5869. The salt is the codec name ("aes-hmac") extended with NULLs to HashLen bytes, and info is the codec configuration string (e.g. "aes-128-ctr,hmac-sha1-128"). This is done to obtain two keys of the required lengths, which are also bound to the codec configuration.

The default configuration is AES-128-CTR cipher and HMAC-SHA1-128 authentication (HMAC output is truncated to 128 bits).

The following options may be used to change the defaults: 192 AES-192 block cipher. 256 AES-256 block cipher. ofb Output feedback mode of operation. sha256 SHA-256 hash function used by HKDF and HMAC.

For example, "aes-hmac:256,ofb,sha256:" will use the AES-256-OFB cipher and HMAC-SHA256-128 authentication.

HEXDUMP

The hexdump codec logs all method calls and dumps the page content for each encode/decode operation to a file. It is intended to be used as an aid when writing your own codecs. The key format is "hexdump::", where is a comma-separated list of codec options described below, and is the output destination. The default destination is stderr. Dash ("-") means stdout. For obvious reasons, this codec cannot be used with an encrypted database except to see the first Codec.Decode call for page 1. The following options are supported: quiet Do not output a hex dump of each page. reserve=N Reserve N bytes in each page. The default is -1, which means don't change the current reserve value.

Codec Operation

Each SQLite database and journal file consists of one or more pages of identical size. Each page may have extra space reserved at the end, which SQLite will not use in any way. The exact number of bytes reserved is stored at offset 20 of the database file header, so the value is between 0 and 255. SQLite requires each page to have at least 480 usable bytes, so the value cannot exceed 32 bytes with a page size of 512. This extra space may be used by a codec to store per-page Initialization Vectors (IVs), Message Authentication Codes (MACs), or any other information.

CodecFunc is called to initialize a registered codec when a key with a matching prefix is provided. If it returns a non-nil Codec implementation, Codec.Reserve is called to determine how much space this codec needs reserved in each page for correct operation. Codec.Resize is called to provide the current page size and reserve values, and for all subsequent changes. The page size may be changed before the database file is created. Once the first CREATE TABLE statement is executed, the page size and reserve values are fixed. Codec.Encode is called when a page is about to be written to the disk. Codec.Decode is called when a page was just read from the disk. This happens for both the main database file and the journal/WAL, so the pages are always encoded on the disk and decoded in memory. Codec.Free is called to free all codec resources when the database is detached.

Write Your Own Codec

A codec can be written by implementing your own version of the Codec interface.

// Codec is the interface used to encode/decode database and journal pages as
// they are written to and read from the disk.
//
// The op value passed to Encode and Decode methods identifies the operation
// being performed. It is undocumented and changed meanings over time since the
// codec API was first introduced in 2004. It is believed to be a bitmask of the
// following values:
//
// 	1 = journal page, not set for WAL, always set when decoding
// 	2 = disk I/O, always set
// 	4 = encode
//
// In the current implementation, op is always 3 when decoding, 6 when encoding
// for the database file or the WAL, and 7 when encoding for the journal. Search
// lib/sqlite3.c for "CODEC1" and "CODEC2" for more information.
type Codec interface {
	// Reserve returns the number of bytes that should be reserved for the codec
	// at the end of each page. The upper limit is 255 (32 if the page size is
	// 512). Returning -1 leaves the current value unchanged.
	Reserve() int

	// Resize is called when the codec is first attached to the pager and for
	// all subsequent page size changes. It can be used to allocate the encode
	// buffer.
	Resize(pageSize, reserve int)

	// Encode returns an encoded copy of a page. The data outside of the reserve
	// area in the original page must not be modified. The codec can either copy
	// this data into a buffer for encoding or return the original page without
	// making any changes. Bytes 16 through 23 of page 1 cannot be encoded. Any
	// non-nil error will be interpreted by SQLite as a NOMEM condition. This is
	// a limitation of underlying C API.
	Encode(page []byte, pageNum uint32, op int) ([]byte, *Error)

	// Decode decodes the page in-place, but it may use the encode buffer as
	// scratch space. Bytes 16 through 23 of page 1 must be left at their
	// original values. Any non-nil error will be interpreted by SQLite as a
	// NOMEM condition. This is a limitation of underlying C API.
	Decode(page []byte, pageNum uint32, op int) *Error

	// Key returns the original key that was used to initialize the codec. Some
	// implementations may be better off returning nil or a fake value. Search
	// lib/sqlite3.c for "sqlite3CodecGetKey" to see how the key is used.
	Key() []byte

	// Free releases codec resources when the pager is destroyed or when the
	// codec attachment fails.
	Free()
}

See codec_aes_hmac.go as an example on how to.

Registration of Codec

When you have written a implementation the codec has to be registerd. This can be done with the global function RegisterCodec(name string, f CodecFunc).

This should be done within a init function.

Available Functions

See codec.go for more information.

Go

  • parseKey
  • hkdf
  • rnd
  • wipe

CGO

  • go_codec_init
  • go_codec_reserve
  • go_codec_resize
  • go_codec_exec
  • go_codec_get_key
  • go_codec_free

Clone this wiki locally