Skip to content

Go pkg to exchange base64 encoded AES-256-CFB (or 128 and 224) encrypted HMAC-SHA256 signed messages.

License

Notifications You must be signed in to change notification settings

sa6mwa/kryptograf

Folders and files

NameName
Last commit message
Last commit date

Latest commit

 

History

5 Commits
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 

Repository files navigation

kryptograf

import "github.com/sa6mwa/kryptograf"

kryptograf is a Go package for exchanging AES-128/224/256-CFB encrypted HMAC-SHA256 signed data or messages as either byte slices, base64 raw standard encoded strings or json streams.

Usage example:

k := kryptograf.NewKryptograf()
ciphertextString, err := k.EncryptToString([]byte("Hello world"))
if err != nil {
	panic(err)
}
plaintext, err := k.DecryptString(ciphertextString)
if err != nil {
	panic(err)
}
fmt.Println(string(plaintext))

newKey := kryptograf.NewKey()
if _, err := k.SetEncryptionKey(newKey); err != nil {
	panic(err)
}
ciphertextString, err := k.EncryptToString([]byte("Once upon a time..."))
if err != nil {
	panic(err)
}
plaintext, err := k.DecryptString(ciphertextString)
if err != nil {
	panic(err)
}
fmt.Println(string(plaintext))

You can generate a new base64 encoded key for use with SetEncryptionKey using the newkey command:

go run github.com/sa6mwa/kryptograf/cmd/newkey@latest

This documentation was generated with the following command:

go run github.com/princjef/gomarkdoc/cmd/gomarkdoc@latest > README.md

kryptograf Copyright (c) 2023 Michel Blomgren sa6mwa@gmail.com

Permission is hereby granted, free of charge, to any person obtaining a copy of this software and associated documentation files (the "Software"), to deal in the Software without restriction, including without limitation the rights to use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies of the Software, and to permit persons to whom the Software is furnished to do so, subject to the following conditions:

The above copyright notice and this permission notice shall be included in all copies or substantial portions of the Software.

THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.

Index

Constants

const (
    DefaultEncryptionKey     string = "TfLe2CpLn6qs8t6eQmGJnFGkU8NskfcC9AWOSEFlnLY"
    DefaultPersisterEndpoint string = "http://localhost:11185"
)

Variables

var (
    ErrKeyLength            error = errors.New("key length must be 16, 24 or 32 (for AES-128, AES-192 or AES-256)")
    ErrHMACValidationFailed error = errors.New("HMAC validation failed (corrupt data or wrong encryption key)")
    ErrStop                 error = errors.New("stopped processing json stream")
    ErrKeyExists            error = errors.New("key already exist in KeyValueMap")
)

var (
    GzipByDefault bool = false
)

func Decrypt

func Decrypt(key []byte, data []byte) ([]byte, error)

Decrypt authenticates and decrypts data using a 16, 24 or 32 byte long key (for AES-128-CFB, AES-224-CFB or AES-256-CFB). The data should start with a HMAC-SHA256 hash (32 bytes) initialized with key. The hash function should hash the rest of data which includes an aes.BlockSize long IV and the AES-CFB encrypted data. Returns clear-data or error in case of failure. Returns anystore.ErrHMACValidationFailed when the key is wrong or the message is corrupt or tampered with.

func Encrypt

func Encrypt(key []byte, data []byte) ([]byte, error)

Encrypt encrypts data using a 16, 24 or 32 byte long key (for AES-128-CFB, AES-224-CFB or AES-256-CFB). The cipher-data is prepended with a HMAC-SHA256 hash (32 bytes) and IV (or salt if you prefer). Same key is used for HMAC and. The format of the output data slice is:

b = bytes
[HMAC_of_IV_and_cipherdata_32_b][IV_16_b][cipherdata]

func NewKey

func NewKey() string

NewKey generates a 32 byte base64 encoded random string for use as an AES-256 key. Get a new key from the command line:

go run github.com/sa6mwa/kryptograf/cmd/newkey@latest

