cryptutil

package module
v0.2.29 Latest Latest
Warning

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

Go to latest
Published: Sep 27, 2024 License: MIT Imports: 26 Imported by: 5

README

GoDoc

cryptutil

Some tools for handling common crypto tasks not found in the go standard library

ECDH Message encryption

Sometimes you want to send a message. Sometimes you want it encrypted. Sending an encrypted message to a ECDSA key can be painful and come with all sorts of difficulties.

This library aims to provide a simple encryption/decryption scheme that just takes a plaintext and a key and returns an encrypted string.

The decryption function accepts any kind of ECDH handler, allowing the actual private key to be stored into a TPM or a HSM.

Bottle

Bottles are containers for arbitrary data (json, cbor, anything) that can be used to add any number of signatures, encryption layers etc to the underlying message, while keeping recovery of the original message fairly easy.

// Create a new bottle with a message inside
bottle := cryptutil.NewBottle([]byte("s.o.s. to the world"))

// encrypt for Alice OR Bob (either will be able to open the bottle)
bottle.Encrypt(rand.Reader, bob.Public(), alice.Public())
bottle.BottleUp() // bottle in a bottle, so that the signature includes the encryption
bottle.Sign(rand.Reader, alice) // sign from Alice

// Bob is opening the bottle
opener, err := cryptutil.NewOpener(bob)
res, info, err := opener.Open(bottle)
// first, check err to see if opening the bottle was successful
// Then you can inspect info to see which signatures were verified, and how many
// layers of encryption were decrypted

ID Card

ID Cards can be used by entities with a signing key to provide alternate encryption keys.

Documentation

Index

Constants

This section is empty.

Variables

View Source
var (
	ErrNoAppropriateKey   = errors.New("no appropriate key available to open bottle")
	ErrVerifyFailed       = errors.New("signature verification failed")
	ErrKeyNotFound        = wraperr("the key was not found", fs.ErrNotExist)
	ErrGroupNotFound      = wraperr("the group was not found", fs.ErrNotExist)
	ErrKeyUnfit           = errors.New("the provided key was not fit")
	ErrEncryptNoRecipient = errors.New("cannot encrypt a message without at least one valid recipient")
)
View Source
var EmptyOpener = &Opener{}

EmptyOpener is an opener without any keys that can open bottles, but can't check keys

Functions

func DecryptShortBuffer added in v0.1.1

func DecryptShortBuffer(k []byte, rcvd any) ([]byte, error)

DecryptShortBuffer decrypts a given buffer

func ECDHDecrypt

func ECDHDecrypt(data []byte, privateKey ECDHHandler) ([]byte, error)

ECDHDecrypt decrypts data received for us, using the private key passed (can be a tpm, etc)

func ECDHEncrypt

func ECDHEncrypt(rnd io.Reader, data []byte, remote *ecdh.PublicKey) ([]byte, error)

ECDHEncrypt encrypts data for receiving by remote

func EncryptShortBuffer added in v0.1.1

func EncryptShortBuffer(rand io.Reader, k []byte, rcvd crypto.PublicKey) ([]byte, error)

EncryptShortBuffer performs a simple encryption of a buffer

func Hash added in v0.1.1

func Hash(b []byte, alg ...func() hash.Hash) []byte

Hash is a helper function to perform hashes on buffers, including multi-level hashing

func MemClr added in v0.1.1

func MemClr(b []byte)

MemClr is a simple function that will clear a buffer in order to make it easier to reset memory storing private keys on defer.

func Sign added in v0.1.4

func Sign(rand io.Reader, key crypto.Signer, buf []byte, opts ...crypto.SignerOpts) ([]byte, error)

Sign generates a signature for the given buffer. Hash will be performed as needed

func Verify added in v0.1.4

func Verify(key crypto.PublicKey, buf, sig []byte, opts ...crypto.SignerOpts) error

