keyexchange

package module
v0.0.13 Latest Latest
Warning

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

Go to latest
Published: Sep 16, 2023 License: MIT Imports: 17 Imported by: 0

README

keyexchange

godoc

Swift Diffie–Hellman key exchange (DHKE) library built using the go standard library and golang.org/x/crypto.

Designed to mirror the Swift calmdocs/SwiftKeyExchange library. For example we use the same test keys, nonces, and results in this library as we use in the Swift calmdocs/SwiftKeyExchange library.

A note on security

We have been as conservative as possible when creating this library. See the security discussion below. However, please note that you use this library at your own risk, and we accept no liability in relation to its use.

Example

curve := ecdh.X25519()
hash := sha256.New
kdf := NewHKDF()
aead := NewAESGCM()

plaintext := []byte("exampleplaintext")
additionalData := []byte("abc")

// Create Alice's store
aliceStore, err := New(
    curve,
    hash,
    kdf,
    aead,
    "",
)
if err != nil {
    t.Fatal(err)
}

// Create Bob's store
bobStore, err := New(
    curve,
    hash,
    kdf,
    aead,
    "",
)
if err != nil {
    t.Fatal(err)
}

// Set public keys
err = aliceStore.SetExternalPublicKey(bobStore.PublicKey())
if err != nil {
    t.Fatal(err)
}
err = bobStore.SetExternalPublicKey(aliceStore.PublicKey())
if err != nil {
    t.Fatal(err)
}

// Alice encrypt
v, err := aliceStore.Encrypt(
    plaintext,
    additionalData,
)
if err != nil {
    t.Fatal(err)
}

// Bob decrypt
bobPlaintext, err := bobStore.Decrypt(
    v.KDFNonce,
    v.Ciphertext,
    v.AEADNonce,
    v.AdditionalData,
)
if err != nil {
    t.Fatal(err)
}

// Bob encrypt
v, err = bobStore.Encrypt(
    plaintext,
    additionalData,
)
if err != nil {
    t.Fatal(err)
}

// Alice decrypt
alicePlaintext, err := aliceStore.Decrypt(
    v.KDFNonce,
    v.Ciphertext,
    v.AEADNonce,
    v.AdditionalData,
)
if err != nil {
    t.Fatal(err)
}

fmt.Println(plaintext = alicePlaintext)
fmt.Println(plaintext = bobPlaintext)

Security approach

We have been as conservative as possible when creating this library. For example, we have only used the go standard library and golang.org/x/crypto, and have not used any third party cryptography libraries to create this package.

Please notify us of any security issues by creating a github issue. Please propose how you would like to securely communicate with us (via email or other communication method). Please do not post the security issue on github.

Why not just use HPKE?

Because, when this library was written:

  • HPKE is in beta in Apple's Cryptokit library; and
  • there is no HPKE implementation in golang's standard library, or even in golang.org/x/crypto.

If the above changes, we will probably add HPKE to this library as well as to calmdocs/SwiftKeyExchange.

Documentation

Index

Constants

This section is empty.

Variables

View Source
var ErrAdditionalData = errors.New("additional data authentication error")
View Source
var ErrDecrypt = errors.New("decrypt error")
View Source
var ErrDeriveKey = errors.New("derive key error")
View Source
var ErrEncrypt = errors.New("encrypt error")
View Source
var ErrKDFNotStarted = errors.New("kdf not initialised")
View Source
var ErrMarsal = errors.New("marshal error")
View Source
var ErrPublicKeyNotSet = errors.New("external public key not set")
View Source
var ErrUnmarsal = errors.New("unmarshal error")
View Source
var ErrUnmarsalAEADStore = errors.New("AEADStore unmarshal error")
View Source
var ErrZeroLengthByteRequest = errors.New("requesting byte array with zero length")

Functions

func AuthTimestamp added in v0.0.3

func AuthTimestamp(additionalData []byte, latestTimestamp int64) (bool, int64, error)

AuthTimestamp converts additionalData ([]byte) into an int64 timestamp, ensurest that this timestamp is after the latestTimestamp, and ensures the timestamp is within the last 10 milliseconds

func CurrentTimestamp added in v0.0.3

func CurrentTimestamp() int64

