Skip to content

Commit

Permalink
Wrap all AEAD functions in new directory structure
Browse files Browse the repository at this point in the history
Includes a significant refactor:

 - Return error instead of integer
 - Replace getter functions with constants
 - Move prepared encryption to cipher.AEAD compatible structure
 - Add tests
  • Loading branch information
silkeh committed Aug 24, 2017
1 parent 190f71a commit aa0fbc0
Show file tree
Hide file tree
Showing 11 changed files with 1,145 additions and 0 deletions.
138 changes: 138 additions & 0 deletions crypto/aead/aes256gcm/crypto_aead_aes256gcm.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,138 @@
// Package aes256gcm contains the libsodium bindings for AES256-GCM.
package aes256gcm

// #cgo pkg-config: libsodium
// #include <stdlib.h>
// #include <sodium.h>
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
}
87 changes: 87 additions & 0 deletions crypto/aead/aes256gcm/crypto_aead_aes256gcm_test.go
Original file line number Diff line number Diff line change
@@ -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)
}
133 changes: 133 additions & 0 deletions crypto/aead/chacha20poly1305/crypto_aead_chacha20poly1305.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,133 @@
// Package chacha20poly1305 contains the libsodium bindings for ChaCha20-Poly1305.
package chacha20poly1305

// #cgo pkg-config: libsodium
// #include <stdlib.h>
// #include <sodium.h>
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
}
Loading

0 comments on commit aa0fbc0

Please sign in to comment.