func NewPersistenceClient(persisterURL, bearerToken string, k Kryptograf) (*Persistence, error)

Returns a new kryptograf.Persistence API client. Persistence is used to send a kryptograf json stream to github.com/sa6mwa/kryptografpersister. The persister is a HTTP API that consume ciphertext from a KeyValueMap (map[string][]byte) json stream (e.g EncryptToJson or SendFunc) and store in an AnystoreDB. The server (persister) does not know of the client's key and can therefore not decrypt or validate the ciphertext. Keys can be retrieved from the server via GET requests and will be seamlessly decrypted using this Persistence client.

newKey := kryptograf.NewKey()
k, err := kryptograf.NewKryptograf().EnableGzip().SetEncryptionKey(newKey)
if err != nil {
	panic(err)
}
// Assume kryptografpersister is running on http://localhost:11185
pc, err := kryptograf.NewPersistenceClient("", newKey, k)
if err != nil {
	panic(err)
}
if err := pc.Store(context.Background(), "myThing", []byte("Hello world")); err != nil {
	panic(err)
}

func RandomStamp(tm ...time.Time) string

RandomStamp returns time.Now().UTC() as time.Format "20060102T150405.999999999_{19 character random int63}". If one tm is provided in the optional variadic argument, the first time.Time from the tm slice is used instead of time.Now().UTC(). Intended usage of this function is for creating keys for a KV map[string][]byte pair (KeyValueMap) sent as a json stream.

func ToBinaryEncryptionKey(base64RawStdEncoding string) ([]byte, error)

ToBinaryEncryptionKey takes a base64 raw standard encoded string and decodes it into a byte slice.

type KeyValueMap map[string][]byte

func (KeyValueMap) Delete

func (m KeyValueMap) Delete(key string)

Deletes key from KeyValueMap.

func (KeyValueMap) ForEach

func (m KeyValueMap) ForEach(f func(key string, data []byte) error) error

ForEach calls function f for each key-value pair in the KeyValueMap. If function f returns kryptograf.ErrStop it is treated as a break from the loop and ForEach will return a nil error. Any other error returned from f is passed as the output error of ForEach.

func (KeyValueMap) Get

func (m KeyValueMap) Get(key string) []byte

func (KeyValueMap) Len

func (m KeyValueMap) Len() int

Returns the length of the KeyValueMap (number of keys).

func (KeyValueMap) Put

func (m KeyValueMap) Put(key string, data []byte) error

Put stores data under key in KeyValueMap. If key already exist, Put returns kryptograf.ErrKeyExists.

func (KeyValueMap) PutSequential

func (m KeyValueMap) PutSequential(key string, data []byte) string

PutSequential will append _{int} to key (e.g key_1) if key already exist in the KeyValueMap. If key_2 exists, it will try key_3, etc. Method returns the key used to store data (key or key_1, key_2, etc). PutSequential is not go routine safe, use sync/atomic for that.

