github.com/opentofu/opentofu@v1.7.1/internal/encryption/method/aesgcm/aesgcm.go (about) 1 // Copyright (c) The OpenTofu Authors 2 // SPDX-License-Identifier: MPL-2.0 3 // Copyright (c) 2023 HashiCorp, Inc. 4 // SPDX-License-Identifier: MPL-2.0 5 6 package aesgcm 7 8 import ( 9 "crypto/aes" 10 "crypto/cipher" 11 "crypto/rand" 12 "errors" 13 14 "github.com/opentofu/opentofu/internal/encryption/method" 15 ) 16 17 // aesgcm contains the encryption/decryption methods according to AES-GCM (NIST SP 800-38D). 18 type aesgcm struct { 19 encryptionKey []byte 20 decryptionKey []byte 21 aad []byte 22 } 23 24 // Encrypt encrypts the passed data with AES-GCM. If the data the encryption fails, it returns an error. 25 func (a aesgcm) Encrypt(data []byte) ([]byte, error) { 26 result, err := handlePanic( 27 func() ([]byte, error) { 28 gcm, err := a.getGCM(a.encryptionKey) 29 if err != nil { 30 return nil, &method.ErrEncryptionFailed{Cause: err} 31 } 32 33 nonce := make([]byte, gcm.NonceSize()) 34 if _, err := rand.Read(nonce); err != nil { 35 return nil, &method.ErrEncryptionFailed{Cause: &method.ErrCryptoFailure{ 36 Message: "could not generate nonce", 37 Cause: err, 38 }} 39 } 40 41 encrypted := gcm.Seal(nil, nonce, data, a.aad) 42 43 return append(nonce, encrypted...), nil 44 }, 45 ) 46 if err != nil { 47 var encryptionFailed *method.ErrEncryptionFailed 48 if errors.As(err, &encryptionFailed) { 49 return nil, err 50 } 51 return nil, &method.ErrEncryptionFailed{Cause: &method.ErrCryptoFailure{Message: "unexpected error", Cause: err}} 52 } 53 return result, nil 54 } 55 56 // Decrypt decrypts an AES-GCM-encrypted data set. If the data set fails decryption, it returns an error. 57 func (a aesgcm) Decrypt(data []byte) ([]byte, error) { 58 if len(a.decryptionKey) == 0 { 59 return nil, &method.ErrDecryptionKeyUnavailable{} 60 } 61 result, err := handlePanic( 62 func() ([]byte, error) { 63 if len(data) == 0 { 64 return nil, &method.ErrDecryptionFailed{ 65 Cause: method.ErrCryptoFailure{ 66 Message: "cannot decrypt empty data", 67 Cause: nil, 68 }, 69 } 70 } 71 72 gcm, err := a.getGCM(a.decryptionKey) 73 if err != nil { 74 return nil, &method.ErrDecryptionFailed{Cause: err} 75 } 76 77 if len(data) < gcm.NonceSize() { 78 return nil, &method.ErrDecryptionFailed{ 79 Cause: method.ErrCryptoFailure{ 80 Message: "cannot decrypt data because it is too small (likely data corruption)", 81 Cause: nil, 82 }, 83 } 84 } 85 86 nonce := data[:gcm.NonceSize()] 87 data = data[gcm.NonceSize():] 88 89 decrypted, err := gcm.Open(nil, nonce, data, a.aad) 90 if err != nil { 91 return nil, &method.ErrDecryptionFailed{Cause: err} 92 } 93 return decrypted, nil 94 }, 95 ) 96 if err != nil { 97 var decryptionFailed *method.ErrDecryptionFailed 98 if errors.As(err, &decryptionFailed) { 99 return nil, err 100 } 101 return nil, &method.ErrDecryptionFailed{ 102 Cause: &method.ErrCryptoFailure{Message: "unexpected error", Cause: err}, 103 } 104 } 105 return result, nil 106 } 107 108 func (a aesgcm) getGCM(key []byte) (cipher.AEAD, error) { 109 cipherBlock, err := aes.NewCipher(key) 110 if err != nil { 111 return nil, &method.ErrCryptoFailure{ 112 Message: "failed to create AES cypher block", 113 Cause: err, 114 } 115 } 116 117 gcm, err := cipher.NewGCM(cipherBlock) 118 if err != nil { 119 return nil, &method.ErrCryptoFailure{ 120 Message: "failed to create AES GCM", 121 Cause: err, 122 } 123 } 124 return gcm, nil 125 }