CurrentTimestamp is the current time since 1970 in milliseconds as an int64.

func CurrentTimestampBytes added in v0.0.3

func CurrentTimestampBytes() []byte

CurrentTimestampBytes is the current time since 1970 in milliseconds as a bytes.

func CurrentTimestampString added in v0.0.3

func CurrentTimestampString() string

CurrentTimestampString is the current time since 1970 in milliseconds as a string.

Types

type AEAD

type AEAD interface {
	Encrypt(symmetricKey, symmetricKeyNonce, plaintext, aeadNonce, additionalData []byte) (*AEADStore, error)
	Decrypt(symmetricKey, ciphertext, aeadNonce, additionalData []byte) (plaintext []byte, err error)
}

AEAD is the interface for authenticated encryption with associated data (AEAD) ciphers.

type AEADStore

type AEADStore struct {
	KDFNonce       []byte
	Ciphertext     []byte
	AEADNonce      []byte
	AdditionalData []byte
}

AEADStore stores authenticated encryption with associated data (AEAD) variables.

type AESGCM

type AESGCM struct {
	AEAD
}

AESGCM is the Advanced Encryption Standard (AES) Galois/Counter Mode (GCM) struct. AESGCM conforms with the AEAD interface.

func NewAESGCM

func NewAESGCM() *AESGCM

NewAESGCM creates a new AESGCM struct.

func (*AESGCM) Decrypt

func (c *AESGCM) Decrypt(symmetricKey, ciphertext, aeadNonce, additionalData []byte) (plaintext []byte, err error)

Decrypt decrypts using the symmetricKey, ciphertext, aeadNonce, and additionalData.

func (*AESGCM) Encrypt

func (c *AESGCM) Encrypt(symmetricKey, kdfNonce, plaintext, aeadNonce, additionalData []byte) (*AEADStore, error)

Encrypt encrypts using the symmetricKey, kdfNonce, plaintext, aeadNonce, and additionalData.

type ChaCha20Poly1305

type ChaCha20Poly1305 struct {
	AEAD
}

ChaCha20Poly1305 combines the ChaCha20 stream cipher with the Poly1305 message authentication code. haCha20Poly1305 conforms with the AEAD interface.

func NewChaCha20Poly1305

func NewChaCha20Poly1305() *ChaCha20Poly1305

NewChaCha20Poly1305 creates a new ChaCha20Poly1305 struct.

func (*ChaCha20Poly1305) Decrypt

func (c *ChaCha20Poly1305) Decrypt(symmetricKey, ciphertext, aeadNonce, additionalData []byte) (plaintext []byte, err error)

Decrypt decrypts using the symmetricKey, ciphertext, aeadNonce, and additionalData.

func (*ChaCha20Poly1305) Encrypt

func (c *ChaCha20Poly1305) Encrypt(symmetricKey, kdfNonce, plaintext, aeadNonce, additionalData []byte) (*AEADStore, error)

Encrypt encrypts using the symmetricKey, kdfNonce, plaintext, aeadNonce, and additionalData.

type HKDF

type HKDF struct {
	KDF

	HashSize int
}

HKDF is a key derivation function (KDF) based on the HMAC message authentication code. HKDF conforms with the KDF interface.

func NewHKDF

func NewHKDF() *HKDF

NewHKDF creates a new HKDF struct.

func (*HKDF) DeriveKey

func (k *HKDF) DeriveKey(
	sharedSecret string,
	hash func() hash.Hash,
	nonce []byte,
	additionalData []byte,
) (b []byte, err error)

func (*HKDF) Next added in v0.0.13

func (k *HKDF) Next(r io.Reader) (b []byte, err error)

func (*HKDF) Reader added in v0.0.13

func (k *HKDF) Reader(
	sharedSecret string,
	hash func() hash.Hash,
	nonce []byte,
	additionalData []byte,
) (r io.Reader, err error)

type KDF

type KDF interface {
	Reader(sharedSecret string, hash func() hash.Hash, nonce []byte, additionalData []byte) (r io.Reader, err error)
	Next(r io.Reader) (b []byte, err error)
	DeriveKey(sharedSecret string, hash func() hash.Hash, nonce []byte, additionalData []byte) (b []byte, err error)
}