Verify will verify the given buffer against the signature, depending on the key type. If the key is a RSA key and PSS options are given, then the signature will be handled as a PSS signature.

Unlike Verify methods found in most packages, this one takes in the actual buffer to be signed and will perform the hash if it needs to be done.

Types

type Bottle added in v0.1.1

type Bottle struct {
	Header     map[string]any      `json:"hdr,omitempty"` // extra values to be stored, will not be signed/encrypted unless the message is bottled
	Message    []byte              `json:"msg"`
	Format     MessageFormat       `json:"fmt"`
	Recipients []*MessageRecipient `json:"dst,omitempty"` // if Format != ClearText
	Signatures []*MessageSignature `json:"sig,omitempty"` // signature
	// contains filtered or unexported fields
}

Bottle is a signed, encrypted message container. Any Format other than ClearText means the Message contains a Bottle that has been encrypted.

func AsCborBottle added in v0.1.3

func AsCborBottle(data []byte) *Bottle

AsCborBottle considers data to be a cbor-encoded Bottle, and will return a Bottle container matching this assumption

func AsJsonBottle added in v0.1.3

func AsJsonBottle(data []byte) *Bottle

AsJsonBottle considers data to be a json-encoded Bottle, and will return a Bottle container matching this assumption

func Marshal added in v0.2.1

func Marshal(data any) (*Bottle, error)

Marshal will use cbor to marshal data into a bottle

func MarshalJson added in v0.2.2

func MarshalJson(data any) (*Bottle, error)

MarshalJson will use json to marshal data into a bottle

func NewBottle added in v0.1.1

func NewBottle(data []byte) *Bottle

NewBottle will return a new clean bottle only containing the provided data

func (*Bottle) BottleUp added in v0.1.1

func (m *Bottle) BottleUp() error

BottleUp encodes the current message into itself, allowing application of extra layers

func (*Bottle) Child added in v0.2.4

func (b *Bottle) Child() (*Bottle, error)

Child is the reverse operation as BottleUp and will return the bottle's child. This will fail if the bottle is encrypted or does not contain another bottle.

func (*Bottle) Encrypt added in v0.1.1

func (m *Bottle) Encrypt(rand io.Reader, recipients ...crypto.PublicKey) error

Encrypt encrypts the message so only recipients can decrypt it

func (*Bottle) IsCleanBottle added in v0.1.1

func (m *Bottle) IsCleanBottle() bool

IsCleanBottle returns true if the Bottle is clean (ie. so signature has been scribbed on top) and contains another Bottle.

func (*Bottle) Sign added in v0.1.1

func (m *Bottle) Sign(rand io.Reader, key crypto.Signer, opts ...crypto.SignerOpts) error

Sign signs the message, and can be called multiple times. Any message can be signed, including a raw message. It is however recommanded to bottle up an encrypted message before signing in order to ensure the encryption information is signed too.

Attempting to apply encryption to a message with a signature will always cause it to be bottled up

type ECDHHandler

type ECDHHandler interface {
	ECDH(remote *ecdh.PublicKey) ([]byte, error)
}

type IDCard added in v0.1.1

type IDCard struct {
	Self    []byte            `json:"self" cbor:"1,keyasint"` // our own public key (PKIX)
	Issued  time.Time         `json:"iss" cbor:"2,keyasint"`  // issuance date. If two IDCard exist for the same public key, the most recent one will be taken into account
	SubKeys []*SubKey         `json:"sub" cbor:"3,keyasint"`  // known sub keys
	Revoke  []*SubKey         `json:"rev" cbor:"4,keyasint"`  // any key into the revoke list will be strongly rejected
	Groups  []*Membership     `json:"grp" cbor:"5,keyasint"`  // groups this key is member of
	Meta    map[string]string `json:"meta" cbor:"6,keyasint"` // self-defined metadata
}

IDCard is a basic ID for a given signature key that allows it to specify keys that can be used for encryption/etc

