Example #1
0
func TestGCMCounterWrap(t *testing.T) {
	// Test that the last 32-bits of the counter wrap correctly.
	tests := []struct {
		nonce, tag string
	}{
		{"0fa72e25", "37e1948cdfff09fbde0c40ad99fee4a7"},   // counter: 7eb59e4d961dad0dfdd75aaffffffff0
		{"afe05cc1", "438f3aa9fee5e54903b1927bca26bbdf"},   // counter: 75d492a7e6e6bfc979ad3a8ffffffff4
		{"9ffecbef", "7b88ca424df9703e9e8611071ec7e16e"},   // counter: c8bb108b0ecdc71747b9d57ffffffff5
		{"ffc3e5b3", "38d49c86e0abe853ac250e66da54c01a"},   // counter: 706414d2de9b36ab3b900a9ffffffff6
		{"cfdd729d", "e08402eaac36a1a402e09b1bd56500e8"},   // counter: cd0b96fe36b04e750584e56ffffffff7
		{"010ae3d486", "5405bb490b1f95d01e2ba735687154bc"}, // counter: e36c18e69406c49722808104fffffff8
		{"01b1107a9d", "939a585f342e01e17844627492d44dbf"}, // counter: e6d56eaf9127912b6d62c6dcffffffff
	}
	key, err := aes.NewCipher(make([]byte, 16))
	if err != nil {
		t.Fatal(err)
	}
	plaintext := make([]byte, 16*17+1)
	for i, test := range tests {
		nonce, _ := hex.DecodeString(test.nonce)
		want, _ := hex.DecodeString(test.tag)
		aead, err := cipher.NewGCMWithNonceSize(key, len(nonce))
		if err != nil {
			t.Fatal(err)
		}
		got := aead.Seal(nil, nonce, plaintext, nil)
		if !bytes.Equal(got[len(plaintext):], want) {
			t.Errorf("test[%v]: got: %x, want: %x", i, got[len(plaintext):], want)
		}
		_, err = aead.Open(nil, nonce, got, nil)
		if err != nil {
			t.Errorf("test[%v]: authentication failed", i)
		}
	}
}
Example #2
0
func TestAESGCM(t *testing.T) {
	for i, test := range aesGCMTests {
		key, _ := hex.DecodeString(test.key)
		aes, err := aes.NewCipher(key)
		if err != nil {
			t.Fatal(err)
		}

		nonce, _ := hex.DecodeString(test.nonce)
		plaintext, _ := hex.DecodeString(test.plaintext)
		ad, _ := hex.DecodeString(test.ad)
		aesgcm, err := cipher.NewGCMWithNonceSize(aes, len(nonce))
		if err != nil {
			t.Fatal(err)
		}

		ct := aesgcm.Seal(nil, nonce, plaintext, ad)
		if ctHex := hex.EncodeToString(ct); ctHex != test.result {
			t.Errorf("#%d: got %s, want %s", i, ctHex, test.result)
			continue
		}

		plaintext2, err := aesgcm.Open(nil, nonce, ct, ad)
		if err != nil {
			t.Errorf("#%d: Open failed", i)
			continue
		}

		if !bytes.Equal(plaintext, plaintext2) {
			t.Errorf("#%d: plaintext's don't match: got %x vs %x", i, plaintext2, plaintext)
			continue
		}

		if len(ad) > 0 {
			ad[0] ^= 0x80
			if _, err := aesgcm.Open(nil, nonce, ct, ad); err == nil {
				t.Errorf("#%d: Open was successful after altering additional data", i)
			}
			ad[0] ^= 0x80
		}

		nonce[0] ^= 0x80
		if _, err := aesgcm.Open(nil, nonce, ct, ad); err == nil {
			t.Errorf("#%d: Open was successful after altering nonce", i)
		}
		nonce[0] ^= 0x80

		ct[0] ^= 0x80
		if _, err := aesgcm.Open(nil, nonce, ct, ad); err == nil {
			t.Errorf("#%d: Open was successful after altering ciphertext", i)
		}
		ct[0] ^= 0x80
	}
}
Example #3
0
// DecryptFile decrypts the file at the specified path using GCM
func DecryptFile(inFilePath, outFilePath string, key, givenIV, aad []byte) error {
	if _, err := os.Stat(inFilePath); os.IsNotExist(err) {
		return fmt.Errorf("A file does not exist at %s", inFilePath)
	}
	// copy the IV since it will potentially be incremented
	iv := make([]byte, len(givenIV))
	copy(iv, givenIV)

	inFile, err := os.Open(inFilePath)
	if err != nil {
		return err
	}
	defer inFile.Close()

	outFile, err := os.OpenFile(outFilePath, os.O_CREATE|os.O_WRONLY|os.O_TRUNC, 0600)
	if err != nil {
		return err
	}
	defer outFile.Close()

	aes, err := aes.NewCipher(key)
	if err != nil {
		return err
	}
	gcm, err := cipher.NewGCMWithNonceSize(aes, len(iv))
	if err != nil {
		return err
	}

	written := false
	chunk := make([]byte, chunkSize+gcm.Overhead())
	for {
		read, err := inFile.Read(chunk)
		// ensure we have written at least one chunk before breaking
		if read > 0 || !written {
			decrChunk, err := gcm.Open(nil, iv, chunk[:read], aad)
			if err != nil {
				return err
			}
			if _, err := outFile.Write(decrChunk); err != nil {
				return err
			}
			written = true
		}
		if err == io.EOF {
			break
		}
		incrementIV(iv)
	}
	return nil
}
Example #4
0
// Encrypt takes one of (string, int, float, bool) and encrypts it with the provided key and additional auth data, returning a sops-format encrypted string.
func (c Cipher) Encrypt(value interface{}, key []byte, path string, stash interface{}) (string, error) {
	if value == "" {
		return "", nil
	}
	aescipher, err := cryptoaes.NewCipher(key)
	if err != nil {
		return "", fmt.Errorf("Could not initialize AES GCM encryption cipher: %s", err)
	}
	var iv []byte
	if stash, ok := stash.(stashData); !ok || stash.plaintext != value {
		iv = make([]byte, nonceSize)
		_, err = rand.Read(iv)
		if err != nil {
			return "", fmt.Errorf("Could not generate random bytes for IV: %s", err)
		}
	} else {
		iv = stash.iv
	}
	gcm, err := cipher.NewGCMWithNonceSize(aescipher, nonceSize)
	if err != nil {
		return "", fmt.Errorf("Could not create GCM: %s", err)
	}
	var plaintext []byte
	var encryptedType string
	switch value := value.(type) {
	case string:
		encryptedType = "str"
		plaintext = []byte(value)
	case int:
		encryptedType = "int"
		plaintext = []byte(strconv.Itoa(value))
	case float64:
		encryptedType = "float"
		// The Python version encodes floats without padding 0s after the decimal point.
		plaintext = []byte(strconv.FormatFloat(value, 'f', -1, 64))
	case bool:
		encryptedType = "bool"
		// The Python version encodes booleans with Titlecase
		plaintext = []byte(strings.Title(strconv.FormatBool(value)))
	default:
		return "", fmt.Errorf("Value to encrypt has unsupported type %T", value)
	}
	out := gcm.Seal(nil, iv, plaintext, []byte(path))
	return fmt.Sprintf("ENC[AES256_GCM,data:%s,iv:%s,tag:%s,type:%s]",
		base64.StdEncoding.EncodeToString(out[:len(out)-cryptoaes.BlockSize]),
		base64.StdEncoding.EncodeToString(iv),
		base64.StdEncoding.EncodeToString(out[len(out)-cryptoaes.BlockSize:]),
		encryptedType), nil
}
Example #5
0
// Decrypt takes a sops-format value string and a key and returns the decrypted value and a stash value
func (c Cipher) Decrypt(value string, key []byte, path string) (plaintext interface{}, stash interface{}, err error) {
	if value == "" {
		return "", nil, nil
	}
	encryptedValue, err := parse(value)
	if err != nil {
		return "", nil, err
	}
	aescipher, err := cryptoaes.NewCipher(key)
	if err != nil {
		return "", nil, err
	}

	gcm, err := cipher.NewGCMWithNonceSize(aescipher, len(encryptedValue.iv))
	if err != nil {
		return "", nil, err
	}
	stashValue := stashData{iv: encryptedValue.iv}
	data := append(encryptedValue.data, encryptedValue.tag...)
	decryptedBytes, err := gcm.Open(nil, encryptedValue.iv, data, []byte(path))
	if err != nil {
		return "", nil, fmt.Errorf("Could not decrypt with AES_GCM: %s", err)
	}
	decryptedValue := string(decryptedBytes)
	switch encryptedValue.datatype {
	case "str":
		stashValue.plaintext = decryptedValue
		return decryptedValue, stashValue, nil
	case "int":
		plaintext, err = strconv.Atoi(decryptedValue)
		stashValue.plaintext = plaintext
		return plaintext, stashValue, err
	case "float":
		plaintext, err = strconv.ParseFloat(decryptedValue, 64)
		stashValue.plaintext = plaintext
		return plaintext, stashValue, err
	case "bytes":
		stashValue.plaintext = decryptedBytes
		return decryptedBytes, stashValue, nil
	case "bool":
		plaintext, err = strconv.ParseBool(decryptedValue)
		stashValue.plaintext = plaintext
		return plaintext, stashValue, err
	default:
		return nil, nil, fmt.Errorf("Unknown datatype: %s", encryptedValue.datatype)
	}
}
Example #6
0
// TestEncryptDecrypt encrypts and decrypts using both stupidgcm and Go's built-in
// GCM implemenatation and verifies that the results are identical.
func TestEncryptDecrypt(t *testing.T) {
	key := randBytes(32)
	sGCM := New(key)
	authData := randBytes(24)
	iv := randBytes(16)
	dst := make([]byte, 71) // 71 = random length

	gAES, err := aes.NewCipher(key)
	if err != nil {
		t.Fatal(err)
	}
	gGCM, err := cipher.NewGCMWithNonceSize(gAES, 16)
	if err != nil {
		t.Fatal(err)
	}

	// Check all block sizes from 1 to 5000
	for i := 1; i < 5000; i++ {
		in := make([]byte, i)

		sOut := sGCM.Seal(dst, iv, in, authData)
		gOut := gGCM.Seal(dst, iv, in, authData)

		// Ciphertext must be identical to Go GCM
		if !bytes.Equal(sOut, gOut) {
			t.Fatalf("Compare failed for encryption, size %d", i)
			t.Log("sOut:")
			t.Log("\n" + hex.Dump(sOut))
			t.Log("gOut:")
			t.Log("\n" + hex.Dump(gOut))
		}

		sOut2, sErr := sGCM.Open(dst, iv, sOut[len(dst):], authData)
		if sErr != nil {
			t.Fatal(sErr)
		}
		gOut2, gErr := gGCM.Open(dst, iv, gOut[len(dst):], authData)
		if gErr != nil {
			t.Fatal(gErr)
		}

		// Plaintext must be identical to Go GCM
		if !bytes.Equal(sOut2, gOut2) {
			t.Fatalf("Compare failed for decryption, size %d", i)
		}
	}
}
Example #7
0
func Benchmark4kEncGoGCM(b *testing.B) {
	key := randBytes(32)
	authData := randBytes(24)
	iv := randBytes(16)
	in := make([]byte, 4096)
	b.SetBytes(int64(len(in)))

	gAES, err := aes.NewCipher(key)
	if err != nil {
		b.Fatal(err)
	}
	gGCM, err := cipher.NewGCMWithNonceSize(gAES, 16)
	if err != nil {
		b.Fatal(err)
	}

	for i := 0; i < b.N; i++ {
		// Encrypt and append to nonce
		gGCM.Seal(iv, iv, in, authData)
	}
}
Example #8
0
// Encrypt takes one of (string, int, float, bool) and encrypts it with the provided key and additional auth data, returning a sops-format encrypted string.
func (c Cipher) Encrypt(value interface{}, key []byte, additionalAuthData []byte) (string, error) {
	aescipher, err := cryptoaes.NewCipher(key)
	if err != nil {
		return "", fmt.Errorf("Could not initialize AES GCM encryption cipher: %s", err)
	}
	iv := make([]byte, nonceSize)
	_, err = rand.Read(iv)
	if err != nil {
		return "", fmt.Errorf("Could not generate random bytes for IV: %s", err)
	}
	gcm, err := cipher.NewGCMWithNonceSize(aescipher, nonceSize)
	if err != nil {
		return "", fmt.Errorf("Could not create GCM: %s", err)
	}
	var plaintext []byte
	var encryptedType string
	switch value := value.(type) {
	case string:
		encryptedType = "str"
		plaintext = []byte(value)
	case int:
		encryptedType = "int"
		plaintext = []byte(strconv.Itoa(value))
	case float64:
		encryptedType = "float"
		plaintext = []byte(strconv.FormatFloat(value, 'f', 9, 64))
	case bool:
		encryptedType = "bool"
		plaintext = []byte(strconv.FormatBool(value))
	default:
		return "", fmt.Errorf("Value to encrypt has unsupported type %T", value)
	}
	out := gcm.Seal(nil, iv, plaintext, additionalAuthData)
	return fmt.Sprintf("ENC[AES256_GCM,data:%s,iv:%s,tag:%s,type:%s]",
		base64.StdEncoding.EncodeToString(out[:len(out)-cryptoaes.BlockSize]),
		base64.StdEncoding.EncodeToString(iv),
		base64.StdEncoding.EncodeToString(out[len(out)-cryptoaes.BlockSize:]),
		encryptedType), nil
}
Example #9
0
// Decrypt takes a sops-format value string and a key and returns the decrypted value.
func (c Cipher) Decrypt(value string, key []byte, additionalAuthData []byte) (interface{}, error) {
	encryptedValue, err := parse(value)
	if err != nil {
		return "", err
	}
	aescipher, err := cryptoaes.NewCipher(key)
	if err != nil {
		return "", err
	}

	gcm, err := cipher.NewGCMWithNonceSize(aescipher, len(encryptedValue.iv))
	if err != nil {
		return "", err
	}

	data := append(encryptedValue.data, encryptedValue.tag...)
	decryptedBytes, err := gcm.Open(nil, encryptedValue.iv, data, additionalAuthData)
	if err != nil {
		return "", fmt.Errorf("Could not decrypt with AES_GCM: %s", err)
	}
	decryptedValue := string(decryptedBytes)
	switch encryptedValue.datatype {
	case "str":
		return decryptedValue, nil
	case "int":
		return strconv.Atoi(decryptedValue)
	case "float":
		return strconv.ParseFloat(decryptedValue, 64)
	case "bytes":
		return decryptedValue, nil
	case "bool":
		return strconv.ParseBool(decryptedValue)
	default:
		return nil, fmt.Errorf("Unknown datatype: %s", encryptedValue.datatype)
	}
}
Example #10
0
// goGCMWrapper - This wrapper makes sure gocryptfs can be compiled on Go
// versions 1.4 and lower that lack NewGCMWithNonceSize().
// 128 bit GCM IVs will not work when using built-in Go crypto, obviously, when
// compiled on 1.4.
func goGCMWrapper(bc cipher.Block, nonceSize int) (cipher.AEAD, error) {
	return cipher.NewGCMWithNonceSize(bc, nonceSize)
}