aboutsummaryrefslogtreecommitdiff
path: root/internal/siv_aead
diff options
context:
space:
mode:
Diffstat (limited to 'internal/siv_aead')
-rwxr-xr-xinternal/siv_aead/benchmark.bash7
-rw-r--r--internal/siv_aead/correctness_test.go66
-rw-r--r--internal/siv_aead/performance_test.go1
-rw-r--r--internal/siv_aead/siv_aead.go59
4 files changed, 133 insertions, 0 deletions
diff --git a/internal/siv_aead/benchmark.bash b/internal/siv_aead/benchmark.bash
new file mode 100755
index 0000000..40b57b3
--- /dev/null
+++ b/internal/siv_aead/benchmark.bash
@@ -0,0 +1,7 @@
+#!/bin/bash
+
+set -eu
+
+cd "$(dirname "$0")"
+
+../stupidgcm/benchmark.bash
diff --git a/internal/siv_aead/correctness_test.go b/internal/siv_aead/correctness_test.go
new file mode 100644
index 0000000..c271970
--- /dev/null
+++ b/internal/siv_aead/correctness_test.go
@@ -0,0 +1,66 @@
+package siv_aead
+
+import (
+ "bytes"
+ "encoding/hex"
+ "testing"
+
+ "github.com/jacobsa/crypto/siv"
+)
+
+func TestAll(t *testing.T) {
+ key := bytes.Repeat([]byte{1}, 32)
+ nonce := bytes.Repeat([]byte{2}, 16)
+ plaintext := []byte{1, 2, 3, 4, 5, 6, 7, 8, 9}
+ aData := make([]byte, 24)
+ // Compare siv and siv_aead results
+ sResult, err := siv.Encrypt(nonce, key, plaintext, [][]byte{aData, nonce})
+ if err != nil {
+ t.Fatal(err)
+ }
+ a := New(key)
+ aResult := a.Seal(nonce, nonce, plaintext, aData)
+ if !bytes.Equal(sResult, aResult) {
+ t.Errorf("siv and siv_aead produce different results")
+ }
+ expectedResult, _ := hex.DecodeString(
+ "02020202020202020202020202020202ad7a4010649a84d8c1dd5f752e935eed57d45b8b10008f3834")
+ if !bytes.Equal(aResult, expectedResult) {
+ t.Errorf(hex.EncodeToString(aResult))
+ }
+ // Verify overhead
+ overhead := len(aResult) - len(plaintext) - len(nonce)
+ if overhead != a.Overhead() {
+ t.Errorf("Overhead() returns a wrong value")
+ }
+ // Decrypt
+ p1, err := a.Open(nil, aResult[:16], aResult[16:], aData)
+ if err != nil {
+ t.Error(err)
+ }
+ if !bytes.Equal(plaintext, p1) {
+ t.Errorf("wrong plaintext")
+ }
+ // Decrypt and append
+ dst := []byte{0xaa, 0xbb, 0xcc}
+ p2, err := a.Open(dst, aResult[:16], aResult[16:], aData)
+ if err != nil {
+ t.Error(err)
+ }
+ p2e := append(dst, plaintext...)
+ if !bytes.Equal(p2e, p2) {
+ t.Errorf("wrong plaintext: %s", hex.EncodeToString(p2))
+ }
+ // Decrypt corrupt
+ aResult[17] = 0
+ _, err = a.Open(nil, aResult[:16], aResult[16:], aData)
+ if err == nil {
+ t.Error("should have failed")
+ }
+ // Decrypt and append corrupt
+ aResult[17] = 0
+ _, err = a.Open(dst, aResult[:16], aResult[16:], aData)
+ if err == nil {
+ t.Error("should have failed")
+ }
+}
diff --git a/internal/siv_aead/performance_test.go b/internal/siv_aead/performance_test.go
new file mode 100644
index 0000000..626024e
--- /dev/null
+++ b/internal/siv_aead/performance_test.go
@@ -0,0 +1 @@
+package siv_aead
diff --git a/internal/siv_aead/siv_aead.go b/internal/siv_aead/siv_aead.go
new file mode 100644
index 0000000..21106a5
--- /dev/null
+++ b/internal/siv_aead/siv_aead.go
@@ -0,0 +1,59 @@
+// Package siv_aead wraps the functions provided by siv
+// in a crypto.AEAD interface.
+package siv_aead
+
+import (
+ "github.com/jacobsa/crypto/siv"
+)
+
+type sivAead struct {
+ key []byte
+}
+
+func New(key []byte) *sivAead {
+ return &sivAead{
+ key: key,
+ }
+}
+
+func (s *sivAead) NonceSize() int {
+ // SIV supports any nonce size, but in gocryptfs we exclusively use 16.
+ return 16
+}
+
+func (s *sivAead) Overhead() int {
+ // RFC5297:
+ // [...] the key length used by AES in CTR and S2V is len(K)/2 and will
+ // each be either 128 bits, 192 bits, or 256 bits.
+ return len(s.key) / 2
+
+}
+
+// Seal - encrypt "in" using "nonce" and "authData" and append the result to "dst"
+func (s *sivAead) Seal(dst, nonce, plaintext, authData []byte) []byte {
+ if len(nonce) != 16 {
+ // SIV supports any nonce size, but in gocryptfs we exclusively use 16.
+ panic("nonce must be 16 bytes long")
+ }
+ // https://github.com/jacobsa/crypto/blob/master/siv/encrypt.go#L48:
+ // As per RFC 5297 section 3, you may use this function for nonce-based
+ // authenticated encryption by passing a nonce as the last associated
+ // data element.
+ associated := [][]byte{authData, nonce}
+ out, err := siv.Encrypt(dst, s.key, plaintext, associated)
+ if err != nil {
+ panic(err)
+ }
+ return out
+}
+
+// Open - decrypt "in" using "nonce" and "authData" and append the result to "dst"
+func (s *sivAead) Open(dst, nonce, ciphertext, authData []byte) ([]byte, error) {
+ if len(nonce) != 16 {
+ // SIV supports any nonce size, but in gocryptfs we exclusively use 16.
+ panic("nonce must be 16 bytes long")
+ }
+ associated := [][]byte{authData, nonce}
+ dec, err := siv.Decrypt(s.key, ciphertext, associated)
+ return append(dst, dec...), err
+}