type Kryptograf interface {
    // GetEncryptionKey returns the instance's encryption key as a byte slice.
    GetEncryptionKey() []byte

    // SetEncryptionKey sets the instance encryption key from a base64
    // raw standard encoded string.
    SetEncryptionKey(key string) (Kryptograf, error)

    // EnableGzip will gzip the message before Encrypt or gunzip the
    // plaintext after Decrypt of ciphertext.
    EnableGzip() Kryptograf

    // DisableGzip will turn off gzipping messages before Encrypt or
    // gunzipping the plaintext after Decrypt of ciphertext.
    DisableGzip() Kryptograf

    // Returns true if the instance will gzip plaintext messages before
    // Encrypt or gunzip ciphertext after Decrypt, false if not.
    Gzip() bool

    // Encrypt enciphers (the optionally gzipped) data byte slice using
    // the instance key and returns a byte slice with encrypted data in
    // the format of the Encrypt function or error in case of
    // failure. The returned byte slice can be decrypted using
    // Kryptograf_Decrypt given matching key and value of gzip boolean.
    Encrypt(data []byte) ([]byte, error)

    // Decrypt deciphers (the optionally gzipped) data byte slice using
    // the instance key and returns a byte slice with the plaintext
    // (decrypted) data or error in case of failure. The format of the
    // ciphertext data slice is documented in the Encrypt function.
    Decrypt(data []byte) ([]byte, error)

    // Recv reads binary ciphertext from r and returns the plaintext as
    // a byte slice or error on failure.
    Recv(r io.Reader) ([]byte, error)

    // Send sends plaintext as ciphertext to io.Writer w. Returns error
    // if encryption or w.Write fails.
    Send(plaintext []byte, w io.Writer) error

    // EncryptToString encrypts data and returns the output of
    // base64.RawStdEncoding.EncodeToString or error in case encryption
    // failed.
    EncryptToString(data []byte) (string, error)

    // DecryptString passes base64RawStdEncodedData through
    // base64.RawStdEncoding.DecodeString and returns the plaintext as a
    // byte slice or error if either decryption or base64 decoding
    // failed.
    DecryptString(base64RawStdEncodedData string) ([]byte, error)

    // RecvFromJson executes at least one json.Decode on j returning
    // exactly one successfully decrypted json key/value pair as a
    // map[string][]byte (kryptograf.KeyValueMap) or error per
    // call. RecvFromJson uses json.Decode underneath and can be
    // repeatedly called on j until returning error io.EOF indicating
    // there is no more data to read from the stream. RecvFromJson uses
    // KeyValueMap_PutSequential to handle any duplicates returned since
    // keys need to be unique. If you require that every incoming json
    // object is successfully decrypted you can set the optional
    // variadic boolean to true, in which case RecvFromJson will return
    // error if any incoming json object fail decryption. Format of the
    // incoming json stream is:
    //
    //	{"msg1":"base64EncodedCipherText"}
    //	{"msg2":"base64EncodedCipherText"}
    //	...etc.
    //
    // RecvFromJson will decrypt json generated with
    // Kryptograf_EncryptToJson.
    //
    // Example:
    //
    //	jsonText := `{"msgkey_eg_timestamp":"base64ciphertext"}`
    //	plaintexts, err := k.RecvFromJson(json.NewDecoder(strings.NewReader(jsonText)))
    //	if err == io.EOF {
    //		// break
    //	} else if err != nil {
    //		// panic(err)
    //	}
    //	for k, v := range plaintexts {
    //		fmt.Printf("%s: %v\n", k, v)
    //	}
    RecvFromJson(j *json.Decoder, allMustDecrypt ...bool) (KeyValueMap, error)

    // EncryptToJson sends the plaintext value as ciphertext value per
    // each key in messages via json.Encode to w. If any of the values
    // in messages fail to be encrypted the function will return an
    // error.
    EncryptToJson(messages KeyValueMap, w io.Writer) error

    // RecvFunc uses json.NewDecoder(jsonStream).Decode to read one or
    // more {"key":"ciphertext"} into a KeyValueMap (map[string][]byte)
    // from jsonStream. Key and decrypted ciphertext is passed as key
    // and plaintext to function f. If json Decode or kryptograf Decrypt
    // returns an error it is passed as err to function f in which case
    // key and plaintext will likely be empty and nil respectively. If
    // function f returns kryptograf.ErrStop it is treated as a break
    // and RecvFunc will return with a nil error. Any other error
    // returned by function f will cause RecvFunc to return immediately
    // with that error while nil errors will continue the receive loop
    // (until jsonStream is closed or an error occurs).
    RecvFunc(jsonStream io.Reader, f func(key string, plaintext []byte, err error) error) error

    // SendFunc uses json.NewEncoder(jsonStream).Encode to write
    // {"key":"ciphertext"} objects to jsonStream compatible with the
    // json encrypt functions provided by Kryptograf (e.g RecvFromJson,
    // RecvFromJsonStream or RecvFunc). Function f is repeatedly called
    // and expected to return a key (for example timestamp, message
    // type, name, etc), plaintext as a byte slice and error code. If
    // the error returned from f is kryptograf.ErrStop it is treated as
    // a break and SendFunc returns a nil error. Any other error
    // returned by f causes SendFunc to return with that error
    // immediately. Plaintext is passed through Encrypt, json encoded
    // into into {"key":"ciphertext"} and written to the
    // jsonStream. Please note, if plaintext is nil, no json message is
    // sent. If there are no errors, function f is repeatedly called and
    // every returned plaintext results in a {"key":"ciphertext"} object
    // written to jsonStream.
    SendFunc(jsonStream io.Writer, f func() (key string, plaintext []byte, err error)) error
}

