sqlcrypter

package module
v0.3.0 Latest Latest
Warning

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

Go to latest
Published: Jun 2, 2026 License: MIT Imports: 12 Imported by: 2

README

go-sqlcrypter

License GoDoc Go Report Card test

go-sqlcrypter is a Go package that enables sensitive data to be encrypted at rest within a relational database. A custom type EncryptedBytes is provided which implements the sql.Scanner and driver.Valuer interfaces allowing data to be encrypted and decrypted when writing to and reading from a SQL database. Column-level encryption provides an additional layer of security.

The following encryption providers are supported:

Refer to each provider for documentation and examples.

Install
go get -u github.com/bincyber/go-sqlcrypter@v0.2.0
Usage

Configure the encryption provider of your choice:

[!WARNING] Never hardcode encryption keys in source code or config files committed to git.

key := []byte("abcdef01234567899876543210fedcba")
provider, err := aescrypter.New(key, nil)
if err != nil {
    log.Fatalf("failed to initialize AES crypter. Error: %s", err)
}

Initialize the sqlcrypter with the encryption provider:

sqlcrypter.Init(provider)

Use the custom type EncryptedBytes for any sensitive data:

type Employee struct {
	Name  string
	SSN   sqlcrypter.EncryptedBytes
	Email string
	Title string
}

func main() {
	e := &Employee{
		Name:  "Tony Stark",
		SSN:   sqlcrypter.NewEncryptedBytes("999-00-1234"),
		Email: "tony@starkindustries.com",
		Title: "Genius, Billionaire, Playboy, Philanthropist",
	}
}

For a full example, see example/main.go.

Nullable columns: NullEncryptedBytes

Use NullEncryptedBytes when a column may be SQL NULL, and you need to tell NULL apart from an empty string stored as ciphertext:

State Valid Plaintext Stored value
Absent false (n/a) SQL NULL
Present, empty true "" Non-NULL BYTEA / blob (encrypted empty)
Present true non-empty Non-NULL BYTEA / blob

By contrast, EncryptedBytes maps empty plaintext to NULL on write (driver.Valuer), which cannot represent “empty but not null.”

Constructors:

present := sqlcrypter.NewNullEncryptedBytes("secret")     // Valid=true; empty string allowed
absent  := sqlcrypter.NullEncryptedBytesNull()            // Valid=false => SQL NULL
  • sqlc / pgx (Postgres BYTEA): put sqlcrypter.NullEncryptedBytes on the generated struct field. The type implements pgtype.BytesScanner and pgtype.BytesValuer. The default BYTEA codec picks these up, therefore no manual pgtype.Map registration is required.
  • JSON: absent values marshal as JSON null while present values marshal as base64-encoded ciphertext (including when plaintext is empty).
  • GORM: GormDataType() is nullencryptedbytes and GormDBDataType follows the same dialect mapping as EncryptedBytes (bytea / binary / blob / varbinary).
Development

Docker Compose backs local services; make dev/up and related targets invoke it against testing/docker-compose.yml.

To bring up the development environment:

make dev/up
make terraform/apply

To run the test suite:

make go/testsum
Contributing

Contributions of new encryption providers (eg, Azure Key Vault, GCP KMS, etc.) are more than welcome!

License

The source code for this library is licensed under the MIT license, which you can find in the LICENSE file.

Documentation

Index

Constants

This section is empty.

Variables

View Source
var (
	// ErrInitWithNil indicates that Init() has been called with a nil Crypterer.
	ErrInitWithNil = errors.New("sqlcrypter: Init() called with nil crypter")

	// ErrCrypterNotInitialized indicates that Init() has not been called
	// with a valid Crypterer before Encrypt()/Decrypt() usage.
	ErrCrypterNotInitialized = errors.New("sqlcrypter: crypter is not initialized")
)

Functions

func Decrypt

func Decrypt(w io.Writer, r io.Reader) error

Decrypt reads ciphertext from an io.Reader and writes plaintext to an io.Writer.

func Encrypt

func Encrypt(w io.Writer, r io.Reader) error

Encrypt reads plaintext from an io.Reader and writes ciphertext to an io.Writer.

func GenerateBytes

func GenerateBytes(n int) ([]byte, error)

GenerateBytes generates random bytes of n length.

func Init

func Init(c Crypterer) error

Init sets the encryption provider used by Encrypt() and Decrypt() and can only ever be called once. Repeated calls have no effect.

An errors is returned if the Crypter is nil, so that encryption does not get silently disabled for the lifetime of the process.

Types

type Crypterer

type Crypterer interface {
	Encrypt(w io.Writer, r io.Reader) error
	Decrypt(w io.Writer, r io.Reader) error
}

type EncryptedBytes

