github.com/opentofu/opentofu@v1.7.1/internal/encryption/keyprovider/pbkdf2/compliance_test.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 pbkdf2
     7  
     8  import (
     9  	"crypto/rand"
    10  	"fmt"
    11  	"testing"
    12  
    13  	"github.com/opentofu/opentofu/internal/encryption/keyprovider"
    14  	"github.com/opentofu/opentofu/internal/encryption/keyprovider/compliancetest"
    15  )
    16  
    17  func TestCompliance(t *testing.T) {
    18  	validConfig := &Config{
    19  		randomSource: rand.Reader,
    20  		Passphrase:   "Hello world! 123",
    21  		KeyLength:    DefaultKeyLength,
    22  		Iterations:   DefaultIterations,
    23  		HashFunction: SHA256HashFunctionName,
    24  		SaltLength:   DefaultSaltLength,
    25  	}
    26  	compliancetest.ComplianceTest(
    27  		t,
    28  		compliancetest.TestConfiguration[*descriptor, *Config, *Metadata, *pbkdf2KeyProvider]{
    29  			Descriptor: New().(*descriptor),
    30  			HCLParseTestCases: map[string]compliancetest.HCLParseTestCase[*Config, *pbkdf2KeyProvider]{
    31  				"empty": {
    32  					HCL: `key_provider "pbkdf2" "foo" {
    33  }`,
    34  					ValidHCL:   false,
    35  					ValidBuild: false,
    36  					Validate:   nil,
    37  				},
    38  				"basic": {
    39  					HCL: `key_provider "pbkdf2" "foo" {
    40      passphrase = "Hello world! 123"
    41  }`,
    42  					ValidHCL:   true,
    43  					ValidBuild: true,
    44  					Validate: func(config *Config, keyProvider *pbkdf2KeyProvider) error {
    45  						if config.Passphrase != "Hello world! 123" {
    46  							return fmt.Errorf("invalid passphrase after HCL parsing")
    47  						}
    48  						if keyProvider.Passphrase != "Hello world! 123" {
    49  							return fmt.Errorf("invalid passphrase in key provideer")
    50  						}
    51  						return nil
    52  					},
    53  				},
    54  				"extended": {
    55  					HCL: fmt.Sprintf(`key_provider "pbkdf2" "foo" {
    56      passphrase = "Hello world! 123"
    57      key_length = %d
    58      iterations = %d
    59      salt_length = %d
    60      hash_function = "%s"
    61  }`, DefaultKeyLength+1, DefaultIterations+1, DefaultSaltLength+1, SHA256HashFunctionName),
    62  					ValidHCL:   true,
    63  					ValidBuild: true,
    64  					Validate: func(config *Config, keyProvider *pbkdf2KeyProvider) error {
    65  						if config.KeyLength != DefaultKeyLength+1 {
    66  							return fmt.Errorf("incorrect key length after HCL parsing: %d", config.KeyLength)
    67  						}
    68  						if config.Iterations != DefaultIterations+1 {
    69  							return fmt.Errorf("incorrect iterations after HCL parsing: %d", config.Iterations)
    70  						}
    71  						if config.SaltLength != DefaultSaltLength+1 {
    72  							return fmt.Errorf("incorrect salt length after HCL parsing: %d", config.SaltLength)
    73  						}
    74  						if config.HashFunction != SHA256HashFunctionName {
    75  							return fmt.Errorf("incorrect hash function after HCL parsing: %s", config.HashFunction)
    76  						}
    77  						return nil
    78  					},
    79  				},
    80  				"short-passphrase": {
    81  					HCL: `key_provider "pbkdf2" "foo" {
    82      passphrase = "Hello world! 12"
    83  }`,
    84  					ValidHCL:   true,
    85  					ValidBuild: false,
    86  				},
    87  				"too-small-iterations": {
    88  					HCL: fmt.Sprintf(`key_provider "pbkdf2" "foo" {
    89      passphrase = "Hello world! 123"
    90      iterations = %d
    91  }`, MinimumIterations-1),
    92  					ValidHCL:   true,
    93  					ValidBuild: false,
    94  				},
    95  				"invalid-hash-function": {
    96  					HCL: `key_provider "pbkdf2" "foo" {
    97      passphrase = "Hello world! 123"
    98      hash_function = "non_existent"
    99  }`,
   100  					ValidHCL:   true,
   101  					ValidBuild: false,
   102  				},
   103  			},
   104  			ConfigStructTestCases: map[string]compliancetest.ConfigStructTestCase[*Config, *pbkdf2KeyProvider]{},
   105  			MetadataStructTestCases: map[string]compliancetest.MetadataStructTestCase[*Config, *Metadata]{
   106  				"not-present-salt": {
   107  					ValidConfig: validConfig,
   108  					Meta: &Metadata{
   109  						Salt:         nil,
   110  						Iterations:   DefaultIterations,
   111  						HashFunction: SHA256HashFunctionName,
   112  						KeyLength:    32,
   113  					},
   114  					IsPresent: false,
   115  				},
   116  				"not-present-iterations": {
   117  					ValidConfig: validConfig,
   118  					Meta: &Metadata{
   119  						Salt:         []byte("Hello world!"),
   120  						Iterations:   0,
   121  						HashFunction: SHA256HashFunctionName,
   122  						KeyLength:    32,
   123  					},
   124  					IsPresent: false,
   125  				},
   126  				"not-present-hash-func": {
   127  					ValidConfig: validConfig,
   128  					Meta: &Metadata{
   129  						Salt:         []byte("Hello world!"),
   130  						Iterations:   DefaultIterations,
   131  						HashFunction: "",
   132  						KeyLength:    32,
   133  					},
   134  					IsPresent: false,
   135  				},
   136  				"not-present-key-length": {
   137  					ValidConfig: validConfig,
   138  					Meta: &Metadata{
   139  						Salt:         []byte("Hello world!"),
   140  						Iterations:   DefaultIterations,
   141  						HashFunction: SHA256HashFunctionName,
   142  						KeyLength:    0,
   143  					},
   144  					IsPresent: false,
   145  				},
   146  				"present-valid": {
   147  					ValidConfig: validConfig,
   148  					Meta: &Metadata{
   149  						Salt:         []byte("Hello world!"),
   150  						Iterations:   DefaultIterations,
   151  						HashFunction: SHA256HashFunctionName,
   152  						KeyLength:    32,
   153  					},
   154  					IsPresent: true,
   155  					IsValid:   true,
   156  				},
   157  				"present-valid-too-few-iterations": {
   158  					ValidConfig: validConfig,
   159  					Meta: &Metadata{
   160  						Salt:         []byte("Hello world!"),
   161  						Iterations:   MinimumIterations - 1,
   162  						HashFunction: SHA256HashFunctionName,
   163  						KeyLength:    32,
   164  					},
   165  					IsPresent: true,
   166  					IsValid:   true,
   167  				},
   168  				"invalid-iterations": {
   169  					ValidConfig: validConfig,
   170  					Meta: &Metadata{
   171  						Salt:         []byte("Hello world!"),
   172  						Iterations:   -1,
   173  						HashFunction: SHA256HashFunctionName,
   174  						KeyLength:    32,
   175  					},
   176  					IsPresent: true,
   177  					IsValid:   false,
   178  				},
   179  				"invalid-salt-length": {
   180  					ValidConfig: validConfig,
   181  					Meta: &Metadata{
   182  						Salt:         []byte("Hello world!"),
   183  						Iterations:   DefaultIterations,
   184  						HashFunction: SHA256HashFunctionName,
   185  						KeyLength:    -1,
   186  					},
   187  					IsPresent: true,
   188  					IsValid:   false,
   189  				},
   190  			},
   191  			ProvideTestCase: compliancetest.ProvideTestCase[*Config, *Metadata]{
   192  				ValidConfig: &Config{
   193  					randomSource: &testRandomSource{t: t},
   194  					Passphrase:   "Hello world! 123",
   195  					KeyLength:    DefaultKeyLength,
   196  					Iterations:   DefaultIterations,
   197  					HashFunction: DefaultHashFunctionName,
   198  					SaltLength:   DefaultSaltLength,
   199  				},
   200  				ExpectedOutput: &keyprovider.Output{
   201  					EncryptionKey: []byte{87, 192, 98, 53, 186, 42, 63, 139, 58, 118, 223, 169, 46, 84, 139, 29, 130, 59, 247, 106, 82, 61, 235, 144, 97, 131, 60, 229, 195, 109, 81, 111},
   202  					DecryptionKey: []byte{87, 192, 98, 53, 186, 42, 63, 139, 58, 118, 223, 169, 46, 84, 139, 29, 130, 59, 247, 106, 82, 61, 235, 144, 97, 131, 60, 229, 195, 109, 81, 111},
   203  				},
   204  				ValidateKeys: nil,
   205  				ValidateMetadata: func(meta *Metadata) error {
   206  					if !meta.isPresent() {
   207  						return fmt.Errorf("output metadata is not present")
   208  					}
   209  					if err := meta.validate(); err != nil {
   210  						return err
   211  					}
   212  					if meta.KeyLength != DefaultKeyLength {
   213  						return fmt.Errorf("incorrect output metadata key length: %d", meta.KeyLength)
   214  					}
   215  					if meta.Iterations != DefaultIterations {
   216  						return fmt.Errorf("incorrect output metadata iterations: %d", meta.Iterations)
   217  					}
   218  					if len(meta.Salt) != DefaultSaltLength {
   219  						return fmt.Errorf("incorrect output salt length: %d", len(meta.Salt))
   220  					}
   221  					if meta.HashFunction != DefaultHashFunctionName {
   222  						return fmt.Errorf("incorrect output hash function name: %s", meta.HashFunction)
   223  					}
   224  					return nil
   225  				},
   226  			},
   227  		},
   228  	)
   229  }