KDF is the interface for key derivation functions (KDFs).

type KDFStore

type KDFStore struct {
	Key            []byte
	Nonce          []byte
	AdditionalData []byte
}

KDFStore stores key derivation function (KDF) variables.

type Store

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

Store stores key exchange variables.

func New

func New(
	curve ecdh.Curve,
	hash func() hash.Hash,
	kdf KDF,
	aead AEAD,
	externalPublicKey string,
) (s *Store, err error)

New creates a new Store with a random private key. If we do not know the externalPublicKey when we create the Store, the externalPublicKey can be set as an empty string and updated later using SetExternalPublicKey().

func NewWithPrivateKey

func NewWithPrivateKey(
	curve ecdh.Curve,
	hash func() hash.Hash,
	kdf KDF,
	aead AEAD,
	privateKey string,
	externalPublicKey string,
) (s *Store, err error)

New creates a new Store with the specified private key. Only use a specified (i.e. non-random) private key for testing. If we do not know the externalPublicKey when we create the Store, the externalPublicKey can be set as an empty string and updated later using SetExternalPublicKey().

func New_Curve25519_SHA256_HKDF_AESGCM

func New_Curve25519_SHA256_HKDF_AESGCM(externalPublicKey string) (s *Store, err error)

New_Curve25519_SHA256_HKDF_AESGCM creates a new Store using the Curve25519 curve, SHA256 as the hash, HKDF as the key derivation function, and AESGCM as the authenticated encryption with associated data (AEAD) cipher. This is a commonly used Store combination as at September 2023. If we do not know the externalPublicKey when we create the Store, the externalPublicKey can be set as an empty string here and updated later using SetExternalPublicKey().

func New_Curve25519_SHA256_HKDF_AESGCM_WithPrivateKey

func New_Curve25519_SHA256_HKDF_AESGCM_WithPrivateKey(
	privateKey string,
	externalPublicKey string,
) (s *Store, err error)

New_Curve25519_SHA256_HKDF_AESGCM_WithPrivateKey is New_Curve25519_SHA256_HKDF_AESGCM() using the specified private key. Only use specified (i.e. non-random) nonces for testing. If we do not know the externalPublicKey when we create the Store, the externalPublicKey can be set as an empty string here and updated later using SetExternalPublicKey().

func (*Store) Decrypt

func (s *Store) Decrypt(
	symmetricKeyNonce []byte,
	ciphertext []byte,
	aeadNonce []byte,
	additionalData []byte,
) (plaintext []byte, err error)

Decrypt decrypts (i.e. performs the opposite of the Encrypt() function) the ciphertext using the provided symmetricKeyNonce, aeadNonce, and additionalData. All of this information is included in AEADStore structs.

func (*Store) DecryptAEADStore

func (s *Store) DecryptAEADStore(v *AEADStore) (plaintext []byte, err error)

DecryptAEADStore performs Decrypt() using the provided AEADStore struct.

func (*Store) EncodeJSONAndEncryptToJSON

func (s *Store) EncodeJSONAndEncryptToJSON(
	value interface{},
	additionalData []byte,
) (jsonData []byte, err error)

EncodeJSONAndEncryptToJSON JSON is a convenience function that encodes the value to JSON (using the provided additionalData), performs Encrypt(), then encodes the AEADStore as JSON bytes.

func (*Store) EncodeJSONAndEncryptToJSONWithNonces

func (s *Store) EncodeJSONAndEncryptToJSONWithNonces(
	symmetricKeyNonce []byte,
	value interface{},
	aeadNonce []byte,
	additionalData []byte,
) (jsonData []byte, err error)

EncodeJSONAndEncryptToJSONWithNonce is the same as EncodeJSONAndEncryptToJSON but with user specified nonces (i.e. the symmetricKeyNonce and aeadNonce). Set these nonces as nil to create random nonces. Only use specified (i.e. non-random) nonces for testing.

func (*Store) Encrypt

func (s *Store) Encrypt(
	plaintext []byte,
	additionalData []byte,
) (v *AEADStore, err error)

