Skip to content

Encrypt and protect data using industry standard algorithms, field level encryption, a unique data key per record, bulk encryption operations, and decryption level identity verification.

License

Notifications You must be signed in to change notification settings

cipherstash/protectgo

Folders and files

NameName
Last commit message
Last commit date

Latest commit

 

History

3 Commits
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 

Repository files navigation

CipherStash Logo
Protect.go

Implement robust data security without sacrificing performance or usability


Warning

This is a work in progress. The package is not yet available on pkg.go.dev.

Protect.go is a Go module for encrypting and decrypting data. Encryption operations happen directly in your app, and the ciphertext is stored in your database. Every value you encrypt with Protect.go has a unique key, made possible by CipherStash ZeroKMS's blazing fast bulk key operations, and backed by a root key in AWS KMS. The encrypted data is structured as an EQL JSON payload, and can be stored in any database that supports JSONB.

Important

Searching, sorting, and filtering on encrypted data is currently only supported when storing encrypted data in PostgreSQL.

Table of contents

For more specific documentation, refer to the docs.

Features

Protect.go protects data using industry-standard AES encryption. Protect.go uses ZeroKMS for bulk encryption and decryption operations. This enables every encrypted value, in every column, in every row in your database to have a unique key — without sacrificing performance.

Features:

  • Bulk encryption and decryption: Protect.go uses ZeroKMS for encrypting and decrypting thousands of records at once, while using a unique key for every value.
  • Single item encryption and decryption: Just looking for a way to encrypt and decrypt single values? Protect.go has you covered.
  • Really fast: ZeroKMS's performance makes using millions of unique keys feasible and performant for real-world applications built with Protect.go.
  • Identity-aware encryption: Lock down access to sensitive data by requiring a valid JWT to perform a decryption.
  • Audit trail: Every decryption event will be logged in ZeroKMS to help you prove compliance.
  • Searchable encryption: Protect.go supports searching encrypted data in PostgreSQL.
  • Type safety: Strong typing with Go structs and interfaces.

Use cases:

  • Trusted data access: make sure only your end-users can access their sensitive data stored in your product.
  • Meet compliance requirements faster: meet and exceed the data encryption requirements of SOC2 and ISO27001.
  • Reduce the blast radius of data breaches: limit the impact of exploited vulnerabilities to only the data your end-users can decrypt.

Installing Protect.go

Prerequisites

  • Go 1.21 or later
  • CipherStash CLI (for configuration)

Install the Go package

go get github.com/cipherstash/protectgo/pkg/protect

Install the CipherStash CLI

  • On macOS:

    brew install cipherstash/tap/stash
  • On Linux, download the binary for your platform, and put it on your PATH:

Getting started

Configuration

Important

Make sure you have installed the CipherStash CLI before following these steps.

To set up all the configuration and credentials required for Protect.go:

stash setup

If you haven't already signed up for a CipherStash account, this will prompt you to do so along the way.

At the end of stash setup, you will have two files in your project:

  • cipherstash.toml which contains the configuration for Protect.go
  • cipherstash.secret.toml: which contains the credentials for Protect.go

Warning

Don't commit cipherstash.secret.toml to git; it contains sensitive credentials.
The stash setup command will attempt to append to your .gitignore file with the cipherstash.secret.toml file.

Environment Variables

The library respects the following environment variables:

  • CIPHERSTASH_WORKSPACE_CRN - Workspace CRN
  • CIPHERSTASH_ACCESS_KEY - Access key
  • CIPHERSTASH_CLIENT_ID - Client ID
  • CIPHERSTASH_CLIENT_KEY - Client key

Basic usage

Simple encryption and decryption

package main

import (
    "context"
    "log"
    
    "github.com/cipherstash/protectgo/pkg/protect"
)

func main() {
    // Configure encryption settings
    config := protect.EncryptConfig{
        Version: 1,
        Tables: protect.Tables{
            "users": protect.Table{
                "email": protect.Column{
                    CastAs: &protect.CastAsText,
                    Indexes: &protect.Indexes{
                        UniqueIndex: &protect.UniqueIndexOpts{
                            TokenFilters: []protect.TokenFilter{
                                {Kind: "downcase"},
                            },
                        },
                    },
                },
            },
        },
    }

    // Create client
    client, err := protect.NewClient(protect.NewClientOptions{
        EncryptConfig: config,
    })
    if err != nil {
        log.Fatal(err)
    }
    defer client.Free()

    // Encrypt data
    encrypted, err := client.Encrypt(protect.EncryptOptions{
        Plaintext: "[email protected]",
        Table:     "users",
        Column:    "email",
    })
    if err != nil {
        log.Fatal(err)
    }

    log.Printf("Encrypted: %s", *encrypted.Ciphertext)

    // Decrypt data
    plaintext, err := client.Decrypt(protect.DecryptOptions{
        Ciphertext: *encrypted.Ciphertext,
    })
    if err != nil {
        log.Fatal(err)
    }

    log.Printf("Decrypted: %s", plaintext)
}

Bulk operations

