diff options
| -rwxr-xr-x | internal/siv_aead/benchmark.bash | 7 | ||||
| -rw-r--r-- | internal/siv_aead/correctness_test.go | 66 | ||||
| -rw-r--r-- | internal/siv_aead/performance_test.go | 1 | ||||
| -rw-r--r-- | internal/siv_aead/siv_aead.go | 59 | 
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 +} | 