type EncryptedBytes []byte

func NewEncryptedBytes

func NewEncryptedBytes(s string) EncryptedBytes

func (EncryptedBytes) Bytes

func (e EncryptedBytes) Bytes() []byte

func (*EncryptedBytes) GormDBDataType

func (e *EncryptedBytes) GormDBDataType(db *gorm.DB, field *schema.Field) string

func (*EncryptedBytes) GormDataType

func (e *EncryptedBytes) GormDataType() string

func (EncryptedBytes) MarshalJSON

func (e EncryptedBytes) MarshalJSON() ([]byte, error)

MarshalJSON implements json.Marshaler interface

func (EncryptedBytes) Plaintext added in v0.3.0

func (e EncryptedBytes) Plaintext() string

Plaintext returns the decrypted contents as a string.

It is the caller's responsibility to ensure this value is not logged or otherwise exposed.

func (*EncryptedBytes) Scan

func (e *EncryptedBytes) Scan(value any) error

Scan implements the scanner interface

func (EncryptedBytes) String

func (e EncryptedBytes) String() string

String intentionally returns a redacted placeholder to safeguard the plaintext from being inadventerly leaked in logs, stack traces, etc.

To deliberately access the plaintext, call Plaintext() or Bytes().

func (*EncryptedBytes) UnmarshalJSON

func (e *EncryptedBytes) UnmarshalJSON(data []byte) error

UnmarshalJSON implements json.Unmarshaler interface

func (EncryptedBytes) Value

func (e EncryptedBytes) Value() (driver.Value, error)

Value implements the valuer interface

type NullEncryptedBytes added in v0.3.0

type NullEncryptedBytes struct {
	Data  EncryptedBytes
	Valid bool
}

NullEncryptedBytes is a nullable encrypted column value for database/sql, GORM, JSON, and pgx/sqlc (BYTEA) workflows.

Semantics:

  • Valid == false: SQL NULL; JSON null; Plaintext/Bytes are empty/nil.
  • Valid == true: plaintext is stored encrypted, including empty plaintext (distinct from SQL NULL).

func NewNullEncryptedBytes added in v0.3.0

func NewNullEncryptedBytes(s string) NullEncryptedBytes

NewNullEncryptedBytes returns a present value with plaintext s (empty string allowed).

func NullEncryptedBytesNull added in v0.3.0

func NullEncryptedBytesNull() NullEncryptedBytes

NullEncryptedBytesNull returns an absent (SQL NULL) value.

func (NullEncryptedBytes) Bytes added in v0.3.0

func (n NullEncryptedBytes) Bytes() []byte

Bytes returns a slice view of plaintext when Valid; otherwise nil. The returned slice aliases Data; mutating it mutates the stored plaintext.

func (NullEncryptedBytes) BytesValue added in v0.3.0

func (n NullEncryptedBytes) BytesValue() ([]byte, error)

BytesValue implements pgtype.BytesValuer for pgx/sqlc BYTEA columns.

func (*NullEncryptedBytes) GormDBDataType added in v0.3.0

func (n *NullEncryptedBytes) GormDBDataType(db *gorm.DB, field *schema.Field) string

func (*NullEncryptedBytes) GormDataType added in v0.3.0

func (n *NullEncryptedBytes) GormDataType() string

func (NullEncryptedBytes) MarshalJSON added in v0.3.0

func (n NullEncryptedBytes) MarshalJSON() ([]byte, error)

MarshalJSON encodes absent values as JSON null; present values as base64 ciphertext.

func (NullEncryptedBytes) Plaintext added in v0.3.0

func (n NullEncryptedBytes) Plaintext() string

Plaintext returns decrypted plaintext when Valid; otherwise "".

func (*NullEncryptedBytes) Scan added in v0.3.0

func (n *NullEncryptedBytes) Scan(value any) error

Scan implements sql.Scanner.

func (*NullEncryptedBytes) ScanBytes added in v0.3.0

func (n *NullEncryptedBytes) ScanBytes(src []byte) error

ScanBytes implements pgtype.BytesScanner for pgx/sqlc BYTEA columns.

func (NullEncryptedBytes) String added in v0.3.0

func (n NullEncryptedBytes) String() string

String intentionally returns a redacted placeholder.

func (*NullEncryptedBytes) UnmarshalJSON added in v0.3.0

func (n *NullEncryptedBytes) UnmarshalJSON(data []byte) error

UnmarshalJSON decodes JSON null as absent; otherwise expects base64 ciphertext bytes.

func (NullEncryptedBytes) Value added in v0.3.0

func (n NullEncryptedBytes) Value() (driver.Value, error)

Value implements driver.Valuer.

Directories

Path Synopsis
example module
providers

Jump to

Keyboard shortcuts

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