func NewIDCard added in v0.1.1

func NewIDCard(k crypto.PublicKey) (*IDCard, error)

NewIDCard generates a new ID card for the given public key

func (*IDCard) AddKeychain added in v0.2.12

func (id *IDCard) AddKeychain(kc *Keychain)

AddKeychain adds the keys found in Keychain to the IDCard.

func (*IDCard) FindGroup added in v0.2.10

func (id *IDCard) FindGroup(k any) (*Membership, error)

FindGroup locates the Membership matching the given key

func (*IDCard) FindKey added in v0.2.9

func (id *IDCard) FindKey(k any, create bool) (*SubKey, error)

FindKey locates the SubKey matching the given key, and optionally creates one if create is set to true

func (*IDCard) GetKeys added in v0.1.1

func (id *IDCard) GetKeys(purpose string) []crypto.PublicKey

GetKeys returns all the keys of an IDCard that fit a given purpose

func (*IDCard) SetKeyDuration added in v0.1.1

func (id *IDCard) SetKeyDuration(k crypto.PublicKey, t time.Duration) error

SetKeyDuration specifies the duration for the given key

func (*IDCard) SetKeyPurposes added in v0.1.1

func (id *IDCard) SetKeyPurposes(k crypto.PublicKey, purposes ...string) error

SetKeyPurposes specifies the purpose of a given key (sign, decrypt, etc)

func (*IDCard) Sign added in v0.1.1

func (id *IDCard) Sign(rand io.Reader, k crypto.Signer) ([]byte, error)

Sign will return a signed bottle containing this ID Card

func (*IDCard) TestKeyPurpose added in v0.2.9

func (id *IDCard) TestKeyPurpose(k any, purpose string) error

TestKeyPurpose return nil if the provided key is fit for the given purpose, a not found error if the key couldn't be found, or a ErrKeyUnfit

func (*IDCard) UnmarshalBinary added in v0.1.1

func (id *IDCard) UnmarshalBinary(b []byte) error

UnmarshalBinary will read a signed ID card, returning an error if it wasn't signed

func (*IDCard) UpdateGroups added in v0.2.15

func (id *IDCard) UpdateGroups(data [][]byte) error

UpdateGroups update the attached memberships based on the provided data

type Keychain added in v0.2.11

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

Keychain is an object storing private keys that can be used to sign or decrypt things.

func NewKeychain added in v0.2.11

func NewKeychain() *Keychain

NewKeychain returns a new, empty keychain

func (*Keychain) AddKey added in v0.2.11

func (kc *Keychain) AddKey(k any) error

AddKey adds a key to the keychain. The value passed must be a PrivateKey whose Public() method returns a public key object that can be marshalled by crypto/x509.MarshalPKIXPublicKey. If another Keychain is passed all its keys will be added.

func (*Keychain) AddKeys added in v0.2.11

func (kc *Keychain) AddKeys(keys ...any) error

AddKeys adds a number of keys to the keychain, and stops at the first error found.

func (*Keychain) All added in v0.2.25

func (kc *Keychain) All(yield func(PrivateKey) bool)

All returns all the keys in the Keychain

func (*Keychain) FirstSigner added in v0.2.18

func (kc *Keychain) FirstSigner() crypto.Signer

FirstSigner returns the first crypto.Signer that was added to this Keychain.

func (*Keychain) GetKey added in v0.2.11

func (kc *Keychain) GetKey(public any) (PrivateKey, error)

GetKey returns the private key matching the passed public key, if known. A []byte of the PKIX marshalled public key or a public key object can be passed.

func (*Keychain) GetSigner added in v0.2.11

func (kc *Keychain) GetSigner(public any) (crypto.Signer, error)

GetSigner will return the signer matching the public key

func (*Keychain) Sign added in v0.2.11

func (kc *Keychain) Sign(rand io.Reader, publicKey any, buf []byte, opts ...crypto.SignerOpts) ([]byte, error)

