From 9ee29882ca740f41161d52aed6071df6aa1d92f2 Mon Sep 17 00:00:00 2001 From: Silke Date: Fri, 28 Apr 2017 20:13:37 +0200 Subject: [PATCH 1/5] Add errors and more support functions --- support/error.go | 32 ++++++++++++++++++++++++++++++++ support/support.go | 32 ++++++++++++++++++++++++-------- 2 files changed, 56 insertions(+), 8 deletions(-) create mode 100644 support/error.go diff --git a/support/error.go b/support/error.go new file mode 100644 index 0000000..7daa19c --- /dev/null +++ b/support/error.go @@ -0,0 +1,32 @@ +package support + +import "strconv" + +// KeySizeError is an error that occurs when a key has an incorrect length. +type KeySizeError int + +func (k KeySizeError) Error() string { + return "invalid key size " + strconv.Itoa(int(k)) +} + +// NonceSizeError is an error that occurs when a nonce has an incorrect length. +type NonceSizeError int + +func (k NonceSizeError) Error() string { + return "invalid nonce size " + strconv.Itoa(int(k)) +} + +// NilPointerError is an error that occurs when a pointer is a nil pointer +type NilPointerError string + +func (k NilPointerError) Error() string { + return string(k) + " is a nil pointer" +} + +// VerificationError is an error that occurs when the verification of +// a signature or authentication tag fails. +type VerificationError struct {} + +func (k VerificationError) Error() string { + return "verification failed" +} diff --git a/support/support.go b/support/support.go index cbac049..657412b 100644 --- a/support/support.go +++ b/support/support.go @@ -1,30 +1,46 @@ +// Package support implements support functions and errors that are used by by other libsodium-go packages. package support import "fmt" -// -// Internal support functions -// - -// CheckSize verifies the expected size of an input or output byte array. +// CheckSize checks if the length of a byte slice is equal to the expected length, +// and panics when this is not the case. func CheckSize(buf []byte, expected int, descrip string) { if len(buf) != expected { panic(fmt.Sprintf("Incorrect %s buffer size, expected (%d), got (%d).", descrip, expected, len(buf))) } } +// CheckSizeMin checks if the length of a byte slice is greater or equal than a minimum length, +// and panics when this is not the case. func CheckSizeMin(buf []byte, min int, descrip string) { if len(buf) < min { panic(fmt.Sprintf("Incorrect %s buffer size, expected (>%d), got (%d).", descrip, min, len(buf))) } } -func CheckSizeInRange(size int, min int, max int, descrip string) { - if size < min || size > max { - panic(fmt.Sprintf("Incorrect %s buffer size, expected (%d - %d), got (%d).", descrip, min, max, size)) +// CheckSizeInRange checks if the length of a byte slice is between a lower and upper boundaries. +func CheckSizeInRange(buf []byte, min int, max int, descrip string) { + if len(buf) < min || len(buf) > max { + panic(fmt.Sprintf("Incorrect %s buffer size, expected (%d - %d), got (%d).", descrip, min, max, len(buf))) + } +} + +// CheckSizeGreaterOrEqual checks if the length of a byte slice is greater or equal to that of a second byte slice. +func CheckSizeGreaterOrEqual(a, b []byte, aDescription, bDescription string) { + if len(a) < len(b) { + panic(fmt.Sprintf("%s smaller than %s", aDescription, bDescription)) + } +} + +// NilPanic is a shorthand that results in a panic when called with true. +func NilPanic(t bool, description string) { + if t { + panic(description + " is a nil pointer") } } +// BytePointer returns a pointer to the start of a byte slice, or nil when the slice is empty. func BytePointer(b []byte) *uint8 { if len(b) > 0 { return &b[0] From 190f71ae9c7b78334fde553ef93a85504130b8c0 Mon Sep 17 00:00:00 2001 From: Silke Date: Sun, 16 Jul 2017 12:06:47 +0200 Subject: [PATCH 2/5] Split range checks to Size and Int CheckSizeInRange now has a []byte argument CheckIntInRange has an Int argument --- cryptogenerichash/crypto_generichash.go | 10 +++++----- cryptokdf/crypto_kdf.go | 2 +- support/support.go | 7 +++++++ 3 files changed, 13 insertions(+), 6 deletions(-) diff --git a/cryptogenerichash/crypto_generichash.go b/cryptogenerichash/crypto_generichash.go index 8e084c0..0a74753 100644 --- a/cryptogenerichash/crypto_generichash.go +++ b/cryptogenerichash/crypto_generichash.go @@ -40,11 +40,11 @@ func CryptoGenericHashStateBytes() int { // I took care of the typedef confusions. This should work okay. func CryptoGenericHash(outlen int, in []byte, key []byte) ([]byte, int) { - support.CheckSizeInRange(outlen, CryptoGenericHashBytesMin(), CryptoGenericHashBytesMax(), "out") + support.CheckIntInRange(outlen, CryptoGenericHashBytesMin(), CryptoGenericHashBytesMax(), "out") // Check size of key only if actually given if len(key) > 0 { - support.CheckSizeInRange(len(key), CryptoGenericHashKeyBytesMin(), CryptoGenericHashKeyBytesMax(), "key") + support.CheckSizeInRange(key, CryptoGenericHashKeyBytesMin(), CryptoGenericHashKeyBytesMax(), "key") } out := make([]byte, outlen) @@ -61,11 +61,11 @@ func CryptoGenericHash(outlen int, in []byte, key []byte) ([]byte, int) { // I took care of the typedef confusions. This should work okay. func CryptoGenericHashInit(key []byte, outlen int) (*C.struct_crypto_generichash_blake2b_state, int) { - support.CheckSizeInRange(outlen, CryptoGenericHashBytesMin(), CryptoGenericHashBytesMax(), "out") + support.CheckIntInRange(outlen, CryptoGenericHashBytesMin(), CryptoGenericHashBytesMax(), "out") // Check size of key only if actually given if len(key) > 0 { - support.CheckSizeInRange(len(key), CryptoGenericHashKeyBytesMin(), CryptoGenericHashKeyBytesMax(), "key") + support.CheckSizeInRange(key, CryptoGenericHashKeyBytesMin(), CryptoGenericHashKeyBytesMax(), "key") } state := new(C.struct_crypto_generichash_blake2b_state) @@ -89,7 +89,7 @@ func CryptoGenericHashUpdate(state *C.struct_crypto_generichash_blake2b_state, i } func CryptoGenericHashFinal(state *C.struct_crypto_generichash_blake2b_state, outlen int) (*C.struct_crypto_generichash_blake2b_state, []byte, int) { - support.CheckSizeInRange(outlen, CryptoGenericHashBytesMin(), CryptoGenericHashBytesMax(), "out") + support.CheckIntInRange(outlen, CryptoGenericHashBytesMin(), CryptoGenericHashBytesMax(), "out") out := make([]byte, outlen) exit := int(C.crypto_generichash_final( state, diff --git a/cryptokdf/crypto_kdf.go b/cryptokdf/crypto_kdf.go index 660150c..e761051 100644 --- a/cryptokdf/crypto_kdf.go +++ b/cryptokdf/crypto_kdf.go @@ -31,7 +31,7 @@ func CryptoKdfKeygen() []byte { func CryptoKdfDeriveFromKey(l int, i uint64, c string, k []byte) ([]byte, int) { support.CheckSize(k, CryptoKdfKeybytes(), "keybytes") support.CheckSize([]byte(c), CryptoKdfContextbytes(), "contextbytes") - support.CheckSizeInRange(l, CryptoKdfBytesMin(), CryptoKdfBytesMax(), "subkey_len") + support.CheckIntInRange(l, CryptoKdfBytesMin(), CryptoKdfBytesMax(), "subkey_len") out := make([]byte, l) exit := int(C.crypto_kdf_derive_from_key( diff --git a/support/support.go b/support/support.go index 657412b..414cd25 100644 --- a/support/support.go +++ b/support/support.go @@ -19,6 +19,13 @@ func CheckSizeMin(buf []byte, min int, descrip string) { } } +// CheckIntInRange checks if the size of an integer is between a lower and upper boundaries. +func CheckIntInRange(n int, min int, max int, descrip string) { + if n < min || n > max { + panic(fmt.Sprintf("Incorrect %s size, expected (%d - %d), got (%d).", descrip, min, max, n)) + } +} + // CheckSizeInRange checks if the length of a byte slice is between a lower and upper boundaries. func CheckSizeInRange(buf []byte, min int, max int, descrip string) { if len(buf) < min || len(buf) > max { From aa0fbc0c361cdbec272c4ced55064d5499f6f5b6 Mon Sep 17 00:00:00 2001 From: Silke Date: Wed, 22 Mar 2017 14:30:40 +0100 Subject: [PATCH 3/5] Wrap all AEAD functions in new directory structure Includes a significant refactor: - Return error instead of integer - Replace getter functions with constants - Move prepared encryption to cipher.AEAD compatible structure - Add tests --- .../aead/aes256gcm/crypto_aead_aes256gcm.go | 138 ++++++++++++++++++ .../aes256gcm/crypto_aead_aes256gcm_test.go | 87 +++++++++++ .../crypto_aead_chacha20poly1305.go | 133 +++++++++++++++++ .../crypto_aead_chacha20poly1305_test.go | 82 +++++++++++ .../crypto_aead_chacha20poly1305_ietf.go | 133 +++++++++++++++++ .../crypto_aead_chacha20poly1305_ietf_test.go | 82 +++++++++++ crypto/aead/crypto_aead.go | 48 ++++++ crypto/aead/crypto_aead_aes256gcm.go | 132 +++++++++++++++++ crypto/aead/crypto_aead_aes256gcm_test.go | 95 ++++++++++++ .../crypto_aead_xchacha20poly1305_ietf.go | 133 +++++++++++++++++ ...crypto_aead_xchacha20poly1305_ietf_test.go | 82 +++++++++++ 11 files changed, 1145 insertions(+) create mode 100644 crypto/aead/aes256gcm/crypto_aead_aes256gcm.go create mode 100644 crypto/aead/aes256gcm/crypto_aead_aes256gcm_test.go create mode 100644 crypto/aead/chacha20poly1305/crypto_aead_chacha20poly1305.go create mode 100644 crypto/aead/chacha20poly1305/crypto_aead_chacha20poly1305_test.go create mode 100644 crypto/aead/chacha20poly1305ietf/crypto_aead_chacha20poly1305_ietf.go create mode 100644 crypto/aead/chacha20poly1305ietf/crypto_aead_chacha20poly1305_ietf_test.go create mode 100644 crypto/aead/crypto_aead.go create mode 100644 crypto/aead/crypto_aead_aes256gcm.go create mode 100644 crypto/aead/crypto_aead_aes256gcm_test.go create mode 100644 crypto/aead/xchacha20poly1305ietf/crypto_aead_xchacha20poly1305_ietf.go create mode 100644 crypto/aead/xchacha20poly1305ietf/crypto_aead_xchacha20poly1305_ietf_test.go diff --git a/crypto/aead/aes256gcm/crypto_aead_aes256gcm.go b/crypto/aead/aes256gcm/crypto_aead_aes256gcm.go new file mode 100644 index 0000000..6d612cc --- /dev/null +++ b/crypto/aead/aes256gcm/crypto_aead_aes256gcm.go @@ -0,0 +1,138 @@ +// Package aes256gcm contains the libsodium bindings for AES256-GCM. +package aes256gcm + +// #cgo pkg-config: libsodium +// #include +// #include +import "C" +import "github.com/GoKillers/libsodium-go/support" + +// Sodium should always be initialised +func init() { + C.sodium_init() +} + +// Sizes of nonces, key and mac. +const ( + KeyBytes int = C.crypto_aead_aes256gcm_KEYBYTES // Size of a secret key in bytes + NSecBytes int = C.crypto_aead_aes256gcm_NSECBYTES // Size of a secret nonce in bytes + NonceBytes int = C.crypto_aead_aes256gcm_NPUBBYTES // Size of a nonce in bytes + ABytes int = C.crypto_aead_aes256gcm_ABYTES // Size of an authentication tag in bytes +) + +// Key represents a secret key +type Key [KeyBytes]byte + +// IsAvailable returns true if AES256 is available on the current CPU +func IsAvailable() bool { + return C.crypto_aead_aes256gcm_is_available() != 0 +} + +// GenerateKey generates a secret key +func GenerateKey() *Key { + k := new(Key) + C.crypto_aead_aes256gcm_keygen((*C.uchar)(&k[0])) + return k +} + +// Encrypt a message `m` with additional data `ad` using a nonce `npub` and a secret key `k`. +// A ciphertext (including authentication tag) and encryption status are returned. +func Encrypt(m, ad, nonce, k []byte) (c []byte) { + support.CheckSize(k, KeyBytes, "secret key") + support.CheckSize(nonce, NonceBytes, "public nonce") + + c = make([]byte, len(m)+ABytes) + + C.crypto_aead_aes256gcm_encrypt( + (*C.uchar)(support.BytePointer(c)), + (*C.ulonglong)(nil), + (*C.uchar)(support.BytePointer(m)), + (C.ulonglong)(len(m)), + (*C.uchar)(support.BytePointer(ad)), + (C.ulonglong)(len(ad)), + (*C.uchar)(nil), + (*C.uchar)(&nonce[0]), + (*C.uchar)(&k[0])) + + return +} + +// Decrypt and verify a ciphertext `c` using additional data `ad`, nonce `npub` and secret key `k`. +// Returns the decrypted message and verification status. +func Decrypt(c, ad, nonce, k []byte) (m []byte, err error) { + support.CheckSize(k, KeyBytes, "secret key") + support.CheckSize(nonce, NonceBytes, "public nonce") + support.CheckSizeMin(c, ABytes, "ciphertext") + + m = make([]byte, len(c)-ABytes) + + exit := C.crypto_aead_aes256gcm_decrypt( + (*C.uchar)(support.BytePointer(m)), + (*C.ulonglong)(nil), + (*C.uchar)(nil), + (*C.uchar)(&c[0]), + (C.ulonglong)(len(c)), + (*C.uchar)(support.BytePointer(ad)), + (C.ulonglong)(len(ad)), + (*C.uchar)(&nonce[0]), + (*C.uchar)(&k[0])) + + if exit != 0 { + err = &support.VerificationError{} + } + + return +} + +// EncryptDetached encrypts a message `m` with additional data `ad` using +// a nonce `npub` and a secret key `k`. +// A ciphertext, authentication tag and encryption status are returned. +func EncryptDetached(m, ad, nonce, k []byte) (c, mac []byte) { + support.CheckSize(k, KeyBytes, "secret key") + support.CheckSize(nonce, NonceBytes, "public nonce") + + c = make([]byte, len(m)) + mac = make([]byte, ABytes) + + C.crypto_aead_aes256gcm_encrypt_detached( + (*C.uchar)(support.BytePointer(c)), + (*C.uchar)(&mac[0]), + (*C.ulonglong)(nil), + (*C.uchar)(support.BytePointer(m)), + (C.ulonglong)(len(m)), + (*C.uchar)(support.BytePointer(ad)), + (C.ulonglong)(len(ad)), + (*C.uchar)(nil), + (*C.uchar)(&nonce[0]), + (*C.uchar)(&k[0])) + + return +} + +// DecryptDetached decrypts and verifies a ciphertext `c` with authentication tag `mac` +// using additional data `ad`, nonce `npub` and secret key `k`. +// Returns the decrypted message and verification status. +func DecryptDetached(c, mac, ad, nonce, k []byte) (m []byte, err error) { + support.CheckSize(k, KeyBytes, "secret key") + support.CheckSize(nonce, NonceBytes, "public nonce") + support.CheckSize(mac, ABytes, "mac") + + m = make([]byte, len(c)) + + exit := C.crypto_aead_aes256gcm_decrypt_detached( + (*C.uchar)(support.BytePointer(m)), + (*C.uchar)(nil), + (*C.uchar)(support.BytePointer(c)), + (C.ulonglong)(len(c)), + (*C.uchar)(&mac[0]), + (*C.uchar)(support.BytePointer(ad)), + (C.ulonglong)(len(ad)), + (*C.uchar)(&nonce[0]), + (*C.uchar)(&k[0])) + + if exit != 0 { + err = &support.VerificationError{} + } + + return +} diff --git a/crypto/aead/aes256gcm/crypto_aead_aes256gcm_test.go b/crypto/aead/aes256gcm/crypto_aead_aes256gcm_test.go new file mode 100644 index 0000000..4cd150f --- /dev/null +++ b/crypto/aead/aes256gcm/crypto_aead_aes256gcm_test.go @@ -0,0 +1,87 @@ +package aes256gcm + +import ( + "bytes" + "github.com/google/gofuzz" + "testing" +) + +var testCount = 100000 + +type TestData struct { + Message []byte + Ad []byte + Key Key + Nonce [NonceBytes]byte +} + +func Test(t *testing.T) { + // Skip the test if unsupported on this platform + if !IsAvailable() { + t.Skip("The CPU does not support this implementation of AES256GCM.") + } + + // Test the key generation + if *GenerateKey() == (Key{}) { + t.Error("Generated key is zero") + } + + // Test the length of NSecBytes + if NSecBytes != 0 { + t.Errorf("NSecBytes is %v but should be %v", NSecBytes, 0) + } + + // Fuzzing + f := fuzz.New() + + // Run tests + for i := 0; i < testCount; i++ { + var c, m, ec, mac []byte + var err error + var test TestData + + // Fuzz the test struct + f.Fuzz(&test) + + // Detached encryption test + c, mac = EncryptDetached(test.Message, test.Ad, test.Nonce[:], test.Key[:]) + + // Encryption test + ec = Encrypt(test.Message, test.Ad, test.Nonce[:], test.Key[:]) + if !bytes.Equal(ec, append(c, mac...)) { + t.Errorf("Encryption failed for %+v", test) + t.FailNow() + } + + // Detached decryption test + m, err = DecryptDetached(c, mac, test.Ad, test.Nonce[:], test.Key[:]) + if err != nil || !bytes.Equal(m, test.Message) { + t.Errorf("Detached decryption failed for %+v", test) + t.FailNow() + } + + // Decryption test + m, err = Decrypt(ec, test.Ad, test.Nonce[:], test.Key[:]) + if err != nil || !bytes.Equal(m, test.Message) { + t.Errorf("Decryption failed for %+v", test) + t.FailNow() + } + + // Failed detached decryption test + mac = make([]byte, ABytes) + m, err = DecryptDetached(c, mac, test.Ad, test.Nonce[:], test.Key[:]) + if err == nil { + t.Errorf("Detached decryption unexpectedly succeeded for %+v", test) + t.FailNow() + } + + // Failed decryption test + copy(ec[len(m):], mac) + m, err = Decrypt(ec, test.Ad, test.Nonce[:], test.Key[:]) + if err == nil { + t.Errorf("Decryption unexpectedly succeeded for %+v", test) + t.FailNow() + } + } + t.Logf("Completed %v tests", testCount) +} diff --git a/crypto/aead/chacha20poly1305/crypto_aead_chacha20poly1305.go b/crypto/aead/chacha20poly1305/crypto_aead_chacha20poly1305.go new file mode 100644 index 0000000..e1f570e --- /dev/null +++ b/crypto/aead/chacha20poly1305/crypto_aead_chacha20poly1305.go @@ -0,0 +1,133 @@ +// Package chacha20poly1305 contains the libsodium bindings for ChaCha20-Poly1305. +package chacha20poly1305 + +// #cgo pkg-config: libsodium +// #include +// #include +import "C" +import "github.com/GoKillers/libsodium-go/support" + +// Sodium should always be initialised +func init() { + C.sodium_init() +} + +// Sizes of nonces, key and mac. +const ( + KeyBytes int = C.crypto_aead_chacha20poly1305_KEYBYTES // Size of a secret key in bytes + NSecBytes int = C.crypto_aead_chacha20poly1305_NSECBYTES // Size of a secret nonce in bytes + NonceBytes int = C.crypto_aead_chacha20poly1305_NPUBBYTES // Size of a nonce in bytes + ABytes int = C.crypto_aead_chacha20poly1305_ABYTES // Size of an authentication tag in bytes +) + +// Key represents a secret key +type Key [KeyBytes]byte + +// GenerateKey generates a secret key +func GenerateKey() *Key { + k := new(Key) + C.crypto_aead_chacha20poly1305_keygen((*C.uchar)(&k[0])) + return k +} + +// Encrypt a message `m` with additional data `ad` using a nonce `npub` and a secret key `k`. +// A ciphertext (including authentication tag) and encryption status are returned. +func Encrypt(m, ad, nonce, k []byte) (c []byte) { + support.CheckSize(k, KeyBytes, "secret key") + support.CheckSize(nonce, NonceBytes, "public nonce") + + c = make([]byte, len(m)+ABytes) + + C.crypto_aead_chacha20poly1305_encrypt( + (*C.uchar)(support.BytePointer(c)), + (*C.ulonglong)(nil), + (*C.uchar)(support.BytePointer(m)), + (C.ulonglong)(len(m)), + (*C.uchar)(support.BytePointer(ad)), + (C.ulonglong)(len(ad)), + (*C.uchar)(nil), + (*C.uchar)(&nonce[0]), + (*C.uchar)(&k[0])) + + return +} + +// Decrypt and verify a ciphertext `c` using additional data `ad`, nonce `npub` and secret key `k`. +// Returns the decrypted message and verification status. +func Decrypt(c, ad, nonce, k []byte) (m []byte, err error) { + support.CheckSize(k, KeyBytes, "secret key") + support.CheckSize(nonce, NonceBytes, "public nonce") + support.CheckSizeMin(c, ABytes, "ciphertext") + + m = make([]byte, len(c)-ABytes) + + exit := C.crypto_aead_chacha20poly1305_decrypt( + (*C.uchar)(support.BytePointer(m)), + (*C.ulonglong)(nil), + (*C.uchar)(nil), + (*C.uchar)(&c[0]), + (C.ulonglong)(len(c)), + (*C.uchar)(support.BytePointer(ad)), + (C.ulonglong)(len(ad)), + (*C.uchar)(&nonce[0]), + (*C.uchar)(&k[0])) + + if exit != 0 { + err = &support.VerificationError{} + } + + return +} + +// EncryptDetached encrypts a message `m` with additional data `ad` using +// a nonce `npub` and a secret key `k`. +// A ciphertext, authentication tag and encryption status are returned. +func EncryptDetached(m, ad, nonce, k []byte) (c, mac []byte) { + support.CheckSize(k, KeyBytes, "secret key") + support.CheckSize(nonce, NonceBytes, "public nonce") + + c = make([]byte, len(m)) + mac = make([]byte, ABytes) + + C.crypto_aead_chacha20poly1305_encrypt_detached( + (*C.uchar)(support.BytePointer(c)), + (*C.uchar)(&mac[0]), + (*C.ulonglong)(nil), + (*C.uchar)(support.BytePointer(m)), + (C.ulonglong)(len(m)), + (*C.uchar)(support.BytePointer(ad)), + (C.ulonglong)(len(ad)), + (*C.uchar)(nil), + (*C.uchar)(&nonce[0]), + (*C.uchar)(&k[0])) + + return +} + +// DecryptDetached decrypts and verifies a ciphertext `c` with authentication tag `mac` +// using additional data `ad`, nonce `npub` and secret key `k`. +// Returns the decrypted message and verification status. +func DecryptDetached(c, mac, ad, nonce, k []byte) (m []byte, err error) { + support.CheckSize(k, KeyBytes, "secret key") + support.CheckSize(nonce, NonceBytes, "public nonce") + support.CheckSize(mac, ABytes, "mac") + + m = make([]byte, len(c)) + + exit := C.crypto_aead_chacha20poly1305_decrypt_detached( + (*C.uchar)(support.BytePointer(m)), + (*C.uchar)(nil), + (*C.uchar)(support.BytePointer(c)), + (C.ulonglong)(len(c)), + (*C.uchar)(&mac[0]), + (*C.uchar)(support.BytePointer(ad)), + (C.ulonglong)(len(ad)), + (*C.uchar)(&nonce[0]), + (*C.uchar)(&k[0])) + + if exit != 0 { + err = &support.VerificationError{} + } + + return +} diff --git a/crypto/aead/chacha20poly1305/crypto_aead_chacha20poly1305_test.go b/crypto/aead/chacha20poly1305/crypto_aead_chacha20poly1305_test.go new file mode 100644 index 0000000..399ee3b --- /dev/null +++ b/crypto/aead/chacha20poly1305/crypto_aead_chacha20poly1305_test.go @@ -0,0 +1,82 @@ +package chacha20poly1305 + +import ( + "bytes" + "github.com/google/gofuzz" + "testing" +) + +var testCount = 100000 + +type TestData struct { + Message []byte + Ad []byte + Key Key + Nonce [NonceBytes]byte +} + +func Test(t *testing.T) { + // Test the key generation + if *GenerateKey() == (Key{}) { + t.Error("Generated key is zero") + } + + // Test the length of NSecBytes + if NSecBytes != 0 { + t.Errorf("NSecBytes is %v but should be %v", NSecBytes, 0) + } + + // Fuzzing + f := fuzz.New() + + // Run tests + for i := 0; i < testCount; i++ { + var c, m, ec, mac []byte + var err error + var test TestData + + // Fuzz the test struct + f.Fuzz(&test) + + // Detached encryption test + c, mac = EncryptDetached(test.Message, test.Ad, test.Nonce[:], test.Key[:]) + + // Encryption test + ec = Encrypt(test.Message, test.Ad, test.Nonce[:], test.Key[:]) + if !bytes.Equal(ec, append(c, mac...)) { + t.Errorf("Encryption failed for %+v", test) + t.FailNow() + } + + // Detached decryption test + m, err = DecryptDetached(c, mac, test.Ad, test.Nonce[:], test.Key[:]) + if err != nil || !bytes.Equal(m, test.Message) { + t.Errorf("Detached decryption failed for %+v", test) + t.FailNow() + } + + // Decryption test + m, err = Decrypt(ec, test.Ad, test.Nonce[:], test.Key[:]) + if err != nil || !bytes.Equal(m, test.Message) { + t.Errorf("Decryption failed for %+v", test) + t.FailNow() + } + + // Failed detached decryption test + mac = make([]byte, ABytes) + m, err = DecryptDetached(c, mac, test.Ad, test.Nonce[:], test.Key[:]) + if err == nil { + t.Errorf("Detached decryption unexpectedly succeeded for %+v", test) + t.FailNow() + } + + // Failed decryption test + copy(ec[len(m):], mac) + m, err = Decrypt(ec, test.Ad, test.Nonce[:], test.Key[:]) + if err == nil { + t.Errorf("Decryption unexpectedly succeeded for %+v", test) + t.FailNow() + } + } + t.Logf("Completed %v tests", testCount) +} diff --git a/crypto/aead/chacha20poly1305ietf/crypto_aead_chacha20poly1305_ietf.go b/crypto/aead/chacha20poly1305ietf/crypto_aead_chacha20poly1305_ietf.go new file mode 100644 index 0000000..03af6cd --- /dev/null +++ b/crypto/aead/chacha20poly1305ietf/crypto_aead_chacha20poly1305_ietf.go @@ -0,0 +1,133 @@ +// Package chacha20poly1305ietf contains the libsodium bindings for the IETF variant of ChaCha20-Poly1305. +package chacha20poly1305ietf + +// #cgo pkg-config: libsodium +// #include +// #include +import "C" +import "github.com/GoKillers/libsodium-go/support" + +// Sodium should always be initialised +func init() { + C.sodium_init() +} + +// Sizes of nonces, key and mac. +const ( + KeyBytes int = C.crypto_aead_chacha20poly1305_ietf_KEYBYTES // Size of a secret key in bytes + NSecBytes int = C.crypto_aead_chacha20poly1305_ietf_NSECBYTES // Size of a secret nonce in bytes + NonceBytes int = C.crypto_aead_chacha20poly1305_ietf_NPUBBYTES // Size of a nonce in bytes + ABytes int = C.crypto_aead_chacha20poly1305_ietf_ABYTES // Size of an authentication tag in bytes +) + +// Key represents a secret key +type Key [KeyBytes]byte + +// GenerateKey generates a secret key +func GenerateKey() *Key { + k := new(Key) + C.crypto_aead_chacha20poly1305_ietf_keygen((*C.uchar)(&k[0])) + return k +} + +// Encrypt a message `m` with additional data `ad` using a nonce `npub` and a secret key `k`. +// A ciphertext (including authentication tag) and encryption status are returned. +func Encrypt(m, ad, nonce, k []byte) (c []byte) { + support.CheckSize(k, KeyBytes, "secret key") + support.CheckSize(nonce, NonceBytes, "public nonce") + + c = make([]byte, len(m)+ABytes) + + C.crypto_aead_chacha20poly1305_ietf_encrypt( + (*C.uchar)(support.BytePointer(c)), + (*C.ulonglong)(nil), + (*C.uchar)(support.BytePointer(m)), + (C.ulonglong)(len(m)), + (*C.uchar)(support.BytePointer(ad)), + (C.ulonglong)(len(ad)), + (*C.uchar)(nil), + (*C.uchar)(&nonce[0]), + (*C.uchar)(&k[0])) + + return +} + +// Decrypt and verify a ciphertext `c` using additional data `ad`, nonce `npub` and secret key `k`. +// Returns the decrypted message and verification status. +func Decrypt(c, ad, nonce, k []byte) (m []byte, err error) { + support.CheckSize(k, KeyBytes, "secret key") + support.CheckSize(nonce, NonceBytes, "public nonce") + support.CheckSizeMin(c, ABytes, "ciphertext") + + m = make([]byte, len(c)-ABytes) + + exit := C.crypto_aead_chacha20poly1305_ietf_decrypt( + (*C.uchar)(support.BytePointer(m)), + (*C.ulonglong)(nil), + (*C.uchar)(nil), + (*C.uchar)(&c[0]), + (C.ulonglong)(len(c)), + (*C.uchar)(support.BytePointer(ad)), + (C.ulonglong)(len(ad)), + (*C.uchar)(&nonce[0]), + (*C.uchar)(&k[0])) + + if exit != 0 { + err = &support.VerificationError{} + } + + return +} + +// EncryptDetached encrypts a message `m` with additional data `ad` using +// a nonce `npub` and a secret key `k`. +// A ciphertext, authentication tag and encryption status are returned. +func EncryptDetached(m, ad, nonce, k []byte) (c, mac []byte) { + support.CheckSize(k, KeyBytes, "secret key") + support.CheckSize(nonce, NonceBytes, "public nonce") + + c = make([]byte, len(m)) + mac = make([]byte, ABytes) + + C.crypto_aead_chacha20poly1305_ietf_encrypt_detached( + (*C.uchar)(support.BytePointer(c)), + (*C.uchar)(&mac[0]), + (*C.ulonglong)(nil), + (*C.uchar)(support.BytePointer(m)), + (C.ulonglong)(len(m)), + (*C.uchar)(support.BytePointer(ad)), + (C.ulonglong)(len(ad)), + (*C.uchar)(nil), + (*C.uchar)(&nonce[0]), + (*C.uchar)(&k[0])) + + return +} + +// DecryptDetached decrypts and verifies a ciphertext `c` with authentication tag `mac` +// using additional data `ad`, nonce `npub` and secret key `k`. +// Returns the decrypted message and verification status. +func DecryptDetached(c, mac, ad, nonce, k []byte) (m []byte, err error) { + support.CheckSize(k, KeyBytes, "secret key") + support.CheckSize(nonce, NonceBytes, "public nonce") + support.CheckSize(mac, ABytes, "mac") + + m = make([]byte, len(c)) + + exit := C.crypto_aead_chacha20poly1305_ietf_decrypt_detached( + (*C.uchar)(support.BytePointer(m)), + (*C.uchar)(nil), + (*C.uchar)(support.BytePointer(c)), + (C.ulonglong)(len(c)), + (*C.uchar)(&mac[0]), + (*C.uchar)(support.BytePointer(ad)), + (C.ulonglong)(len(ad)), + (*C.uchar)(&nonce[0]), + (*C.uchar)(&k[0])) + + if exit != 0 { + err = &support.VerificationError{} + } + + return +} diff --git a/crypto/aead/chacha20poly1305ietf/crypto_aead_chacha20poly1305_ietf_test.go b/crypto/aead/chacha20poly1305ietf/crypto_aead_chacha20poly1305_ietf_test.go new file mode 100644 index 0000000..c4da073 --- /dev/null +++ b/crypto/aead/chacha20poly1305ietf/crypto_aead_chacha20poly1305_ietf_test.go @@ -0,0 +1,82 @@ +package chacha20poly1305ietf + +import ( + "bytes" + "github.com/google/gofuzz" + "testing" +) + +var testCount = 100000 + +type TestData struct { + Message []byte + Ad []byte + Key Key + Nonce [NonceBytes]byte +} + +func Test(t *testing.T) { + // Test the key generation + if *GenerateKey() == (Key{}) { + t.Error("Generated key is zero") + } + + // Test the length of NSecBytes + if NSecBytes != 0 { + t.Errorf("NSecBytes is %v but should be %v", NSecBytes, 0) + } + + // Fuzzing + f := fuzz.New() + + // Run tests + for i := 0; i < testCount; i++ { + var c, m, ec, mac []byte + var err error + var test TestData + + // Fuzz the test struct + f.Fuzz(&test) + + // Detached encryption test + c, mac = EncryptDetached(test.Message, test.Ad, test.Nonce[:], test.Key[:]) + + // Encryption test + ec = Encrypt(test.Message, test.Ad, test.Nonce[:], test.Key[:]) + if !bytes.Equal(ec, append(c, mac...)) { + t.Errorf("Encryption failed for %+v", test) + t.FailNow() + } + + // Detached decryption test + m, err = DecryptDetached(c, mac, test.Ad, test.Nonce[:], test.Key[:]) + if err != nil || !bytes.Equal(m, test.Message) { + t.Errorf("Detached decryption failed for %+v", test) + t.FailNow() + } + + // Decryption test + m, err = Decrypt(ec, test.Ad, test.Nonce[:], test.Key[:]) + if err != nil || !bytes.Equal(m, test.Message) { + t.Errorf("Decryption failed for %+v", test) + t.FailNow() + } + + // Failed detached decryption test + mac = make([]byte, ABytes) + m, err = DecryptDetached(c, mac, test.Ad, test.Nonce[:], test.Key[:]) + if err == nil { + t.Errorf("Detached decryption unexpectedly succeeded for %+v", test) + t.FailNow() + } + + // Failed decryption test + copy(ec[len(m):], mac) + m, err = Decrypt(ec, test.Ad, test.Nonce[:], test.Key[:]) + if err == nil { + t.Errorf("Decryption unexpectedly succeeded for %+v", test) + t.FailNow() + } + } + t.Logf("Completed %v tests", testCount) +} diff --git a/crypto/aead/crypto_aead.go b/crypto/aead/crypto_aead.go new file mode 100644 index 0000000..bdff214 --- /dev/null +++ b/crypto/aead/crypto_aead.go @@ -0,0 +1,48 @@ +// Package aead contains bindings for authenticated encryption with additional data. +package aead + +// #cgo pkg-config: libsodium +// #include +// #include +import "C" +import "crypto/cipher" + +// Sodium should always be initialised +func init() { + C.sodium_init() +} + +// AEAD is and extended version of cipher.AEAD +type AEAD interface { + cipher.AEAD + + // SealDetached encrypts and authenticates plaintext, authenticates the + // additional data and appends the result to dst, returning the updated + // slice and the authentication code (mac) separately. + // The nonce must be NonceSize() bytes long and unique for all time, for a given key. + // The mac is Overhead() bytes long. + // + // The plaintext and dst may alias exactly or not at all. To reuse + // plaintext's storage for the encrypted output, use plaintext[:0] as dst. + SealDetached(dst, nonce, plaintext, additionalData []byte) ([]byte, []byte) + + // OpenDetached decrypts a ciphertext, authenticates the additional data using + // the autentication code (mac) and, if successful, appends the resulting plaintext + // to dst, returning the updated slice. The nonce must be NonceSize() + // bytes long and both it and the additional data must match the + // value passed to Seal. + // + // The ciphertext and dst may alias exactly or not at all. To reuse + // ciphertext's storage for the decrypted output, use ciphertext[:0] as dst. + // + // Even if the function fails, the contents of dst, up to its capacity, + // may be overwritten. + OpenDetached(dst, nonce, ciphertext, mac, additionalData []byte) ([]byte, error) +} + +// appendSlices appends a slice with a number of empty bytes and +// returns the new slice and a slice pointing to the empty data. +func appendSlices(in []byte, n int) ([]byte, []byte) { + slice := append(in, make([]byte, n)...) + return slice, slice[len(in):] +} diff --git a/crypto/aead/crypto_aead_aes256gcm.go b/crypto/aead/crypto_aead_aes256gcm.go new file mode 100644 index 0000000..3e853de --- /dev/null +++ b/crypto/aead/crypto_aead_aes256gcm.go @@ -0,0 +1,132 @@ +package aead + +// #cgo pkg-config: libsodium +// #include +// #include +import "C" +import ( + "github.com/GoKillers/libsodium-go/crypto/aead/aes256gcm" + "github.com/GoKillers/libsodium-go/support" +) + +// AES256GCM represents the cryptographic state for the AES256 GCM cipher +type AES256GCM C.crypto_aead_aes256gcm_state + +// NewAES256GCM returns a AES256GCM cipher for an AES256 key. +func NewAES256GCM(k *aes256gcm.Key) AEAD { + support.NilPanic(k == nil, "key") + + ctx := new(AES256GCM) + + C.crypto_aead_aes256gcm_beforenm( + (*C.crypto_aead_aes256gcm_state)(ctx), + (*C.uchar)(&k[0])) + + return ctx +} + +// NonceSize returns the size of the nonce for Seal() and Open() +func (a *AES256GCM) NonceSize() int { + return aes256gcm.NonceBytes +} + +// Overhead returns the size of the MAC overhead for Seal() and Open() +func (a *AES256GCM) Overhead() int { + return aes256gcm.ABytes +} + +// Seal encrypts plaintext using nonce and additional data and appends it to a destination. +// See aead.AEAD for details. +func (a *AES256GCM) Seal(dst, nonce, plaintext, additionalData []byte) (ret []byte) { + support.CheckSize(nonce, a.NonceSize(), "nonce") + + ret, c := appendSlices(dst, len(plaintext)+a.Overhead()) + + C.crypto_aead_aes256gcm_encrypt_afternm( + (*C.uchar)(&c[0]), + (*C.ulonglong)(nil), + (*C.uchar)(support.BytePointer(plaintext)), + (C.ulonglong)(len(plaintext)), + (*C.uchar)(support.BytePointer(additionalData)), + (C.ulonglong)(len(additionalData)), + (*C.uchar)(nil), + (*C.uchar)(&nonce[0]), + (*C.crypto_aead_aes256gcm_state)(a)) + + return +} + +// Open decrypts a ciphertext using a nonce and additional data and appends the result to a destination. +// See aead.AEAD for details. +func (a *AES256GCM) Open(dst, nonce, ciphertext, additionalData []byte) (ret []byte, err error) { + support.CheckSize(nonce, a.NonceSize(), "nonce") + support.CheckSizeMin(ciphertext, a.Overhead(), "ciphertext") + + ret, m := appendSlices(dst, len(ciphertext)-a.Overhead()) + + exit := C.crypto_aead_aes256gcm_decrypt_afternm( + (*C.uchar)(support.BytePointer(m)), + (*C.ulonglong)(nil), + (*C.uchar)(nil), + (*C.uchar)(&ciphertext[0]), + (C.ulonglong)(len(ciphertext)), + (*C.uchar)(support.BytePointer(additionalData)), + (C.ulonglong)(len(additionalData)), + (*C.uchar)(&nonce[0]), + (*C.crypto_aead_aes256gcm_state)(a)) + + if exit != 0 { + err = &support.VerificationError{} + } + + return +} + +// SealDetached encrypts plaintext using nonce and additional data and appends it to a destination. +// See aead.AEAD for details. +func (a *AES256GCM) SealDetached(dst, nonce, plaintext, additionalData []byte) (ret, mac []byte) { + support.CheckSize(nonce, a.NonceSize(), "nonce") + + ret, c := appendSlices(dst, len(plaintext)) + mac = make([]byte, a.Overhead()) + + C.crypto_aead_aes256gcm_encrypt_detached_afternm( + (*C.uchar)(support.BytePointer(c)), + (*C.uchar)(&mac[0]), + (*C.ulonglong)(nil), + (*C.uchar)(support.BytePointer(plaintext)), + (C.ulonglong)(len(plaintext)), + (*C.uchar)(support.BytePointer(additionalData)), + (C.ulonglong)(len(additionalData)), + (*C.uchar)(nil), + (*C.uchar)(&nonce[0]), + (*C.crypto_aead_aes256gcm_state)(a)) + + return +} + +// OpenDetached decrypts a ciphertext using a nonce, mac and additional data and appends the result to a destination. +// See aead.AEAD for details. +func (a *AES256GCM) OpenDetached(dst, nonce, ciphertext, mac, additionalData []byte) (ret []byte, err error) { + support.CheckSize(nonce, a.NonceSize(), "nonce") + support.CheckSize(mac, a.Overhead(), "mac") + + ret, m := appendSlices(dst, len(ciphertext)) + + exit := C.crypto_aead_aes256gcm_decrypt_detached_afternm( + (*C.uchar)(support.BytePointer(m)), + (*C.uchar)(nil), + (*C.uchar)(support.BytePointer(ciphertext)), + (C.ulonglong)(len(ciphertext)), + (*C.uchar)(&mac[0]), + (*C.uchar)(support.BytePointer(additionalData)), + (C.ulonglong)(len(additionalData)), + (*C.uchar)(&nonce[0]), + (*C.crypto_aead_aes256gcm_state)(a)) + + if exit != 0 { + err = &support.VerificationError{} + } + + return +} diff --git a/crypto/aead/crypto_aead_aes256gcm_test.go b/crypto/aead/crypto_aead_aes256gcm_test.go new file mode 100644 index 0000000..1686a04 --- /dev/null +++ b/crypto/aead/crypto_aead_aes256gcm_test.go @@ -0,0 +1,95 @@ +package aead + +import ( + "bytes" + "github.com/GoKillers/libsodium-go/crypto/aead/aes256gcm" + "github.com/google/gofuzz" + "testing" +) + +var testCount = 100000 + +type TestData struct { + Message []byte + Ad []byte + Dst []byte + Key aes256gcm.Key + Nonce [aes256gcm.NonceBytes]byte +} + +func Test(t *testing.T) { + // Skip the test if unsupported on this platform + if !aes256gcm.IsAvailable() { + t.Skip("The CPU does not support this implementation of AES256GCM.") + } + + // Fuzzing + f := fuzz.New() + + // Run tests + for i := 0; i < testCount; i++ { + var c, m, ec, mac []byte + var err error + var test TestData + + // Fuzz the test struct + f.Fuzz(&test) + + // Create a key context + ctx := NewAES256GCM(&test.Key) + + // Detached encryption test + c, mac = ctx.SealDetached(test.Dst, test.Nonce[:], test.Message, test.Ad) + + // Check if dst was prepended + if !bytes.Equal(c[:len(test.Dst)], test.Dst) { + t.Error("dst was not prepended") + t.FailNow() + } + + // Encryption test + ec = ctx.Seal(test.Dst, test.Nonce[:], test.Message, test.Ad) + if !bytes.Equal(ec, append(c, mac...)) { + t.Errorf("Encryption failed for %+v", test) + t.FailNow() + } + + // Detached decryption test + m, err = ctx.OpenDetached(test.Dst, test.Nonce[:], c[len(test.Dst):], mac, test.Ad) + if err != nil || !bytes.Equal(m[len(test.Dst):], test.Message) { + t.Errorf("Detached decryption failed for %+v", test) + t.FailNow() + } + + // Check if dst was prepended + if !bytes.Equal(m[:len(test.Dst)], test.Dst) { + t.Error("dst was not prepended") + t.FailNow() + } + + // Decryption test + m, err = ctx.Open(test.Dst, test.Nonce[:], ec[len(test.Dst):], test.Ad) + if err != nil || !bytes.Equal(m[len(test.Dst):], test.Message) { + t.Errorf("Decryption failed for %+v", test) + t.FailNow() + } + + // Failed detached decryption test + mac = make([]byte, ctx.Overhead()) + m, err = ctx.OpenDetached(test.Dst, test.Nonce[:], c[len(test.Dst):], mac, test.Ad) + if err == nil { + t.Errorf("Detached decryption unexpectedly succeeded for %+v", test) + t.FailNow() + } + + // Failed decryption test + copy(ec[len(test.Dst)+len(m):], mac) + m, err = ctx.Open(test.Dst, test.Nonce[:], ec[len(test.Dst):], test.Ad) + if err == nil { + t.Errorf("Decryption unexpectedly succeeded for %+v", test) + t.FailNow() + } + } + + t.Logf("Completed %v tests", testCount) +} diff --git a/crypto/aead/xchacha20poly1305ietf/crypto_aead_xchacha20poly1305_ietf.go b/crypto/aead/xchacha20poly1305ietf/crypto_aead_xchacha20poly1305_ietf.go new file mode 100644 index 0000000..17984ae --- /dev/null +++ b/crypto/aead/xchacha20poly1305ietf/crypto_aead_xchacha20poly1305_ietf.go @@ -0,0 +1,133 @@ +// Package xchacha20poly1305ietf contains the libsodium bindings for the IETF variant of XChaCha20-Poly1305. +package xchacha20poly1305ietf + +// #cgo pkg-config: libsodium +// #include +// #include +import "C" +import "github.com/GoKillers/libsodium-go/support" + +// Sodium should always be initialised +func init() { + C.sodium_init() +} + +// Sizes of nonces, key and mac. +const ( + KeyBytes int = C.crypto_aead_xchacha20poly1305_ietf_KEYBYTES // Size of a secret key in bytes + NSecBytes int = C.crypto_aead_xchacha20poly1305_ietf_NSECBYTES // Size of a secret nonce in bytes + NonceBytes int = C.crypto_aead_xchacha20poly1305_ietf_NPUBBYTES // Size of a nonce in bytes + ABytes int = C.crypto_aead_xchacha20poly1305_ietf_ABYTES // Size of an authentication tag in bytes +) + +// Key represents a secret key +type Key [KeyBytes]byte + +// GenerateKey generates a secret key +func GenerateKey() *Key { + k := new(Key) + C.crypto_aead_xchacha20poly1305_ietf_keygen((*C.uchar)(&k[0])) + return k +} + +// Encrypt a message `m` with additional data `ad` using a nonce `npub` and a secret key `k`. +// A ciphertext (including authentication tag) and encryption status are returned. +func Encrypt(m, ad, nonce, k []byte) (c []byte) { + support.CheckSize(k, KeyBytes, "secret key") + support.CheckSize(nonce, NonceBytes, "public nonce") + + c = make([]byte, len(m)+ABytes) + + C.crypto_aead_xchacha20poly1305_ietf_encrypt( + (*C.uchar)(support.BytePointer(c)), + (*C.ulonglong)(nil), + (*C.uchar)(support.BytePointer(m)), + (C.ulonglong)(len(m)), + (*C.uchar)(support.BytePointer(ad)), + (C.ulonglong)(len(ad)), + (*C.uchar)(nil), + (*C.uchar)(&nonce[0]), + (*C.uchar)(&k[0])) + + return +} + +// Decrypt and verify a ciphertext `c` using additional data `ad`, nonce `npub` and secret key `k`. +// Returns the decrypted message and verification status. +func Decrypt(c, ad, nonce, k []byte) (m []byte, err error) { + support.CheckSize(k, KeyBytes, "secret key") + support.CheckSize(nonce, NonceBytes, "public nonce") + support.CheckSizeMin(c, ABytes, "ciphertext") + + m = make([]byte, len(c)-ABytes) + + exit := C.crypto_aead_xchacha20poly1305_ietf_decrypt( + (*C.uchar)(support.BytePointer(m)), + (*C.ulonglong)(nil), + (*C.uchar)(nil), + (*C.uchar)(&c[0]), + (C.ulonglong)(len(c)), + (*C.uchar)(support.BytePointer(ad)), + (C.ulonglong)(len(ad)), + (*C.uchar)(&nonce[0]), + (*C.uchar)(&k[0])) + + if exit != 0 { + err = &support.VerificationError{} + } + + return +} + +// EncryptDetached encrypts a message `m` with additional data `ad` using +// a nonce `npub` and a secret key `k`. +// A ciphertext, authentication tag and encryption status are returned. +func EncryptDetached(m, ad, nonce, k []byte) (c, mac []byte) { + support.CheckSize(k, KeyBytes, "secret key") + support.CheckSize(nonce, NonceBytes, "public nonce") + + c = make([]byte, len(m)) + mac = make([]byte, ABytes) + + C.crypto_aead_xchacha20poly1305_ietf_encrypt_detached( + (*C.uchar)(support.BytePointer(c)), + (*C.uchar)(&mac[0]), + (*C.ulonglong)(nil), + (*C.uchar)(support.BytePointer(m)), + (C.ulonglong)(len(m)), + (*C.uchar)(support.BytePointer(ad)), + (C.ulonglong)(len(ad)), + (*C.uchar)(nil), + (*C.uchar)(&nonce[0]), + (*C.uchar)(&k[0])) + + return +} + +// DecryptDetached decrypts and verifies a ciphertext `c` with authentication tag `mac` +// using additional data `ad`, nonce `npub` and secret key `k`. +// Returns the decrypted message and verification status. +func DecryptDetached(c, mac, ad, nonce, k []byte) (m []byte, err error) { + support.CheckSize(k, KeyBytes, "secret key") + support.CheckSize(nonce, NonceBytes, "public nonce") + support.CheckSize(mac, ABytes, "mac") + + m = make([]byte, len(c)) + + exit := C.crypto_aead_xchacha20poly1305_ietf_decrypt_detached( + (*C.uchar)(support.BytePointer(m)), + (*C.uchar)(nil), + (*C.uchar)(support.BytePointer(c)), + (C.ulonglong)(len(c)), + (*C.uchar)(&mac[0]), + (*C.uchar)(support.BytePointer(ad)), + (C.ulonglong)(len(ad)), + (*C.uchar)(&nonce[0]), + (*C.uchar)(&k[0])) + + if exit != 0 { + err = &support.VerificationError{} + } + + return +} diff --git a/crypto/aead/xchacha20poly1305ietf/crypto_aead_xchacha20poly1305_ietf_test.go b/crypto/aead/xchacha20poly1305ietf/crypto_aead_xchacha20poly1305_ietf_test.go new file mode 100644 index 0000000..e87dae2 --- /dev/null +++ b/crypto/aead/xchacha20poly1305ietf/crypto_aead_xchacha20poly1305_ietf_test.go @@ -0,0 +1,82 @@ +package xchacha20poly1305ietf + +import ( + "bytes" + "github.com/google/gofuzz" + "testing" +) + +var testCount = 100000 + +type TestData struct { + Message []byte + Ad []byte + Key Key + Nonce [NonceBytes]byte +} + +func Test(t *testing.T) { + // Test the key generation + if *GenerateKey() == (Key{}) { + t.Error("Generated key is zero") + } + + // Test the length of NSecBytes + if NSecBytes != 0 { + t.Errorf("NSecBytes is %v but should be %v", NSecBytes, 0) + } + + // Fuzzing + f := fuzz.New() + + // Run tests + for i := 0; i < testCount; i++ { + var c, m, ec, mac []byte + var err error + var test TestData + + // Fuzz the test struct + f.Fuzz(&test) + + // Detached encryption test + c, mac = EncryptDetached(test.Message, test.Ad, test.Nonce[:], test.Key[:]) + + // Encryption test + ec = Encrypt(test.Message, test.Ad, test.Nonce[:], test.Key[:]) + if !bytes.Equal(ec, append(c, mac...)) { + t.Errorf("Encryption failed for %+v", test) + t.FailNow() + } + + // Detached decryption test + m, err = DecryptDetached(c, mac, test.Ad, test.Nonce[:], test.Key[:]) + if err != nil || !bytes.Equal(m, test.Message) { + t.Errorf("Detached decryption failed for %+v", test) + t.FailNow() + } + + // Decryption test + m, err = Decrypt(ec, test.Ad, test.Nonce[:], test.Key[:]) + if err != nil || !bytes.Equal(m, test.Message) { + t.Errorf("Decryption failed for %+v", test) + t.FailNow() + } + + // Failed detached decryption test + mac = make([]byte, ABytes) + m, err = DecryptDetached(c, mac, test.Ad, test.Nonce[:], test.Key[:]) + if err == nil { + t.Errorf("Detached decryption unexpectedly succeeded for %+v", test) + t.FailNow() + } + + // Failed decryption test + copy(ec[len(m):], mac) + m, err = Decrypt(ec, test.Ad, test.Nonce[:], test.Key[:]) + if err == nil { + t.Errorf("Decryption unexpectedly succeeded for %+v", test) + t.FailNow() + } + } + t.Logf("Completed %v tests", testCount) +} From cf2ff0558100959856c314b66a40d810f57d9bd5 Mon Sep 17 00:00:00 2001 From: Silke Date: Mon, 17 Jul 2017 14:35:33 +0200 Subject: [PATCH 4/5] Make key and nonce size explicit The key and nonce size are now given as an array pointer to make more clear that the key and nonce size are fixed. --- .../aead/aes256gcm/crypto_aead_aes256gcm.go | 31 +++++++++---------- .../aes256gcm/crypto_aead_aes256gcm_test.go | 16 +++++----- .../crypto_aead_chacha20poly1305.go | 31 +++++++++---------- .../crypto_aead_chacha20poly1305_test.go | 16 +++++----- .../crypto_aead_chacha20poly1305_ietf.go | 31 +++++++++---------- .../crypto_aead_chacha20poly1305_ietf_test.go | 16 +++++----- crypto/aead/crypto_aead_aes256gcm.go | 2 +- crypto/aead/crypto_aead_aes256gcm_test.go | 2 +- .../crypto_aead_xchacha20poly1305_ietf.go | 31 +++++++++---------- ...crypto_aead_xchacha20poly1305_ietf_test.go | 16 +++++----- 10 files changed, 90 insertions(+), 102 deletions(-) diff --git a/crypto/aead/aes256gcm/crypto_aead_aes256gcm.go b/crypto/aead/aes256gcm/crypto_aead_aes256gcm.go index 6d612cc..3287f91 100644 --- a/crypto/aead/aes256gcm/crypto_aead_aes256gcm.go +++ b/crypto/aead/aes256gcm/crypto_aead_aes256gcm.go @@ -20,26 +20,23 @@ const ( ABytes int = C.crypto_aead_aes256gcm_ABYTES // Size of an authentication tag in bytes ) -// Key represents a secret key -type Key [KeyBytes]byte - // IsAvailable returns true if AES256 is available on the current CPU func IsAvailable() bool { return C.crypto_aead_aes256gcm_is_available() != 0 } // GenerateKey generates a secret key -func GenerateKey() *Key { - k := new(Key) +func GenerateKey() *[KeyBytes]byte { + k := new([KeyBytes]byte) C.crypto_aead_aes256gcm_keygen((*C.uchar)(&k[0])) return k } // Encrypt a message `m` with additional data `ad` using a nonce `npub` and a secret key `k`. // A ciphertext (including authentication tag) and encryption status are returned. -func Encrypt(m, ad, nonce, k []byte) (c []byte) { - support.CheckSize(k, KeyBytes, "secret key") - support.CheckSize(nonce, NonceBytes, "public nonce") +func Encrypt(m, ad []byte, nonce *[NonceBytes]byte, k *[KeyBytes]byte) (c []byte) { + support.NilPanic(k == nil, "secret key") + support.NilPanic(nonce == nil, "nonce") c = make([]byte, len(m)+ABytes) @@ -59,9 +56,9 @@ func Encrypt(m, ad, nonce, k []byte) (c []byte) { // Decrypt and verify a ciphertext `c` using additional data `ad`, nonce `npub` and secret key `k`. // Returns the decrypted message and verification status. -func Decrypt(c, ad, nonce, k []byte) (m []byte, err error) { - support.CheckSize(k, KeyBytes, "secret key") - support.CheckSize(nonce, NonceBytes, "public nonce") +func Decrypt(c, ad []byte, nonce *[NonceBytes]byte, k *[KeyBytes]byte) (m []byte, err error) { + support.NilPanic(k == nil, "secret key") + support.NilPanic(nonce == nil, "nonce") support.CheckSizeMin(c, ABytes, "ciphertext") m = make([]byte, len(c)-ABytes) @@ -87,9 +84,9 @@ func Decrypt(c, ad, nonce, k []byte) (m []byte, err error) { // EncryptDetached encrypts a message `m` with additional data `ad` using // a nonce `npub` and a secret key `k`. // A ciphertext, authentication tag and encryption status are returned. -func EncryptDetached(m, ad, nonce, k []byte) (c, mac []byte) { - support.CheckSize(k, KeyBytes, "secret key") - support.CheckSize(nonce, NonceBytes, "public nonce") +func EncryptDetached(m, ad []byte, nonce *[NonceBytes]byte, k *[KeyBytes]byte) (c, mac []byte) { + support.NilPanic(k == nil, "secret key") + support.NilPanic(nonce == nil, "nonce") c = make([]byte, len(m)) mac = make([]byte, ABytes) @@ -112,9 +109,9 @@ func EncryptDetached(m, ad, nonce, k []byte) (c, mac []byte) { // DecryptDetached decrypts and verifies a ciphertext `c` with authentication tag `mac` // using additional data `ad`, nonce `npub` and secret key `k`. // Returns the decrypted message and verification status. -func DecryptDetached(c, mac, ad, nonce, k []byte) (m []byte, err error) { - support.CheckSize(k, KeyBytes, "secret key") - support.CheckSize(nonce, NonceBytes, "public nonce") +func DecryptDetached(c, mac, ad []byte, nonce *[NonceBytes]byte, k *[KeyBytes]byte) (m []byte, err error) { + support.NilPanic(k == nil, "secret key") + support.NilPanic(nonce == nil, "nonce") support.CheckSize(mac, ABytes, "mac") m = make([]byte, len(c)) diff --git a/crypto/aead/aes256gcm/crypto_aead_aes256gcm_test.go b/crypto/aead/aes256gcm/crypto_aead_aes256gcm_test.go index 4cd150f..2806951 100644 --- a/crypto/aead/aes256gcm/crypto_aead_aes256gcm_test.go +++ b/crypto/aead/aes256gcm/crypto_aead_aes256gcm_test.go @@ -11,7 +11,7 @@ var testCount = 100000 type TestData struct { Message []byte Ad []byte - Key Key + Key [KeyBytes]byte Nonce [NonceBytes]byte } @@ -22,7 +22,7 @@ func Test(t *testing.T) { } // Test the key generation - if *GenerateKey() == (Key{}) { + if *GenerateKey() == ([KeyBytes]byte{}) { t.Error("Generated key is zero") } @@ -44,24 +44,24 @@ func Test(t *testing.T) { f.Fuzz(&test) // Detached encryption test - c, mac = EncryptDetached(test.Message, test.Ad, test.Nonce[:], test.Key[:]) + c, mac = EncryptDetached(test.Message, test.Ad, &test.Nonce, &test.Key) // Encryption test - ec = Encrypt(test.Message, test.Ad, test.Nonce[:], test.Key[:]) + ec = Encrypt(test.Message, test.Ad, &test.Nonce, &test.Key) if !bytes.Equal(ec, append(c, mac...)) { t.Errorf("Encryption failed for %+v", test) t.FailNow() } // Detached decryption test - m, err = DecryptDetached(c, mac, test.Ad, test.Nonce[:], test.Key[:]) + m, err = DecryptDetached(c, mac, test.Ad, &test.Nonce, &test.Key) if err != nil || !bytes.Equal(m, test.Message) { t.Errorf("Detached decryption failed for %+v", test) t.FailNow() } // Decryption test - m, err = Decrypt(ec, test.Ad, test.Nonce[:], test.Key[:]) + m, err = Decrypt(ec, test.Ad, &test.Nonce, &test.Key) if err != nil || !bytes.Equal(m, test.Message) { t.Errorf("Decryption failed for %+v", test) t.FailNow() @@ -69,7 +69,7 @@ func Test(t *testing.T) { // Failed detached decryption test mac = make([]byte, ABytes) - m, err = DecryptDetached(c, mac, test.Ad, test.Nonce[:], test.Key[:]) + m, err = DecryptDetached(c, mac, test.Ad, &test.Nonce, &test.Key) if err == nil { t.Errorf("Detached decryption unexpectedly succeeded for %+v", test) t.FailNow() @@ -77,7 +77,7 @@ func Test(t *testing.T) { // Failed decryption test copy(ec[len(m):], mac) - m, err = Decrypt(ec, test.Ad, test.Nonce[:], test.Key[:]) + m, err = Decrypt(ec, test.Ad, &test.Nonce, &test.Key) if err == nil { t.Errorf("Decryption unexpectedly succeeded for %+v", test) t.FailNow() diff --git a/crypto/aead/chacha20poly1305/crypto_aead_chacha20poly1305.go b/crypto/aead/chacha20poly1305/crypto_aead_chacha20poly1305.go index e1f570e..a3ee82f 100644 --- a/crypto/aead/chacha20poly1305/crypto_aead_chacha20poly1305.go +++ b/crypto/aead/chacha20poly1305/crypto_aead_chacha20poly1305.go @@ -20,21 +20,18 @@ const ( ABytes int = C.crypto_aead_chacha20poly1305_ABYTES // Size of an authentication tag in bytes ) -// Key represents a secret key -type Key [KeyBytes]byte - // GenerateKey generates a secret key -func GenerateKey() *Key { - k := new(Key) +func GenerateKey() *[KeyBytes]byte { + k := new([KeyBytes]byte) C.crypto_aead_chacha20poly1305_keygen((*C.uchar)(&k[0])) return k } // Encrypt a message `m` with additional data `ad` using a nonce `npub` and a secret key `k`. // A ciphertext (including authentication tag) and encryption status are returned. -func Encrypt(m, ad, nonce, k []byte) (c []byte) { - support.CheckSize(k, KeyBytes, "secret key") - support.CheckSize(nonce, NonceBytes, "public nonce") +func Encrypt(m, ad []byte, nonce *[NonceBytes]byte, k *[KeyBytes]byte) (c []byte) { + support.NilPanic(k == nil, "secret key") + support.NilPanic(nonce == nil, "nonce") c = make([]byte, len(m)+ABytes) @@ -54,9 +51,9 @@ func Encrypt(m, ad, nonce, k []byte) (c []byte) { // Decrypt and verify a ciphertext `c` using additional data `ad`, nonce `npub` and secret key `k`. // Returns the decrypted message and verification status. -func Decrypt(c, ad, nonce, k []byte) (m []byte, err error) { - support.CheckSize(k, KeyBytes, "secret key") - support.CheckSize(nonce, NonceBytes, "public nonce") +func Decrypt(c, ad []byte, nonce *[NonceBytes]byte, k *[KeyBytes]byte) (m []byte, err error) { + support.NilPanic(k == nil, "secret key") + support.NilPanic(nonce == nil, "nonce") support.CheckSizeMin(c, ABytes, "ciphertext") m = make([]byte, len(c)-ABytes) @@ -82,9 +79,9 @@ func Decrypt(c, ad, nonce, k []byte) (m []byte, err error) { // EncryptDetached encrypts a message `m` with additional data `ad` using // a nonce `npub` and a secret key `k`. // A ciphertext, authentication tag and encryption status are returned. -func EncryptDetached(m, ad, nonce, k []byte) (c, mac []byte) { - support.CheckSize(k, KeyBytes, "secret key") - support.CheckSize(nonce, NonceBytes, "public nonce") +func EncryptDetached(m, ad []byte, nonce *[NonceBytes]byte, k *[KeyBytes]byte) (c, mac []byte) { + support.NilPanic(k == nil, "secret key") + support.NilPanic(nonce == nil, "nonce") c = make([]byte, len(m)) mac = make([]byte, ABytes) @@ -107,9 +104,9 @@ func EncryptDetached(m, ad, nonce, k []byte) (c, mac []byte) { // DecryptDetached decrypts and verifies a ciphertext `c` with authentication tag `mac` // using additional data `ad`, nonce `npub` and secret key `k`. // Returns the decrypted message and verification status. -func DecryptDetached(c, mac, ad, nonce, k []byte) (m []byte, err error) { - support.CheckSize(k, KeyBytes, "secret key") - support.CheckSize(nonce, NonceBytes, "public nonce") +func DecryptDetached(c, mac, ad []byte, nonce *[NonceBytes]byte, k *[KeyBytes]byte) (m []byte, err error) { + support.NilPanic(k == nil, "secret key") + support.NilPanic(nonce == nil, "nonce") support.CheckSize(mac, ABytes, "mac") m = make([]byte, len(c)) diff --git a/crypto/aead/chacha20poly1305/crypto_aead_chacha20poly1305_test.go b/crypto/aead/chacha20poly1305/crypto_aead_chacha20poly1305_test.go index 399ee3b..9f9a25c 100644 --- a/crypto/aead/chacha20poly1305/crypto_aead_chacha20poly1305_test.go +++ b/crypto/aead/chacha20poly1305/crypto_aead_chacha20poly1305_test.go @@ -11,13 +11,13 @@ var testCount = 100000 type TestData struct { Message []byte Ad []byte - Key Key + Key [KeyBytes]byte Nonce [NonceBytes]byte } func Test(t *testing.T) { // Test the key generation - if *GenerateKey() == (Key{}) { + if *GenerateKey() == ([KeyBytes]byte{}) { t.Error("Generated key is zero") } @@ -39,24 +39,24 @@ func Test(t *testing.T) { f.Fuzz(&test) // Detached encryption test - c, mac = EncryptDetached(test.Message, test.Ad, test.Nonce[:], test.Key[:]) + c, mac = EncryptDetached(test.Message, test.Ad, &test.Nonce, &test.Key) // Encryption test - ec = Encrypt(test.Message, test.Ad, test.Nonce[:], test.Key[:]) + ec = Encrypt(test.Message, test.Ad, &test.Nonce, &test.Key) if !bytes.Equal(ec, append(c, mac...)) { t.Errorf("Encryption failed for %+v", test) t.FailNow() } // Detached decryption test - m, err = DecryptDetached(c, mac, test.Ad, test.Nonce[:], test.Key[:]) + m, err = DecryptDetached(c, mac, test.Ad, &test.Nonce, &test.Key) if err != nil || !bytes.Equal(m, test.Message) { t.Errorf("Detached decryption failed for %+v", test) t.FailNow() } // Decryption test - m, err = Decrypt(ec, test.Ad, test.Nonce[:], test.Key[:]) + m, err = Decrypt(ec, test.Ad, &test.Nonce, &test.Key) if err != nil || !bytes.Equal(m, test.Message) { t.Errorf("Decryption failed for %+v", test) t.FailNow() @@ -64,7 +64,7 @@ func Test(t *testing.T) { // Failed detached decryption test mac = make([]byte, ABytes) - m, err = DecryptDetached(c, mac, test.Ad, test.Nonce[:], test.Key[:]) + m, err = DecryptDetached(c, mac, test.Ad, &test.Nonce, &test.Key) if err == nil { t.Errorf("Detached decryption unexpectedly succeeded for %+v", test) t.FailNow() @@ -72,7 +72,7 @@ func Test(t *testing.T) { // Failed decryption test copy(ec[len(m):], mac) - m, err = Decrypt(ec, test.Ad, test.Nonce[:], test.Key[:]) + m, err = Decrypt(ec, test.Ad, &test.Nonce, &test.Key) if err == nil { t.Errorf("Decryption unexpectedly succeeded for %+v", test) t.FailNow() diff --git a/crypto/aead/chacha20poly1305ietf/crypto_aead_chacha20poly1305_ietf.go b/crypto/aead/chacha20poly1305ietf/crypto_aead_chacha20poly1305_ietf.go index 03af6cd..34bf9da 100644 --- a/crypto/aead/chacha20poly1305ietf/crypto_aead_chacha20poly1305_ietf.go +++ b/crypto/aead/chacha20poly1305ietf/crypto_aead_chacha20poly1305_ietf.go @@ -20,21 +20,18 @@ const ( ABytes int = C.crypto_aead_chacha20poly1305_ietf_ABYTES // Size of an authentication tag in bytes ) -// Key represents a secret key -type Key [KeyBytes]byte - // GenerateKey generates a secret key -func GenerateKey() *Key { - k := new(Key) +func GenerateKey() *[KeyBytes]byte { + k := new([KeyBytes]byte) C.crypto_aead_chacha20poly1305_ietf_keygen((*C.uchar)(&k[0])) return k } // Encrypt a message `m` with additional data `ad` using a nonce `npub` and a secret key `k`. // A ciphertext (including authentication tag) and encryption status are returned. -func Encrypt(m, ad, nonce, k []byte) (c []byte) { - support.CheckSize(k, KeyBytes, "secret key") - support.CheckSize(nonce, NonceBytes, "public nonce") +func Encrypt(m, ad []byte, nonce *[NonceBytes]byte, k *[KeyBytes]byte) (c []byte) { + support.NilPanic(k == nil, "secret key") + support.NilPanic(nonce == nil, "nonce") c = make([]byte, len(m)+ABytes) @@ -54,9 +51,9 @@ func Encrypt(m, ad, nonce, k []byte) (c []byte) { // Decrypt and verify a ciphertext `c` using additional data `ad`, nonce `npub` and secret key `k`. // Returns the decrypted message and verification status. -func Decrypt(c, ad, nonce, k []byte) (m []byte, err error) { - support.CheckSize(k, KeyBytes, "secret key") - support.CheckSize(nonce, NonceBytes, "public nonce") +func Decrypt(c, ad []byte, nonce *[NonceBytes]byte, k *[KeyBytes]byte) (m []byte, err error) { + support.NilPanic(k == nil, "secret key") + support.NilPanic(nonce == nil, "nonce") support.CheckSizeMin(c, ABytes, "ciphertext") m = make([]byte, len(c)-ABytes) @@ -82,9 +79,9 @@ func Decrypt(c, ad, nonce, k []byte) (m []byte, err error) { // EncryptDetached encrypts a message `m` with additional data `ad` using // a nonce `npub` and a secret key `k`. // A ciphertext, authentication tag and encryption status are returned. -func EncryptDetached(m, ad, nonce, k []byte) (c, mac []byte) { - support.CheckSize(k, KeyBytes, "secret key") - support.CheckSize(nonce, NonceBytes, "public nonce") +func EncryptDetached(m, ad []byte, nonce *[NonceBytes]byte, k *[KeyBytes]byte) (c, mac []byte) { + support.NilPanic(k == nil, "secret key") + support.NilPanic(nonce == nil, "nonce") c = make([]byte, len(m)) mac = make([]byte, ABytes) @@ -107,9 +104,9 @@ func EncryptDetached(m, ad, nonce, k []byte) (c, mac []byte) { // DecryptDetached decrypts and verifies a ciphertext `c` with authentication tag `mac` // using additional data `ad`, nonce `npub` and secret key `k`. // Returns the decrypted message and verification status. -func DecryptDetached(c, mac, ad, nonce, k []byte) (m []byte, err error) { - support.CheckSize(k, KeyBytes, "secret key") - support.CheckSize(nonce, NonceBytes, "public nonce") +func DecryptDetached(c, mac, ad []byte, nonce *[NonceBytes]byte, k *[KeyBytes]byte) (m []byte, err error) { + support.NilPanic(k == nil, "secret key") + support.NilPanic(nonce == nil, "nonce") support.CheckSize(mac, ABytes, "mac") m = make([]byte, len(c)) diff --git a/crypto/aead/chacha20poly1305ietf/crypto_aead_chacha20poly1305_ietf_test.go b/crypto/aead/chacha20poly1305ietf/crypto_aead_chacha20poly1305_ietf_test.go index c4da073..672c5ee 100644 --- a/crypto/aead/chacha20poly1305ietf/crypto_aead_chacha20poly1305_ietf_test.go +++ b/crypto/aead/chacha20poly1305ietf/crypto_aead_chacha20poly1305_ietf_test.go @@ -11,13 +11,13 @@ var testCount = 100000 type TestData struct { Message []byte Ad []byte - Key Key + Key [KeyBytes]byte Nonce [NonceBytes]byte } func Test(t *testing.T) { // Test the key generation - if *GenerateKey() == (Key{}) { + if *GenerateKey() == ([KeyBytes]byte{}) { t.Error("Generated key is zero") } @@ -39,24 +39,24 @@ func Test(t *testing.T) { f.Fuzz(&test) // Detached encryption test - c, mac = EncryptDetached(test.Message, test.Ad, test.Nonce[:], test.Key[:]) + c, mac = EncryptDetached(test.Message, test.Ad, &test.Nonce, &test.Key) // Encryption test - ec = Encrypt(test.Message, test.Ad, test.Nonce[:], test.Key[:]) + ec = Encrypt(test.Message, test.Ad, &test.Nonce, &test.Key) if !bytes.Equal(ec, append(c, mac...)) { t.Errorf("Encryption failed for %+v", test) t.FailNow() } // Detached decryption test - m, err = DecryptDetached(c, mac, test.Ad, test.Nonce[:], test.Key[:]) + m, err = DecryptDetached(c, mac, test.Ad, &test.Nonce, &test.Key) if err != nil || !bytes.Equal(m, test.Message) { t.Errorf("Detached decryption failed for %+v", test) t.FailNow() } // Decryption test - m, err = Decrypt(ec, test.Ad, test.Nonce[:], test.Key[:]) + m, err = Decrypt(ec, test.Ad, &test.Nonce, &test.Key) if err != nil || !bytes.Equal(m, test.Message) { t.Errorf("Decryption failed for %+v", test) t.FailNow() @@ -64,7 +64,7 @@ func Test(t *testing.T) { // Failed detached decryption test mac = make([]byte, ABytes) - m, err = DecryptDetached(c, mac, test.Ad, test.Nonce[:], test.Key[:]) + m, err = DecryptDetached(c, mac, test.Ad, &test.Nonce, &test.Key) if err == nil { t.Errorf("Detached decryption unexpectedly succeeded for %+v", test) t.FailNow() @@ -72,7 +72,7 @@ func Test(t *testing.T) { // Failed decryption test copy(ec[len(m):], mac) - m, err = Decrypt(ec, test.Ad, test.Nonce[:], test.Key[:]) + m, err = Decrypt(ec, test.Ad, &test.Nonce, &test.Key) if err == nil { t.Errorf("Decryption unexpectedly succeeded for %+v", test) t.FailNow() diff --git a/crypto/aead/crypto_aead_aes256gcm.go b/crypto/aead/crypto_aead_aes256gcm.go index 3e853de..5eb8685 100644 --- a/crypto/aead/crypto_aead_aes256gcm.go +++ b/crypto/aead/crypto_aead_aes256gcm.go @@ -13,7 +13,7 @@ import ( type AES256GCM C.crypto_aead_aes256gcm_state // NewAES256GCM returns a AES256GCM cipher for an AES256 key. -func NewAES256GCM(k *aes256gcm.Key) AEAD { +func NewAES256GCM(k *[aes256gcm.KeyBytes]byte) AEAD { support.NilPanic(k == nil, "key") ctx := new(AES256GCM) diff --git a/crypto/aead/crypto_aead_aes256gcm_test.go b/crypto/aead/crypto_aead_aes256gcm_test.go index 1686a04..e5da969 100644 --- a/crypto/aead/crypto_aead_aes256gcm_test.go +++ b/crypto/aead/crypto_aead_aes256gcm_test.go @@ -13,7 +13,7 @@ type TestData struct { Message []byte Ad []byte Dst []byte - Key aes256gcm.Key + Key [aes256gcm.KeyBytes]byte Nonce [aes256gcm.NonceBytes]byte } diff --git a/crypto/aead/xchacha20poly1305ietf/crypto_aead_xchacha20poly1305_ietf.go b/crypto/aead/xchacha20poly1305ietf/crypto_aead_xchacha20poly1305_ietf.go index 17984ae..782c010 100644 --- a/crypto/aead/xchacha20poly1305ietf/crypto_aead_xchacha20poly1305_ietf.go +++ b/crypto/aead/xchacha20poly1305ietf/crypto_aead_xchacha20poly1305_ietf.go @@ -20,21 +20,18 @@ const ( ABytes int = C.crypto_aead_xchacha20poly1305_ietf_ABYTES // Size of an authentication tag in bytes ) -// Key represents a secret key -type Key [KeyBytes]byte - // GenerateKey generates a secret key -func GenerateKey() *Key { - k := new(Key) +func GenerateKey() *[KeyBytes]byte { + k := new([KeyBytes]byte) C.crypto_aead_xchacha20poly1305_ietf_keygen((*C.uchar)(&k[0])) return k } // Encrypt a message `m` with additional data `ad` using a nonce `npub` and a secret key `k`. // A ciphertext (including authentication tag) and encryption status are returned. -func Encrypt(m, ad, nonce, k []byte) (c []byte) { - support.CheckSize(k, KeyBytes, "secret key") - support.CheckSize(nonce, NonceBytes, "public nonce") +func Encrypt(m, ad []byte, nonce *[NonceBytes]byte, k *[KeyBytes]byte) (c []byte) { + support.NilPanic(k == nil, "secret key") + support.NilPanic(nonce == nil, "nonce") c = make([]byte, len(m)+ABytes) @@ -54,9 +51,9 @@ func Encrypt(m, ad, nonce, k []byte) (c []byte) { // Decrypt and verify a ciphertext `c` using additional data `ad`, nonce `npub` and secret key `k`. // Returns the decrypted message and verification status. -func Decrypt(c, ad, nonce, k []byte) (m []byte, err error) { - support.CheckSize(k, KeyBytes, "secret key") - support.CheckSize(nonce, NonceBytes, "public nonce") +func Decrypt(c, ad []byte, nonce *[NonceBytes]byte, k *[KeyBytes]byte) (m []byte, err error) { + support.NilPanic(k == nil, "secret key") + support.NilPanic(nonce == nil, "nonce") support.CheckSizeMin(c, ABytes, "ciphertext") m = make([]byte, len(c)-ABytes) @@ -82,9 +79,9 @@ func Decrypt(c, ad, nonce, k []byte) (m []byte, err error) { // EncryptDetached encrypts a message `m` with additional data `ad` using // a nonce `npub` and a secret key `k`. // A ciphertext, authentication tag and encryption status are returned. -func EncryptDetached(m, ad, nonce, k []byte) (c, mac []byte) { - support.CheckSize(k, KeyBytes, "secret key") - support.CheckSize(nonce, NonceBytes, "public nonce") +func EncryptDetached(m, ad []byte, nonce *[NonceBytes]byte, k *[KeyBytes]byte) (c, mac []byte) { + support.NilPanic(k == nil, "secret key") + support.NilPanic(nonce == nil, "nonce") c = make([]byte, len(m)) mac = make([]byte, ABytes) @@ -107,9 +104,9 @@ func EncryptDetached(m, ad, nonce, k []byte) (c, mac []byte) { // DecryptDetached decrypts and verifies a ciphertext `c` with authentication tag `mac` // using additional data `ad`, nonce `npub` and secret key `k`. // Returns the decrypted message and verification status. -func DecryptDetached(c, mac, ad, nonce, k []byte) (m []byte, err error) { - support.CheckSize(k, KeyBytes, "secret key") - support.CheckSize(nonce, NonceBytes, "public nonce") +func DecryptDetached(c, mac, ad []byte, nonce *[NonceBytes]byte, k *[KeyBytes]byte) (m []byte, err error) { + support.NilPanic(k == nil, "secret key") + support.NilPanic(nonce == nil, "nonce") support.CheckSize(mac, ABytes, "mac") m = make([]byte, len(c)) diff --git a/crypto/aead/xchacha20poly1305ietf/crypto_aead_xchacha20poly1305_ietf_test.go b/crypto/aead/xchacha20poly1305ietf/crypto_aead_xchacha20poly1305_ietf_test.go index e87dae2..98beb07 100644 --- a/crypto/aead/xchacha20poly1305ietf/crypto_aead_xchacha20poly1305_ietf_test.go +++ b/crypto/aead/xchacha20poly1305ietf/crypto_aead_xchacha20poly1305_ietf_test.go @@ -11,13 +11,13 @@ var testCount = 100000 type TestData struct { Message []byte Ad []byte - Key Key + Key [KeyBytes]byte Nonce [NonceBytes]byte } func Test(t *testing.T) { // Test the key generation - if *GenerateKey() == (Key{}) { + if *GenerateKey() == ([KeyBytes]byte{}) { t.Error("Generated key is zero") } @@ -39,24 +39,24 @@ func Test(t *testing.T) { f.Fuzz(&test) // Detached encryption test - c, mac = EncryptDetached(test.Message, test.Ad, test.Nonce[:], test.Key[:]) + c, mac = EncryptDetached(test.Message, test.Ad, &test.Nonce, &test.Key) // Encryption test - ec = Encrypt(test.Message, test.Ad, test.Nonce[:], test.Key[:]) + ec = Encrypt(test.Message, test.Ad, &test.Nonce, &test.Key) if !bytes.Equal(ec, append(c, mac...)) { t.Errorf("Encryption failed for %+v", test) t.FailNow() } // Detached decryption test - m, err = DecryptDetached(c, mac, test.Ad, test.Nonce[:], test.Key[:]) + m, err = DecryptDetached(c, mac, test.Ad, &test.Nonce, &test.Key) if err != nil || !bytes.Equal(m, test.Message) { t.Errorf("Detached decryption failed for %+v", test) t.FailNow() } // Decryption test - m, err = Decrypt(ec, test.Ad, test.Nonce[:], test.Key[:]) + m, err = Decrypt(ec, test.Ad, &test.Nonce, &test.Key) if err != nil || !bytes.Equal(m, test.Message) { t.Errorf("Decryption failed for %+v", test) t.FailNow() @@ -64,7 +64,7 @@ func Test(t *testing.T) { // Failed detached decryption test mac = make([]byte, ABytes) - m, err = DecryptDetached(c, mac, test.Ad, test.Nonce[:], test.Key[:]) + m, err = DecryptDetached(c, mac, test.Ad, &test.Nonce, &test.Key) if err == nil { t.Errorf("Detached decryption unexpectedly succeeded for %+v", test) t.FailNow() @@ -72,7 +72,7 @@ func Test(t *testing.T) { // Failed decryption test copy(ec[len(m):], mac) - m, err = Decrypt(ec, test.Ad, test.Nonce[:], test.Key[:]) + m, err = Decrypt(ec, test.Ad, &test.Nonce, &test.Key) if err == nil { t.Errorf("Decryption unexpectedly succeeded for %+v", test) t.FailNow() From 1aaaa111446878b4948064dd58b3d0f6d14ac4da Mon Sep 17 00:00:00 2001 From: Silke Hofstra Date: Sat, 7 Oct 2017 21:25:46 +0200 Subject: [PATCH 5/5] Ensure correct alignment of crypto_aead_aes256gcm_state --- crypto/aead/crypto_aead_aes256gcm.go | 34 ++++++++++++++++++++++------ 1 file changed, 27 insertions(+), 7 deletions(-) diff --git a/crypto/aead/crypto_aead_aes256gcm.go b/crypto/aead/crypto_aead_aes256gcm.go index 5eb8685..c45ed2a 100644 --- a/crypto/aead/crypto_aead_aes256gcm.go +++ b/crypto/aead/crypto_aead_aes256gcm.go @@ -7,10 +7,16 @@ import "C" import ( "github.com/GoKillers/libsodium-go/crypto/aead/aes256gcm" "github.com/GoKillers/libsodium-go/support" + "unsafe" ) -// AES256GCM represents the cryptographic state for the AES256 GCM cipher -type AES256GCM C.crypto_aead_aes256gcm_state +// AES256GCM state struct +type AES256GCM struct { + // Represents crypto_aead_aes256gcm_state, which must be 16 byte aligned. + // This is not enforced by Go, so 16 extra bytes are allocated and + // the 512 aligned bytes in them are used. + state1 [512 + 16]byte +} // NewAES256GCM returns a AES256GCM cipher for an AES256 key. func NewAES256GCM(k *[aes256gcm.KeyBytes]byte) AEAD { @@ -19,12 +25,26 @@ func NewAES256GCM(k *[aes256gcm.KeyBytes]byte) AEAD { ctx := new(AES256GCM) C.crypto_aead_aes256gcm_beforenm( - (*C.crypto_aead_aes256gcm_state)(ctx), + ctx.state(), (*C.uchar)(&k[0])) return ctx } +// state returns a pointer to the space allocated for the state +func (a *AES256GCM) state() *C.crypto_aead_aes256gcm_state { + var offset uintptr + mod := uintptr(unsafe.Pointer(&a.state1)) % 16 + + if mod == 0 { + offset = mod + } else { + offset = 16 - mod + } + + return (*C.crypto_aead_aes256gcm_state)(unsafe.Pointer(&a.state1[offset])) +} + // NonceSize returns the size of the nonce for Seal() and Open() func (a *AES256GCM) NonceSize() int { return aes256gcm.NonceBytes @@ -51,7 +71,7 @@ func (a *AES256GCM) Seal(dst, nonce, plaintext, additionalData []byte) (ret []by (C.ulonglong)(len(additionalData)), (*C.uchar)(nil), (*C.uchar)(&nonce[0]), - (*C.crypto_aead_aes256gcm_state)(a)) + a.state()) return } @@ -73,7 +93,7 @@ func (a *AES256GCM) Open(dst, nonce, ciphertext, additionalData []byte) (ret []b (*C.uchar)(support.BytePointer(additionalData)), (C.ulonglong)(len(additionalData)), (*C.uchar)(&nonce[0]), - (*C.crypto_aead_aes256gcm_state)(a)) + a.state()) if exit != 0 { err = &support.VerificationError{} @@ -100,7 +120,7 @@ func (a *AES256GCM) SealDetached(dst, nonce, plaintext, additionalData []byte) ( (C.ulonglong)(len(additionalData)), (*C.uchar)(nil), (*C.uchar)(&nonce[0]), - (*C.crypto_aead_aes256gcm_state)(a)) + a.state()) return } @@ -122,7 +142,7 @@ func (a *AES256GCM) OpenDetached(dst, nonce, ciphertext, mac, additionalData []b (*C.uchar)(support.BytePointer(additionalData)), (C.ulonglong)(len(additionalData)), (*C.uchar)(&nonce[0]), - (*C.crypto_aead_aes256gcm_state)(a)) + a.state()) if exit != 0 { err = &support.VerificationError{}