Skip to content

Commit

Permalink
go: Add in-place support for SIV Seal / Open
Browse files Browse the repository at this point in the history
  • Loading branch information
conradoplg authored and Conrado P. L. Gouvea committed May 17, 2018
1 parent 61ad13a commit f724781
Show file tree
Hide file tree
Showing 2 changed files with 55 additions and 4 deletions.
32 changes: 28 additions & 4 deletions go/siv.go
Original file line number Diff line number Diff line change
Expand Up @@ -31,6 +31,11 @@ var (
ErrTooManyAssociatedDataItems = errors.New("siv: too many associated data items")
)

//areAliased returns if two slices are exactly aliased
func areAliased(dst, source []byte) bool {
return cap(dst) != 0 && cap(source) != 0 && &dst[:1][0] == &source[:1][0]
}

// Cipher is an instance of AES-SIV, configured with either AES-CMAC or
// AES-PMAC as a message authentication code.
type Cipher struct {
Expand Down Expand Up @@ -112,16 +117,28 @@ func (c *Cipher) Seal(dst []byte, plaintext []byte, data ...[]byte) ([]byte, err
if len(data) > MaxAssociatedDataItems {
return nil, ErrTooManyAssociatedDataItems
}
aliased := areAliased(dst, plaintext)

// Authenticate
iv := c.s2v(data, plaintext)
ret, out := sliceForAppend(dst, len(iv)+len(plaintext))
copy(out, iv)
if aliased {
copy(out[len(out)-len(iv):], iv)
} else {
copy(out, iv)
}

// Encrypt
zeroIVBits(iv)
ctr := cipher.NewCTR(c.b, iv)
ctr.XORKeyStream(out[len(iv):], plaintext)
if aliased {
ctr.XORKeyStream(out, plaintext)
copy(iv, out[len(out)-len(iv):])
copy(out[len(iv):], out)
copy(out, iv)
} else {
ctr.XORKeyStream(out[len(iv):], plaintext)
}

return ret, nil
}
Expand All @@ -141,18 +158,25 @@ func (c *Cipher) Open(dst []byte, ciphertext []byte, data ...[]byte) ([]byte, er
if len(ciphertext) < c.Overhead() {
return nil, ErrNotAuthentic
}
aliased := areAliased(dst, ciphertext)

// Decrypt
iv := c.tmp1[:c.Overhead()]
copy(iv, ciphertext)
zeroIVBits(iv)
ctr := cipher.NewCTR(c.b, iv)
copy(iv, ciphertext)
ret, out := sliceForAppend(dst, len(ciphertext)-len(iv))
ctr.XORKeyStream(out, ciphertext[len(iv):])
if aliased {
copy(ciphertext, ciphertext[len(iv):])
ctr.XORKeyStream(out, ciphertext[:len(ciphertext)-len(iv)])
} else {
ctr.XORKeyStream(out, ciphertext[len(iv):])
}

// Authenticate
expected := c.s2v(data, out)
if subtle.ConstantTimeCompare(ciphertext[:len(iv)], expected) != 1 {
if subtle.ConstantTimeCompare(iv, expected) != 1 {
return nil, ErrNotAuthentic
}
return ret, nil
Expand Down
27 changes: 27 additions & 0 deletions go/siv_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -183,6 +183,33 @@ func TestAESCMACSIVAppend(t *testing.T) {
}
}

func TestAESCMACSIVInPlace(t *testing.T) {
v := loadAESSIVExamples("aes_siv.tjson")[0]

c, err := NewAESCMACSIV(v.key)
if err != nil {
t.Fatalf("NewAESCMACSIV: %s", err)
}
pt := make([]byte, len(v.plaintext), len(v.plaintext)+c.Overhead())
copy(pt, v.plaintext)
ct, err := c.Seal(pt[:0], pt, v.ad...)
if err != nil {
t.Errorf("Seal: %s", err)
}
if !bytes.Equal(v.ciphertext, ct) {
t.Errorf("Seal: expected: %x\ngot: %x", v.ciphertext, ct)
}

copy(ct, v.ciphertext)
pt, err = c.Open(ct[:0], ct, v.ad...)
if err != nil {
t.Errorf("Open: %s", err)
}
if !bytes.Equal(v.plaintext, pt) {
t.Errorf("Open: expected: %x\ngot: %x", v.plaintext, pt)
}
}

func BenchmarkSIVAES128_Seal_1K(b *testing.B) {
a := make([]byte, 64)
m := make([]byte, 1024)
Expand Down

0 comments on commit f724781

Please sign in to comment.