|
7 | 7 | "crypto/rand" |
8 | 8 | "crypto/x509" |
9 | 9 | "encoding/gob" |
| 10 | + "encoding/hex" |
10 | 11 | "encoding/json" |
11 | 12 | "encoding/pem" |
12 | 13 | "errors" |
@@ -934,6 +935,182 @@ func (s *NanoSuite) Test_NanoTDF_Obligations() { |
934 | 935 | } |
935 | 936 | } |
936 | 937 |
|
| 938 | +func (s *NanoSuite) Test_PolicyBinding_GMAC() { |
| 939 | + // Create test policy data |
| 940 | + policyData := []byte(`{"body":{"dataAttributes":["https://example.com/attr/classification/value/secret"]}}`) |
| 941 | + |
| 942 | + // Create GMAC binding - need to simulate having GMAC at end of the digest |
| 943 | + gmacBytes := []byte{0x01, 0x02, 0x03, 0x04, 0x05, 0x06, 0x07, 0x08} |
| 944 | + // Append GMAC to the digest to simulate real scenario |
| 945 | + policyData = append(policyData, gmacBytes...) |
| 946 | + |
| 947 | + digest := ocrypto.CalculateSHA256(policyData) |
| 948 | + |
| 949 | + // For testing, we will use the last bytes as the GMAC binding |
| 950 | + gmacBytes = digest[len(digest)-len(gmacBytes):] |
| 951 | + |
| 952 | + binding := &gmacPolicyBinding{ |
| 953 | + binding: gmacBytes, |
| 954 | + digest: digest, |
| 955 | + } |
| 956 | + |
| 957 | + // Test String function |
| 958 | + s.Require().Equal(hex.EncodeToString(gmacBytes), binding.String(), "GMAC hash should return binding data directly") |
| 959 | + |
| 960 | + // Test Verify function - should pass with correct binding |
| 961 | + valid, err := binding.Verify() |
| 962 | + s.Require().NoError(err) |
| 963 | + s.Require().True(valid, "GMAC binding should be valid when binding matches digest suffix") |
| 964 | + |
| 965 | + // Test Verify function with wrong binding - should fail |
| 966 | + wrongBinding := &gmacPolicyBinding{ |
| 967 | + binding: []byte{0x08, 0x07, 0x06, 0x05, 0x04, 0x03, 0x02, 0x01}, |
| 968 | + digest: digest, |
| 969 | + } |
| 970 | + valid, err = wrongBinding.Verify() |
| 971 | + s.Require().NoError(err) |
| 972 | + s.Require().False(valid, "GMAC binding should be invalid when binding doesn't match digest suffix") |
| 973 | +} |
| 974 | + |
| 975 | +func (s *NanoSuite) Test_PolicyBinding_ECDSA() { |
| 976 | + // Create a test ECDSA key pair |
| 977 | + keyPair, err := ocrypto.NewECKeyPair(ocrypto.ECCModeSecp256r1) |
| 978 | + s.Require().NoError(err) |
| 979 | + |
| 980 | + // Create test policy data |
| 981 | + policyData := []byte(`{"body":{"dataAttributes":["https://example.com/attr/classification/value/secret"]}}`) |
| 982 | + digest := ocrypto.CalculateSHA256(policyData) |
| 983 | + |
| 984 | + // Sign the digest |
| 985 | + r, sBytes, err := ocrypto.ComputeECDSASig(digest, keyPair.PrivateKey) |
| 986 | + s.Require().NoError(err) |
| 987 | + |
| 988 | + // Get the public key in compressed format |
| 989 | + compressedPubKey, err := ocrypto.CompressedECPublicKey(ocrypto.ECCModeSecp256r1, keyPair.PrivateKey.PublicKey) |
| 990 | + s.Require().NoError(err) |
| 991 | + |
| 992 | + binding := &ecdsaPolicyBinding{ |
| 993 | + r: r, |
| 994 | + s: sBytes, |
| 995 | + ephemeralPubKey: compressedPubKey, |
| 996 | + digest: digest, |
| 997 | + curve: keyPair.PrivateKey.Curve, |
| 998 | + } |
| 999 | + |
| 1000 | + // Test String function |
| 1001 | + expectedHash := string(ocrypto.SHA256AsHex(append(r, sBytes...))) |
| 1002 | + s.Require().NotEmpty(binding.String(), "Hash should not be empty") |
| 1003 | + s.Require().Equal(expectedHash, binding.String(), "ECDSA hash should be SHA256 of r||s") |
| 1004 | + |
| 1005 | + // Test Verify function - should pass with correct signature |
| 1006 | + valid, err := binding.Verify() |
| 1007 | + s.Require().NoError(err) |
| 1008 | + s.Require().True(valid, "ECDSA binding should be valid with correct signature") |
| 1009 | + |
| 1010 | + // Test Verify function with wrong signature - should fail |
| 1011 | + invalidR := make([]byte, 32) |
| 1012 | + invalidS := make([]byte, 32) |
| 1013 | + for i := range invalidR { |
| 1014 | + invalidR[i] = byte(i) |
| 1015 | + invalidS[i] = byte(i + 10) |
| 1016 | + } |
| 1017 | + |
| 1018 | + wrongBinding := &ecdsaPolicyBinding{ |
| 1019 | + r: invalidR, |
| 1020 | + s: invalidS, |
| 1021 | + ephemeralPubKey: compressedPubKey, |
| 1022 | + digest: digest, |
| 1023 | + curve: keyPair.PrivateKey.Curve, |
| 1024 | + } |
| 1025 | + |
| 1026 | + valid, err = wrongBinding.Verify() |
| 1027 | + s.Require().NoError(err) |
| 1028 | + s.Require().False(valid, "ECDSA binding should be invalid with wrong signature") |
| 1029 | +} |
| 1030 | + |
| 1031 | +func (s *NanoSuite) Test_NanoTDFHeader_VerifyPolicyBinding() { |
| 1032 | + s.Run("ECDSA Policy Binding Verification", func() { |
| 1033 | + // Create a test ECDSA key pair |
| 1034 | + keyPair, err := ocrypto.NewECKeyPair(ocrypto.ECCModeSecp256r1) |
| 1035 | + s.Require().NoError(err) |
| 1036 | + |
| 1037 | + // Create test policy data |
| 1038 | + policyData := []byte(`{"body":{"dataAttributes":["https://example.com/attr/classification/value/secret"]}}`) |
| 1039 | + digest := ocrypto.CalculateSHA256(policyData) |
| 1040 | + |
| 1041 | + // Sign the digest |
| 1042 | + r, sBytes, err := ocrypto.ComputeECDSASig(digest, keyPair.PrivateKey) |
| 1043 | + s.Require().NoError(err) |
| 1044 | + |
| 1045 | + // Get compressed public key |
| 1046 | + compressedPubKey, err := ocrypto.CompressedECPublicKey(ocrypto.ECCModeSecp256r1, keyPair.PrivateKey.PublicKey) |
| 1047 | + s.Require().NoError(err) |
| 1048 | + |
| 1049 | + // Create header with ECDSA binding |
| 1050 | + header := &NanoTDFHeader{ |
| 1051 | + bindCfg: bindingConfig{ |
| 1052 | + useEcdsaBinding: true, |
| 1053 | + eccMode: ocrypto.ECCModeSecp256r1, |
| 1054 | + }, |
| 1055 | + PolicyBody: policyData, |
| 1056 | + EphemeralKey: compressedPubKey, |
| 1057 | + ecdsaPolicyBindingR: r, |
| 1058 | + ecdsaPolicyBindingS: sBytes, |
| 1059 | + } |
| 1060 | + |
| 1061 | + // Test VerifyPolicyBinding method |
| 1062 | + valid, err := header.VerifyPolicyBinding() |
| 1063 | + s.Require().NoError(err) |
| 1064 | + s.Require().True(valid, "ECDSA policy binding should be valid") |
| 1065 | + }) |
| 1066 | + |
| 1067 | + s.Run("GMAC Policy Binding Verification", func() { |
| 1068 | + // Create test policy data |
| 1069 | + policyData := []byte(`{"body":{"dataAttributes":["https://example.com/attr/classification/value/secret"]}}`) |
| 1070 | + |
| 1071 | + // Create GMAC binding - need to simulate having GMAC at end of the digest |
| 1072 | + gmacBytes := []byte{0x01, 0x02, 0x03, 0x04, 0x05, 0x06, 0x07, 0x08} |
| 1073 | + // Append GMAC to the digest to simulate real scenario |
| 1074 | + policyData = append(policyData, gmacBytes...) |
| 1075 | + |
| 1076 | + digest := ocrypto.CalculateSHA256(policyData) |
| 1077 | + |
| 1078 | + // For testing, we will use the last bytes as the GMAC binding |
| 1079 | + gmacBytes = digest[len(digest)-len(gmacBytes):] |
| 1080 | + |
| 1081 | + // Create header with GMAC binding |
| 1082 | + header := &NanoTDFHeader{ |
| 1083 | + bindCfg: bindingConfig{ |
| 1084 | + useEcdsaBinding: false, |
| 1085 | + }, |
| 1086 | + PolicyBody: policyData, |
| 1087 | + gmacPolicyBinding: gmacBytes, |
| 1088 | + } |
| 1089 | + |
| 1090 | + // Test VerifyPolicyBinding method |
| 1091 | + valid, err := header.VerifyPolicyBinding() |
| 1092 | + s.Require().NoError(err) |
| 1093 | + s.Require().True(valid, "GMAC hash should match") |
| 1094 | + }) |
| 1095 | + |
| 1096 | + s.Run("Policy Binding Creation Error", func() { |
| 1097 | + // Create header with invalid ECC mode to trigger error in PolicyBinding() |
| 1098 | + header := &NanoTDFHeader{ |
| 1099 | + bindCfg: bindingConfig{ |
| 1100 | + useEcdsaBinding: true, |
| 1101 | + eccMode: 255, // Invalid ECC mode |
| 1102 | + }, |
| 1103 | + PolicyBody: []byte("test"), |
| 1104 | + } |
| 1105 | + |
| 1106 | + // Test VerifyPolicyBinding method with error case |
| 1107 | + valid, err := header.VerifyPolicyBinding() |
| 1108 | + s.Require().Error(err) |
| 1109 | + s.Require().False(valid) |
| 1110 | + s.Require().Contains(err.Error(), "unsupported nanoTDF ecc mode", "Error should be related to curve/ECC mode") |
| 1111 | + }) |
| 1112 | +} |
| 1113 | + |
937 | 1114 | // Helper function to create real NanoTDF data for testing |
938 | 1115 | func (s *NanoSuite) createRealNanoTDF(sdk *SDK) ([]byte, error) { |
939 | 1116 | // Read the test file content |
|
0 commit comments