Guillaume Leroi has uploaded this change for review.
crypto/cipher: improves GCM for custom tag sizes * NewGCM takes a size for the tag * NewGCMWithTagSize takes a custom nounce and tag size * Seal does its computation as if the tag size is maximal, but only output tagSize bytes * Open only uses the last tagSize bytes from input as the tag, the expected tag is computed as if it previously. Only tagSize bytes are compared for authentication Fixes #19594 Change-Id: I346179dd382c603019aa02fec15fe763e7a04bd4 --- M src/crypto/aes/aes_gcm.go M src/crypto/aes/modes.go M src/crypto/cipher/gcm.go M src/crypto/cipher/gcm_test.go 4 files changed, 47 insertions(+), 17 deletions(-)
diff --git a/src/crypto/aes/aes_gcm.go b/src/crypto/aes/aes_gcm.go
index 5e2de02..26f545c 100644
--- a/src/crypto/aes/aes_gcm.go
+++ b/src/crypto/aes/aes_gcm.go
@@ -53,8 +53,12 @@
// NewGCM returns the AES cipher wrapped in Galois Counter Mode. This is only
// called by crypto/cipher.NewGCM via the gcmAble interface.
-func (c *aesCipherGCM) NewGCM(nonceSize int) (cipher.AEAD, error) {
- g := &gcmAsm{ks: c.enc, nonceSize: nonceSize}
+func (c *aesCipherGCM) NewGCM(nonceSize, tagSize int) (cipher.AEAD, error) {
+ if tagSize < 8 || tagSize > 16 {
+ return nil, errors.New("cipher: NewGCM requires a tag size in range [8-16] bytes")
+ }
+
+ g := &gcmAsm{ks: c.enc, nonceSize: nonceSize, tagSize: tagSize}
gcmAesInit(&g.productTable, g.ks)
return g, nil
}
@@ -68,6 +72,8 @@
productTable [256]byte
// nonceSize contains the expected size of the nonce, in bytes.
nonceSize int
+ // tagSize contains the expected size of the tag, in bytes
+ tagSize int
}
func (g *gcmAsm) NonceSize() int {
@@ -75,7 +81,7 @@
}
func (*gcmAsm) Overhead() int {
- return gcmTagSize
+ return g.tagSize
}
// sliceForAppend takes a slice and a requested number of bytes. It returns a
@@ -120,12 +126,12 @@
var tagOut [gcmTagSize]byte
gcmAesData(&g.productTable, data, &tagOut)
- ret, out := sliceForAppend(dst, len(plaintext)+gcmTagSize)
+ ret, out := sliceForAppend(dst, len(plaintext)+g.tagSize)
if len(plaintext) > 0 {
gcmAesEnc(&g.productTable, out, plaintext, &counter, &tagOut, g.ks)
}
gcmAesFinish(&g.productTable, &tagMask, &tagOut, uint64(len(plaintext)), uint64(len(data)))
- copy(out[len(plaintext):], tagOut[:])
+ copy(out[len(plaintext):], tagOut[:g.tagSize])
return ret
}
diff --git a/src/crypto/aes/modes.go b/src/crypto/aes/modes.go
index 1623fc1..8c46036 100644
--- a/src/crypto/aes/modes.go
+++ b/src/crypto/aes/modes.go
@@ -12,7 +12,7 @@
// implementation of GCM through the AEAD interface.
// See crypto/cipher/gcm.go.
type gcmAble interface {
- NewGCM(size int) (cipher.AEAD, error)
+ NewGCM(size int, tagSize int) (cipher.AEAD, error)
}
// cbcEncAble is implemented by cipher.Blocks that can provide an optimized
diff --git a/src/crypto/cipher/gcm.go b/src/crypto/cipher/gcm.go
index 62085aa..1b363e8 100644
--- a/src/crypto/cipher/gcm.go
+++ b/src/crypto/cipher/gcm.go
@@ -48,7 +48,7 @@
// implementation of GCM, like crypto/aes. NewGCM will check for this interface
// and return the specific AEAD if found.
type gcmAble interface {
- NewGCM(int) (AEAD, error)
+ NewGCM(int, int) (AEAD, error)
}
// gcmFieldElement represents a value in GF(2¹²⁸). In order to reflect the GCM
@@ -67,6 +67,7 @@
type gcm struct {
cipher Block
nonceSize int
+ tagSize int
// productTable contains the first sixteen powers of the key, H.
// However, they are in bit reversed order. See NewGCMWithNonceSize.
productTable [16]gcmFieldElement
@@ -89,18 +90,29 @@
// cryptosystem that uses non-standard nonce lengths. All other users should use
// NewGCM, which is faster and more resistant to misuse.
func NewGCMWithNonceSize(cipher Block, size int) (AEAD, error) {
+ return NewGCMWithTagSize(cipher, size, gcmTagSize)
+}
+
+// NewGCMWithTagSize returns the given 128-bit block cipher wrapped in Galois
+// Counter Modem, which accepts nonces of the given nounceSize and expects
+// tags to be of given tagSize
+func NewGCMWithTagSize(cipher Block, nounceSize, tagSize int) (AEAD, error) {
if cipher, ok := cipher.(gcmAble); ok {
- return cipher.NewGCM(size)
+ return cipher.NewGCM(nounceSize, tagSize)
}
if cipher.BlockSize() != gcmBlockSize {
return nil, errors.New("cipher: NewGCM requires 128-bit block cipher")
}
+ if tagSize < 8 || tagSize > 16 {
+ return nil, errors.New("cipher: NewGCM requires a tag size in range [8-16] bytes")
+ }
+
var key [gcmBlockSize]byte
cipher.Encrypt(key[:], key[:])
- g := &gcm{cipher: cipher, nonceSize: size}
+ g := &gcm{cipher: cipher, nonceSize: nounceSize, tagSize: tagSize}
// We precompute 16 multiples of |key|. However, when we do lookups
// into this table we'll be using bits from a field element and
@@ -131,8 +143,8 @@
return g.nonceSize
}
-func (*gcm) Overhead() int {
- return gcmTagSize
+func (g *gcm) Overhead() int {
+ return g.tagSize
}
func (g *gcm) Seal(dst, nonce, plaintext, data []byte) []byte {
@@ -143,7 +155,7 @@
panic("cipher: message too large for GCM")
}
- ret, out := sliceForAppend(dst, len(plaintext)+gcmTagSize)
+ ret, out := sliceForAppend(dst, len(plaintext)+g.tagSize)
var counter, tagMask [gcmBlockSize]byte
g.deriveCounter(&counter, nonce)
@@ -171,8 +183,8 @@
return nil, errOpen
}
- tag := ciphertext[len(ciphertext)-gcmTagSize:]
- ciphertext = ciphertext[:len(ciphertext)-gcmTagSize]
+ tag := ciphertext[len(ciphertext)-g.tagSize:]
+ ciphertext = ciphertext[:len(ciphertext)-g.tagSize]
var counter, tagMask [gcmBlockSize]byte
g.deriveCounter(&counter, nonce)
@@ -185,7 +197,7 @@
ret, out := sliceForAppend(dst, len(ciphertext))
- if subtle.ConstantTimeCompare(expectedTag[:], tag) != 1 {
+ if subtle.ConstantTimeCompare(expectedTag[:g.tagSize], tag) != 1 {
// The AESNI code decrypts and authenticates concurrently, and
// so overwrites dst in the event of a tag mismatch. That
// behavior is mimicked here in order to be consistent across
@@ -381,7 +393,7 @@
g.mul(&y)
putUint64(out, y.low)
- putUint64(out[8:], y.high)
+ putUintRemainingTagSize(out[8:], y.high)
xorWords(out, out, tagMask[:])
}
@@ -408,3 +420,11 @@
out[6] = byte(v >> 8)
out[7] = byte(v)
}
+
+func putUintRemainingTagSize(out []byte, v uint64) {
+ shift := uint(56)
+ for i := 0; i < len(out); i++ {
+ out[i] = byte(v >> shift)
+ shift -= 8
+ }
+}
diff --git a/src/crypto/cipher/gcm_test.go b/src/crypto/cipher/gcm_test.go
index 6878b4c..32c61dd 100644
--- a/src/crypto/cipher/gcm_test.go
+++ b/src/crypto/cipher/gcm_test.go
@@ -191,6 +191,10 @@
}
func TestAESGCM(t *testing.T) {
+ testAESGCMForTagSize(t, 16)
+}
+
+func testAESGCMForTagSize(t *testing.T, tagSize int) {
for i, test := range aesGCMTests {
key, _ := hex.DecodeString(test.key)
aes, err := aes.NewCipher(key)
@@ -201,7 +205,7 @@
nonce, _ := hex.DecodeString(test.nonce)
plaintext, _ := hex.DecodeString(test.plaintext)
ad, _ := hex.DecodeString(test.ad)
- aesgcm, err := cipher.NewGCMWithNonceSize(aes, len(nonce))
+ aesgcm, err := cipher.NewGCMWithTagSize(aes, len(nonce), 16)
if err != nil {
t.Fatal(err)
}
To view, visit change 38691. To unsubscribe, visit settings.
Adam Langley posted comments on this change.
Patch set 2:
Needs tests for truncated tags.
(3 comments)
File src/crypto/cipher/gcm.go:
Patch Set #2, Line 99: nounceSize
do you need to be able to specify the nonce size? (Also, "nonce" rather than "nounce".) The non-standard nonce support is basically for a specific Apple mistake and it doesn't use truncated tags.
Patch Set #2, Line 108: if tagSize < 8 || tagSize > 16 {
Where did the number 8 come from?
File src/crypto/cipher/gcm_test.go:
tagSize rather than 16?
To view, visit change 38691. To unsubscribe, visit settings.
Guillaume Leroi uploaded patch set #3 to this change.
crypto/cipher: improves GCM for custom tag sizes
* NewGCM takes a size for the tag
* NewGCMWithTagSize takes a custom nounce and tag size
* Seal does its computation as if the tag size is maximal, but only output
tagSize bytes
* Open only uses the last tagSize bytes from input as the tag, the
expected tag is computed as if it previously. Only tagSize bytes are
compared for authentication
Fixes #19594
Change-Id: I346179dd382c603019aa02fec15fe763e7a04bd4
---
M src/crypto/aes/aes_gcm.go
M src/crypto/aes/modes.go
M src/crypto/cipher/gcm.go
M src/crypto/cipher/gcm_test.go
4 files changed, 154 insertions(+), 69 deletions(-)
To view, visit change 38691. To unsubscribe, visit settings.
Guillaume Leroi posted comments on this change.
Patch set 3:
Patch Set 2:
(3 comments)
Needs tests for truncated tags.
I took in account your remarks.
I added tests, but i'm not sure if they are passing because all.bat runs, seems to compile but does not indicate any failures or success.
Sorry for any inconveniences.
(3 comments)
do you need to be able to specify the nonce size? (Also, "nonce" rather tha
No, i don't need it. I thought it could be better to have a generalized function, since it is authorized (but not recommended) by the specs
Patch Set #2, Line 108: // Specs 2.1: length of tag can be any value between 64 and 128 bits
Where did the number 8 come from?
The specs specify the tag to be any value between 64 and 128 bits.
I kept my implementation to plain bytes.
File src/crypto/cipher/gcm_test.go:
tagSize rather than 16?
Ack
To view, visit change 38691. To unsubscribe, visit settings.
Brad Fitzpatrick posted comments on this change.
Patch set 3:Run-TryBot +1
(3 comments)
File src/crypto/cipher/gcm.go:
Patch Set #3, Line 51: int, int
NewGCM(size int, tagSize int) like in the other file.
Patch Set #3, Line 97: nounceSize
typo: nonceSize
Patch Set #3, Line 98: tagSize
period at end of sentence. And clarify which tag sizes are allowed.
To view, visit change 38691. To unsubscribe, visit settings.
Gobot Gobot posted comments on this change.
Patch set 3:
TryBots beginning. Status page: https://farmer.golang.org/try?commit=b66d313c
Build is still in progress...
This change failed on linux-arm:
See https://storage.googleapis.com/go-build-log/b66d313c/linux-arm_2a1862b1.log
Consult https://build.golang.org/ to see whether it's a new failure. Other builds still in progress; subsequent failure notices suppressed until final report.
Gobot Gobot posted comments on this change.
Patch set 3:TryBot-Result -1
17 of 18 TryBots failed:
Failed on linux-arm: https://storage.googleapis.com/go-build-log/b66d313c/linux-arm_2a1862b1.log
Failed on misc-vet-vetall: https://storage.googleapis.com/go-build-log/b66d313c/misc-vet-vetall_16b3f451.log
Failed on misc-compile-netbsd: https://storage.googleapis.com/go-build-log/b66d313c/misc-compile-netbsd_c175aa9c.log
Failed on linux-amd64-race: https://storage.googleapis.com/go-build-log/b66d313c/linux-amd64-race_126559f7.log
Failed on misc-compile-plan9: https://storage.googleapis.com/go-build-log/b66d313c/misc-compile-plan9_45a4a62b.log
Failed on misc-compile-ppc: https://storage.googleapis.com/go-build-log/b66d313c/misc-compile-ppc_bf44715e.log
Failed on misc-compile: https://storage.googleapis.com/go-build-log/b66d313c/misc-compile_096368e7.log
Failed on linux-amd64: https://storage.googleapis.com/go-build-log/b66d313c/linux-amd64_afa7b08a.log
Failed on nacl-386: https://storage.googleapis.com/go-build-log/b66d313c/nacl-386_27b03a36.log
Failed on darwin-amd64-10_11: https://storage.googleapis.com/go-build-log/b66d313c/darwin-amd64-10_11_157dbeeb.log
Failed on nacl-amd64p32: https://storage.googleapis.com/go-build-log/b66d313c/nacl-amd64p32_930bce17.log
Failed on linux-amd64-ssacheck: https://storage.googleapis.com/go-build-log/b66d313c/linux-amd64-ssacheck_33142d0e.log
Failed on linux-386: https://storage.googleapis.com/go-build-log/b66d313c/linux-386_dfa77051.log
Failed on freebsd-amd64-gce101: https://storage.googleapis.com/go-build-log/b66d313c/freebsd-amd64-gce101_62baa720.log
Failed on windows-386-gce: https://storage.googleapis.com/go-build-log/b66d313c/windows-386-gce_fbad53aa.log
Failed on openbsd-amd64-60: https://storage.googleapis.com/go-build-log/b66d313c/openbsd-amd64-60_9766e1ba.log
Failed on windows-amd64-gce: https://storage.googleapis.com/go-build-log/b66d313c/windows-amd64-gce_ec64083a.log
Consult https://build.golang.org/ to see whether they are new failures.
Brad Fitzpatrick posted comments on this change.
Patch set 4:Run-TryBot +1
Also, please rebase this when you re-upload with changes.
RELNOTE=yes
Gobot Gobot posted comments on this change.
Patch set 4:
TryBots beginning. Status page: https://farmer.golang.org/try?commit=62a70c75
Build is still in progress...
This change failed on misc-compile-plan9:
See https://storage.googleapis.com/go-build-log/62a70c75/misc-compile-plan9_98845cac.log
Consult https://build.golang.org/ to see whether it's a new failure. Other builds still in progress; subsequent failure notices suppressed until final report.
To view, visit change 38691. To unsubscribe, visit settings.
Brad Fitzpatrick posted comments on this change.
Patch set 4:
(1 comment)
File src/crypto/aes/aes_gcm.go:
Patch Set #4, Line 84: return g.tagSize
This doesn't compile.
Please run tests before mailing code.
To view, visit change 38691. To unsubscribe, visit settings.
Gobot Gobot posted comments on this change.
Patch set 4:TryBot-Result -1
18 of 18 TryBots failed:
Failed on misc-compile-plan9: https://storage.googleapis.com/go-build-log/62a70c75/misc-compile-plan9_98845cac.log
Failed on linux-arm: https://storage.googleapis.com/go-build-log/62a70c75/linux-arm_2e334cf7.log
Failed on misc-vet-vetall: https://storage.googleapis.com/go-build-log/62a70c75/misc-vet-vetall_b1c56735.log
Failed on misc-compile: https://storage.googleapis.com/go-build-log/62a70c75/misc-compile_8954866c.log
Failed on linux-amd64: https://storage.googleapis.com/go-build-log/62a70c75/linux-amd64_dade27a8.log
Failed on misc-compile-ppc: https://storage.googleapis.com/go-build-log/62a70c75/misc-compile-ppc_de32eede.log
Failed on misc-compile-netbsd: https://storage.googleapis.com/go-build-log/62a70c75/misc-compile-netbsd_69464887.log
Failed on linux-amd64-race: https://storage.googleapis.com/go-build-log/62a70c75/linux-amd64-race_b69bbc81.log
Failed on darwin-amd64-10_11: https://storage.googleapis.com/go-build-log/62a70c75/darwin-amd64-10_11_78a044ea.log
Failed on nacl-386: https://storage.googleapis.com/go-build-log/62a70c75/nacl-386_892d178f.log
Failed on nacl-amd64p32: https://storage.googleapis.com/go-build-log/62a70c75/nacl-amd64p32_a5c8d015.log
Failed on linux-amd64-racecompile: https://storage.googleapis.com/go-build-log/62a70c75/linux-amd64-racecompile_91c65be1.log
Failed on linux-amd64-ssacheck: https://storage.googleapis.com/go-build-log/62a70c75/linux-amd64-ssacheck_4463df39.log
Failed on linux-386: https://storage.googleapis.com/go-build-log/62a70c75/linux-386_49691268.log
Failed on freebsd-amd64-gce101: https://storage.googleapis.com/go-build-log/62a70c75/freebsd-amd64-gce101_39894fc2.log
Failed on windows-amd64-gce: https://storage.googleapis.com/go-build-log/62a70c75/windows-amd64-gce_b28de386.log
Failed on openbsd-amd64-60: https://storage.googleapis.com/go-build-log/62a70c75/openbsd-amd64-60_c2cd948d.log
Failed on windows-386-gce: https://storage.googleapis.com/go-build-log/62a70c75/windows-386-gce_7f25b680.log
Consult https://build.golang.org/ to see whether they are new failures.
Brad Fitzpatrick abandoned this change.
To view, visit change 38691. To unsubscribe, or for help writing mail filters, visit settings.