Sign will use the specified key from the keychain to sign the given buffer. Unlike Go's standard sign method, the whole buffer should be passed and will be signed as needed.

func (*Keychain) Signers added in v0.2.29

func (kc *Keychain) Signers(yield func(crypto.Signer) bool)

Signers returns all the signing-capable keys in the Keychain

type Membership added in v0.1.4

type Membership struct {
	Subject   []byte            `json:"sub" cbor:"1,keyasint"` // must be == parent.Self (if empty, fill with parent.Self before sig)
	Key       []byte            `json:"key" cbor:"2,keyasint"` // group key (group identification)
	Status    string            `json:"sta" cbor:"3,keyasint"` // status of membership (valid|suspended)
	Issued    time.Time         `json:"iss" cbor:"4,keyasint"` // update time of membership info
	Info      map[string]string `json:"nfo" cbor:"5,keyasint"` // subject information (name, etc)
	SignKey   []byte            `json:"sky" cbor:"6,keyasint"` // signature generating key (must be listed as sign key for the Key's IDCard)
	Signature []byte            `json:"sig" cbor:"7,keyasint"` // signature of structure with sign=nil by group key
}

Membership is a membership in a group.

func NewMembership added in v0.1.4

func NewMembership(member *IDCard, key []byte) *Membership

func (*Membership) Sign added in v0.1.4

func (m *Membership) Sign(rand io.Reader, key crypto.Signer, opts ...crypto.SignerOpts) error

Sign signs the membership using the provided key

func (*Membership) SignatureBytes added in v0.1.4

func (m *Membership) SignatureBytes() ([]byte, error)

SignatureBytes returns a representation of Membership that can be used to sign or verify the structure

func (*Membership) Verify added in v0.2.3

func (m *Membership) Verify(groupId *IDCard) error

Verify ensures the signature is correct. If the group ID is known, it must be passed.

type MessageFormat added in v0.1.1

type MessageFormat int
const (
	ClearText  MessageFormat = iota
	CborBottle               // bottle in a bottle
	AES                      // AES+AEAD encrypted cbor bottle
	JsonBottle               // bottle in a bottle (json version)
)

type MessageRecipient added in v0.1.1

type MessageRecipient struct {
	Type      int    `json:"typ,omitempty"` // always 0 (for now)
	Recipient []byte `json:"key"`           // recipient's public key
	Data      []byte `json:"dat"`           // encrypted key payload (only recipient's eyes)
	// contains filtered or unexported fields
}

func (*MessageRecipient) OpenWith added in v0.1.1

func (r *MessageRecipient) OpenWith(k any) ([]byte, error)

type MessageSignature added in v0.1.1

type MessageSignature struct {
	Type   int    `json:"typ,omitempty"` // always 0 (for now)
	Signer []byte `json:"key"`           // signature's key
	Data   []byte `json:"dat"`           // signature payload, similar format to jwt (NOTE: ECDSA signatures are weird)
	// contains filtered or unexported fields
}

func (*MessageSignature) Verify added in v0.1.1

func (sig *MessageSignature) Verify(buf []byte, opts ...crypto.SignerOpts) error

type OpenResult added in v0.1.1

type OpenResult struct {
	Decryption int                 // number of performed decryptions
	Signatures []*MessageSignature // verified message signatures
	Bottles    []*Bottle
}

func (*OpenResult) First added in v0.2.27

func (or *OpenResult) First() *Bottle

First returns the first (outside-most) bottle, that will be what has been passed to Open

func (*OpenResult) Last added in v0.2.1

func (or *OpenResult) Last() *Bottle

Last returns the last (inside-most) bottle, which will contain any relevant meta-data

func (*OpenResult) SignedBy added in v0.2.24

func (or *OpenResult) SignedBy(signer any) bool

SignedBy returns true if the message was signed by the signer (either a public key or a IDCard)

