Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Crypto stream #35

Open
wants to merge 6 commits into
base: master
Choose a base branch
from
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
87 changes: 87 additions & 0 deletions crypto/stream/chacha20/crypto_stream_chacha20.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,87 @@
// Package chacha20 contains the libsodium bindings for the ChaCha20 stream cipher.
package chacha20

// #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()
}

// Required length of secret key and nonce
const (
KeyBytes = C.crypto_stream_chacha20_KEYBYTES
NonceBytes = C.crypto_stream_chacha20_NONCEBYTES
MessageBytesMax = C.crypto_stream_chacha20_MESSAGEBYTES_MAX
)

// KeyStream fills an output buffer `c` with pseudo random bytes using a nonce `n` and a secret key `k`.
func KeyStream(c []byte, n *[NonceBytes]byte, k *[KeyBytes]byte) {
support.NilPanic(n == nil, "nonce")
support.NilPanic(k == nil, "key")

if len(c) == 0 {
return
}

C.crypto_stream_chacha20(
(*C.uchar)(&c[0]),
(C.ulonglong)(len(c)),
(*C.uchar)(&n[0]),
(*C.uchar)(&k[0]))
}

// XORKeyStream encrypts a message `m` using a nonce `n` and a secret key `k` and puts the resulting ciphertext into `c`.
// If `m` and `c` are the same slice, in-place encryption is performed.
func XORKeyStream(c, m []byte, n *[NonceBytes]byte, k *[KeyBytes]byte) {
support.CheckSizeMax(m, MessageBytesMax, "message")
support.NilPanic(n == nil, "nonce")
support.NilPanic(k == nil, "key")
support.CheckSizeGreaterOrEqual(c, m, "output", "input")

if len(c) == 0 {
return
}

C.crypto_stream_chacha20_xor(
(*C.uchar)(&c[0]),
(*C.uchar)(&m[0]),
(C.ulonglong)(len(m)),
(*C.uchar)(&n[0]),
(*C.uchar)(&k[0]))
}

// XORKeyStreamIC encrypts a message `m` using a nonce `n` and a secret key `k`,
// but with a block counter starting at `ic`.
// If `m` and `c` are the same slice, in-place encryption is performed.
func XORKeyStreamIC(c, m []byte, n *[NonceBytes]byte, k *[KeyBytes]byte, ic uint64) {
support.CheckSizeMax(m, MessageBytesMax, "message")
support.NilPanic(n == nil, "nonce")
support.NilPanic(k == nil, "key")
support.CheckSizeGreaterOrEqual(c, m, "output", "input")

if len(c) == 0 {
return
}

C.crypto_stream_chacha20_xor_ic(
(*C.uchar)(&c[0]),
(*C.uchar)(&m[0]),
(C.ulonglong)(len(m)),
(*C.uchar)(&n[0]),
(C.uint64_t)(ic),
(*C.uchar)(&k[0]))
}

// GenerateKey generates a secret key
func GenerateKey() *[KeyBytes]byte {
k := new([KeyBytes]byte)

C.crypto_stream_chacha20_keygen((*C.uchar)(&k[0]))

return k
}
72 changes: 72 additions & 0 deletions crypto/stream/chacha20/crypto_stream_chacha20_test.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,72 @@
package chacha20

import (
"bytes"
"github.com/google/gofuzz"
"testing"
)

var TestCount = 100000

