xts: fix incorrect length check

This change does two things:
 1. Fix a length checking bug in the Decrypt function.
 2. Use binary.LittleEndian for byte conversions.

Fixes golang/go#19881

Change-Id: I9d33b92f2bd7e6ca8f69308388f1e8a5c6df2be8
Reviewed-on: https://go-review.googlesource.com/39954
Reviewed-by: Adam Langley <agl@golang.org>
diff --git a/xts/xts.go b/xts/xts.go
index c9a283b..a7643fd 100644
--- a/xts/xts.go
+++ b/xts/xts.go
@@ -23,6 +23,7 @@
 
 import (
 	"crypto/cipher"
+	"encoding/binary"
 	"errors"
 )
 
@@ -65,21 +66,20 @@
 	}
 
 	var tweak [blockSize]byte
-	for i := 0; i < 8; i++ {
-		tweak[i] = byte(sectorNum)
-		sectorNum >>= 8
-	}
+	binary.LittleEndian.PutUint64(tweak[:8], sectorNum)
 
 	c.k2.Encrypt(tweak[:], tweak[:])
 
-	for i := 0; i < len(plaintext); i += blockSize {
-		for j := 0; j < blockSize; j++ {
-			ciphertext[i+j] = plaintext[i+j] ^ tweak[j]
+	for len(plaintext) > 0 {
+		for j := range tweak {
+			ciphertext[j] = plaintext[j] ^ tweak[j]
 		}
-		c.k1.Encrypt(ciphertext[i:], ciphertext[i:])
-		for j := 0; j < blockSize; j++ {
-			ciphertext[i+j] ^= tweak[j]
+		c.k1.Encrypt(ciphertext, ciphertext)
+		for j := range tweak {
+			ciphertext[j] ^= tweak[j]
 		}
+		plaintext = plaintext[blockSize:]
+		ciphertext = ciphertext[blockSize:]
 
 		mul2(&tweak)
 	}
@@ -97,21 +97,20 @@
 	}
 
 	var tweak [blockSize]byte
-	for i := 0; i < 8; i++ {
-		tweak[i] = byte(sectorNum)
-		sectorNum >>= 8
-	}
+	binary.LittleEndian.PutUint64(tweak[:8], sectorNum)
 
 	c.k2.Encrypt(tweak[:], tweak[:])
 
-	for i := 0; i < len(plaintext); i += blockSize {
-		for j := 0; j < blockSize; j++ {
-			plaintext[i+j] = ciphertext[i+j] ^ tweak[j]
+	for len(ciphertext) > 0 {
+		for j := range tweak {
+			plaintext[j] = ciphertext[j] ^ tweak[j]
 		}
-		c.k1.Decrypt(plaintext[i:], plaintext[i:])
-		for j := 0; j < blockSize; j++ {
-			plaintext[i+j] ^= tweak[j]
+		c.k1.Decrypt(plaintext, plaintext)
+		for j := range tweak {
+			plaintext[j] ^= tweak[j]
 		}
+		plaintext = plaintext[blockSize:]
+		ciphertext = ciphertext[blockSize:]
 
 		mul2(&tweak)
 	}
diff --git a/xts/xts_test.go b/xts/xts_test.go
index 7a5e9fa..96d3b6c 100644
--- a/xts/xts_test.go
+++ b/xts/xts_test.go
@@ -83,3 +83,23 @@
 		}
 	}
 }
+
+func TestShorterCiphertext(t *testing.T) {
+	// Decrypt used to panic if the input was shorter than the output. See
+	// https://go-review.googlesource.com/c/39954/
+	c, err := NewCipher(aes.NewCipher, make([]byte, 32))
+	if err != nil {
+		t.Fatalf("NewCipher failed: %s", err)
+	}
+
+	plaintext := make([]byte, 32)
+	encrypted := make([]byte, 48)
+	decrypted := make([]byte, 48)
+
+	c.Encrypt(encrypted, plaintext, 0)
+	c.Decrypt(decrypted, encrypted[:len(plaintext)], 0)
+
+	if !bytes.Equal(plaintext, decrypted[:len(plaintext)]) {
+		t.Errorf("En/Decryption is not inverse")
+	}
+}