func NewKryptograf() Kryptograf

NewKryptograf returns a new kryptograf instance with the default encryption key and gzip disabled by default (the value of kryptograf.GzipByDefault). Use the method SetEncryptionKey to set your own encryption key (from a base64 raw standard encoded string).

Persistence API client toward server github.com/sa6mwa/kryptografpersister.

type Persistence struct {
    // contains filtered or unexported fields
}

func (*Persistence) LoadAll

func (p *Persistence) LoadAll(ctx context.Context, f func(key string, plaintext []byte, err error) error) error

LoadAll creates a new http request with ctx and calls function f for every decrypted key-value pair returned by the server. The logic of function f is the same as RecvFunc, refer to the RecvFunc documentation for further information.

func (*Persistence) SetHTTPClient

func (p *Persistence) SetHTTPClient(client *http.Client) *Persistence

SetHTTPClient can be used to replace the default http.Client used by the Persistence client.

func (*Persistence) SetHTTPTransport

func (p *Persistence) SetHTTPTransport(transport *http.Transport) *Persistence

SetHTTPTransport replaces the http.Client Transport.

func (*Persistence) Store

func (p *Persistence) Store(ctx context.Context, key string, plaintext []byte) error

Store persists a single key-value pair in the persister.

func (*Persistence) StoreFunc

func (p *Persistence) StoreFunc(ctx context.Context, f func() (key string, plaintext []byte, err error)) error

StoreFunc persists one or multiple key-value pairs in the persister. Request is ended when function f returns kryptograf.ErrStop or other error (uses SendFunc underneath). If function f returns a nil error it sends the key and plaintext, any error including ErrStop discards any key and plaintext return values.

newkey

import "github.com/sa6mwa/kryptograf/cmd/newkey"

Index

crand

import "github.com/sa6mwa/kryptograf/internal/pkg/crand"

Index

func ExpFloat64() float64

func Float32

func Float32() float32

func Float64

func Float64() float64

func Int

func Int() int

func Int31

func Int31() int32

func Int31n

func Int31n(n int32) int32

func Int63

func Int63() int64

func Int63n

func Int63n(n int64) int64

func Intn

func Intn(n int) int

func NormFloat64() float64

func Perm

func Perm(n int) []int

func Read

func Read(p []byte) (n int, err error)

func ReadRunes(p []rune) (n int, err error)

func Seed

func Seed(seed int64)

These functions are frontends to math/rand...

func Shuffle

func Shuffle(n int, swap func(i, j int))

func Uint32

func Uint32() uint32

func Uint64

func Uint64() uint64

tokenauth

import "github.com/sa6mwa/kryptograf/internal/pkg/tokenauth"

Index

Injector is accessed by Injector_RoundTrip to inject an Authorization Bearer token on every HTTP request.

type Injector struct {
    Token             string
    OriginalTransport http.RoundTripper
}

func (*Injector) RoundTrip

func (t *Injector) RoundTrip(r *http.Request) (*http.Response, error)

Implements the http.RoundTripper interface injecting an Authorization: Bearer token header with every http request. Example:

c := http.Client{}
c.Timeout = 10 * time.Second
c.Transport = &authtoken.Injector{Token: "secret", OriginalTransport: c.Transport}

Generated by gomarkdoc

About

Go pkg to exchange base64 encoded AES-256-CFB (or 128 and 224) encrypted HMAC-SHA256 signed messages.

Resources

License

Stars

Watchers

Forks

Packages

No packages published