func TestChaCha20(t *testing.T) {
// Test the key generation
if *GenerateKey() == ([KeyBytes]byte{}) {
t.Error("Generated key is zero")
}

// Fuzzing
fm := fuzz.New()
fn := fuzz.New().NilChance(0)

// Run tests
for i := 0; i < TestCount; i++ {
var c, m, m2, r, d []byte
n := new([NonceBytes]byte)

// Generate random data
fm.Fuzz(&m)
fn.Fuzz(&n)
k := GenerateKey()

// Generate pseudo-random data
r = make([]byte, len(m))
KeyStream(r, n, k)

// Perform XOR
d = make([]byte, len(m))
for i := range r {
d[i] = r[i] ^ m[i]
}

// Generate a ciphertext
c = make([]byte, len(m))
XORKeyStream(c, m, n, k)
if !bytes.Equal(c, d) {
t.Errorf("Encryption failed for m: %x, n: %x, k: %x", m, n, k)
t.FailNow()
}

// Generate one with IC
XORKeyStreamIC(c, m, n, k, 0)
if !bytes.Equal(c, d) {
t.Errorf("Encryption with IC failed for m: %x, n: %x, k: %x", m, n, k)
t.FailNow()
}

// Check if in-place encryption works
m2 = make([]byte, len(m))
copy(m2, m)
XORKeyStream(m2, m2, n, k)
if !bytes.Equal(c, m2) {
t.Errorf("In place encryption failed for m: %x, n: %x, k: %x", m, n, k)
t.FailNow()
}

// Check again with IC
XORKeyStreamIC(m, m, n, k, 0)
if !bytes.Equal(c, m2) {
t.Errorf("In place encryption with IC failed for m: %x, n: %x, k: %x", m, n, k)
t.FailNow()
}
}
}
87 changes: 87 additions & 0 deletions crypto/stream/chacha20ietf/crypto_stream_chacha20_ietf.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,87 @@
// Package chacha20ietf contains the libsodium bindings for the IETF variant of the ChaCha20 stream cipher.
package chacha20ietf

// #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()
}

// Required length of secret key and nonce
const (
KeyBytes = C.crypto_stream_chacha20_ietf_KEYBYTES
NonceBytes = C.crypto_stream_chacha20_ietf_NONCEBYTES
MessageBytesMax = C.crypto_stream_chacha20_ietf_MESSAGEBYTES_MAX
)

// KeyStream fills an output buffer `c` with pseudo random bytes using a nonce `n` and a secret key `k`.
func KeyStream(c []byte, n *[NonceBytes]byte, k *[KeyBytes]byte) {
support.NilPanic(n == nil, "nonce")
support.NilPanic(k == nil, "key")

if len(c) == 0 {
return
}

C.crypto_stream_chacha20_ietf(
(*C.uchar)(&c[0]),
(C.ulonglong)(len(c)),
(*C.uchar)(&n[0]),
(*C.uchar)(&k[0]))
}

// XORKeyStream encrypts a message `m` using a nonce `n` and a secret key `k` and puts the resulting ciphertext into `c`.
// If `m` and `c` are the same slice, in-place encryption is performed.
func XORKeyStream(c, m []byte, n *[NonceBytes]byte, k *[KeyBytes]byte) {
support.CheckSizeMax(m, MessageBytesMax, "message")
support.NilPanic(n == nil, "nonce")
support.NilPanic(k == nil, "key")
support.CheckSizeGreaterOrEqual(c, m, "output", "input")

if len(c) == 0 {
return
}

C.crypto_stream_chacha20_ietf_xor(
(*C.uchar)(&c[0]),
(*C.uchar)(&m[0]),
(C.ulonglong)(len(m)),
(*C.uchar)(&n[0]),
(*C.uchar)(&k[0]))
}

// XORKeyStreamIC encrypts a message `m` using a nonce `n` and a secret key `k`,
// but with a block counter starting at `ic`.
// If `m` and `c` are the same slice, in-place encryption is performed.
func XORKeyStreamIC(c, m []byte, n *[NonceBytes]byte, k *[KeyBytes]byte, ic uint64) {
support.CheckSizeMax(m, MessageBytesMax, "message")
support.NilPanic(n == nil, "nonce")
support.NilPanic(k == nil, "key")
support.CheckSizeGreaterOrEqual(c, m, "output", "input")

if len(c) == 0 {
return
}

C.crypto_stream_chacha20_ietf_xor_ic(
(*C.uchar)(&c[0]),
(*C.uchar)(&m[0]),
(C.ulonglong)(len(m)),
(*C.uchar)(&n[0]),
(C.uint32_t)(ic),
(*C.uchar)(&k[0]))
}

// GenerateKey generates a secret key
func GenerateKey() *[KeyBytes]byte {
k := new([KeyBytes]byte)

C.crypto_stream_chacha20_ietf_keygen((*C.uchar)(&k[0]))

return k
}
72 changes: 72 additions & 0 deletions crypto/stream/chacha20ietf/crypto_stream_chacha20_ietf_test.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,72 @@
package chacha20ietf