// Bulk encryption
bulkEncrypted, err := client.EncryptBulk(protect.EncryptBulkOptions{
    Plaintexts: []protect.PlaintextPayload{
        {
            Plaintext: "[email protected]",
            Table:     "users",
            Column:    "email",
        },
        {
            Plaintext: "[email protected]", 
            Table:     "users",
            Column:    "email",
        },
    },
})
if err != nil {
    log.Fatal(err)
}

// Bulk decryption
ciphertexts := make([]protect.BulkDecryptPayload, len(bulkEncrypted))
for i, enc := range bulkEncrypted {
    ciphertexts[i] = protect.BulkDecryptPayload{
        Ciphertext: *enc.Ciphertext,
    }
}

plaintexts, err := client.DecryptBulk(protect.DecryptBulkOptions{
    Ciphertexts: ciphertexts,
})
if err != nil {
    log.Fatal(err)
}

log.Printf("Decrypted values: %v", plaintexts)

Configuration

Encryption Configuration

type EncryptConfig struct {
    Version uint32 `json:"v"`
    Tables  Tables `json:"tables"`
}

type Tables map[string]Table
type Table map[string]Column

type Column struct {
    CastAs  *CastAs  `json:"cast_as,omitempty"`
    Indexes *Indexes `json:"indexes,omitempty"`
}

Client Configuration

type ClientOpts struct {
    WorkspaceCrn *string `json:"workspaceCrn,omitempty"`
    AccessKey    *string `json:"accessKey,omitempty"`
    ClientID     *string `json:"clientId,omitempty"`
    ClientKey    *string `json:"clientKey,omitempty"`
}

Index Types

The library supports all the same index types as other Protect libraries:

  • ORE Index: For range queries and ordering
  • Match Index: For full-text search
  • Unique Index: For exact matching and uniqueness constraints
  • SteVec Index: For vector similarity searches

Data Types

Supported cast types:

  • CastAsText - UTF-8 strings
  • CastAsInt - 32-bit integers
  • CastAsBigInt - 64-bit integers
  • CastAsBoolean - Boolean values
  • CastAsDate - Date values
  • CastAsReal/CastAsDouble - Floating point numbers
  • CastAsJsonB - JSON data

Identity-aware encryption

Important

Identity-aware encryption requires implementing JWT validation in your application.

Protect.go can add an additional layer of protection to your data by requiring a valid JWT to perform a decryption. This ensures that only the user who encrypted data is able to decrypt it.

Using Lock Context

// Create lock context from JWT claims
lockContext := protect.LockContext{
    IdentityClaim: []string{"user:12345"}, // Extract from JWT
}

// Encrypt with lock context
encrypted, err := client.Encrypt(protect.EncryptOptions{
    Plaintext:   "sensitive-data",
    Table:       "users",
    Column:      "email",
    LockContext: &lockContext,
})

// Decrypt with the same lock context
plaintext, err := client.Decrypt(protect.DecryptOptions{
    Ciphertext:  *encrypted.Ciphertext,
    LockContext: &lockContext,
})

Caution

You must use the same lock context to encrypt and decrypt data.
If you use different lock contexts, you will be unable to decrypt the data.

Supported data types

Protect.go currently supports encrypting and decrypting text. Other data types like booleans, dates, ints, floats, and JSON are well-supported in other CipherStash products, and will be coming to Protect.go soon.

Searchable encryption

Important

Searchable encryption requires PostgreSQL with EQL extensions installed.

To enable searchable encryption, you need to install EQL in your PostgreSQL database:

  1. Download the latest EQL install script:

    curl -sLo cipherstash-encrypt.sql \
      https://github.com/cipherstash/encrypt-query-language/releases/latest/download/cipherstash-encrypt.sql
  2. Run this command to install the custom types and functions:

    psql -f cipherstash-encrypt.sql
  3. Create tables with the eql_v2_encrypted type:

    CREATE TABLE users (
        id BIGINT GENERATED ALWAYS AS IDENTITY PRIMARY KEY,
        email eql_v2_encrypted
    );

Read more about searching encrypted data in the docs.

API Reference

Client Methods

  • NewClient(options NewClientOptions) (*Client, error) - Create a new client
  • client.Free() - Release client resources
  • client.Encrypt(options EncryptOptions) (*Encrypted, error) - Encrypt single value
  • client.EncryptBulk(options EncryptBulkOptions) ([]Encrypted, error) - Encrypt multiple values
  • client.Decrypt(options DecryptOptions) (string, error) - Decrypt single value
  • client.DecryptBulk(options DecryptBulkOptions) ([]string, error) - Decrypt multiple values
  • client.DecryptBulkFallible(options DecryptBulkOptions) ([]DecryptResult, error) - Decrypt with error handling per item

Error Handling

All operations return Go-style errors. Use standard Go error handling patterns:

if err != nil {
    // Handle error
    log.Printf("Encryption failed: %v", err)
    return err
}

Didn't find what you wanted?

Click here to let us know what was missing from our docs.

About

Encrypt and protect data using industry standard algorithms, field level encryption, a unique data key per record, bulk encryption operations, and decryption level identity verification.

Resources

License

Code of conduct

Stars

Watchers

Forks

Releases

No releases published

Packages

No packages published