siv_aead: add AES-SIV AEAD wrapper
This commit is contained in:
parent
0e277ba19e
commit
d9fc652df0
|
@ -0,0 +1,7 @@
|
||||||
|
#!/bin/bash
|
||||||
|
|
||||||
|
set -eu
|
||||||
|
|
||||||
|
cd "$(dirname "$0")"
|
||||||
|
|
||||||
|
../stupidgcm/benchmark.bash
|
|
@ -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")
|
||||||
|
}
|
||||||
|
}
|
|
@ -0,0 +1 @@
|
||||||
|
package siv_aead
|
|
@ -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
|
||||||
|
}
|
Loading…
Reference in New Issue