import (
"bytes"
"github.com/google/gofuzz"
"testing"
)

var TestCount = 100000

func TestChaCha20IETF(t *testing.T) {
// Test the key generation
if *GenerateKey() == ([KeyBytes]byte{}) {
t.Error("Generated key is zero")
}

// Fuzzing
fm := fuzz.New()
fn := fuzz.New().NilChance(0)

// Run tests
for i := 0; i < TestCount; i++ {
var c, m, m2, r, d []byte
n := new([NonceBytes]byte)

// Generate random data
fm.Fuzz(&m)
fn.Fuzz(&n)
k := GenerateKey()

// Generate pseudo-random data
r = make([]byte, len(m))
KeyStream(r, n, k)

// Perform XOR
d = make([]byte, len(m))
for i := range r {
d[i] = r[i] ^ m[i]
}

// Generate a ciphertext
c = make([]byte, len(m))
XORKeyStream(c, m, n, k)
if !bytes.Equal(c, d) {
t.Errorf("Encryption failed for m: %x, n: %x, k: %x", m, n, k)
t.FailNow()
}

// Generate one with IC
XORKeyStreamIC(c, m, n, k, 0)
if !bytes.Equal(c, d) {
t.Errorf("Encryption with IC failed for m: %x, n: %x, k: %x", m, n, k)
t.FailNow()
}

// Check if in-place encryption works
m2 = make([]byte, len(m))
copy(m2, m)
XORKeyStream(m2, m2, n, k)
if !bytes.Equal(c, m2) {
t.Errorf("In place encryption failed for m: %x, n: %x, k: %x", m, n, k)
t.FailNow()
}

// Check again with IC
XORKeyStreamIC(m, m, n, k, 0)
if !bytes.Equal(c, m2) {
t.Errorf("In place encryption with IC failed for m: %x, n: %x, k: %x", m, n, k)
t.FailNow()
}
}
}
66 changes: 66 additions & 0 deletions crypto/stream/crypto_stream.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,66 @@
// Package stream contains the libsodium bindings for the XSalsa20 stream cipher.
package stream

// #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()
}

// Stream byte lengths and algorithm name
const (
KeyBytes = C.crypto_stream_KEYBYTES // Length of a secret key
NonceBytes = C.crypto_stream_NONCEBYTES // Length of a nonce
MessageBytesMax = C.crypto_stream_MESSAGEBYTES_MAX // Maximum length of a message
Primitive = C.crypto_stream_PRIMITIVE // Name of the used algorithm
)

// KeyStream fills an output buffer `c` with pseudo random bytes using a nonce `n` and a secret key `k`.
func KeyStream(c []byte, n *[NonceBytes]byte, k *[KeyBytes]byte) {
support.NilPanic(n == nil, "nonce")
support.NilPanic(k == nil, "key")

if len(c) == 0 {
return
}

C.crypto_stream(
(*C.uchar)(&c[0]),
(C.ulonglong)(len(c)),
(*C.uchar)(&n[0]),
(*C.uchar)(&k[0]))
}

// XORKeyStream encrypts a message `m` using a nonce `n` and a secret key `k` and puts the resulting ciphertext into `c`.
// If `m` and `c` are the same slice, in-place encryption is performed.
func XORKeyStream(c, m []byte, n *[NonceBytes]byte, k *[KeyBytes]byte) {
support.CheckSizeMax(m, MessageBytesMax, "message")
support.NilPanic(n == nil, "nonce")
support.NilPanic(k == nil, "key")
support.CheckSizeGreaterOrEqual(c, m, "output", "input")

if len(c) == 0 {
return
}

C.crypto_stream_xor(
(*C.uchar)(&c[0]),
(*C.uchar)(&m[0]),
(C.ulonglong)(len(m)),
(*C.uchar)(&n[0]),
(*C.uchar)(&k[0]))
}

// GenerateKey generates a secret key
func GenerateKey() *[KeyBytes]byte {
c := new([KeyBytes]byte)

C.crypto_stream_keygen((*C.uchar)(&c[0]))

return c
}
Loading