Skip to content

Commit

Permalink
Fail on double encrypt (#34)
Browse files Browse the repository at this point in the history
  • Loading branch information
giarc3 authored Aug 4, 2023
1 parent 1585166 commit 2be0935
Show file tree
Hide file tree
Showing 5 changed files with 55 additions and 10 deletions.
4 changes: 3 additions & 1 deletion CHANGELOG.md
Original file line number Diff line number Diff line change
@@ -1,7 +1,9 @@
# Changelog

## v0.3.0 (Unreleased)
## v0.3.0

- Encryption now returns a Crypto error when trying to encrypt a document that has already been IronCore encrypted.
- If you have a use case for double-encrypting a document, please open an issue explaining and we can work on accommodating you.
- Change minimum required Go version to 1.19

## v0.2.2
Expand Down
7 changes: 7 additions & 0 deletions crypto.go
Original file line number Diff line number Diff line change
Expand Up @@ -71,6 +71,13 @@ func encryptWithNonce(plaintext []byte, key []byte, nonce []byte) ([]byte, error

// encryptDocumentBytes generates a header, encrypts the document, and returns the two appended together.
func encryptDocumentBytes(document []byte, tenantID string, dek []byte) ([]byte, error) {
// Check if the provided document is already IronCore encrypted
if len(document) >= documentHeaderMetaLength {
fixedPreamble := document[0:documentHeaderMetaLength]
if verifyPreamble(fixedPreamble) {
return nil, makeErrorf(errorKindCrypto, "The provided document is already IronCore encrypted.")
}
}
header, err := generateHeader(dek, tenantID)
if err != nil {
return nil, err
Expand Down
34 changes: 26 additions & 8 deletions crypto_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -71,23 +71,26 @@ func TestDecryptingBadTag(t *testing.T) {
hexString := knownGoodEncryptedValueHexString[0:len(knownGoodEncryptedValueHexString)-2] + "00"
encryptedDocument, _ := hex.DecodeString(hexString)
_, err := decryptDocumentBytes(encryptedDocument, knownDek)
assert.ErrorIsf(t, err, ErrKindCrypto, "AES decryption failed")
assert.ErrorIs(t, err, ErrKindCrypto)
assert.ErrorContains(t, err, "AES decryption failed")
}

// This is an incorrect preamble.
func TestDecryptInvalidDocument(t *testing.T) {
hexString := "00000000000000"
encryptedDocument, _ := hex.DecodeString(hexString)
_, err := decryptDocumentBytes(encryptedDocument, knownDek)
assert.ErrorIsf(t, err, ErrKindCrypto, "provided bytes were not an IronCore encrypted document")
assert.ErrorIs(t, err, ErrKindCrypto)
assert.ErrorContains(t, err, "provided bytes were not an IronCore encrypted document")
}

// This is an incorrect preamble.
func TestDecryptInvalidDocumentIncorrectLength(t *testing.T) {
hexString := "00000000000100" // Length of 256
encryptedDocument, _ := hex.DecodeString(hexString)
_, err := decryptDocumentBytes(encryptedDocument, knownDek)
assert.ErrorIsf(t, err, ErrKindCrypto, "provided bytes were not an IronCore encrypted document")
assert.ErrorIs(t, err, ErrKindCrypto)
assert.ErrorContains(t, err, "provided bytes were not an IronCore encrypted document")
}

func TestVerifyWithWrongDek(t *testing.T) {
Expand All @@ -99,22 +102,25 @@ func TestVerifyWithWrongDek(t *testing.T) {
func TestDecryptDocumentWithCorruptHeader(t *testing.T) {
corruptDocument, _ := hex.DecodeString(strings.Replace(knownGoodEncryptedValueHexString, "f4e016c0", "00000000", 1))
_, err := decryptDocumentBytes(corruptDocument, knownDek)
assert.ErrorIsf(t, err, ErrKindCrypto, "provided bytes were not an IronCore encrypted document")
assert.ErrorIs(t, err, ErrKindCrypto)
assert.ErrorContains(t, err, "provided bytes were not an IronCore encrypted document")
}

func TestEncryptWithBadNonce(t *testing.T) {
plaintext := []byte("This is a non base64 string.")
dek := generateDek()
badNonce := []byte("foobar")
_, err := encryptWithNonce(plaintext, dek, badNonce)
assert.ErrorIsf(t, err, ErrKindCrypto, "the nonce passed had length")
assert.ErrorIs(t, err, ErrKindCrypto)
assert.ErrorContains(t, err, "the nonce passed had length")
}

func TestDecryptTooShort(t *testing.T) {
badCiphertext := []byte("foo")
dek := generateDek()
_, err := decrypt(badCiphertext, dek)
assert.ErrorIsf(t, err, ErrKindCrypto, "ciphertext is too short")
assert.ErrorIs(t, err, ErrKindCrypto)
assert.ErrorContains(t, err, "ciphertext is too short")
}

func TestRoundtripDocumentBytes(t *testing.T) {
Expand All @@ -126,21 +132,33 @@ func TestRoundtripDocumentBytes(t *testing.T) {
assert.Equal(t, decrypted, document)
}

func TestDoubleEncrypt(t *testing.T) {
dek := generateDek()
document := []byte("bytes")
tenantID := "thisTenant"
encrypted, _ := encryptDocumentBytes(document, tenantID, dek)
_, err := encryptDocumentBytes(encrypted, tenantID, dek)
assert.ErrorIs(t, err, ErrKindCrypto)
assert.ErrorContains(t, err, "already IronCore encrypted")
}

func TestDecryptBadDocument(t *testing.T) {
dek := generateDek()
document := []byte("bytes")
tenantID := "tenant"
encrypted, _ := encryptDocumentBytes(document, tenantID, dek)
badDek := []byte("bar")
_, err := decryptDocumentBytes(encrypted, badDek)
assert.ErrorIsf(t, err, ErrKindCrypto, "the signature computed did not match; the document key is likely incorrect")
assert.ErrorIs(t, err, ErrKindCrypto)
assert.ErrorContains(t, err, "the signature computed did not match; the document key is likely incorrect")
}

func TestGenerateHeaderTooLarge(t *testing.T) {
dek := generateDek()
tenantID := strings.Repeat("g", 100000)
_, err := generateHeader(dek, tenantID)
assert.ErrorIsf(t, err, ErrKindCrypto, "header size")
assert.ErrorIs(t, err, ErrKindCrypto)
assert.ErrorContains(t, err, "header size")
}

func TestVerifyWrongHeader(t *testing.T) {
Expand Down
18 changes: 18 additions & 0 deletions tenant_security_client_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -112,6 +112,24 @@ func TestEncryptDecryptRoundtrip(t *testing.T) {
assert.Equal(t, decryptResult.DecryptedFields, document)
}

func TestDoubleEncryptIntegration(t *testing.T) {
if integrationTestTSC == nil {
t.Skip("not doing integration tests")
}

ctx := context.Background()
document := PlaintextDocument{"foo": []byte("data")}
metadata := RequestMetadata{
TenantID: gcpTenantID,
IclFields: IclFields{RequestingID: "foo", RequestID: "blah", SourceIP: "f", DataLabel: "sda", ObjectID: "ew"},
CustomFields: map[string]string{"f": "foo"}}
encryptResult, err := integrationTestTSC.Encrypt(ctx, document, &metadata)
assert.Nil(t, err)
_, err2 := integrationTestTSC.Encrypt(ctx, encryptResult.EncryptedFields, &metadata)
assert.ErrorIs(t, err2, ErrKindCrypto)
assert.ErrorContains(t, err2, "already IronCore encrypted")
}

func TestEncryptWithExistingKey(t *testing.T) {
if integrationTestTSC == nil {
t.Skip("not doing integration tests")
Expand Down
2 changes: 1 addition & 1 deletion version.go
Original file line number Diff line number Diff line change
@@ -1,3 +1,3 @@
package tsc

const Version = "0.2.3-pre"
const Version = "0.3.0-pre"

0 comments on commit 2be0935

Please sign in to comment.