Encrypt creates a random nonce (the symmetricKeyNonce), uses the key derivation function with this symmetricKeyNonceto to create a derived key for our SharedSecret, creates a second random nonce (the aeadNonce), and then encrypts the plaintext with the aeadNonce. The additionalData bytes (which do not need to be secret) are used with both the key derivation function and when encrypting (using encryption with associated data (AEAD) encryption). Encrypt() returns an AEADStore struct containing both nonces, the ciphertext, and the additionalData.

func (*Store) EncryptWithNonces

func (s *Store) EncryptWithNonces(
	symmetricKeyNonce []byte,
	plaintext []byte,
	aeadNonce []byte,
	additionalData []byte,
) (v *AEADStore, err error)

EncryptWithNonces is the same as Encrypt() but with user specified nonces (i.e. the symmetricKeyNonce and aeadNonce). Set these nonces as nil to create random nonces. Only use specified (i.e. non-random) nonces for testing.

func (*Store) ExternalPublicKey

func (s *Store) ExternalPublicKey() string

ExternalPublicKey prints the external Elliptic-curve Diffie–Hellman (ECDH) public key for the Store. We use the ECDH private key and this ECDH ExternalPublicKey to create the SharedSecret for the Store.

func (*Store) PrivateKey

func (s *Store) PrivateKey() string

PrivateKey prints the Elliptic-curve Diffie–Hellman (ECDH) private key for the Store.

func (*Store) PrivateKeyPEM

func (s *Store) PrivateKeyPEM() (string, error)

PrivateKeyPEM returns the private key in the Privacy-Enhanced Mail (PEM) format.

func (*Store) PublicKey

func (s *Store) PublicKey() string

PublicKey prints the Elliptic-curve Diffie–Hellman (ECDH) public key for the Store.

func (*Store) PublicKeyPEM

func (s *Store) PublicKeyPEM() (string, error)

PrivateKeyPEM returns the public key in the Privacy-Enhanced Mail (PEM) format.

func (*Store) SetExternalPublicKey

func (s *Store) SetExternalPublicKey(externalPublicKey string) error

SetExternalPublicKey sets the externalPublicKey, and uses the ECDH private key and the ECDH ExternalPublicKey to create the SharedSecret for the Store. We only need to call this function if we set the externalPublicKey as an empty string when we created the Store.

func (*Store) SharedSecret

func (s *Store) SharedSecret() string

SharedSecret is the Elliptic-curve Diffie–Hellman (ECDH) sharded secret for the Store. We use the ECDH private key and the ECDH ExternalPublicKey to create this SharedSecret for the Store.

func (*Store) UnmarshalJSONAndDecryptFromJSON

func (s *Store) UnmarshalJSONAndDecryptFromJSON(jsonData []byte, v any) (err error)

UnmarshalJSONAndDecryptFromJSON is a convenience function that performs the opposite of EncodeJSONAndEncryptToJSON(). This function unmarshals JSON AEADStore bytes, decrypts, and then unmarshals the resulting JSON plaintext.

func (*Store) UnmarshalJSONAndDecryptFromJSONWithADCheck

func (s *Store) UnmarshalJSONAndDecryptFromJSONWithADCheck(
	jsonData []byte,
	v any,
	authFunc func(additionalData []byte) (bool, error),
) (err error)

UnmarshalJSONAndDecryptFromJSONWithAdditionalData is a convenience function that performs the opposite of EncodeJSONAndEncryptToJSON(). This function unmarshals JSON AEADStore bytes, decrypts, and then unmarshals the resulting JSON plaintext. This function also checks whether the provided additionalData matches with the AEADStore additionalData.

func (*Store) X963RepresentationPrivateKey

func (s *Store) X963RepresentationPrivateKey() []byte

X963RepresentationPrivateKey prints the Store Elliptic-curve Diffie–Hellman (ECDH) public key and private key as bytes. This produces the same result as calling X963RepresentationPrivateKey in the Apple CryptoKit library.

func (*Store) X963RepresentationPublicKey

func (s *Store) X963RepresentationPublicKey() []byte

X963RepresentationPublicKey prints the Store Elliptic-curve Diffie–Hellman (ECDH) public key as bytes. This produces the same result as calling X963RepresentationPublicKey in the Apple CryptoKit library.

Jump to

Keyboard shortcuts

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