CVE-2016-9123 (published 2017-03-28T02:59Z)

go-jose before 1.0.5 suffers from a CBC-HMAC integer overflow on 32-bit architectures. An integer overflow could lead to authentication bypass for CBC-HMAC encrypted ciphertexts on 32-bit architectures.

Severity: MEDIUM
CVSS v2 base score: 5.0 (AV:N/AC:L/Au:N/C:N/I:P/A:N)
CVSS v3 base score: 7.5, HIGH (CVSS:3.0/AV:N/AC:L/PR:N/UI:N/S:U/C:N/I:H/A:N)

Weakness information:
cwe_id: CWE-190
cwe_name: Integer Overflow or Wraparound
description: The product performs a calculation that can
         produce an integer overflow or wraparound when the logic
         assumes that the resulting value will always be larger than
         the original value. This occurs when an integer value is
         incremented to a value that is too large to store in the
         associated representation. When this occurs, the value may
         become a very small or negative number.
extended_description: 
            
            
            
            
         
         

Fix information:
repo_url: https://github.com/square/go-jose
commit_details: repo_url: https://github.com/square/go-jose
author: Cedric Staub
author_date: 2016-09-02 21:38:01-07:00
msg: Use uint64 for all size calculations, size checks
repository: repo_url: https://github.com/square/go-jose
repo_name: square/go-jose
description: An implementation of JOSE standards (JWE, JWS, JWT) in Go
owner: square
file_changes: filename: cbc_hmac.go
change_type: ModificationType.MODIFY
diff: @@ -82,7 +82,7 @@ func (ctx *cbcAEAD) Overhead() int {
 // Seal encrypts and authenticates the plaintext.
 func (ctx *cbcAEAD) Seal(dst, nonce, plaintext, data []byte) []byte {
 	// Output buffer -- must take care not to mangle plaintext input.
-	ciphertext := make([]byte, len(plaintext)+ctx.Overhead())[:len(plaintext)]
+	ciphertext := make([]byte, uint64(len(plaintext))+uint64(ctx.Overhead()))[:len(plaintext)]
 	copy(ciphertext, plaintext)
 	ciphertext = padBuffer(ciphertext, ctx.blockCipher.BlockSize())
 
@@ -91,7 +91,7 @@ func (ctx *cbcAEAD) Seal(dst, nonce, plaintext, data []byte) []byte {
 	cbc.CryptBlocks(ciphertext, ciphertext)
 	authtag := ctx.computeAuthTag(data, nonce, ciphertext)
 
-	ret, out := resize(dst, len(dst)+len(ciphertext)+len(authtag))
+	ret, out := resize(dst, uint64(len(dst))+uint64(len(ciphertext))+uint64(len(authtag)))
 	copy(out, ciphertext)
 	copy(out[len(ciphertext):], authtag)
 
@@ -128,7 +128,7 @@ func (ctx *cbcAEAD) Open(dst, nonce, ciphertext, data []byte) ([]byte, error) {
 		return nil, err
 	}
 
-	ret, out := resize(dst, len(dst)+len(plaintext))
+	ret, out := resize(dst, uint64(len(dst))+uint64(len(plaintext)))
 	copy(out, plaintext)
 
 	return ret, nil
@@ -136,12 +136,12 @@ func (ctx *cbcAEAD) Open(dst, nonce, ciphertext, data []byte) ([]byte, error) {
 
 // Compute an authentication tag
 func (ctx *cbcAEAD) computeAuthTag(aad, nonce, ciphertext []byte) []byte {
-	buffer := make([]byte, len(aad)+len(nonce)+len(ciphertext)+8)
+	buffer := make([]byte, uint64(len(aad))+uint64(len(nonce))+uint64(len(ciphertext))+8)
 	n := 0
 	n += copy(buffer, aad)
 	n += copy(buffer[n:], nonce)
 	n += copy(buffer[n:], ciphertext)
-	binary.BigEndian.PutUint64(buffer[n:], uint64(len(aad)*8))
+	binary.BigEndian.PutUint64(buffer[n:], uint64(len(aad))*8)
 
 	// According to documentation, Write() on hash.Hash never fails.
 	hmac := hmac.New(ctx.hash, ctx.integrityKey)
@@ -153,8 +153,8 @@ func (ctx *cbcAEAD) computeAuthTag(aad, nonce, ciphertext []byte) []byte {
 // resize ensures the the given slice has a capacity of at least n bytes.
 // If the capacity of the slice is less than n, a new slice is allocated
 // and the existing data will be copied.
-func resize(in []byte, n int) (head, tail []byte) {
-	if cap(in) >= n {
+func resize(in []byte, n uint64) (head, tail []byte) {
+	if uint64(cap(in)) >= n {
 		head = in[:n]
 	} else {
 		head = make([]byte, n)
@@ -168,7 +168,7 @@ func resize(in []byte, n int) (head, tail []byte) {
 // Apply padding
 func padBuffer(buffer []byte, blockSize int) []byte {
 	missing := blockSize - (len(buffer) % blockSize)
-	ret, out := resize(buffer, len(buffer)+missing)
+	ret, out := resize(buffer, uint64(len(buffer))+uint64(missing))
 	padding := bytes.Repeat([]byte{byte(missing)}, missing)
 	copy(out, padding)
 	return ret

programming_language: Go
filename: cbc_hmac_test.go
change_type: ModificationType.MODIFY
diff: @@ -283,7 +283,7 @@ func TestTruncatedCiphertext(t *testing.T) {
 	ct := aead.Seal(nil, nonce, data, nil)
 
 	// Truncated ciphertext, but with correct auth tag
-	truncated, tail := resize(ct[:len(ct)-ctx.authtagBytes-2], len(ct)-2)
+	truncated, tail := resize(ct[:len(ct)-ctx.authtagBytes-2], uint64(len(ct))-2)
 	copy(tail, ctx.computeAuthTag(nil, nonce, truncated[:len(truncated)-ctx.authtagBytes]))
 
 	// Open should fail
@@ -313,8 +313,8 @@ func TestInvalidPaddingOpen(t *testing.T) {
 	ctx := aead.(*cbcAEAD)
 
 	// Mutated ciphertext, but with correct auth tag
-	size := len(buffer)
-	ciphertext, tail := resize(buffer, size+(len(key)/2))
+	size := uint64(len(buffer))
+	ciphertext, tail := resize(buffer, size+(uint64(len(key))/2))
 	copy(tail, ctx.computeAuthTag(nil, nonce, ciphertext[:size]))
 
 	// Open should fail (b/c of invalid padding, even though tag matches)

programming_language: Go
filename: concat_kdf.go
change_type: ModificationType.MODIFY
diff: @@ -32,7 +32,7 @@ type concatKDF struct {
 
 // NewConcatKDF builds a KDF reader based on the given inputs.
 func NewConcatKDF(hash crypto.Hash, z, algID, ptyUInfo, ptyVInfo, supPubInfo, supPrivInfo []byte) io.Reader {
-	buffer := make([]byte, len(algID)+len(ptyUInfo)+len(ptyVInfo)+len(supPubInfo)+len(supPrivInfo))
+	buffer := make([]byte, uint64(len(algID))+uint64(len(ptyUInfo))+uint64(len(ptyVInfo))+uint64(len(supPubInfo))+uint64(len(supPrivInfo)))
 	n := 0
 	n += copy(buffer, algID)
 	n += copy(buffer[n:], ptyUInfo)

programming_language: Go
filename: ecdh_es.go
change_type: ModificationType.MODIFY
diff: @@ -24,8 +24,13 @@ import (
 
 // DeriveECDHES derives a shared encryption key using ECDH/ConcatKDF as described in JWE/JWA.
 // It is an error to call this function with a private/public key that are not on the same
-// curve. Callers must ensure that the keys are valid before calling this function.
+// curve. Callers must ensure that the keys are valid before calling this function. Output
+// size may be at most 1<<16 bytes (64 KiB).
 func DeriveECDHES(alg string, apuData, apvData []byte, priv *ecdsa.PrivateKey, pub *ecdsa.PublicKey, size int) []byte {
+	if size > 1<<16 {
+		panic("ECDH-ES output size too large, must be less than 1<<16")
+	}
+
 	// algId, partyUInfo, partyVInfo inputs must be prefixed with the length
 	algID := lengthPrefixed([]byte(alg))
 	ptyUInfo := lengthPrefixed(apuData)

programming_language: Go