type Opener added in v0.1.1

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

Opener allows opening a Bottle

func MustOpener added in v0.2.1

func MustOpener(keys ...any) *Opener

MustOpener returns an opener that can be used to open a Bottle and panics if it fails

func NewOpener added in v0.1.1

func NewOpener(keys ...any) (*Opener, error)

NewOpener returns an opener that can be used to open a Bottle using any or all of the given keys.

func (*Opener) Open added in v0.1.1

func (o *Opener) Open(b *Bottle) ([]byte, *OpenResult, error)

Open opens the given Bottle, decrypting any encrypted elements, checking all signatures and returning the embedded buffer in the end

func (*Opener) OpenCbor added in v0.1.3

func (o *Opener) OpenCbor(b []byte) ([]byte, *OpenResult, error)

OpenCbor opens the given Bottle encoded as cbor data.

func (*Opener) OpenHttp added in v0.2.26

func (o *Opener) OpenHttp(req *http.Request) ([]byte, *OpenResult, error)

OpenHttp will read the data from a http.Request handling the content-type header.

func (*Opener) OpenJson added in v0.1.3

func (o *Opener) OpenJson(b []byte) ([]byte, *OpenResult, error)

OpenJson opens the given Bottle encoded as json data.

func (*Opener) Unmarshal added in v0.2.1

func (o *Opener) Unmarshal(b *Bottle, v any) (*OpenResult, error)

Unmarshal will open the given bottle and pour the contents into v

func (*Opener) UnmarshalCbor added in v0.2.1

func (o *Opener) UnmarshalCbor(b []byte, v any) (*OpenResult, error)

UnmarshalCbor will open the given cbor-encoded bottle and pour the contents into v

func (*Opener) UnmarshalHttp added in v0.2.26

func (o *Opener) UnmarshalHttp(req *http.Request, v any) (*OpenResult, error)

UnmarshalHttp will read the data from a http.Request and unmarshal it into v.

func (*Opener) UnmarshalJson added in v0.2.1

func (o *Opener) UnmarshalJson(b []byte, v any) (*OpenResult, error)

UnmarshalJson will open the given json-encoded bottle and pour the contents into v

type PrivateKey added in v0.2.16

type PrivateKey interface {
	Public() crypto.PublicKey
}

PrivateKey represents a private key using an unspecified algorithm.

All private keys must implement a method to retrieve the matching public key. The ones in the standard lbirary do.

type PublicKeyIntf added in v0.2.17

type PublicKeyIntf interface {
	Equal(x crypto.PublicKey) bool
}

PublicKeyIntf represents a public key using an unspecified algorithm.

all public key types in the standard library implement this interface

func PublicKey added in v0.2.14

func PublicKey(privKey crypto.PrivateKey) PublicKeyIntf

PublicKey returns the public key for a given private key, or nil if the argumlent is not a private key or if its Public() method returned nil.

type SubKey added in v0.1.1

type SubKey struct {
	Key      []byte     `json:"key" cbor:"1,keyasint"`                     // public key as PKIX
	Issued   time.Time  `json:"iss" cbor:"2,keyasint"`                     // issuance (addition) date
	Expires  *time.Time `json:"exp,omitempty" cbor:"3,keyasint,omitempty"` // expiration date (if any)
	Purposes []string   `json:"pur" cbor:"4,keyasint"`                     // purposes: can contain "sign", "decrypt"
}

SubKey is a key found in a given id card

func (*SubKey) AddPurpose added in v0.2.21

func (sk *SubKey) AddPurpose(purpose ...string)

func (*SubKey) HasPurpose added in v0.2.9

func (sk *SubKey) HasPurpose(purpose string) bool

HasPurpose returns true if the key has the specified purpose listed

func (*SubKey) String added in v0.2.28

func (sk *SubKey) String() string

Jump to

Keyboard shortcuts

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