Skip to content

Commit 3809035

Browse files
FiloSottilegopherbot
authored andcommitted
crypto/internal/fips/aes/gcm: add GCMForSSH
For #69536 Change-Id: Ia368f515893a95e176149e23239a8e253fc5272f Reviewed-on: https://go-review.googlesource.com/c/go/+/629095 LUCI-TryBot-Result: Go LUCI <[email protected]> Reviewed-by: Russ Cox <[email protected]> Auto-Submit: Filippo Valsorda <[email protected]> Reviewed-by: Roland Shoemaker <[email protected]>
1 parent bedde1b commit 3809035

File tree

2 files changed

+127
-0
lines changed

2 files changed

+127
-0
lines changed

src/crypto/cipher/gcm_test.go

Lines changed: 73 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -795,3 +795,76 @@ func TestFIPSServiceIndicator(t *testing.T) {
795795
// Wrap with overflow.
796796
expectPanic(t, g, []byte{1, 2, 3, 5, 0, 0, 0, 0, 0, 0, 0, 0})
797797
}
798+
799+
func TestGCMForSSH(t *testing.T) {
800+
// incIV from x/crypto/ssh/cipher.go.
801+
incIV := func(iv []byte) {
802+
for i := 4 + 7; i >= 4; i-- {
803+
iv[i]++
804+
if iv[i] != 0 {
805+
break
806+
}
807+
}
808+
}
809+
810+
expectOK := func(aead cipher.AEAD, iv []byte) {
811+
aead.Seal(nil, iv, []byte("hello, world"), nil)
812+
}
813+
814+
expectPanic := func(aead cipher.AEAD, iv []byte) {
815+
defer func() {
816+
if recover() == nil {
817+
t.Errorf("expected panic")
818+
}
819+
}()
820+
aead.Seal(nil, iv, []byte("hello, world"), nil)
821+
}
822+
823+
key := make([]byte, 16)
824+
block, _ := fipsaes.New(key)
825+
aead, err := gcm.NewGCMForSSH(block)
826+
if err != nil {
827+
t.Fatal(err)
828+
}
829+
iv := decodeHex(t, "11223344"+"0000000000000000")
830+
expectOK(aead, iv)
831+
incIV(iv)
832+
expectOK(aead, iv)
833+
iv = decodeHex(t, "11223344"+"fffffffffffffffe")
834+
expectOK(aead, iv)
835+
incIV(iv)
836+
expectPanic(aead, iv)
837+
838+
aead, _ = gcm.NewGCMForSSH(block)
839+
iv = decodeHex(t, "11223344"+"fffffffffffffffe")
840+
expectOK(aead, iv)
841+
incIV(iv)
842+
expectOK(aead, iv)
843+
incIV(iv)
844+
expectOK(aead, iv)
845+
incIV(iv)
846+
expectOK(aead, iv)
847+
848+
aead, _ = gcm.NewGCMForSSH(block)
849+
iv = decodeHex(t, "11223344"+"aaaaaaaaaaaaaaaa")
850+
expectOK(aead, iv)
851+
iv = decodeHex(t, "11223344"+"ffffffffffffffff")
852+
expectOK(aead, iv)
853+
incIV(iv)
854+
expectOK(aead, iv)
855+
iv = decodeHex(t, "11223344"+"aaaaaaaaaaaaaaa8")
856+
expectOK(aead, iv)
857+
incIV(iv)
858+
expectPanic(aead, iv)
859+
iv = decodeHex(t, "11223344"+"bbbbbbbbbbbbbbbb")
860+
expectPanic(aead, iv)
861+
}
862+
863+
func decodeHex(t *testing.T, s string) []byte {
864+
t.Helper()
865+
b, err := hex.DecodeString(s)
866+
if err != nil {
867+
t.Fatal(err)
868+
}
869+
return b
870+
}

src/crypto/internal/fips/aes/gcm/gcm_nonces.go

Lines changed: 54 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -201,3 +201,57 @@ func (g *GCMForTLS13) Open(dst, nonce, ciphertext, data []byte) ([]byte, error)
201201
fips.RecordApproved()
202202
return g.g.Open(dst, nonce, ciphertext, data)
203203
}
204+
205+
// NewGCMForSSH returns a new AEAD that works like GCM, but enforces the
206+
// construction of nonces as specified in RFC 5647.
207+
//
208+
// This complies with FIPS 140-3 IG C.H Scenario 1.d.
209+
func NewGCMForSSH(cipher *aes.Block) (*GCMForSSH, error) {
210+
g, err := newGCM(&GCM{}, cipher, gcmStandardNonceSize, gcmTagSize)
211+
if err != nil {
212+
return nil, err
213+
}
214+
return &GCMForSSH{g: *g}, nil
215+
}
216+
217+
type GCMForSSH struct {
218+
g GCM
219+
ready bool
220+
start uint64
221+
next uint64
222+
}
223+
224+
func (g *GCMForSSH) NonceSize() int { return gcmStandardNonceSize }
225+
226+
func (g *GCMForSSH) Overhead() int { return gcmTagSize }
227+
228+
func (g *GCMForSSH) Seal(dst, nonce, plaintext, data []byte) []byte {
229+
if len(nonce) != gcmStandardNonceSize {
230+
panic("crypto/cipher: incorrect nonce length given to GCM")
231+
}
232+
233+
counter := byteorder.BeUint64(nonce[len(nonce)-8:])
234+
if !g.ready {
235+
// In the first call we learn the start value.
236+
g.ready = true
237+
g.start = counter
238+
}
239+
counter -= g.start
240+
241+
// Ensure the counter is monotonically increasing.
242+
if counter == math.MaxUint64 {
243+
panic("crypto/cipher: counter wrapped")
244+
}
245+
if counter < g.next {
246+
panic("crypto/cipher: counter decreased")
247+
}
248+
g.next = counter + 1
249+
250+
fips.RecordApproved()
251+
return g.g.sealAfterIndicator(dst, nonce, plaintext, data)
252+
}
253+
254+
func (g *GCMForSSH) Open(dst, nonce, ciphertext, data []byte) ([]byte, error) {
255+
fips.RecordApproved()
256+
return g.g.Open(dst, nonce, ciphertext, data)
257+
}

0 commit comments

Comments
 (0)