├── .github ├── CODEOWNERS ├── actions │ ├── lint-commit │ │ └── action.yaml │ └── setup-build-env │ │ └── action.yaml ├── commitlint.config.js ├── dependabot.yml ├── release-note.toml └── workflows │ ├── pull-request.yaml │ ├── release.yaml │ └── test.yaml ├── .gitignore ├── .golangci.yaml ├── AUTHORS ├── CONTRIBUTING.md ├── CONTRIBUTORS ├── LICENSE ├── LICENSES ├── LICENSES.txt └── go │ └── github.com │ ├── golang │ └── go │ │ └── LICENSE │ └── google │ └── uuid │ └── LICENSE ├── MAINTAINERS ├── README.md ├── attributes.go ├── attributes_test.go ├── bitmasks.go ├── enums.go ├── examples ├── cleanup.go ├── encrypt_decrypt.go ├── main.go ├── sign_verify.go └── state_transitions.go ├── go.mod ├── go.sum ├── kmip.go ├── kmip_test.go ├── kmipclient ├── activate.go ├── add_attribute.go ├── archive_recover.go ├── client.go ├── client_test.go ├── conn.go ├── create.go ├── create_keypair.go ├── delete_attribute.go ├── destroy.go ├── encrypt_decrypt.go ├── get.go ├── get_attribute_list.go ├── get_attributes.go ├── get_usage.go ├── locate.go ├── middlewares.go ├── middlewares_test.go ├── modify_attribute.go ├── obtain_lease.go ├── query.go ├── register.go ├── register_test.go ├── rekey.go ├── revoke.go ├── sign_verify.go └── sign_verify_test.go ├── kmipserver ├── conn.go ├── context.go ├── errors.go ├── http.go ├── http_test.go ├── middlewares.go ├── router.go ├── server.go └── server_test.go ├── kmiptest ├── clientserver.go ├── clientserver_test.go ├── middlewares.go ├── oasis_tc.go ├── oasis_tc_test.go └── testdata │ ├── v1.0 │ ├── AKLC_M_1_10_AsymmetricKey_1.xml │ ├── AKLC_M_2_10_AsymmetricKey-2.xml │ ├── AKLC_M_3_10_AsymmetricKey-3.xml │ ├── AKLC_O_1_10_AsymmetricKey-4.xml │ ├── OMOS_M_1_10_small_opaque_object.xml │ ├── OMOS_O_1_10_big_opaque_object.xml │ ├── SASED_M_1_10_storage_array_config.xml │ ├── SASED_M_2_10_storage_array_register.xml │ ├── SASED_M_3_10_storage_array_retrieve.xml │ ├── SKFF_M_10_10_SymmetricKeyFoundryAdvanced_10.xml │ ├── SKFF_M_11_10_SymmetricKeyFoundryAdvanced_11.xml │ ├── SKFF_M_12_10_SymmetricKeyFoundryAdvanced_12.xml │ ├── SKFF_M_1_10_SymmetricKeyFoundryBasic_1.xml │ ├── SKFF_M_2_10_SymmetricKeyFoundryBasic_2.xml │ ├── SKFF_M_3_10_SymmetricKeyFoundryBasic_3.xml │ ├── SKFF_M_4_10_SymmetricKeyFoundryBasic_4.xml │ ├── SKFF_M_5_10_SymmetricKeyFoundryIntermediate_5.xml │ ├── SKFF_M_6_10_SymmetricKeyFoundryIntermediate_6.xml │ ├── SKFF_M_7_10_SymmetricKeyFoundryIntermediate_7.xml │ ├── SKFF_M_8_10_SymmetricKeyFoundryIntermediate_8.xml │ ├── SKFF_M_9_10_SymmetricKeyFoundryAdvanced_9.xml │ ├── SKFF_O_1_10_SymmetricKeyOptional_1.xml │ ├── SKFF_O_2_10_SymmetricKeyOptional_2.xml │ ├── SKFF_O_3_10_SymmetricKeyOptional_3.xml │ ├── SKFF_O_4_10_SymmetricKeyOptional_4.xml │ ├── SKFF_O_5_10_SymmetricKeyOptional_5.xml │ ├── SKFF_O_6_10_SymmetricKeyOptional_6.xml │ ├── SKLC_M_1_10_SymmetricKeyLifecycle_1.xml │ ├── SKLC_M_2_10_SymmetricKeyLifecycle_2.xml │ ├── SKLC_M_3_10_SymmetricKeyLifecycle_3.xml │ ├── SKLC_O_1_10-SymmetricKeysLifecycle_1.xml │ ├── TC_101_10_Create_a_Key_Archive_and_Recover_it.xml │ ├── TC_111_10_Credential_Operation_Policy_Destroy_Date.xml │ ├── TC_121_10_Query_Maximum_Response_Size.xml │ ├── TC_131_10_Register_an_Asymmetric_Key_Pair_in_PKCS1_Format.xml │ ├── TC_132_10_Register_an_Asymmetric_Key_Pair_and_a_Corresponding_X509_Certificate.xml │ ├── TC_134_10_Register_Key_Pair_Certify_and_Re_certify_Public_Key.xml │ ├── TC_311_10_Create_Destroy.xml │ ├── TC_312_10_Register_Create_Get_attributes_Destroy.xml │ ├── TC_313_10_Create_Locate_Get_Destroy.xml │ ├── TC_314_10_Dual_Client_Test_Case_ID_Placeholder_linked_Locate_Get_Batch.xml │ ├── TC_315_10_Register_Destroy_Secret_Data.xml │ ├── TC_32_10_Asynchronous_Locate.xml │ ├── TC_41_10_Revoke_Scenario.xml │ ├── TC_51_10_Get_Usage_Allocation_Scenario.xml │ ├── TC_61_10_Import_of_a_Third_party_Key.xml │ ├── TC_71_10_Unrecognized_Message_Extension_with_Criticality_Indicator_False.xml │ ├── TC_72_10_Unrecognized_Message_Extension_with_Criticality_Indicator_True.xml │ ├── TC_81_10_Create_a_Key_Pair.xml │ ├── TC_82_10_Register_Both_Halves_of_a_Key_Pair.xml │ ├── TC_91_10_Create_a_Key_Re_key.xml │ ├── TC_92_10_Existing_Key_Expired_Re_key_with_Same_Life_cycle.xml │ ├── TC_93_10_Existing_Key_Compromised_Re_key_with_Same_Life_cycle.xml │ ├── TC_94_10_Create_Key_Re_key_with_New_Life_cycle.xml │ ├── TC_95_10_Obtain_Lease_for_Expired_Key.xml │ ├── TC_ECC_1_10_Register_an_ECC_Key_Pair.xml │ ├── TC_ECC_2_10_Register_an_ECC_Key_Pair_in_PKCS8_Format.xml │ ├── TC_ECC_2_10_Register_an_ECC_Key_Pair_in_PKCS8_Format_Unencrypted.xml │ ├── TC_ECC_3_10_Register_an_ECC_Key_Pair_and_ECDSA_Certificate.xml │ ├── TC_NP_1_10_Put.xml │ ├── TC_NP_2_10_Notify_Put.xml │ ├── TL_M_1_10_tape_library_config.xml │ ├── TL_M_2_10_tape_write_new_key.xml │ └── TL_M_3_10_tape_read.xml │ ├── v1.1 │ ├── AKLC_M_1_11_AsymmetricKey_1.xml │ ├── AKLC_M_2_11_AsymmetricKey_2.xml │ ├── AKLC_M_3_11_AsymmetricKey_3.xml │ ├── AKLC_O_1_11_AsymmetricKey_1.xml │ ├── SKFF_M_10_11_SymmetricKeyFoundryAdvanced_10.xml │ ├── SKFF_M_11_11_SymmetricKeyFoundryAdvanced_11.xml │ ├── SKFF_M_12_11_SymmetricKeyFoundryAdvanced_12.xml │ ├── SKFF_M_1_11_SymmetricKeyFoundryBasic_1.xml │ ├── SKFF_M_2_11_SymmetricKeyFoundryBasic_2.xml │ ├── SKFF_M_3_11_SymmetricKeyFoundryBasic_3.xml │ ├── SKFF_M_4_11_SymmetricKeyFoundryBasic_4.xml │ ├── SKFF_M_5_11_SymmetricKeyFoundryIntermediate_5.xml │ ├── SKFF_M_6_11_SymmetricKeyFoundryIntermediate_6.xml │ ├── SKFF_M_7_11_SymmetricKeyFoundryIntermediate_7.xml │ ├── SKFF_M_8_11_SymmetricKeyFoundryIntermediate_8.xml │ ├── SKFF_M_9_11_SymmetricKeyFoundryAdvanced_9.xml │ ├── SKFF_O_1_11_SymmetricKeyOptional_1.xml │ ├── SKFF_O_2_11_SymmetricKeyOptional_2.xml │ ├── SKFF_O_3_11_SymmetricKeyOptional_3.xml │ ├── SKFF_O_4_11_SymmetricKeyOptional_4.xml │ ├── SKFF_O_5_11_SymmetricKeyOptional_5.xml │ ├── SKFF_O_6_11_SymmetricKeyOptional_6.xml │ ├── SKLC_M_1_11_SymmetricKeyLifecycle_1.xml │ ├── SKLC_M_2_11_SymmetricKeyLifecycle_2.xml │ ├── SKLC_M_3_11_SymmetricKeyLifecycle_3.xml │ ├── SKLC_O_1_11_SymmetricKeyLifecycle_1.xml │ ├── TC_101_11_Create_a_Key_Archive_and_Recover_it.xml │ ├── TC_111_11_Credential_Operation_Policy_Destroy_Date.xml │ ├── TC_112_11_Device_Credential_Operation_Policy_Destroy_Date.xml │ ├── TC_121_11_Query_Maximum_Response_Size.xml │ ├── TC_122_11_Query_Vendor_Extensions.xml │ ├── TC_131_11_Register_an_Asymmetric_Key_Pair_in_PKCS1_Format.xml │ ├── TC_132_11_Register_an_Asymmetric_Key_Pair_and_a_Corresponding_X509_Certificate.xml │ ├── TC_133_11_Create_Re_key_Key_Pair.xml │ ├── TC_134_11_Register_Key_Pair_Certify_and_Re_certify_Public_Key.xml │ ├── TC_141_11_Key_Wrapping_using_AES_Key_Wrap_and_No_Encoding.xml │ ├── TC_142_11_Key_Wrapping_using_AES_Key_Wrap_with_Attributes.xml │ ├── TC_151_11_Locate_a_Fresh_Object_from_the_Default_Group.xml │ ├── TC_152_11_Client_side_Group_Management.xml │ ├── TC_153_11_Default_Object_Group_Member.xml │ ├── TC_161_11_Discover_Versions.xml │ ├── TC_171_11_Handling_of_Attributes_and_Attribute_Index_Values.xml │ ├── TC_181_11_Digests_of_Symmetric_Keys.xml │ ├── TC_182_11_Digests_of_RSA_Private_Keys.xml │ ├── TC_311_11_Create_Destroy.xml │ ├── TC_312_11_Register_Create_Get_attributes_Destroy.xml │ ├── TC_313_11_Create_Locate_Get_Destroy.xml │ ├── TC_314_11_Dual_Client_Test_Case_ID_Placeholder_linked_Locate_Get_Batch.xml │ ├── TC_315_11_Register_Destroy_Secret_Data.xml │ ├── TC_32_11_Asynchronous_Locate.xml │ ├── TC_41_11_Revoke_Scenario.xml │ ├── TC_51_11_Get_Usage_Allocation_Scenario.xml │ ├── TC_61_11_Import_of_a_Third_party_Key.xml │ ├── TC_71_11_Unrecognized_Message_Extension_with_Criticality_Indicator_False.xml │ ├── TC_72_11_Unrecognized_Message_Extension_with_Criticality_Indicator_True.xml │ ├── TC_81_11_Create_a_Key_Pair.xml │ ├── TC_82_11_Register_Both_Halves_of_a_Key_Pair.xml │ ├── TC_91_11_Create_a_Key_Re_key.xml │ ├── TC_92_11_Existing_Key_Expired_Re_key_with_Same_Life_cycle.xml │ ├── TC_93_11_Existing_Key_Compromised_Re_key_with_Same_Life_cycle.xml │ ├── TC_94_11_Create_Key_Re_key_with_New_Life_cycle.xml │ ├── TC_95_11_Obtain_Lease_for_Expired_Key.xml │ ├── TC_ECC_1_11_Register_an_ECC_Key_Pair.xml │ ├── TC_ECC_2_11_Register_an_ECC_Key_Pair_in_PKCS8_Format.xml │ ├── TC_ECC_3_11_Register_an_ECC_Key_Pair_and_ECDSA_Certificate.xml │ ├── TC_NP_1_11_Put.xml │ └── TC_NP_2_11_Notify_Put.xml │ ├── v1.2 │ ├── CS-BC-M-1-12_Encrypt_with_New_Symmetric_Key.xml │ ├── CS-BC-M-10-12_Encrypt_and_Decrypt_with_Known_Symmetric_Key_and_PKCS5_Padding_and_CBC.xml │ ├── CS-BC-M-11-12_Encrypt_and_Decrypt_with_Known_Symmetric_Key_and_PKCS5_Padding_and_CBC_and_IV.xml │ ├── CS-BC-M-12-12_Encrypt_and_Decrypt_with_Known_Symmetric_Key_and_PKCS5_Padding_and_CBC_and_IV.xml │ ├── CS-BC-M-13-12_Encrypt_and_Decrypt_with_Known_Symmetric_Key_and_PKCS5_Padding_and_CBC_and_Random_IV.xml │ ├── CS-BC-M-2-12_Decrypt_with_New_Symmetric_Key.xml │ ├── CS-BC-M-3-12_Encrypt_and_Decrypt_with_New_Symmetric_Key.xml │ ├── CS-BC-M-4-12_Encrypt_with_Known_Symmetric_Key.xml │ ├── CS-BC-M-5-12_Decrypt_with_Known_Symmetric_Key.xml │ ├── CS-BC-M-6-12_Encrypt_and_Decrypt_with_Known_Symmetric_Key.xml │ ├── CS-BC-M-7-12_Encrypt_with_Known_Symmetric_Key_with_Usage_Limits.xml │ ├── CS-BC-M-8-12_Encrypt_and_Decrypt_with_Known_Symmetric_Key_and_PKCS5_Padding.xml │ ├── CS-BC-M-9-12_Encrypt_and_Decrypt_with_Known_Symmetric_Key_and_PKCS5_Padding.xml │ ├── CS_BC_M_14_12_Encrypt_and_Decrypt_with_Known_Symmetric_Key_Date_Checks.xml │ ├── CS_BC_M_7_12_Encrypt_with_All_Fields.xml │ ├── TC-311-10-CREATE-DESTROY.xml │ ├── TC-SK-CREATE-ENCRYPT-DECRYPT-DESTROY.xml │ ├── TC_101_12_Create_a_Key_Archive_and_Recover_it.xml │ ├── TC_111_12_Credential_Operation_Policy_Destroy_Date.xml │ ├── TC_112_12_Device_Credential_Operation_Policy_Destroy_Date.xml │ ├── TC_121_12_Query_Maximum_Response_Size.xml │ ├── TC_122_12_Query_Vendor_Extensions.xml │ ├── TC_131_12_Register_an_Asymmetric_Key_Pair_in_PKCS1_Format.xml │ ├── TC_132_12_Register_an_Asymmetric_Key_Pair_and_a_Corresponding_X509_Certificate.xml │ ├── TC_133_12_Create_Re_key_Key_Pair.xml │ ├── TC_134_12_Register_Key_Pair_Certify_and_Re_certify_Public_Key.xml │ ├── TC_141_12_Key_Wrapping_using_AES_Key_Wrap_and_No_Encoding.xml │ ├── TC_142_12_Key_Wrapping_using_AES_Key_Wrap_with_Attributes.xml │ ├── TC_151_12_Locate_a_Fresh_Object_from_the_Default_Group.xml │ ├── TC_152_12_Client_side_Group_Management.xml │ ├── TC_153_12_Default_Object_Group_Member.xml │ ├── TC_161_12_Discover_Versions.xml │ ├── TC_171_12_Handling_of_Attributes_and_Attribute_Index_Values.xml │ ├── TC_181_12_Digests_of_Symmetric_Keys.xml │ ├── TC_182_12_Digests_of_RSA_Private_Keys.xml │ ├── TC_311_12_Create_Destroy.xml │ ├── TC_312_12_Register_Create_Get_attributes_Destroy.xml │ ├── TC_313_12_Create_Locate_Get_Destroy.xml │ ├── TC_314_12_Dual_Client_Test_Case_ID_Placeholder_linked_Locate_Get_Batch.xml │ ├── TC_315_12_Register_Destroy_Secret_Data.xml │ ├── TC_32_12_Asynchronous_Locate.xml │ ├── TC_41_12_Revoke_Scenario.xml │ ├── TC_51_12_Get_Usage_Allocation_Scenario.xml │ ├── TC_61_12_Import_of_a_Third_party_Key.xml │ ├── TC_71_12_Unrecognized_Message_Extension_with_Criticality_Indicator_False.xml │ ├── TC_72_12_Unrecognized_Message_Extension_with_Criticality_Indicator_True.xml │ ├── TC_81_12_Create_a_Key_Pair.xml │ ├── TC_82_12_Register_Both_Halves_of_a_Key_Pair.xml │ ├── TC_91_12_Create_a_Key_Re_key.xml │ ├── TC_92_12_Existing_Key_Expired_Re_key_with_Same_Life_cycle.xml │ ├── TC_93_12_Existing_Key_Compromised_Re_key_with_Same_Life_cycle.xml │ ├── TC_94_12_Create_Key_Re_key_with_New_Life_cycle.xml │ ├── TC_95_12_Obtain_Lease_for_Expired_Key.xml │ ├── TC_ECC_1_12_Register_an_ECC_Key_Pair.xml │ ├── TC_ECC_2_12_Register_an_ECC_Key_Pair_in_PKCS8_Format.xml │ ├── TC_ECC_3_12_Register_an_ECC_Key_Pair_and_ECDSA_Certificate.xml │ ├── TC_MDO_1_12_Register_MDO_Key.xml │ ├── TC_MDO_2_12_Locate_MDO_keys_by_Key_Value_Present.xml │ ├── TC_MDO_3_12_Register_MDO_Key_using_PKCS11_URI.xml │ ├── TC_NP_1_12_Put.xml │ ├── TC_NP_2_12_Notify_Put.xml │ ├── TC_PGP_1_12_Register_PGP_Key_RSA.xml │ ├── TC_SJ_1_12_Create_and_SplitJoin.xml │ ├── TC_SJ_2_12_Register_and_Split_Join.xml │ ├── TC_SJ_3_12_Join_Split_Keys.xml │ └── TC_SJ_4_12_Register_and_Split_Join_with_XOR.xml │ ├── v1.3 │ ├── AKLC-M-1-13.xml │ ├── AKLC-M-2-13.xml │ ├── AKLC-M-3-13.xml │ ├── AKLC-O-1-13.xml │ ├── CS-AC-M-1-13.xml │ ├── CS-AC-M-2-13.xml │ ├── CS-AC-M-3-13.xml │ ├── CS-AC-M-3-ECC-13.xml │ ├── CS-AC-M-4-13.xml │ ├── CS-AC-M-5-13.xml │ ├── CS-AC-M-6-13.xml │ ├── CS-AC-M-7-13.xml │ ├── CS-AC-M-8-13.xml │ ├── CS-BC-M-1-13.xml │ ├── CS-BC-M-1-13_Encrypt_with_New_Symmetric_Key.xml │ ├── CS-BC-M-10-13.xml │ ├── CS-BC-M-10-13_Encrypt_and_Decrypt_with_Known_Symmetric_Key_and_PKCS5_Padding_and_CBC.xml │ ├── CS-BC-M-11-13.xml │ ├── CS-BC-M-11-13_Encrypt_and_Decrypt_with_Known_Symmetric_Key_and_PKCS5_Padding_and_CBC_and_IV.xml │ ├── CS-BC-M-12-13.xml │ ├── CS-BC-M-12-13_Encrypt_and_Decrypt_with_Known_Symmetric_Key_and_PKCS5_Padding_and_CBC_and_IV.xml │ ├── CS-BC-M-13-13.xml │ ├── CS-BC-M-13-13_Encrypt_and_Decrypt_with_Known_Symmetric_Key_and_PKCS5_Padding_and_CBC_and_Random_IV.xml │ ├── CS-BC-M-14-13.xml │ ├── CS-BC-M-14-13_Encrypt_and_Decrypt_with_Known_Symmetric_Key_Date_Checks.xml │ ├── CS-BC-M-2-13.xml │ ├── CS-BC-M-2-13_Decrypt_with_New_Symmetric_Key.xml │ ├── CS-BC-M-3-13.xml │ ├── CS-BC-M-3-13_Encrypt_and_Decrypt_with_New_Symmetric_Key.xml │ ├── CS-BC-M-4-12-3DES-13.xml │ ├── CS-BC-M-4-13.xml │ ├── CS-BC-M-4-13_Encrypt_with_Known_Symmetric_Key.xml │ ├── CS-BC-M-5-13.xml │ ├── CS-BC-M-5-13_Decrypt_with_Known_Symmetric_Key.xml │ ├── CS-BC-M-6-13.xml │ ├── CS-BC-M-6-13_Encrypt_and_Decrypt_with_Known_Symmetric_Key.xml │ ├── CS-BC-M-7-13.xml │ ├── CS-BC-M-7-13_Encrypt_with_Known_Symmetric_Key_with_Usage_Limits.xml │ ├── CS-BC-M-8-13.xml │ ├── CS-BC-M-8-13_Encrypt_and_Decrypt_with_Known_Symmetric_Key_and_PKCS5_Padding.xml │ ├── CS-BC-M-9-13.xml │ ├── CS-BC-M-9-13_Encrypt_and_Decrypt_with_Known_Symmetric_Key_and_PKCS5_Padding.xml │ ├── CS-RNG-M-1-13.xml │ ├── CS-RNG-O-1-13.xml │ ├── CS-RNG-O-2-13.xml │ ├── CS-RNG-O-3-13.xml │ ├── CS-RNG-O-4-13.xml │ ├── MSGENC-HTTPS-M-1-13.xml │ ├── MSGENC-JSON-M-1-13.xml │ ├── MSGENC-XML-M-1-13.xml │ ├── OMOS-M-1-13.xml │ ├── OMOS-O-1-13.xml │ ├── SASED-M-1-13.xml │ ├── SASED-M-2-13.xml │ ├── SASED-M-3-13.xml │ ├── SKFF-M-1-13.xml │ ├── SKFF-M-10-13.xml │ ├── SKFF-M-11-13.xml │ ├── SKFF-M-12-13.xml │ ├── SKFF-M-2-13.xml │ ├── SKFF-M-3-13.xml │ ├── SKFF-M-4-13.xml │ ├── SKFF-M-5-13.xml │ ├── SKFF-M-6-13.xml │ ├── SKFF-M-7-13.xml │ ├── SKFF-M-8-13.xml │ ├── SKFF-M-9-13.xml │ ├── SKLC-M-1-13.xml │ ├── SKLC-M-2-13.xml │ ├── SKLC-M-3-13.xml │ ├── SKLC-O-1-13.xml │ ├── SUITEB_128-M-1-13.xml │ ├── SUITEB_192-M-1-13.xml │ ├── TL-M-1-13.xml │ ├── TL-M-2-13.xml │ └── TL-M-3-13.xml │ └── v1.4 │ ├── AKLC-M-1-14.xml │ ├── AKLC-M-2-14.xml │ ├── AKLC-M-3-14.xml │ ├── AKLC-O-1-14.xml │ ├── AX-M-1-14.xml │ ├── AX-M-2-14.xml │ ├── CS-AC-M-1-14.xml │ ├── CS-AC-M-2-14.xml │ ├── CS-AC-M-3-14.xml │ ├── CS-AC-M-4-14.xml │ ├── CS-AC-M-5-14.xml │ ├── CS-AC-M-6-14.xml │ ├── CS-AC-M-7-14.xml │ ├── CS-AC-M-8-14.xml │ ├── CS-AC-M-OAEP-1-14.xml │ ├── CS-AC-M-OAEP-1-14_okms.xml │ ├── CS-AC-M-OAEP-10-14.xml │ ├── CS-AC-M-OAEP-2-14.xml │ ├── CS-AC-M-OAEP-2-14_okms.xml │ ├── CS-AC-M-OAEP-3-14.xml │ ├── CS-AC-M-OAEP-3-14_okms.xml │ ├── CS-AC-M-OAEP-4-14.xml │ ├── CS-AC-M-OAEP-4-14_okms.xml │ ├── CS-AC-M-OAEP-5-14.xml │ ├── CS-AC-M-OAEP-5-14_okms.xml │ ├── CS-AC-M-OAEP-6-14.xml │ ├── CS-AC-M-OAEP-6-14_okms.xml │ ├── CS-AC-M-OAEP-7-14.xml │ ├── CS-AC-M-OAEP-8-14.xml │ ├── CS-AC-M-OAEP-9-14.xml │ ├── CS-BC-M-1-14.xml │ ├── CS-BC-M-1-14_Encrypt_with_New_Symmetric_Key.xml │ ├── CS-BC-M-10-14.xml │ ├── CS-BC-M-10-14_Encrypt_and_Decrypt_with_Known_Symmetric_Key_and_PKCS5_Padding_and_CBC.xml │ ├── CS-BC-M-11-14.xml │ ├── CS-BC-M-11-14_Encrypt_and_Decrypt_with_Known_Symmetric_Key_and_PKCS5_Padding_and_CBC_and_IV.xml │ ├── CS-BC-M-12-14.xml │ ├── CS-BC-M-12-14_Encrypt_and_Decrypt_with_Known_Symmetric_Key_and_PKCS5_Padding_and_CBC_and_IV.xml │ ├── CS-BC-M-13-14.xml │ ├── CS-BC-M-13-14_Encrypt_and_Decrypt_with_Known_Symmetric_Key_and_PKCS5_Padding_and_CBC_and_Random_IV.xml │ ├── CS-BC-M-14-14.xml │ ├── CS-BC-M-14-14_Encrypt_and_Decrypt_with_Known_Symmetric_Key_Date_Checks.xml │ ├── CS-BC-M-2-14.xml │ ├── CS-BC-M-2-14_Decrypt_with_New_Symmetric_Key.xml │ ├── CS-BC-M-3-14.xml │ ├── CS-BC-M-3-14_Encrypt_and_Decrypt_with_New_Symmetric_Key.xml │ ├── CS-BC-M-4-14.xml │ ├── CS-BC-M-4-14_Encrypt_with_Known_Symmetric_Key.xml │ ├── CS-BC-M-5-14.xml │ ├── CS-BC-M-5-14_Decrypt_with_Known_Symmetric_Key.xml │ ├── CS-BC-M-6-14.xml │ ├── CS-BC-M-6-14_Encrypt_and_Decrypt_with_Known_Symmetric_Key.xml │ ├── CS-BC-M-7-14.xml │ ├── CS-BC-M-7-14_Encrypt_with_Known_Symmetric_Key_with_Usage_Limits.xml │ ├── CS-BC-M-8-14.xml │ ├── CS-BC-M-8-14_Encrypt_and_Decrypt_with_Known_Symmetric_Key_and_PKCS5_Padding.xml │ ├── CS-BC-M-9-14.xml │ ├── CS-BC-M-9-14_Encrypt_and_Decrypt_with_Known_Symmetric_Key_and_PKCS5_Padding.xml │ ├── CS-BC-M-GCM-1-14.xml │ ├── CS-BC-M-GCM-1-14_okms.xml │ ├── CS-BC-M-GCM-2-14.xml │ ├── CS-BC-M-GCM-2-14_okms.xml │ ├── CS-BC-M-GCM-2-14_okms_test0.xml │ ├── CS-BC-M-GCM-2-14_okms_test1.xml │ ├── CS-BC-M-GCM-2-14_okms_test10.xml │ ├── CS-BC-M-GCM-2-14_okms_test11.xml │ ├── CS-BC-M-GCM-2-14_okms_test12.xml │ ├── CS-BC-M-GCM-2-14_okms_test13.xml │ ├── CS-BC-M-GCM-2-14_okms_test14.xml │ ├── CS-BC-M-GCM-2-14_okms_test15.xml │ ├── CS-BC-M-GCM-2-14_okms_test16.xml │ ├── CS-BC-M-GCM-2-14_okms_test17.xml │ ├── CS-BC-M-GCM-2-14_okms_test18.xml │ ├── CS-BC-M-GCM-2-14_okms_test19.xml │ ├── CS-BC-M-GCM-2-14_okms_test2.xml │ ├── CS-BC-M-GCM-2-14_okms_test20.xml │ ├── CS-BC-M-GCM-2-14_okms_test21.xml │ ├── CS-BC-M-GCM-2-14_okms_test22.xml │ ├── CS-BC-M-GCM-2-14_okms_test3.xml │ ├── CS-BC-M-GCM-2-14_okms_test4.xml │ ├── CS-BC-M-GCM-2-14_okms_test5.xml │ ├── CS-BC-M-GCM-2-14_okms_test6.xml │ ├── CS-BC-M-GCM-2-14_okms_test7.xml │ ├── CS-BC-M-GCM-2-14_okms_test8.xml │ ├── CS-BC-M-GCM-2-14_okms_test9.xml │ ├── CS-BC-M-GCM-3-14.xml │ ├── CS-BC-M-GCM-3-14_okms.xml │ ├── CS-RNG-M-1-14.xml │ ├── CS-RNG-O-1-14.xml │ ├── CS-RNG-O-2-14.xml │ ├── CS-RNG-O-3-14.xml │ ├── CS-RNG-O-4-14.xml │ ├── MSGENC-HTTPS-M-1-14.xml │ ├── MSGENC-JSON-M-1-14.xml │ ├── MSGENC-XML-M-1-14.xml │ ├── OMOS-M-1-14.xml │ ├── OMOS-O-1-14.xml │ ├── SASED-M-1-14.xml │ ├── SASED-M-2-14.xml │ ├── SASED-M-3-14.xml │ ├── SKFF-M-1-14.xml │ ├── SKFF-M-10-14.xml │ ├── SKFF-M-11-14.xml │ ├── SKFF-M-12-14.xml │ ├── SKFF-M-2-14.xml │ ├── SKFF-M-3-14.xml │ ├── SKFF-M-4-14.xml │ ├── SKFF-M-5-14.xml │ ├── SKFF-M-6-14.xml │ ├── SKFF-M-7-14.xml │ ├── SKFF-M-8-14.xml │ ├── SKFF-M-9-14.xml │ ├── SKLC-M-1-14.xml │ ├── SKLC-M-2-14.xml │ ├── SKLC-M-3-14.xml │ ├── SKLC-O-1-14.xml │ ├── SUITEB_128-M-1-14.xml │ ├── SUITEB_192-M-1-14.xml │ ├── TC-CS-CORVAL-1-14.xml │ ├── TC-DERIVEKEY-1-10.xml │ ├── TC-ECDSA-SIGN-1-14.xml │ ├── TC-I18N-1-10.xml │ ├── TC-MDO-1-14.xml │ ├── TC-NP-1-14.xml │ ├── TC-PKCS12-1-14.xml │ ├── TC-Q-CAP-1-14.xml │ ├── TC-Q-PROF-1-14.xml │ ├── TC-Q-RNGS-1-14.xml │ ├── TC-Q-S2C-1-14.xml │ ├── TC-REKEY-1-10.xml │ ├── TC-RNG-ATTR-1-14.xml │ ├── TC-SJ-1-14.xml │ ├── TC-STREAM-ENC-1-14.xml │ ├── TC-WRAP-1-14.xml │ ├── TL-M-1-14.xml │ ├── TL-M-2-14.xml │ └── TL-M-3-14.xml ├── objects.go ├── objects_test.go ├── operations.go ├── payloads ├── activate.go ├── add_attribute.go ├── archive_recover.go ├── create.go ├── create_keypair.go ├── delete_attribute.go ├── destroy.go ├── discover.go ├── encrypt_decrypt.go ├── get.go ├── get_attribute_list.go ├── get_attributes.go ├── get_usage.go ├── locate.go ├── modify_attribute.go ├── obtain_lease.go ├── payloads_test.go ├── query.go ├── register.go ├── rekey.go ├── revoke.go └── sign_verify.go ├── requests.go ├── responses.go ├── tags.go └── ttlv ├── decoder.go ├── decoder_test.go ├── encoder.go ├── encoder_test.go ├── encoding_json.go ├── encoding_json_test.go ├── encoding_text.go ├── encoding_text_test.go ├── encoding_ttlv.go ├── encoding_ttlv_test.go ├── encoding_xml.go ├── encoding_xml_test.go ├── io.go ├── reflect.go ├── registry.go ├── ttlv.go ├── types.go ├── utils.go ├── value.go ├── value_test.go ├── version.go └── version_test.go /.github/CODEOWNERS: -------------------------------------------------------------------------------- 1 | # see doc on: https://help.github.com/articles/about-codeowners/ 2 | * @ovh/okms-codeowners-team -------------------------------------------------------------------------------- /.github/actions/lint-commit/action.yaml: -------------------------------------------------------------------------------- 1 | name: Lint Conventional Commits 2 | description: Verify that all the commits complies to the conventional commit convention 3 | 4 | inputs: 5 | config: 6 | description: Path to the configuration file 7 | default: .github/commitlint.config.js 8 | 9 | runs: 10 | using: composite 11 | steps: 12 | - name: Install commitlint 13 | shell: bash 14 | run: | 15 | npm install conventional-changelog-conventionalcommits 16 | npm install commitlint@latest 17 | npm install @commitlint/{cli,config-conventional} 18 | 19 | - name: Validate current commit (last commit) with commitlint 20 | if: github.event_name == 'push' 21 | shell: bash 22 | run: npx commitlint --config ${{ inputs.config }} --last --verbose 23 | 24 | - name: Validate PR commits with commitlint 25 | if: github.event_name == 'pull_request' 26 | shell: bash 27 | run: npx commitlint --config ${{ inputs.config }} --from ${{ github.event.pull_request.head.sha }}~${{ github.event.pull_request.commits }} --to ${{ github.event.pull_request.head.sha }} --verbose 28 | -------------------------------------------------------------------------------- /.github/actions/setup-build-env/action.yaml: -------------------------------------------------------------------------------- 1 | name: Setup Build Env 2 | description: Setup build environment with go 3 | 4 | runs: 5 | using: composite 6 | steps: 7 | - name: Set up Go 8 | uses: actions/setup-go@v4 9 | with: 10 | go-version: "1.23" 11 | -------------------------------------------------------------------------------- /.github/commitlint.config.js: -------------------------------------------------------------------------------- 1 | const Configuration = { 2 | // See https://github.com/conventional-changelog/commitlint/blob/master/%40commitlint/config-conventional/src/index.ts 3 | extends: ['@commitlint/config-conventional'], 4 | rules: { 5 | 'subject-case': [ 6 | 0, 7 | 'never', 8 | // Allow Sentence-case. See https://commitlint.js.org/reference/rules.html#subject-case 9 | ['start-case', 'pascal-case', 'upper-case'] 10 | ] 11 | } 12 | }; 13 | 14 | module.exports = Configuration; -------------------------------------------------------------------------------- /.github/dependabot.yml: -------------------------------------------------------------------------------- 1 | # To get started with Dependabot version updates, you'll need to specify which 2 | # package ecosystems to update and where the package manifests are located. 3 | # Please see the documentation for all configuration options: 4 | # https://docs.github.com/github/administering-a-repository/configuration-options-for-dependency-updates 5 | 6 | version: 2 7 | updates: 8 | - package-ecosystem: github-actions 9 | directory: / 10 | schedule: 11 | interval: weekly 12 | reviewers: 13 | - ovh/kms 14 | - package-ecosystem: "gomod" 15 | directories: 16 | - "/" 17 | schedule: 18 | interval: "weekly" 19 | allow: 20 | - dependency-type: all 21 | reviewers: 22 | - ovh/kms 23 | open-pull-requests-limit: 10 24 | -------------------------------------------------------------------------------- /.github/workflows/pull-request.yaml: -------------------------------------------------------------------------------- 1 | name: pull-request 2 | 3 | on: 4 | pull_request: 5 | branches: ["main"] 6 | 7 | permissions: {} 8 | 9 | jobs: 10 | commitlint: 11 | runs-on: ubuntu-latest 12 | steps: 13 | - uses: actions/checkout@v4 14 | with: 15 | fetch-depth: 0 16 | - name: Lint commits 17 | if: github.event.pull_request.user.login != 'dependabot[bot]' 18 | uses: ./.github/actions/lint-commit 19 | 20 | build: 21 | needs: 22 | - commitlint 23 | uses: ./.github/workflows/test.yaml -------------------------------------------------------------------------------- /.github/workflows/release.yaml: -------------------------------------------------------------------------------- 1 | name: release 2 | 3 | on: 4 | push: 5 | # run only against tags 6 | tags: 7 | - "v*" 8 | 9 | permissions: 10 | contents: write 11 | packages: write 12 | 13 | jobs: 14 | test: 15 | uses: ./.github/workflows/test.yaml 16 | secrets: inherit 17 | 18 | release: 19 | needs: test 20 | runs-on: ubuntu-latest 21 | steps: 22 | - uses: actions/checkout@v4 23 | with: 24 | fetch-depth: 0 25 | fetch-tags: true 26 | - run: git fetch --force --tags 27 | - uses: ./.github/actions/setup-build-env 28 | - name: Generate a changelog 29 | uses: orhun/git-cliff-action@v4 30 | with: 31 | config: .github/release-note.toml 32 | args: --verbose --current 33 | env: 34 | OUTPUT: tmp.CHANGELOG.md 35 | - name: Release 36 | uses: softprops/action-gh-release@v2 37 | if: startsWith(github.ref, 'refs/tags/') 38 | with: 39 | # prerelease: true 40 | name: ${{ github.ref_name }} 41 | # draft: true 42 | body_path: tmp.CHANGELOG.md 43 | -------------------------------------------------------------------------------- /.github/workflows/test.yaml: -------------------------------------------------------------------------------- 1 | # This workflow will build a golang project 2 | # For more information see: https://docs.github.com/en/actions/automating-builds-and-tests/building-and-testing-go 3 | 4 | name: test 5 | 6 | on: 7 | push: 8 | branches: ["main"] 9 | workflow_call: {} 10 | 11 | permissions: {} 12 | 13 | jobs: 14 | test: 15 | runs-on: ubuntu-latest 16 | steps: 17 | - uses: actions/checkout@v4 18 | - uses: ./.github/actions/setup-build-env 19 | - name: Build examples 20 | run: go build -o _examples_build ./examples 21 | - name: Unit Test 22 | run: go test -race -v ./... 23 | - name: Run benchmarks 24 | run: go test -benchmem -bench . -run ^$ ./... 25 | 26 | lint: 27 | runs-on: ubuntu-latest 28 | steps: 29 | - uses: actions/checkout@v4 30 | - uses: ./.github/actions/setup-build-env 31 | - name: Lint library 32 | uses: golangci/golangci-lint-action@v8 33 | with: 34 | version: v2.1.6 35 | -------------------------------------------------------------------------------- /.gitignore: -------------------------------------------------------------------------------- 1 | certs/ 2 | dist/ 3 | .build/ 4 | *.pem 5 | *.log 6 | *.db 7 | *.exe 8 | 9 | bin/ 10 | git-cliff-* 11 | tmp.CHANGELOG.md -------------------------------------------------------------------------------- /AUTHORS: -------------------------------------------------------------------------------- 1 | # This is the official list of kmip-go authors for copyright purposes. 2 | # This file is distinct from the CONTRIBUTORS files 3 | # and it lists the copyright holders only. 4 | 5 | # Names should be added to this file as one of 6 | # Organization's name 7 | # Individual's name 8 | # Individual's name 9 | # See CONTRIBUTORS for the meaning of multiple email addresses. 10 | 11 | # Please keep the list sorted. 12 | 13 | OVH SAS 14 | Pierre-Henri Symoneaux -------------------------------------------------------------------------------- /CONTRIBUTORS: -------------------------------------------------------------------------------- 1 | # This is the official list of people who can contribute 2 | # (and typically have contributed) code to the kmip-go repository. 3 | # 4 | # Names should be added to this file only after verifying that 5 | # the individual or the individual's organization has agreed to 6 | # the appropriate CONTRIBUTING.md file. 7 | # 8 | # Names should be added to this file like so: 9 | # Individual's name 10 | # Individual's name 11 | # 12 | # Please keep the list sorted. 13 | # 14 | 15 | Pierre-Henri Symoneaux 16 | Gabriel Bahezre 17 | -------------------------------------------------------------------------------- /LICENSE: -------------------------------------------------------------------------------- 1 | Copyright 2025 OVHcloud 2 | 3 | Licensed under the Apache License, Version 2.0 (the "License"); 4 | you may not use this file except in compliance with the License. 5 | You may obtain a copy of the License at 6 | 7 | http://www.apache.org/licenses/LICENSE-2.0 8 | 9 | Unless required by applicable law or agreed to in writing, software 10 | distributed under the License is distributed on an "AS IS" BASIS, 11 | WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 12 | See the License for the specific language governing permissions and 13 | limitations under the License. -------------------------------------------------------------------------------- /LICENSES/LICENSES.txt: -------------------------------------------------------------------------------- 1 | Licences: 2 | BSD-3-Clause 3 | 4 | Packages: 5 | github.com/golang/go; BSD-3-Clause 6 | github.com/google/uuid; BSD-3-Clause -------------------------------------------------------------------------------- /LICENSES/go/github.com/golang/go/LICENSE: -------------------------------------------------------------------------------- 1 | == github.com/golang/go == 2 | 3 | Copyright 2009 The Go Authors. 4 | 5 | Redistribution and use in source and binary forms, with or without modification, are permitted provided that the following conditions are met: 6 | 7 | 1. Redistributions of source code must retain the above copyright notice, this list of conditions and the following disclaimer. 8 | 9 | 2. Redistributions in binary form must reproduce the above copyright notice, this list of conditions and the following disclaimer in the documentation and/or other materials provided with the distribution. 10 | 11 | 3. Neither the name of the copyright holder nor the names of its contributors may be used to endorse or promote products derived from this software without specific prior written permission. 12 | 13 | THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS “AS IS” AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. 14 | 15 | -------------------------------------------------------------------------------- /LICENSES/go/github.com/google/uuid/LICENSE: -------------------------------------------------------------------------------- 1 | == github.com/google/uuid == 2 | 3 | Copyright (c) 2009,2014 Google Inc. All rights reserved. 4 | 5 | Redistribution and use in source and binary forms, with or without modification, are permitted provided that the following conditions are met: 6 | 7 | 1. Redistributions of source code must retain the above copyright notice, this list of conditions and the following disclaimer. 8 | 9 | 2. Redistributions in binary form must reproduce the above copyright notice, this list of conditions and the following disclaimer in the documentation and/or other materials provided with the distribution. 10 | 11 | 3. Neither the name of the copyright holder nor the names of its contributors may be used to endorse or promote products derived from this software without specific prior written permission. 12 | 13 | THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS “AS IS” AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. 14 | 15 | -------------------------------------------------------------------------------- /MAINTAINERS: -------------------------------------------------------------------------------- 1 | # This is the official list of the project maintainers. 2 | # This is mostly useful for contributors that want to push 3 | # significant pull requests or for project management issues. 4 | # 5 | # 6 | # Names should be added to this file like so: 7 | # Individual's name 8 | # Individual's name 9 | # 10 | # Please keep the list sorted. 11 | # 12 | 13 | Alexandre Gagneux 14 | Pierre-Henri Symoneaux 15 | Gabriel Bahezre 16 | Inesse Ben-Zekri 17 | Raphael Catolino -------------------------------------------------------------------------------- /attributes_test.go: -------------------------------------------------------------------------------- 1 | package kmip 2 | 3 | import ( 4 | "testing" 5 | 6 | "github.com/ovh/kmip-go/ttlv" 7 | 8 | "github.com/stretchr/testify/require" 9 | ) 10 | 11 | func TestAttribute_EncodeDecode(t *testing.T) { 12 | attr := Attribute{ 13 | AttributeName: AttributeNameName, 14 | AttributeValue: Name{ 15 | NameType: NameTypeUninterpretedTextString, 16 | NameValue: "foobar", 17 | }, 18 | } 19 | bytes := ttlv.MarshalTTLV(&attr) 20 | 21 | newAttr := Attribute{} 22 | err := ttlv.UnmarshalTTLV(bytes, &newAttr) 23 | require.NoError(t, err) 24 | require.EqualValues(t, attr, newAttr) 25 | 26 | index := int32(12) 27 | attr.AttributeIndex = &index 28 | bytes = ttlv.MarshalTTLV(&attr) 29 | 30 | newAttr = Attribute{} 31 | err = ttlv.UnmarshalTTLV(bytes, &newAttr) 32 | require.NoError(t, err) 33 | require.EqualValues(t, attr, newAttr) 34 | } 35 | -------------------------------------------------------------------------------- /bitmasks.go: -------------------------------------------------------------------------------- 1 | package kmip 2 | 3 | import "github.com/ovh/kmip-go/ttlv" 4 | 5 | func init() { 6 | ttlv.RegisterBitmask[CryptographicUsageMask]( 7 | TagCryptographicUsageMask, 8 | "Sign", 9 | "Verify", 10 | "Encrypt", 11 | "Decrypt", 12 | "WrapKey", 13 | "UnwrapKey", 14 | "Export", 15 | "MACGenerate", 16 | "DeriveKey", 17 | "ContentCommitment", 18 | "KeyAgreement", 19 | "CertificateSign", 20 | "CRLSign", 21 | "GenerateCryptogram", 22 | "ValidateCryptogram", 23 | "TranslateEncrypt", 24 | "TranslateDecrypt", 25 | "TranslateWrap", 26 | "TranslateUnwrap", 27 | ) 28 | ttlv.RegisterBitmask[StorageStatusMask]( 29 | TagStorageStatusMask, 30 | "OnlineStorage", 31 | "ArchivalStorage", 32 | ) 33 | } 34 | 35 | type CryptographicUsageMask int32 36 | 37 | const ( 38 | CryptographicUsageSign CryptographicUsageMask = 1 << iota 39 | CryptographicUsageVerify 40 | CryptographicUsageEncrypt 41 | CryptographicUsageDecrypt 42 | CryptographicUsageWrapKey 43 | CryptographicUsageUnwrapKey 44 | CryptographicUsageExport 45 | CryptographicUsageMACGenerate 46 | CryptographicUsageDeriveKey 47 | CryptographicUsageContentCommitment 48 | CryptographicUsageKeyAgreement 49 | CryptographicUsageCertificateSign 50 | CryptographicUsageCRLSign 51 | CryptographicUsageGenerateCryptogram 52 | CryptographicUsageValidateCryptogram 53 | CryptographicUsageTranslateEncrypt 54 | CryptographicUsageTranslateDecrypt 55 | CryptographicUsageTranslateWrap 56 | CryptographicUsageTranslateUnwrap 57 | ) 58 | 59 | func (mask CryptographicUsageMask) MarshalText() ([]byte, error) { 60 | return []byte(ttlv.BitmaskStr(mask, " | ")), nil 61 | } 62 | 63 | type StorageStatusMask int32 64 | 65 | const ( 66 | StorageStatusOnlineStorage StorageStatusMask = 1 << iota 67 | StorageStatusArchivalStorage 68 | ) 69 | 70 | func (mask StorageStatusMask) MarshalText() ([]byte, error) { 71 | return []byte(ttlv.BitmaskStr(mask, " | ")), nil 72 | } 73 | -------------------------------------------------------------------------------- /examples/cleanup.go: -------------------------------------------------------------------------------- 1 | package main 2 | 3 | import ( 4 | "github.com/ovh/kmip-go" 5 | "github.com/ovh/kmip-go/kmipclient" 6 | ) 7 | 8 | func cleanupDomain(client *kmipclient.Client) { 9 | println("Listing all objects") 10 | resp := client.Locate().MustExec() 11 | for _, id := range resp.UniqueIdentifier { 12 | resp := client.GetAttributes(id, kmip.AttributeNameState).MustExec() 13 | 14 | for _, attr := range resp.Attribute { 15 | if attr.AttributeName == kmip.AttributeNameState && attr.AttributeValue.(kmip.State) == kmip.StateActive { 16 | println("Revoking", id) 17 | client.Revoke(id).WithRevocationReasonCode(kmip.RevocationReasonCodeCessationOfOperation).MustExec() 18 | break 19 | } 20 | } 21 | 22 | println("Deleting", id) 23 | client.Destroy(id).MustExec() 24 | } 25 | println("Deleted", len(resp.UniqueIdentifier), "managed objects") 26 | } 27 | 28 | func activateAll(client *kmipclient.Client) { 29 | println("Listing all objects") 30 | resp := client.Locate().MustExec() 31 | for _, id := range resp.UniqueIdentifier { 32 | resp := client.GetAttributes(id, kmip.AttributeNameState).MustExec() 33 | 34 | for _, attr := range resp.Attribute { 35 | if attr.AttributeName == kmip.AttributeNameState && attr.AttributeValue.(kmip.State) == kmip.StatePreActive { 36 | println("Activating", id) 37 | client.Activate(id).MustExec() 38 | break 39 | } 40 | } 41 | } 42 | } 43 | -------------------------------------------------------------------------------- /go.mod: -------------------------------------------------------------------------------- 1 | module github.com/ovh/kmip-go 2 | 3 | go 1.23.0 4 | 5 | require ( 6 | github.com/google/uuid v1.6.0 7 | github.com/stretchr/testify v1.10.0 8 | ) 9 | 10 | require ( 11 | github.com/davecgh/go-spew v1.1.1 // indirect 12 | github.com/pmezard/go-difflib v1.0.0 // indirect 13 | gopkg.in/yaml.v3 v3.0.1 // indirect 14 | ) 15 | -------------------------------------------------------------------------------- /go.sum: -------------------------------------------------------------------------------- 1 | github.com/davecgh/go-spew v1.1.1 h1:vj9j/u1bqnvCEfJOwUhtlOARqs3+rkHYY13jYWTU97c= 2 | github.com/davecgh/go-spew v1.1.1/go.mod h1:J7Y8YcW2NihsgmVo/mv3lAwl/skON4iLHjSsI+c5H38= 3 | github.com/google/uuid v1.6.0 h1:NIvaJDMOsjHA8n1jAhLSgzrAzy1Hgr+hNrb57e+94F0= 4 | github.com/google/uuid v1.6.0/go.mod h1:TIyPZe4MgqvfeYDBFedMoGGpEw/LqOeaOT+nhxU+yHo= 5 | github.com/pmezard/go-difflib v1.0.0 h1:4DBwDE0NGyQoBHbLQYPwSUPoCMWR5BEzIk/f1lZbAQM= 6 | github.com/pmezard/go-difflib v1.0.0/go.mod h1:iKH77koFhYxTK1pcRnkKkqfTogsbg7gZNVY4sRDYZ/4= 7 | github.com/stretchr/testify v1.10.0 h1:Xv5erBjTwe/5IxqUQTdXv5kgmIvbHo3QQyRwhJsOfJA= 8 | github.com/stretchr/testify v1.10.0/go.mod h1:r2ic/lqez/lEtzL7wO/rwa5dbSLXVDPFyf8C91i36aY= 9 | gopkg.in/check.v1 v0.0.0-20161208181325-20d25e280405 h1:yhCVgyC4o1eVCa2tZl7eS0r+SDo693bJlVdllGtEeKM= 10 | gopkg.in/check.v1 v0.0.0-20161208181325-20d25e280405/go.mod h1:Co6ibVJAznAaIkqp8huTwlJQCZ016jof/cbN4VW5Yz0= 11 | gopkg.in/yaml.v3 v3.0.1 h1:fxVm/GzAzEWqLHuvctI91KS9hhNmmWOoWu0XTYJS7CA= 12 | gopkg.in/yaml.v3 v3.0.1/go.mod h1:K4uyk7z7BCEPqu6E+C64Yfv1cQ7kz7rIZviUmN+EgEM= 13 | -------------------------------------------------------------------------------- /kmipclient/activate.go: -------------------------------------------------------------------------------- 1 | package kmipclient 2 | 3 | import ( 4 | "github.com/ovh/kmip-go/payloads" 5 | ) 6 | 7 | func (c *Client) Activate(id string) ExecActivate { 8 | return ExecActivate{ 9 | client: c, 10 | req: &payloads.ActivateRequestPayload{ 11 | UniqueIdentifier: id, 12 | }, 13 | } 14 | } 15 | 16 | type ExecActivate = Executor[*payloads.ActivateRequestPayload, *payloads.ActivateResponsePayload] 17 | -------------------------------------------------------------------------------- /kmipclient/add_attribute.go: -------------------------------------------------------------------------------- 1 | package kmipclient 2 | 3 | import ( 4 | "github.com/ovh/kmip-go" 5 | "github.com/ovh/kmip-go/payloads" 6 | ) 7 | 8 | func (c *Client) AddAttribute(id string, name kmip.AttributeName, value any) ExecAddAttribute { 9 | return ExecAddAttribute{ 10 | Executor[*payloads.AddAttributeRequestPayload, *payloads.AddAttributeResponsePayload]{ 11 | client: c, 12 | req: &payloads.AddAttributeRequestPayload{ 13 | UniqueIdentifier: id, 14 | Attribute: kmip.Attribute{AttributeName: name, AttributeValue: value}, 15 | }, 16 | }, 17 | } 18 | } 19 | 20 | type ExecAddAttribute struct { 21 | Executor[*payloads.AddAttributeRequestPayload, *payloads.AddAttributeResponsePayload] 22 | } 23 | 24 | func (ex ExecAddAttribute) WithIndex(index int32) ExecAddAttribute { 25 | ex.req.Attribute.AttributeIndex = &index 26 | return ex 27 | } 28 | -------------------------------------------------------------------------------- /kmipclient/archive_recover.go: -------------------------------------------------------------------------------- 1 | package kmipclient 2 | 3 | import ( 4 | "github.com/ovh/kmip-go/payloads" 5 | ) 6 | 7 | func (c *Client) Archive(id string) ExecArchive { 8 | return ExecArchive{ 9 | client: c, 10 | req: &payloads.ArchiveRequestPayload{ 11 | UniqueIdentifier: id, 12 | }, 13 | } 14 | } 15 | 16 | type ExecArchive = Executor[*payloads.ArchiveRequestPayload, *payloads.ArchiveResponsePayload] 17 | 18 | func (c *Client) Recover(id string) ExecRecover { 19 | return ExecRecover{ 20 | client: c, 21 | req: &payloads.RecoverRequestPayload{ 22 | UniqueIdentifier: id, 23 | }, 24 | } 25 | } 26 | 27 | type ExecRecover = Executor[*payloads.RecoverRequestPayload, *payloads.RecoverResponsePayload] 28 | -------------------------------------------------------------------------------- /kmipclient/create.go: -------------------------------------------------------------------------------- 1 | package kmipclient 2 | 3 | import ( 4 | "math" 5 | 6 | "github.com/ovh/kmip-go" 7 | "github.com/ovh/kmip-go/payloads" 8 | ) 9 | 10 | func (c *Client) Create() ExecCreateWantType { 11 | return ExecCreateWantType{ 12 | client: c, 13 | } 14 | } 15 | 16 | type ExecCreateWantType struct { 17 | client *Client 18 | } 19 | 20 | func (ex ExecCreateWantType) Object(objectType kmip.ObjectType, attrs ...kmip.Attribute) ExecCreate { 21 | return ExecCreate{ 22 | AttributeExecutor[*payloads.CreateRequestPayload, *payloads.CreateResponsePayload, ExecCreate]{ 23 | Executor[*payloads.CreateRequestPayload, *payloads.CreateResponsePayload]{ 24 | client: ex.client, 25 | req: &payloads.CreateRequestPayload{ 26 | ObjectType: objectType, 27 | TemplateAttribute: kmip.TemplateAttribute{Attribute: attrs}, 28 | }, 29 | }, 30 | func(crp **payloads.CreateRequestPayload) *[]kmip.Attribute { 31 | return &(*crp).TemplateAttribute.Attribute 32 | }, 33 | func(ae AttributeExecutor[*payloads.CreateRequestPayload, *payloads.CreateResponsePayload, ExecCreate]) ExecCreate { 34 | return ExecCreate{ae} 35 | }, 36 | }, 37 | } 38 | } 39 | 40 | func (ex ExecCreateWantType) SymmetricKey(alg kmip.CryptographicAlgorithm, length int, usage kmip.CryptographicUsageMask) ExecCreate { 41 | if length > math.MaxInt32 || length < 0 { 42 | panic("length is out of range") 43 | } 44 | return ex.Object(kmip.ObjectTypeSymmetricKey). 45 | WithAttribute(kmip.AttributeNameCryptographicAlgorithm, alg). 46 | WithAttribute(kmip.AttributeNameCryptographicLength, int32(length)). 47 | WithAttribute(kmip.AttributeNameCryptographicUsageMask, usage) 48 | } 49 | 50 | func (ex ExecCreateWantType) AES(length int, usage kmip.CryptographicUsageMask) ExecCreate { 51 | return ex.SymmetricKey(kmip.CryptographicAlgorithmAES, length, usage) 52 | } 53 | 54 | func (ex ExecCreateWantType) TDES(length int, usage kmip.CryptographicUsageMask) ExecCreate { 55 | return ex.SymmetricKey(kmip.CryptographicAlgorithmTDES, length, usage) 56 | } 57 | 58 | func (ex ExecCreateWantType) Skipjack(usage kmip.CryptographicUsageMask) ExecCreate { 59 | return ex.SymmetricKey(kmip.CryptographicAlgorithmSKIPJACK, 80, usage) 60 | } 61 | 62 | type ExecCreate struct { 63 | AttributeExecutor[*payloads.CreateRequestPayload, *payloads.CreateResponsePayload, ExecCreate] 64 | } 65 | 66 | // Deprecated: Templates have been deprecated in KMIP v1.3. 67 | func (ex ExecCreate) WithTemplates(names ...kmip.Name) ExecCreate { 68 | ex.req.TemplateAttribute.Name = append(ex.req.TemplateAttribute.Name, names...) 69 | return ex 70 | } 71 | 72 | // Deprecated: Templates have been deprecated in KMIP v1.3. 73 | func (ex ExecCreate) WithTemplate(name string, nameType kmip.NameType) ExecCreate { 74 | ex.req.TemplateAttribute.Name = append(ex.req.TemplateAttribute.Name, kmip.Name{NameValue: name, NameType: nameType}) 75 | return ex 76 | } 77 | -------------------------------------------------------------------------------- /kmipclient/delete_attribute.go: -------------------------------------------------------------------------------- 1 | package kmipclient 2 | 3 | import ( 4 | "github.com/ovh/kmip-go" 5 | "github.com/ovh/kmip-go/payloads" 6 | ) 7 | 8 | func (c *Client) DeleteAttribute(id string, name kmip.AttributeName) ExecDeleteAttribute { 9 | return ExecDeleteAttribute{ 10 | Executor[*payloads.DeleteAttributeRequestPayload, *payloads.DeleteAttributeResponsePayload]{ 11 | client: c, 12 | req: &payloads.DeleteAttributeRequestPayload{ 13 | UniqueIdentifier: id, 14 | AttributeName: name, 15 | }, 16 | }, 17 | } 18 | } 19 | 20 | type ExecDeleteAttribute struct { 21 | Executor[*payloads.DeleteAttributeRequestPayload, *payloads.DeleteAttributeResponsePayload] 22 | } 23 | 24 | func (ex ExecDeleteAttribute) WithIndex(index int32) ExecDeleteAttribute { 25 | ex.req.AttributeIndex = &index 26 | return ex 27 | } 28 | -------------------------------------------------------------------------------- /kmipclient/destroy.go: -------------------------------------------------------------------------------- 1 | package kmipclient 2 | 3 | import ( 4 | "github.com/ovh/kmip-go/payloads" 5 | ) 6 | 7 | func (c *Client) Destroy(id string) ExecDestroy { 8 | return ExecDestroy{ 9 | client: c, 10 | req: &payloads.DestroyRequestPayload{ 11 | UniqueIdentifier: id, 12 | }, 13 | } 14 | } 15 | 16 | type ExecDestroy = Executor[*payloads.DestroyRequestPayload, *payloads.DestroyResponsePayload] 17 | -------------------------------------------------------------------------------- /kmipclient/encrypt_decrypt.go: -------------------------------------------------------------------------------- 1 | package kmipclient 2 | 3 | import ( 4 | "github.com/ovh/kmip-go" 5 | "github.com/ovh/kmip-go/payloads" 6 | ) 7 | 8 | type ExecEncrypt struct { 9 | Executor[*payloads.EncryptRequestPayload, *payloads.EncryptResponsePayload] 10 | } 11 | 12 | type ExecDecrypt struct { 13 | Executor[*payloads.DecryptRequestPayload, *payloads.DecryptResponsePayload] 14 | } 15 | 16 | type ExecEncryptWantsData struct { 17 | req *payloads.EncryptRequestPayload 18 | client *Client 19 | } 20 | 21 | type ExecDecryptWantsData struct { 22 | req *payloads.DecryptRequestPayload 23 | client *Client 24 | } 25 | 26 | func (c *Client) Encrypt(id string) ExecEncryptWantsData { 27 | return ExecEncryptWantsData{ 28 | client: c, 29 | req: &payloads.EncryptRequestPayload{ 30 | UniqueIdentifier: id, 31 | }, 32 | } 33 | } 34 | 35 | func (ex ExecEncryptWantsData) WithIvCounterNonce(iv []byte) ExecEncryptWantsData { 36 | ex.req.IVCounterNonce = iv 37 | return ex 38 | } 39 | 40 | func (ex ExecEncryptWantsData) WithAAD(aad []byte) ExecEncryptWantsData { 41 | ex.req.AuthenticatedEncryptionAdditionalData = aad 42 | return ex 43 | } 44 | 45 | func (ex ExecEncryptWantsData) WithCryptographicParameters(params kmip.CryptographicParameters) ExecEncryptWantsData { 46 | ex.req.CryptographicParameters = ¶ms 47 | return ex 48 | } 49 | 50 | func (ex ExecEncryptWantsData) Data(data []byte) ExecEncrypt { 51 | ex.req.Data = data 52 | return ExecEncrypt{ 53 | Executor[*payloads.EncryptRequestPayload, *payloads.EncryptResponsePayload]{ 54 | client: ex.client, 55 | req: ex.req, 56 | }, 57 | } 58 | } 59 | 60 | func (c *Client) Decrypt(id string) ExecDecryptWantsData { 61 | return ExecDecryptWantsData{ 62 | client: c, 63 | req: &payloads.DecryptRequestPayload{ 64 | UniqueIdentifier: id, 65 | }, 66 | } 67 | } 68 | 69 | func (ex ExecDecryptWantsData) WithIvCounterNonce(iv []byte) ExecDecryptWantsData { 70 | ex.req.IVCounterNonce = iv 71 | return ex 72 | } 73 | 74 | func (ex ExecDecryptWantsData) WithAAD(aad []byte) ExecDecryptWantsData { 75 | ex.req.AuthenticatedEncryptionAdditionalData = aad 76 | return ex 77 | } 78 | 79 | func (ex ExecDecryptWantsData) WithCryptographicParameters(params kmip.CryptographicParameters) ExecDecryptWantsData { 80 | ex.req.CryptographicParameters = ¶ms 81 | return ex 82 | } 83 | 84 | func (ex ExecDecryptWantsData) WithAuthTag(tag []byte) ExecDecryptWantsData { 85 | ex.req.AuthenticatedEncryptionTag = tag 86 | return ex 87 | } 88 | 89 | func (ex ExecDecryptWantsData) Data(data []byte) ExecDecrypt { 90 | ex.req.Data = data 91 | return ExecDecrypt{ 92 | Executor[*payloads.DecryptRequestPayload, *payloads.DecryptResponsePayload]{ 93 | client: ex.client, 94 | req: ex.req, 95 | }, 96 | } 97 | } 98 | -------------------------------------------------------------------------------- /kmipclient/get.go: -------------------------------------------------------------------------------- 1 | package kmipclient 2 | 3 | import ( 4 | "github.com/ovh/kmip-go" 5 | "github.com/ovh/kmip-go/payloads" 6 | ) 7 | 8 | func (c *Client) Get(id string) ExecGet { 9 | return ExecGet{ 10 | Executor[*payloads.GetRequestPayload, *payloads.GetResponsePayload]{ 11 | client: c, 12 | req: &payloads.GetRequestPayload{ 13 | UniqueIdentifier: id, 14 | }, 15 | }, 16 | } 17 | } 18 | 19 | type ExecGet struct { 20 | Executor[*payloads.GetRequestPayload, *payloads.GetResponsePayload] 21 | } 22 | 23 | func (ex ExecGet) WithKeyFormat(format kmip.KeyFormatType) ExecGet { 24 | ex.req.KeyFormatType = format 25 | return ex 26 | } 27 | 28 | func (ex ExecGet) WithKeyWrapType(format kmip.KeyFormatType) ExecGet { 29 | ex.req.KeyWrapType = format 30 | return ex 31 | } 32 | 33 | func (ex ExecGet) WithKeyCompression(compression kmip.KeyCompressionType) ExecGet { 34 | ex.req.KeyCompressionType = compression 35 | return ex 36 | } 37 | 38 | func (ex ExecGet) WithKeyWrapping(spec kmip.KeyWrappingSpecification) ExecGet { 39 | ex.req.KeyWrappingSpecification = &spec 40 | return ex 41 | } 42 | -------------------------------------------------------------------------------- /kmipclient/get_attribute_list.go: -------------------------------------------------------------------------------- 1 | package kmipclient 2 | 3 | import "github.com/ovh/kmip-go/payloads" 4 | 5 | func (c *Client) GetAttributeList(id string) ExecGetAttributeList { 6 | return ExecGetAttributeList{ 7 | client: c, 8 | req: &payloads.GetAttributeListRequestPayload{ 9 | UniqueIdentifier: id, 10 | }, 11 | } 12 | } 13 | 14 | type ExecGetAttributeList = Executor[*payloads.GetAttributeListRequestPayload, *payloads.GetAttributeListResponsePayload] 15 | -------------------------------------------------------------------------------- /kmipclient/get_attributes.go: -------------------------------------------------------------------------------- 1 | package kmipclient 2 | 3 | import ( 4 | "github.com/ovh/kmip-go" 5 | "github.com/ovh/kmip-go/payloads" 6 | ) 7 | 8 | func (c *Client) GetAttributes(id string, attributes ...kmip.AttributeName) ExecGetAttributes { 9 | return ExecGetAttributes{ 10 | Executor[*payloads.GetAttributesRequestPayload, *payloads.GetAttributesResponsePayload]{ 11 | client: c, 12 | req: &payloads.GetAttributesRequestPayload{ 13 | UniqueIdentifier: id, 14 | }, 15 | }, 16 | }.WithAttributes(attributes...) 17 | } 18 | 19 | type ExecGetAttributes struct { 20 | Executor[*payloads.GetAttributesRequestPayload, *payloads.GetAttributesResponsePayload] 21 | } 22 | 23 | func (ex ExecGetAttributes) WithAttributes(names ...kmip.AttributeName) ExecGetAttributes { 24 | ex.req.AttributeName = append(ex.req.AttributeName, names...) 25 | return ex 26 | } 27 | -------------------------------------------------------------------------------- /kmipclient/get_usage.go: -------------------------------------------------------------------------------- 1 | package kmipclient 2 | 3 | import "github.com/ovh/kmip-go/payloads" 4 | 5 | func (c *Client) GetUsageAllocation(id string, limitCount int64) ExecGetUsageAllocation { 6 | return ExecGetUsageAllocation{ 7 | client: c, 8 | req: &payloads.GetUsageAllocationRequestPayload{ 9 | UniqueIdentifier: id, 10 | UsageLimitsCount: limitCount, 11 | }, 12 | } 13 | } 14 | 15 | type ExecGetUsageAllocation = Executor[*payloads.GetUsageAllocationRequestPayload, *payloads.GetUsageAllocationResponsePayload] 16 | -------------------------------------------------------------------------------- /kmipclient/locate.go: -------------------------------------------------------------------------------- 1 | package kmipclient 2 | 3 | import ( 4 | "github.com/ovh/kmip-go" 5 | "github.com/ovh/kmip-go/payloads" 6 | ) 7 | 8 | func (c *Client) Locate() ExecLocate { 9 | return ExecLocate{ 10 | AttributeExecutor[*payloads.LocateRequestPayload, *payloads.LocateResponsePayload, ExecLocate]{ 11 | Executor[*payloads.LocateRequestPayload, *payloads.LocateResponsePayload]{ 12 | client: c, 13 | req: &payloads.LocateRequestPayload{}, 14 | }, 15 | func(lrp **payloads.LocateRequestPayload) *[]kmip.Attribute { 16 | return &(*lrp).Attribute 17 | }, 18 | func(ae AttributeExecutor[*payloads.LocateRequestPayload, *payloads.LocateResponsePayload, ExecLocate]) ExecLocate { 19 | return ExecLocate{ae} 20 | }, 21 | }, 22 | } 23 | } 24 | 25 | type ExecLocate struct { 26 | AttributeExecutor[*payloads.LocateRequestPayload, *payloads.LocateResponsePayload, ExecLocate] 27 | } 28 | 29 | func (ex ExecLocate) WithStorageStatusMask(mask kmip.StorageStatusMask) ExecLocate { 30 | ex.req.StorageStatusMask = mask 31 | return ex 32 | } 33 | 34 | func (ex ExecLocate) WithMaxItems(maximum int32) ExecLocate { 35 | ex.req.MaximumItems = maximum 36 | return ex 37 | } 38 | 39 | func (ex ExecLocate) WithOffset(offset int32) ExecLocate { 40 | ex.req.OffsetItems = offset 41 | return ex 42 | } 43 | 44 | func (ex ExecLocate) WithObjectGroupMember(groupMember kmip.ObjectGroupMember) ExecLocate { 45 | ex.req.ObjectGroupMember = groupMember 46 | return ex 47 | } 48 | -------------------------------------------------------------------------------- /kmipclient/middlewares.go: -------------------------------------------------------------------------------- 1 | package kmipclient 2 | 3 | import ( 4 | "context" 5 | "fmt" 6 | "io" 7 | "time" 8 | 9 | "github.com/ovh/kmip-go" 10 | "github.com/ovh/kmip-go/ttlv" 11 | ) 12 | 13 | type Next func(context.Context, *kmip.RequestMessage) (*kmip.ResponseMessage, error) 14 | type Middleware func(next Next, ctx context.Context, msg *kmip.RequestMessage) (*kmip.ResponseMessage, error) 15 | 16 | func DebugMiddleware(out io.Writer, marshal func(data any) []byte) Middleware { 17 | if marshal == nil { 18 | marshal = ttlv.MarshalXML 19 | } 20 | return func(next Next, ctx context.Context, rm *kmip.RequestMessage) (*kmip.ResponseMessage, error) { 21 | if flushable, ok := out.(interface{ Flush() error }); ok { 22 | defer flushable.Flush() 23 | } 24 | fmt.Fprintln(out, "Request:") 25 | fmt.Fprintln(out, string(marshal(rm))) 26 | now := time.Now() 27 | resp, err := next(ctx, rm) 28 | if err != nil { 29 | return resp, err 30 | } 31 | fmt.Fprintf(out, "\nResponse in %s:\n", time.Since(now)) 32 | fmt.Fprintln(out, string(marshal(resp))) 33 | return resp, nil 34 | } 35 | } 36 | 37 | func CorrelationValueMiddleware(fn func() string) Middleware { 38 | if fn == nil { 39 | panic("correlation value generator function cannot be null") 40 | } 41 | return func(next Next, ctx context.Context, msg *kmip.RequestMessage) (*kmip.ResponseMessage, error) { 42 | if msg.Header.ClientCorrelationValue == "" && ttlv.CompareVersions(msg.Header.ProtocolVersion, kmip.V1_4) >= 0 { 43 | msg.Header.ClientCorrelationValue = fn() 44 | } 45 | return next(ctx, msg) 46 | } 47 | } 48 | 49 | func TimeoutMiddleware(timeout time.Duration) Middleware { 50 | if timeout == 0 { 51 | return func(next Next, ctx context.Context, msg *kmip.RequestMessage) (*kmip.ResponseMessage, error) { 52 | return next(ctx, msg) 53 | } 54 | } 55 | return func(next Next, ctx context.Context, msg *kmip.RequestMessage) (*kmip.ResponseMessage, error) { 56 | var cancel context.CancelFunc 57 | ctx, cancel = context.WithTimeout(ctx, timeout) 58 | defer cancel() 59 | return next(ctx, msg) 60 | } 61 | } 62 | -------------------------------------------------------------------------------- /kmipclient/middlewares_test.go: -------------------------------------------------------------------------------- 1 | package kmipclient_test 2 | 3 | import ( 4 | "context" 5 | "testing" 6 | "time" 7 | 8 | "github.com/ovh/kmip-go" 9 | "github.com/ovh/kmip-go/kmipclient" 10 | "github.com/ovh/kmip-go/kmipserver" 11 | "github.com/ovh/kmip-go/kmiptest" 12 | "github.com/ovh/kmip-go/payloads" 13 | "github.com/stretchr/testify/require" 14 | ) 15 | 16 | func TestTimeoutMiddleware_Expire(t *testing.T) { 17 | router := kmipserver.NewBatchExecutor() 18 | router.Route(kmip.OperationActivate, kmipserver.HandleFunc(func(ctx context.Context, req *payloads.ActivateRequestPayload) (*payloads.ActivateResponsePayload, error) { 19 | timer := time.NewTimer(10 * time.Second) 20 | defer timer.Stop() 21 | 22 | select { 23 | case <-ctx.Done(): 24 | return nil, ctx.Err() 25 | case <-timer.C: 26 | } 27 | t.Fatal("Timer should not have expired") 28 | return nil, nil 29 | })) 30 | 31 | addr, ca := kmiptest.NewServer(t, router) 32 | 33 | client, err := kmipclient.Dial(addr, kmipclient.WithRootCAPem([]byte(ca)), kmipclient.WithMiddlewares( 34 | kmipclient.TimeoutMiddleware(5*time.Second), 35 | )) 36 | require.NoError(t, err) 37 | 38 | _, err = client.Activate("foobar").Exec() 39 | require.Error(t, err) 40 | } 41 | 42 | func TestTimeoutMiddleware_NoExpire(t *testing.T) { 43 | router := kmipserver.NewBatchExecutor() 44 | router.Route(kmip.OperationActivate, kmipserver.HandleFunc(func(ctx context.Context, req *payloads.ActivateRequestPayload) (*payloads.ActivateResponsePayload, error) { 45 | return &payloads.ActivateResponsePayload{}, nil 46 | })) 47 | 48 | addr, ca := kmiptest.NewServer(t, router) 49 | 50 | client, err := kmipclient.Dial(addr, kmipclient.WithRootCAPem([]byte(ca)), kmipclient.WithMiddlewares( 51 | kmipclient.TimeoutMiddleware(5*time.Second), 52 | )) 53 | require.NoError(t, err) 54 | 55 | _, err = client.Activate("foobar").Exec() 56 | require.NoError(t, err) 57 | } 58 | 59 | func TestTimeoutMiddleware_ZeroTimeout(t *testing.T) { 60 | router := kmipserver.NewBatchExecutor() 61 | router.Route(kmip.OperationActivate, kmipserver.HandleFunc(func(ctx context.Context, req *payloads.ActivateRequestPayload) (*payloads.ActivateResponsePayload, error) { 62 | timer := time.NewTimer(1 * time.Second) 63 | defer timer.Stop() 64 | 65 | select { 66 | case <-ctx.Done(): 67 | t.Fatal("Timeout should not have happened") 68 | case <-timer.C: 69 | } 70 | 71 | return &payloads.ActivateResponsePayload{}, nil 72 | })) 73 | 74 | addr, ca := kmiptest.NewServer(t, router) 75 | 76 | client, err := kmipclient.Dial(addr, kmipclient.WithRootCAPem([]byte(ca)), kmipclient.WithMiddlewares( 77 | kmipclient.TimeoutMiddleware(0), 78 | )) 79 | require.NoError(t, err) 80 | 81 | _, err = client.Activate("foobar").Exec() 82 | require.NoError(t, err) 83 | } 84 | -------------------------------------------------------------------------------- /kmipclient/modify_attribute.go: -------------------------------------------------------------------------------- 1 | package kmipclient 2 | 3 | import ( 4 | "github.com/ovh/kmip-go" 5 | "github.com/ovh/kmip-go/payloads" 6 | ) 7 | 8 | func (c *Client) ModifyAttribute(id string, name kmip.AttributeName, value any) ExecModifyAttribute { 9 | return ExecModifyAttribute{ 10 | Executor[*payloads.ModifyAttributeRequestPayload, *payloads.ModifyAttributeResponsePayload]{ 11 | client: c, 12 | req: &payloads.ModifyAttributeRequestPayload{ 13 | UniqueIdentifier: id, 14 | Attribute: kmip.Attribute{AttributeName: name, AttributeValue: value}, 15 | }, 16 | }, 17 | } 18 | } 19 | 20 | type ExecModifyAttribute struct { 21 | Executor[*payloads.ModifyAttributeRequestPayload, *payloads.ModifyAttributeResponsePayload] 22 | } 23 | 24 | func (ex ExecModifyAttribute) WithIndex(index int32) ExecModifyAttribute { 25 | ex.req.Attribute.AttributeIndex = &index 26 | return ex 27 | } 28 | -------------------------------------------------------------------------------- /kmipclient/obtain_lease.go: -------------------------------------------------------------------------------- 1 | package kmipclient 2 | 3 | import "github.com/ovh/kmip-go/payloads" 4 | 5 | func (c *Client) ObtainLease(id string) ExecObtainLease { 6 | return ExecObtainLease{ 7 | client: c, 8 | req: &payloads.ObtainLeaseRequestPayload{ 9 | UniqueIdentifier: id, 10 | }, 11 | } 12 | } 13 | 14 | type ExecObtainLease = Executor[*payloads.ObtainLeaseRequestPayload, *payloads.ObtainLeaseResponsePayload] 15 | -------------------------------------------------------------------------------- /kmipclient/query.go: -------------------------------------------------------------------------------- 1 | package kmipclient 2 | 3 | import ( 4 | "github.com/ovh/kmip-go" 5 | "github.com/ovh/kmip-go/payloads" 6 | ) 7 | 8 | func (c *Client) Query() ExecQuery { 9 | return ExecQuery{ 10 | Executor[*payloads.QueryRequestPayload, *payloads.QueryResponsePayload]{ 11 | client: c, 12 | req: &payloads.QueryRequestPayload{}, 13 | }, 14 | } 15 | } 16 | 17 | type ExecQuery struct { 18 | Executor[*payloads.QueryRequestPayload, *payloads.QueryResponsePayload] 19 | } 20 | 21 | func (ex ExecQuery) Operations() ExecQuery { 22 | ex.req.QueryFunction = append(ex.req.QueryFunction, kmip.QueryFunctionOperations) 23 | return ex 24 | } 25 | func (ex ExecQuery) Objects() ExecQuery { 26 | ex.req.QueryFunction = append(ex.req.QueryFunction, kmip.QueryFunctionObjects) 27 | return ex 28 | } 29 | func (ex ExecQuery) ServerInformation() ExecQuery { 30 | ex.req.QueryFunction = append(ex.req.QueryFunction, kmip.QueryFunctionServerInformation) 31 | return ex 32 | } 33 | func (ex ExecQuery) ApplicationNamespaces() ExecQuery { 34 | ex.req.QueryFunction = append(ex.req.QueryFunction, kmip.QueryFunctionApplicationNamespaces) 35 | return ex 36 | } 37 | 38 | // KMIP 1.1. 39 | func (ex ExecQuery) ExtensionList() ExecQuery { 40 | //TODO: Check client version first 41 | ex.req.QueryFunction = append(ex.req.QueryFunction, kmip.QueryFunctionExtensionList) 42 | return ex 43 | } 44 | 45 | // KMIP 1.1. 46 | func (ex ExecQuery) ExtensionMap() ExecQuery { 47 | //TODO: Check client version first 48 | ex.req.QueryFunction = append(ex.req.QueryFunction, kmip.QueryFunctionExtensionMap) 49 | return ex 50 | } 51 | 52 | // KMIP 1.2. 53 | func (ex ExecQuery) AttestationTypes() ExecQuery { 54 | //TODO: Check client version first 55 | ex.req.QueryFunction = append(ex.req.QueryFunction, kmip.QueryFunctionAttestationTypes) 56 | return ex 57 | } 58 | 59 | // KMIP 1.3. 60 | func (ex ExecQuery) RNGs() ExecQuery { 61 | //TODO: Check client version first 62 | ex.req.QueryFunction = append(ex.req.QueryFunction, kmip.QueryFunctionRNGs) 63 | return ex 64 | } 65 | func (ex ExecQuery) Validations() ExecQuery { 66 | //TODO: Check client version first 67 | ex.req.QueryFunction = append(ex.req.QueryFunction, kmip.QueryFunctionValidations) 68 | return ex 69 | } 70 | func (ex ExecQuery) Profiles() ExecQuery { 71 | //TODO: Check client version first 72 | ex.req.QueryFunction = append(ex.req.QueryFunction, kmip.QueryFunctionProfiles) 73 | return ex 74 | } 75 | func (ex ExecQuery) Capabilities() ExecQuery { 76 | //TODO: Check client version first 77 | ex.req.QueryFunction = append(ex.req.QueryFunction, kmip.QueryFunctionCapabilities) 78 | return ex 79 | } 80 | func (ex ExecQuery) ClientRegistrationMethods() ExecQuery { 81 | //TODO: Check client version first 82 | ex.req.QueryFunction = append(ex.req.QueryFunction, kmip.QueryFunctionClientRegistrationMethods) 83 | return ex 84 | } 85 | 86 | func (ex ExecQuery) All() ExecQuery { 87 | return ex. 88 | Operations(). 89 | Objects(). 90 | ServerInformation(). 91 | ApplicationNamespaces(). 92 | ExtensionList(). 93 | ExtensionMap(). 94 | AttestationTypes(). 95 | RNGs(). 96 | Validations(). 97 | Profiles(). 98 | Capabilities(). 99 | ClientRegistrationMethods() 100 | } 101 | -------------------------------------------------------------------------------- /kmipclient/rekey.go: -------------------------------------------------------------------------------- 1 | package kmipclient 2 | 3 | import ( 4 | "time" 5 | 6 | "github.com/ovh/kmip-go" 7 | "github.com/ovh/kmip-go/payloads" 8 | ) 9 | 10 | func (c *Client) Rekey(id string) ExecRekey { 11 | return ExecRekey{ 12 | AttributeExecutor[*payloads.RekeyRequestPayload, *payloads.RekeyResponsePayload, ExecRekey]{ 13 | Executor[*payloads.RekeyRequestPayload, *payloads.RekeyResponsePayload]{ 14 | client: c, 15 | req: &payloads.RekeyRequestPayload{ 16 | UniqueIdentifier: id, 17 | TemplateAttribute: &kmip.TemplateAttribute{}, 18 | }, 19 | }, 20 | func(lrp **payloads.RekeyRequestPayload) *[]kmip.Attribute { 21 | return &(*lrp).TemplateAttribute.Attribute 22 | }, 23 | func(ae AttributeExecutor[*payloads.RekeyRequestPayload, *payloads.RekeyResponsePayload, ExecRekey]) ExecRekey { 24 | return ExecRekey{ae} 25 | }, 26 | }, 27 | } 28 | } 29 | 30 | type ExecRekey struct { 31 | AttributeExecutor[*payloads.RekeyRequestPayload, *payloads.RekeyResponsePayload, ExecRekey] 32 | } 33 | 34 | func (ex ExecRekey) WithOffset(offset time.Duration) ExecRekey { 35 | ex.req.Offset = &offset 36 | return ex 37 | } 38 | 39 | // Deprecated: Templates have been deprecated in KMIP v1.3. 40 | func (ex ExecRekey) WithTemplates(names ...kmip.Name) ExecRekey { 41 | ex.req.TemplateAttribute.Name = append(ex.req.TemplateAttribute.Name, names...) 42 | return ex 43 | } 44 | 45 | // Deprecated: Templates have been deprecated in KMIP v1.3. 46 | func (ex ExecRekey) WithTemplate(name string, nameType kmip.NameType) ExecRekey { 47 | ex.req.TemplateAttribute.Name = append(ex.req.TemplateAttribute.Name, kmip.Name{NameValue: name, NameType: nameType}) 48 | return ex 49 | } 50 | -------------------------------------------------------------------------------- /kmipclient/revoke.go: -------------------------------------------------------------------------------- 1 | package kmipclient 2 | 3 | import ( 4 | "time" 5 | 6 | "github.com/ovh/kmip-go" 7 | "github.com/ovh/kmip-go/payloads" 8 | ) 9 | 10 | func (c *Client) Revoke(id string) ExecRevoke { 11 | return ExecRevoke{ 12 | Executor[*payloads.RevokeRequestPayload, *payloads.RevokeResponsePayload]{ 13 | client: c, 14 | req: &payloads.RevokeRequestPayload{ 15 | UniqueIdentifier: id, 16 | RevocationReason: kmip.RevocationReason{ 17 | RevocationReasonCode: kmip.RevocationReasonCodeUnspecified, 18 | }, 19 | }, 20 | }, 21 | } 22 | } 23 | 24 | type ExecRevoke struct { 25 | Executor[*payloads.RevokeRequestPayload, *payloads.RevokeResponsePayload] 26 | } 27 | 28 | func (ex ExecRevoke) WithRevocationReasonCode(code kmip.RevocationReasonCode) ExecRevoke { 29 | ex.req.RevocationReason.RevocationReasonCode = code 30 | return ex 31 | } 32 | 33 | func (ex ExecRevoke) WithRevocationMessage(msg string) ExecRevoke { 34 | if msg != "" { 35 | ex.req.RevocationReason.RevocationMessage = msg 36 | } 37 | return ex 38 | } 39 | 40 | func (ex ExecRevoke) WithCompromiseOccurrenceDate(dt time.Time) ExecRevoke { 41 | ex.req.CompromiseOccurrenceDate = &dt 42 | return ex 43 | } 44 | -------------------------------------------------------------------------------- /kmipserver/context.go: -------------------------------------------------------------------------------- 1 | package kmipserver 2 | 3 | import ( 4 | "context" 5 | "crypto/tls" 6 | "crypto/x509" 7 | "errors" 8 | 9 | "github.com/ovh/kmip-go" 10 | ) 11 | 12 | type ctxConn struct{} 13 | 14 | type connData struct { 15 | remoteAddr string 16 | tlsConnState *tls.ConnectionState 17 | } 18 | 19 | func newConnContext(parent context.Context, remoteAddr string, tlsConnState *tls.ConnectionState) context.Context { 20 | data := connData{ 21 | remoteAddr: remoteAddr, 22 | tlsConnState: tlsConnState, 23 | } 24 | return context.WithValue(parent, ctxConn{}, data) 25 | } 26 | 27 | func RemoteAddr(ctx context.Context) string { 28 | v, _ := ctx.Value(ctxConn{}).(connData) 29 | return v.remoteAddr 30 | } 31 | 32 | func PeerCertificates(ctx context.Context) []*x509.Certificate { 33 | v, _ := ctx.Value(ctxConn{}).(connData) 34 | if v.tlsConnState == nil { 35 | return nil 36 | } 37 | return v.tlsConnState.PeerCertificates 38 | } 39 | 40 | type ctxBatch struct{} 41 | type batchData struct { 42 | idPlaceholder string 43 | header kmip.RequestHeader 44 | } 45 | 46 | func newBatchContext(parent context.Context, hdr kmip.RequestHeader) context.Context { 47 | bdata := &batchData{ 48 | header: hdr, 49 | } 50 | return context.WithValue(parent, ctxBatch{}, bdata) 51 | } 52 | 53 | func IdPlaceholder(ctx context.Context) string { 54 | bd, _ := ctx.Value(ctxBatch{}).(*batchData) 55 | if bd == nil { 56 | return "" 57 | } 58 | return bd.idPlaceholder 59 | } 60 | 61 | func GetIdOrPlaceholder(ctx context.Context, reqId string) (string, error) { 62 | if reqId != "" { 63 | return reqId, nil 64 | } 65 | if idp := IdPlaceholder(ctx); idp != "" { 66 | return idp, nil 67 | } 68 | //TODO: Proper error 69 | return "", errors.New("ID Placeholder is empty") 70 | } 71 | 72 | func SetIdPlaceholder(ctx context.Context, id string) { 73 | bd, _ := ctx.Value(ctxBatch{}).(*batchData) 74 | if bd == nil { 75 | panic("not in a batch context") 76 | } 77 | bd.idPlaceholder = id 78 | } 79 | 80 | func ClearIdPlaceholder(ctx context.Context) { 81 | bd, _ := ctx.Value(ctxBatch{}).(*batchData) 82 | if bd == nil { 83 | // Silently ignore if not in a batch context 84 | return 85 | } 86 | bd.idPlaceholder = "" 87 | } 88 | 89 | func GetProtocolVersion(ctx context.Context) kmip.ProtocolVersion { 90 | bd, _ := ctx.Value(ctxBatch{}).(*batchData) 91 | if bd == nil { 92 | panic("not in a batch context") 93 | } 94 | return bd.header.ProtocolVersion 95 | } 96 | -------------------------------------------------------------------------------- /kmipserver/errors.go: -------------------------------------------------------------------------------- 1 | package kmipserver 2 | 3 | import ( 4 | "context" 5 | "errors" 6 | "fmt" 7 | "time" 8 | 9 | "github.com/ovh/kmip-go" 10 | "github.com/ovh/kmip-go/ttlv" 11 | ) 12 | 13 | type Error struct { 14 | Reason kmip.ResultReason 15 | Message string 16 | } 17 | 18 | func (e Error) Error() string { 19 | if e.Message == "" { 20 | return ttlv.EnumStr(e.Reason) 21 | } 22 | return e.Message 23 | } 24 | 25 | func Errorf(reason kmip.ResultReason, format string, args ...any) error { 26 | return Error{ 27 | Reason: reason, 28 | Message: fmt.Sprintf(format, args...), 29 | } 30 | } 31 | 32 | var ( 33 | ErrOperationNotSupported = Errorf(kmip.ResultReasonOperationNotSupported, "Operation not supported") 34 | ErrFeatureNotSupported = Errorf(kmip.ResultReasonFeatureNotSupported, "Feature not supported") 35 | ErrMissingData = Errorf(kmip.ResultReasonMissingData, "Missing data") 36 | ErrItemNotFound = Errorf(kmip.ResultReasonItemNotFound, "Item not found") 37 | ErrPermissionDenied = Errorf(kmip.ResultReasonPermissionDenied, "Permission denied") 38 | ErrInvalidMessage = Errorf(kmip.ResultReasonInvalidMessage, "Invalid message") 39 | ErrInvalidField = Errorf(kmip.ResultReasonInvalidField, "Invalid field") 40 | ) 41 | 42 | func handleMessageError(ctx context.Context, req *kmip.RequestMessage, err error) *kmip.ResponseMessage { 43 | header := kmip.ResponseHeader{ 44 | ProtocolVersion: kmip.V1_0, 45 | TimeStamp: time.Now(), 46 | BatchCount: 1, 47 | } 48 | if req != nil { 49 | if req.Header.ProtocolVersion != (kmip.ProtocolVersion{}) { 50 | header.ProtocolVersion = req.Header.ProtocolVersion 51 | } 52 | header.ClientCorrelationValue = req.Header.ClientCorrelationValue 53 | header.ServerCorrelationValue = req.Header.ServerCorrelationValue 54 | } 55 | 56 | bi := kmip.ResponseBatchItem{} 57 | handleBatchItemError(ctx, &bi, err) 58 | 59 | return &kmip.ResponseMessage{ 60 | Header: header, 61 | BatchItem: []kmip.ResponseBatchItem{bi}, 62 | } 63 | } 64 | 65 | func handleBatchItemError(ctx context.Context, bi *kmip.ResponseBatchItem, err error) { 66 | if err == nil { 67 | return 68 | } 69 | // Always clear the ID placeholder on error 70 | //TODO: Double check against the KMIP specification about this 71 | ClearIdPlaceholder(ctx) 72 | bi.ResultStatus = kmip.ResultStatusOperationFailed 73 | var e Error 74 | if errors.As(err, &e) { 75 | bi.ResultReason = e.Reason 76 | } else { 77 | bi.ResultReason = kmip.ResultReasonGeneralFailure 78 | } 79 | //TODO: Do not return the error message if the error is not of type kmipserver.Error. Log it instead. 80 | bi.ResultMessage = err.Error() 81 | } 82 | -------------------------------------------------------------------------------- /kmipserver/http.go: -------------------------------------------------------------------------------- 1 | package kmipserver 2 | 3 | import ( 4 | "context" 5 | "io" 6 | "log/slog" 7 | "net/http" 8 | "strconv" 9 | 10 | "github.com/ovh/kmip-go" 11 | "github.com/ovh/kmip-go/ttlv" 12 | ) 13 | 14 | const DEFAULT_MAX_BODY_SIZE = 1 * 1024 * 1024 // Max body size is 1 MB 15 | 16 | func NewHTTPHandler(hdl RequestHandler) http.Handler { 17 | return httpHandler{inner: hdl} 18 | } 19 | 20 | type httpHandler struct { 21 | inner RequestHandler 22 | } 23 | 24 | func (hdl httpHandler) ServeHTTP(rw http.ResponseWriter, req *http.Request) { 25 | if body := req.Body; body != nil { 26 | defer body.Close() 27 | } 28 | if req.Method != http.MethodPost { 29 | rw.WriteHeader(http.StatusMethodNotAllowed) 30 | _, _ = io.WriteString(rw, "Only POST method are allowed") 31 | return 32 | } 33 | 34 | var unmarshaller func(data []byte, ptr any) error 35 | var marshaller func(data any) []byte 36 | switch req.Header.Get("Content-Type") { 37 | case "text/xml": 38 | unmarshaller = ttlv.UnmarshalXML 39 | marshaller = ttlv.MarshalXML 40 | case "application/json": 41 | unmarshaller = ttlv.UnmarshalJSON 42 | marshaller = ttlv.MarshalJSON 43 | case "application/octet-stream": 44 | unmarshaller = ttlv.UnmarshalTTLV 45 | marshaller = ttlv.MarshalTTLV 46 | default: 47 | rw.WriteHeader(http.StatusNotAcceptable) 48 | _, _ = io.WriteString(rw, "Unsupported Content-Type header") 49 | return 50 | } 51 | 52 | //TODO: Check the Accept header if present 53 | 54 | contentLen, err := strconv.Atoi(req.Header.Get("Content-Length")) 55 | if err != nil || contentLen <= 0 { 56 | rw.WriteHeader(http.StatusLengthRequired) 57 | return 58 | } 59 | if contentLen > DEFAULT_MAX_BODY_SIZE { 60 | rw.WriteHeader(http.StatusBadRequest) 61 | _, _ = io.WriteString(rw, "The request is too large") 62 | return 63 | } 64 | 65 | buf := make([]byte, contentLen) 66 | if _, err := io.ReadFull(req.Body, buf); err != nil { 67 | rw.WriteHeader(http.StatusBadRequest) 68 | _, _ = io.WriteString(rw, "Amount of data is lower than Content-Length") 69 | return 70 | } 71 | 72 | msg := kmip.RequestMessage{} 73 | var resp *kmip.ResponseMessage 74 | if err := unmarshaller(buf, &msg); err != nil { 75 | // If encoding error, send back the kmip error response 76 | resp = hdl.handleError(req.Context(), err, &msg) 77 | } else { 78 | ctx := newConnContext(req.Context(), req.RemoteAddr, req.TLS) 79 | resp = hdl.inner.HandleRequest(ctx, &msg) 80 | } 81 | 82 | buf = marshaller(resp) 83 | if _, err := rw.Write(buf); err != nil { 84 | //TODO: Use user provided logger maybe ? 85 | slog.Error("Failed to write HTTP response", "err", err) 86 | } 87 | } 88 | 89 | func (hdl httpHandler) handleError(ctx context.Context, err error, req *kmip.RequestMessage) *kmip.ResponseMessage { 90 | return handleMessageError(ctx, req, err) 91 | } 92 | -------------------------------------------------------------------------------- /kmipserver/http_test.go: -------------------------------------------------------------------------------- 1 | package kmipserver_test 2 | 3 | import ( 4 | "bytes" 5 | "context" 6 | "net/http" 7 | "net/http/httptest" 8 | "strconv" 9 | "testing" 10 | 11 | "github.com/ovh/kmip-go" 12 | "github.com/ovh/kmip-go/kmipserver" 13 | "github.com/ovh/kmip-go/payloads" 14 | "github.com/ovh/kmip-go/ttlv" 15 | 16 | "github.com/stretchr/testify/require" 17 | ) 18 | 19 | func TestHttpHandler_TTLV(t *testing.T) { 20 | mux := kmipserver.NewBatchExecutor() 21 | 22 | mux.Route(kmip.OperationActivate, kmipserver.HandleFunc(func(ctx context.Context, req *payloads.ActivateRequestPayload) (*payloads.ActivateResponsePayload, error) { 23 | require.NotEmpty(t, kmipserver.RemoteAddr(ctx)) 24 | // require.NotEmpty(t, kmipserver.PeerCertificates(ctx)) 25 | return &payloads.ActivateResponsePayload{UniqueIdentifier: req.UniqueIdentifier}, nil 26 | })) 27 | 28 | hdl := kmipserver.NewHTTPHandler(mux) 29 | 30 | for _, tc := range []struct { 31 | name string 32 | marshal func(data any) []byte 33 | unmarshal func(data []byte, ptr any) error 34 | mime string 35 | }{ 36 | {"TTLV", ttlv.MarshalTTLV, ttlv.UnmarshalTTLV, "application/octet-stream"}, 37 | {"XML", ttlv.MarshalXML, ttlv.UnmarshalXML, "text/xml"}, 38 | {"JSON", ttlv.MarshalJSON, ttlv.UnmarshalJSON, "application/json"}, 39 | } { 40 | t.Run(tc.name, func(t *testing.T) { 41 | uid := "foobar" 42 | req := kmip.NewRequestMessage(kmip.V1_4, &payloads.ActivateRequestPayload{ 43 | UniqueIdentifier: uid, 44 | }) 45 | body := tc.marshal(req) 46 | httpReq := httptest.NewRequest(http.MethodPost, "/kmip", bytes.NewReader(body)) 47 | httpReq.Header.Set("Content-Type", tc.mime) 48 | httpReq.Header.Set("Content-Length", strconv.Itoa(len(body))) 49 | rec := httptest.NewRecorder() 50 | hdl.ServeHTTP(rec, httpReq) 51 | 52 | require.Equal(t, http.StatusOK, rec.Code) 53 | resp := kmip.ResponseMessage{} 54 | err := tc.unmarshal(rec.Body.Bytes(), &resp) 55 | require.NoError(t, err) 56 | require.Len(t, resp.BatchItem, 1) 57 | require.Equal(t, uid, resp.BatchItem[0].ResponsePayload.(*payloads.ActivateResponsePayload).UniqueIdentifier) 58 | }) 59 | } 60 | 61 | } 62 | -------------------------------------------------------------------------------- /kmipserver/middlewares.go: -------------------------------------------------------------------------------- 1 | package kmipserver 2 | 3 | import ( 4 | "context" 5 | "fmt" 6 | "io" 7 | "time" 8 | 9 | "github.com/ovh/kmip-go" 10 | "github.com/ovh/kmip-go/ttlv" 11 | ) 12 | 13 | type Next func(context.Context, *kmip.RequestMessage) (*kmip.ResponseMessage, error) 14 | type Middleware func(next Next, ctx context.Context, msg *kmip.RequestMessage) (*kmip.ResponseMessage, error) 15 | 16 | func DebugMiddleware(out io.Writer, marshal func(data any) []byte) Middleware { 17 | if marshal == nil { 18 | marshal = ttlv.MarshalXML 19 | } 20 | return func(next Next, ctx context.Context, rm *kmip.RequestMessage) (*kmip.ResponseMessage, error) { 21 | if flushable, ok := out.(interface{ Flush() error }); ok { 22 | defer flushable.Flush() 23 | } 24 | fmt.Fprintln(out, "Request:") 25 | fmt.Fprintln(out, string(marshal(rm))) 26 | now := time.Now() 27 | resp, err := next(ctx, rm) 28 | if err != nil { 29 | fmt.Fprintf(out, "[ERROR] %s", err.Error()) 30 | return nil, err 31 | } 32 | fmt.Fprintf(out, "\nResponse in %s:\n", time.Since(now)) 33 | fmt.Fprintln(out, string(marshal(resp))) 34 | return resp, nil 35 | } 36 | } 37 | 38 | // type BatchItemMiddleware func(next func(ctx context.Context, bi *kmip.RequestBatchItem) *kmip.ResponseBatchItem, ctx context.Context, bi *kmip.RequestBatchItem) *kmip.ResponseBatchItem 39 | -------------------------------------------------------------------------------- /kmiptest/clientserver.go: -------------------------------------------------------------------------------- 1 | package kmiptest 2 | 3 | import ( 4 | "crypto/ecdsa" 5 | "crypto/elliptic" 6 | "crypto/rand" 7 | "crypto/tls" 8 | "crypto/x509" 9 | "encoding/pem" 10 | "errors" 11 | "math/big" 12 | "net" 13 | "os" 14 | "time" 15 | 16 | "github.com/ovh/kmip-go/kmipclient" 17 | "github.com/ovh/kmip-go/kmipserver" 18 | "github.com/ovh/kmip-go/ttlv" 19 | 20 | "github.com/google/uuid" 21 | "github.com/stretchr/testify/require" 22 | ) 23 | 24 | // TestingT is an interface wrapper around *testing.T. 25 | type TestingT interface { 26 | Errorf(format string, args ...any) 27 | FailNow() 28 | Cleanup(func()) 29 | } 30 | 31 | func NewServer(t TestingT, hdl kmipserver.RequestHandler) (addr, ca string) { 32 | caTpl := x509.Certificate{ 33 | KeyUsage: x509.KeyUsageDigitalSignature, 34 | ExtKeyUsage: []x509.ExtKeyUsage{x509.ExtKeyUsageServerAuth}, 35 | SerialNumber: big.NewInt(2), 36 | IPAddresses: []net.IP{net.IPv4(127, 0, 0, 1)}, 37 | NotAfter: time.Now().AddDate(1, 0, 0), 38 | } 39 | 40 | k, err := ecdsa.GenerateKey(elliptic.P256(), rand.Reader) 41 | require.NoError(t, err) 42 | cert, err := x509.CreateCertificate(rand.Reader, &caTpl, &caTpl, k.Public(), k) 43 | require.NoError(t, err) 44 | 45 | require.NoError(t, err) 46 | list, err := tls.Listen("tcp", "127.0.0.1:0", &tls.Config{ 47 | Certificates: []tls.Certificate{{Certificate: [][]byte{cert}, PrivateKey: k}}, 48 | MinVersion: tls.VersionTLS12, 49 | }) 50 | require.NoError(t, err) 51 | 52 | srv := kmipserver.NewServer(list, hdl) 53 | go func() { 54 | if err := srv.Serve(); err != nil && !errors.Is(err, kmipserver.ErrShutdown) { 55 | t.Errorf("server error: %w", err) 56 | } 57 | }() 58 | t.Cleanup(func() { 59 | if err := srv.Shutdown(); err != nil { 60 | t.Errorf("server failed to shutdown: %w", err) 61 | } 62 | }) 63 | 64 | pemCA := pem.EncodeToMemory(&pem.Block{Type: "CERTIFICATE", Bytes: cert}) 65 | return list.Addr().String(), string(pemCA) 66 | } 67 | 68 | func NewClientAndServer(t TestingT, hdl kmipserver.RequestHandler) *kmipclient.Client { 69 | addr, ca := NewServer(t, hdl) 70 | client, err := kmipclient.Dial(addr, kmipclient.WithRootCAPem([]byte(ca)), kmipclient.WithMiddlewares( 71 | kmipclient.CorrelationValueMiddleware(uuid.NewString), 72 | TestingMiddleware(t), 73 | kmipclient.DebugMiddleware(os.Stderr, ttlv.MarshalXML), 74 | )) 75 | require.NoError(t, err) 76 | require.NotNil(t, client) 77 | t.Cleanup(func() { 78 | _ = client.Close() 79 | }) 80 | return client 81 | } 82 | -------------------------------------------------------------------------------- /kmiptest/clientserver_test.go: -------------------------------------------------------------------------------- 1 | package kmiptest 2 | 3 | import ( 4 | "context" 5 | "testing" 6 | 7 | "github.com/ovh/kmip-go/kmipserver" 8 | "github.com/ovh/kmip-go/payloads" 9 | 10 | "github.com/stretchr/testify/require" 11 | ) 12 | 13 | func TestClientServer(t *testing.T) { 14 | client := NewClientAndServer(t, kmipserver.NewBatchExecutor()) 15 | resp, err := client.Request(context.Background(), &payloads.DiscoverVersionsRequestPayload{}) 16 | require.NoError(t, err) 17 | require.NotNil(t, resp) 18 | } 19 | -------------------------------------------------------------------------------- /kmiptest/middlewares.go: -------------------------------------------------------------------------------- 1 | package kmiptest 2 | 3 | import ( 4 | "context" 5 | 6 | "github.com/ovh/kmip-go" 7 | "github.com/ovh/kmip-go/kmipclient" 8 | "github.com/ovh/kmip-go/ttlv" 9 | 10 | "github.com/stretchr/testify/require" 11 | ) 12 | 13 | func TestingMiddleware(t TestingT) kmipclient.Middleware { 14 | return func(next kmipclient.Next, ctx context.Context, rm *kmip.RequestMessage) (*kmip.ResponseMessage, error) { 15 | resp, err := next(ctx, rm) 16 | if err != nil { 17 | return resp, err 18 | } 19 | 20 | rq := &kmip.RequestMessage{} 21 | err = ttlv.UnmarshalXML(ttlv.MarshalXML(rm), &rq) 22 | require.NoError(t, err, "Could not unmarshal XML request") 23 | require.EqualValues(t, ttlv.MarshalTTLV(rm), ttlv.MarshalTTLV(rq), "XML requests not equal") 24 | 25 | rq = &kmip.RequestMessage{} 26 | err = ttlv.UnmarshalJSON(ttlv.MarshalJSON(rm), &rq) 27 | require.NoError(t, err, "Could not unmarshal JSON request") 28 | require.EqualValues(t, ttlv.MarshalTTLV(rm), ttlv.MarshalTTLV(rq), "JSON requests not equal") 29 | 30 | rr := &kmip.ResponseMessage{} 31 | err = ttlv.UnmarshalXML(ttlv.MarshalXML(resp), &rr) 32 | require.NoError(t, err, "Could not unmarshal XML response") 33 | require.EqualValues(t, ttlv.MarshalTTLV(resp), ttlv.MarshalTTLV(rr), "XML responses not equal") 34 | 35 | rr = &kmip.ResponseMessage{} 36 | err = ttlv.UnmarshalJSON(ttlv.MarshalJSON(resp), &rr) 37 | require.NoError(t, err, "Could not unmarshal JSON responses") 38 | require.EqualValues(t, ttlv.MarshalTTLV(resp), ttlv.MarshalTTLV(rr), "JSON responses not equal") 39 | 40 | return resp, nil 41 | } 42 | } 43 | -------------------------------------------------------------------------------- /kmiptest/oasis_tc.go: -------------------------------------------------------------------------------- 1 | package kmiptest 2 | 3 | import ( 4 | "encoding/xml" 5 | "os" 6 | "regexp" 7 | "strconv" 8 | "time" 9 | 10 | "github.com/ovh/kmip-go" 11 | "github.com/ovh/kmip-go/ttlv" 12 | 13 | "github.com/stretchr/testify/require" 14 | ) 15 | 16 | var ( 17 | nowRe = regexp.MustCompile(`"\$NOW((\-|\+)\d+)?"`) 18 | varRe = regexp.MustCompile(`"\$[A-Za-z0-9_]+"`) 19 | ) 20 | 21 | type TestCase struct { 22 | RequestMessage kmip.RequestMessage 23 | ResponseMessage kmip.ResponseMessage 24 | } 25 | 26 | type TestSuite struct { 27 | Version string 28 | File string 29 | TestCases []TestCase 30 | } 31 | 32 | func (ts TestSuite) Name() string { 33 | return ts.Version + "/" + ts.File 34 | } 35 | 36 | func (ts *TestSuite) DecodeTTLV(d *ttlv.Decoder) error { 37 | for d.Tag() == kmip.TagRequestMessage { 38 | tc := TestCase{} 39 | if err := d.TagAny(kmip.TagRequestMessage, &tc.RequestMessage); err != nil { 40 | return err 41 | } 42 | if err := d.TagAny(kmip.TagResponseMessage, &tc.ResponseMessage); err != nil { 43 | return err 44 | } 45 | ts.TestCases = append(ts.TestCases, tc) 46 | } 47 | return nil 48 | } 49 | 50 | func (ts *TestSuite) UnmarshalXML(d *xml.Decoder, start xml.StartElement) error { 51 | dec, err := ttlv.NewXMLFromDecoder(d) 52 | if err != nil { 53 | return err 54 | } 55 | return ts.DecodeTTLV(&dec) 56 | } 57 | 58 | func ListTestSuites(t TestingT, root, version string) []string { 59 | direntry, err := os.ReadDir(root + "/" + version) 60 | require.NoError(t, err) 61 | 62 | names := []string{} 63 | for _, e := range direntry { 64 | if e.IsDir() { 65 | continue 66 | } 67 | names = append(names, e.Name()) 68 | } 69 | return names 70 | } 71 | 72 | func LoadTestSuite(t TestingT, root, version, file string) TestSuite { 73 | f, err := os.ReadFile(root + "/" + version + "/" + file) 74 | require.NoError(t, err) 75 | 76 | f = nowRe.ReplaceAllFunc(f, func(b []byte) []byte { 77 | now := time.Now() 78 | offset, _ := strconv.ParseInt(string(b[4:]), 10, 64) 79 | t := now.Add(time.Duration(offset) * time.Second) 80 | res := []byte{'"'} 81 | res = t.AppendFormat(res, time.RFC3339) 82 | return append(res, '"') 83 | }) 84 | f = varRe.ReplaceAll(f, []byte(`"DEADBEEFCAFE"`)) 85 | 86 | ts := TestSuite{ 87 | Version: version, 88 | File: file, 89 | } 90 | err = xml.Unmarshal(f, &ts) 91 | require.NoError(t, err) 92 | return ts 93 | } 94 | 95 | var TestCaseVersions = []string{"v1.0", "v1.1", "v1.2", "v1.3", "v1.4"} 96 | 97 | var UnsupportedTestCases = []string{ 98 | "v1.0/TC_134_10_Register_Key_Pair_Certify_and_Re_certify_Public_Key.xml", 99 | "v1.0/TC_NP_1_10_Put.xml", 100 | "v1.0/TC_NP_2_10_Notify_Put.xml", 101 | "v1.1/TC_134_11_Register_Key_Pair_Certify_and_Re_certify_Public_Key.xml", 102 | "v1.1/TC_NP_1_11_Put.xml", 103 | "v1.1/TC_NP_2_11_Notify_Put.xml", 104 | "v1.2/TC_134_12_Register_Key_Pair_Certify_and_Re_certify_Public_Key.xml", 105 | "v1.2/TC_NP_1_12_Put.xml", 106 | "v1.2/TC_NP_2_12_Notify_Put.xml", 107 | "v1.2/TC_SJ_1_12_Create_and_SplitJoin.xml", 108 | "v1.2/TC_SJ_2_12_Register_and_Split_Join.xml", 109 | "v1.2/TC_SJ_3_12_Join_Split_Keys.xml", 110 | "v1.2/TC_SJ_4_12_Register_and_Split_Join_with_XOR.xml", 111 | "v1.4/TC-DERIVEKEY-1-10.xml", 112 | "v1.4/TC-NP-1-14.xml", 113 | "v1.4/TC-SJ-1-14.xml", 114 | } 115 | -------------------------------------------------------------------------------- /kmiptest/oasis_tc_test.go: -------------------------------------------------------------------------------- 1 | package kmiptest 2 | 3 | import ( 4 | "slices" 5 | "testing" 6 | 7 | "github.com/stretchr/testify/require" 8 | ) 9 | 10 | func TestLoadTestSuites(t *testing.T) { 11 | for _, vers := range TestCaseVersions { 12 | suites := ListTestSuites(t, "testdata", vers) 13 | for _, e := range suites { 14 | name := vers + "/" + e 15 | if slices.Contains(UnsupportedTestCases, name) { 16 | continue 17 | } 18 | 19 | ts := LoadTestSuite(t, "testdata", vers, e) 20 | require.NotEmpty(t, ts.TestCases) 21 | require.NotEmpty(t, ts.TestCases) 22 | } 23 | } 24 | } 25 | -------------------------------------------------------------------------------- /kmiptest/testdata/v1.0/OMOS_M_1_10_small_opaque_object.xml: -------------------------------------------------------------------------------- 1 | 2 | 5 | # TIME 0 6 | 7 | 8 | 9 | 10 | 11 | 12 | 13 | 14 | 15 | 16 | 17 | 18 | 19 | 20 | 21 | 22 | 23 | 24 | 25 | 26 | 27 | 28 | 29 | 30 | 31 | 32 | 33 | 34 | 35 | 36 | 37 | 38 | 39 | 40 | 41 | 42 | 43 | 44 | 45 | 46 | 47 | 48 | 49 | 50 | # TIME 1 51 | 52 | 53 | 54 | 55 | 56 | 57 | 58 | 59 | 60 | 61 | 62 | 63 | 64 | 65 | 66 | 67 | 68 | 69 | 70 | 71 | 72 | 73 | 74 | 75 | 76 | 77 | 78 | 79 | 80 | 81 | 82 | 83 | -------------------------------------------------------------------------------- /kmiptest/testdata/v1.0/SASED_M_1_10_storage_array_config.xml: -------------------------------------------------------------------------------- 1 | 2 | 5 | # TIME 0 6 | 7 | 8 | 9 | 10 | 11 | 12 | 13 | 14 | 15 | 16 | 17 | 18 | 19 | 20 | 21 | 22 | 0018 0019 0020 0021 0022 0023 0024 0025 0026 0027 0028 0029 0030 0031 0032 0033 0034 0035 0036 23 | 0037 0038 0039 0040 0041 0042 0043 0044 0045 0046 24 | 25 | 26 | 27 | 28 | 29 | 30 | 31 | 32 | 33 | 34 | 35 | 36 | 37 | 38 | 39 | 40 | 41 | 42 | 43 | 44 | 45 | 46 | 47 | 48 | 49 | 50 | 51 | 52 | 53 | -------------------------------------------------------------------------------- /kmiptest/testdata/v1.0/SKFF_M_1_10_SymmetricKeyFoundryBasic_1.xml: -------------------------------------------------------------------------------- 1 | 2 | 4 | 5 | # TIME 0 6 | 7 | 8 | 9 | 10 | 11 | 12 | 13 | 14 | 15 | 16 | 17 | 18 | 19 | 20 | 21 | 22 | 23 | 24 | 25 | 26 | 27 | 28 | 29 | 30 | 31 | 32 | 33 | 34 | 35 | 36 | 37 | 38 | 39 | 40 | 41 | 42 | 43 | 44 | 45 | 46 | 47 | 48 | 49 | 50 | 51 | 52 | # TIME 1 53 | 54 | 55 | 56 | 57 | 58 | 59 | 60 | 61 | 62 | 63 | 64 | 65 | 66 | 67 | 68 | 69 | 70 | 71 | 72 | 73 | 74 | 75 | 76 | 77 | 78 | 79 | 80 | 81 | 82 | 83 | 84 | 85 | -------------------------------------------------------------------------------- /kmiptest/testdata/v1.0/SKFF_M_3_10_SymmetricKeyFoundryBasic_3.xml: -------------------------------------------------------------------------------- 1 | 2 | 5 | # TIME 0 6 | 7 | 8 | 9 | 10 | 11 | 12 | 13 | 14 | 15 | 16 | 17 | 18 | 19 | 20 | 21 | 22 | 23 | 24 | 25 | 26 | 27 | 28 | 29 | 30 | 31 | 32 | 33 | 34 | 35 | 36 | 37 | 38 | 39 | 40 | 41 | 42 | 43 | 44 | 45 | 46 | 47 | 48 | 49 | 50 | 51 | 52 | # TIME 1 53 | 54 | 55 | 56 | 57 | 58 | 59 | 60 | 61 | 62 | 63 | 64 | 65 | 66 | 67 | 68 | 69 | 70 | 71 | 72 | 73 | 74 | 75 | 76 | 77 | 78 | 79 | 80 | 81 | 82 | 83 | 84 | 85 | -------------------------------------------------------------------------------- /kmiptest/testdata/v1.0/SKFF_M_4_10_SymmetricKeyFoundryBasic_4.xml: -------------------------------------------------------------------------------- 1 | 2 | 5 | # TIME 0 6 | 7 | 8 | 9 | 10 | 11 | 12 | 13 | 14 | 15 | 16 | 17 | 18 | 19 | 20 | 21 | 22 | 23 | 24 | 25 | 26 | 27 | 28 | 29 | 30 | 31 | 32 | 33 | 34 | 35 | 36 | 37 | 38 | 39 | 40 | 41 | 42 | 43 | 44 | 45 | 46 | 47 | 48 | 49 | 50 | 51 | 52 | # TIME 1 53 | 54 | 55 | 56 | 57 | 58 | 59 | 60 | 61 | 62 | 63 | 64 | 65 | 66 | 67 | 68 | 69 | 70 | 71 | 72 | 73 | 74 | 75 | 76 | 77 | 78 | 79 | 80 | 81 | 82 | 83 | 84 | 85 | -------------------------------------------------------------------------------- /kmiptest/testdata/v1.0/SKFF_O_1_10_SymmetricKeyOptional_1.xml: -------------------------------------------------------------------------------- 1 | 2 | 5 | # TIME 0 6 | 7 | 8 | 9 | 10 | 11 | 12 | 13 | 14 | 15 | 16 | 17 | 18 | 19 | 20 | 21 | 22 | 23 | 24 | 25 | 26 | 27 | 28 | 29 | 30 | 31 | 32 | 33 | 34 | 35 | 36 | 37 | 38 | 39 | 40 | 41 | 42 | 43 | 44 | 45 | 46 | 47 | 48 | 49 | 50 | 51 | 52 | # TIME 1 53 | 54 | 55 | 56 | 57 | 58 | 59 | 60 | 61 | 62 | 63 | 64 | 65 | 66 | 67 | 68 | 69 | 70 | 71 | 72 | 73 | 74 | 75 | 76 | 77 | 78 | 79 | 80 | 81 | 82 | 83 | 84 | 85 | -------------------------------------------------------------------------------- /kmiptest/testdata/v1.0/SKFF_O_4_10_SymmetricKeyOptional_4.xml: -------------------------------------------------------------------------------- 1 | 2 | 5 | # TIME 0 6 | 7 | 8 | 9 | 10 | 11 | 12 | 13 | 14 | 15 | 16 | 17 | 18 | 19 | 20 | 21 | 22 | 23 | 24 | 25 | 26 | 27 | 28 | 29 | 30 | 31 | 32 | 33 | 34 | 35 | 36 | 37 | 38 | 39 | 40 | 41 | 42 | 43 | 44 | 45 | 46 | 47 | 48 | 49 | 50 | 51 | 52 | # TIME 1 53 | 54 | 55 | 56 | 57 | 58 | 59 | 60 | 61 | 62 | 63 | 64 | 65 | 66 | 67 | 68 | 69 | 70 | 71 | 72 | 73 | 74 | 75 | 76 | 77 | 78 | 79 | 80 | 81 | 82 | 83 | 84 | 85 | -------------------------------------------------------------------------------- /kmiptest/testdata/v1.0/TC_311_10_Create_Destroy.xml: -------------------------------------------------------------------------------- 1 | 2 | 5 | 6 | # TIME 0 7 | 8 | 9 | 10 | 11 | 12 | 13 | 14 | 15 | 16 | 17 | 18 | 19 | 20 | 21 | 22 | 23 | 24 | 25 | 26 | 27 | 28 | 29 | 30 | 31 | 32 | 33 | 34 | 35 | 36 | 37 | 38 | 39 | 40 | 41 | 42 | 43 | 44 | 45 | 46 | 47 | 48 | 49 | 50 | 51 | 52 | 53 | 54 | # TIME 1 55 | 56 | 57 | 58 | 59 | 60 | 61 | 62 | 63 | 64 | 65 | 66 | 67 | 68 | 69 | 70 | 71 | 72 | 73 | 74 | 75 | 76 | 77 | 78 | 79 | 80 | 81 | 82 | 83 | 84 | 85 | 86 | 87 | -------------------------------------------------------------------------------- /kmiptest/testdata/v1.0/TC_315_10_Register_Destroy_Secret_Data.xml: -------------------------------------------------------------------------------- 1 | 2 | 6 | 7 | # TIME 0 8 | 9 | 10 | 11 | 12 | 13 | 14 | 15 | 16 | 17 | 18 | 19 | 20 | 21 | 22 | 23 | 24 | 25 | 26 | 27 | 28 | 29 | 30 | 31 | 32 | 33 | 34 | 35 | 36 | 37 | 38 | 39 | 40 | 41 | 42 | 43 | 44 | 45 | 46 | 47 | 48 | 49 | 50 | 51 | 52 | 53 | 54 | 55 | # TIME 1 56 | 57 | 58 | 59 | 60 | 61 | 62 | 63 | 64 | 65 | 66 | 67 | 68 | 69 | 70 | 71 | 72 | 73 | 74 | 75 | 76 | 77 | 78 | 79 | 80 | 81 | 82 | 83 | 84 | 85 | 86 | 87 | 88 | -------------------------------------------------------------------------------- /kmiptest/testdata/v1.0/TC_72_10_Unrecognized_Message_Extension_with_Criticality_Indicator_True.xml: -------------------------------------------------------------------------------- 1 | 2 | 6 | 7 | # TIME 0 8 | 9 | 10 | 11 | 12 | 13 | 14 | 15 | 16 | 17 | 18 | 19 | 20 | 21 | 22 | 23 | 24 | 25 | 26 | 27 | 28 | 29 | 30 | 31 | 32 | 33 | 34 | 35 | 36 | 37 | 38 | 39 | 40 | 41 | 42 | 43 | 44 | 45 | 46 | 47 | 48 | 49 | 50 | 51 | 52 | 53 | 54 | 55 | 56 | 57 | 58 | 59 | 60 | 61 | 62 | 63 | 64 | -------------------------------------------------------------------------------- /kmiptest/testdata/v1.0/TL_M_1_10_tape_library_config.xml: -------------------------------------------------------------------------------- 1 | 2 | 8 | # TIME 0 9 | 10 | 11 | 12 | 13 | 14 | 15 | 16 | 17 | 18 | 19 | 20 | 21 | 22 | 23 | 24 | 25 | 26 | 27 | 28 | 29 | 30 | 31 | 32 | 33 | 34 | 35 | 36 | 37 | 38 | 39 | 40 | 41 | 42 | 43 | 44 | 45 | 46 | 47 | 48 | 49 | 50 | 51 | 52 | 53 | 54 | 55 | 56 | 57 | 58 | 59 | 60 | 61 | 62 | 63 | 64 | -------------------------------------------------------------------------------- /kmiptest/testdata/v1.1/TC_122_11_Query_Vendor_Extensions.xml: -------------------------------------------------------------------------------- 1 | 2 | 4 | 5 | # TIME 0 6 | 7 | 8 | 9 | 10 | 11 | 12 | 13 | 14 | 15 | 16 | 17 | 18 | 19 | 20 | 21 | 22 | 23 | 24 | 25 | 26 | 27 | 28 | 29 | 30 | 31 | 32 | 33 | 34 | 35 | 36 | 37 | 38 | 39 | 40 | 41 | 42 | 43 | # TIME 1 44 | 45 | 46 | 47 | 48 | 49 | 50 | 51 | 52 | 53 | 54 | 55 | 56 | 57 | 58 | 59 | 60 | 61 | 62 | 63 | 64 | 65 | 66 | 67 | 68 | 69 | 70 | 71 | 72 | 73 | 74 | 75 | 76 | 77 | 78 | 79 | 80 | 81 | 82 | 83 | 84 | 85 | -------------------------------------------------------------------------------- /kmiptest/testdata/v1.1/TC_315_11_Register_Destroy_Secret_Data.xml: -------------------------------------------------------------------------------- 1 | 2 | 5 | 6 | # TIME 0 7 | 8 | 9 | 10 | 11 | 12 | 13 | 14 | 15 | 16 | 17 | 18 | 19 | 20 | 21 | 22 | 23 | 24 | 25 | 26 | 27 | 28 | 29 | 30 | 31 | 32 | 33 | 34 | 35 | 36 | 37 | 38 | 39 | 40 | 41 | 42 | 43 | 44 | 45 | 46 | 47 | 48 | 49 | 50 | 51 | 52 | 53 | 54 | 55 | 56 | 57 | 58 | # TIME 1 59 | 60 | 61 | 62 | 63 | 64 | 65 | 66 | 67 | 68 | 69 | 70 | 71 | 72 | 73 | 74 | 75 | 76 | 77 | 78 | 79 | 80 | 81 | 82 | 83 | 84 | 85 | 86 | 87 | 88 | 89 | 90 | 91 | -------------------------------------------------------------------------------- /kmiptest/testdata/v1.1/TC_72_11_Unrecognized_Message_Extension_with_Criticality_Indicator_True.xml: -------------------------------------------------------------------------------- 1 | 2 | 6 | 7 | # TIME 0 8 | 9 | 10 | 11 | 12 | 13 | 14 | 15 | 16 | 17 | 18 | 19 | 20 | 21 | 22 | 23 | 24 | 25 | 26 | 27 | 28 | 29 | 30 | 31 | 32 | 33 | 34 | 35 | 36 | 37 | 38 | 39 | 40 | 41 | 42 | 43 | 44 | 45 | 46 | 47 | 48 | 49 | 50 | 51 | 52 | 53 | 54 | 55 | 56 | 57 | 58 | 59 | 60 | 61 | 62 | 63 | 64 | -------------------------------------------------------------------------------- /kmiptest/testdata/v1.2/CS_BC_M_7_12_Encrypt_with_All_Fields.xml: -------------------------------------------------------------------------------- 1 | 2 | 5 | 6 | # TIME 0 7 | 8 | 9 | 10 | 11 | 12 | 13 | 14 | 15 | 16 | 17 | 18 | 19 | 20 | 21 | 22 | 23 | 24 | 25 | 26 | 27 | 28 | 29 | 30 | 31 | 32 | 33 | 34 | 35 | 36 | 37 | 38 | 39 | 40 | 41 | 42 | 43 | 44 | 45 | 46 | 47 | 48 | 49 | 50 | -------------------------------------------------------------------------------- /kmiptest/testdata/v1.2/TC_122_12_Query_Vendor_Extensions.xml: -------------------------------------------------------------------------------- 1 | 2 | 4 | 5 | # TIME 0 6 | 7 | 8 | 9 | 10 | 11 | 12 | 13 | 14 | 15 | 16 | 17 | 18 | 19 | 20 | 21 | 22 | 23 | 24 | 25 | 26 | 27 | 28 | 29 | 30 | 31 | 32 | 33 | 34 | 35 | 36 | 37 | 38 | 39 | 40 | 41 | 42 | 43 | # TIME 1 44 | 45 | 46 | 47 | 48 | 49 | 50 | 51 | 52 | 53 | 54 | 55 | 56 | 57 | 58 | 59 | 60 | 61 | 62 | 63 | 64 | 65 | 66 | 67 | 68 | 69 | 70 | 71 | 72 | 73 | 74 | 75 | 76 | 77 | 78 | 79 | 80 | 81 | 82 | 83 | 84 | 85 | -------------------------------------------------------------------------------- /kmiptest/testdata/v1.2/TC_315_12_Register_Destroy_Secret_Data.xml: -------------------------------------------------------------------------------- 1 | 2 | 5 | 6 | # TIME 0 7 | 8 | 9 | 10 | 11 | 12 | 13 | 14 | 15 | 16 | 17 | 18 | 19 | 20 | 21 | 22 | 23 | 24 | 25 | 26 | 27 | 28 | 29 | 30 | 31 | 32 | 33 | 34 | 35 | 36 | 37 | 38 | 39 | 40 | 41 | 42 | 43 | 44 | 45 | 46 | 47 | 48 | 49 | 50 | 51 | 52 | 53 | 54 | 55 | 56 | 57 | 58 | # TIME 1 59 | 60 | 61 | 62 | 63 | 64 | 65 | 66 | 67 | 68 | 69 | 70 | 71 | 72 | 73 | 74 | 75 | 76 | 77 | 78 | 79 | 80 | 81 | 82 | 83 | 84 | 85 | 86 | 87 | 88 | 89 | 90 | 91 | -------------------------------------------------------------------------------- /kmiptest/testdata/v1.2/TC_72_12_Unrecognized_Message_Extension_with_Criticality_Indicator_True.xml: -------------------------------------------------------------------------------- 1 | 2 | 6 | 7 | # TIME 0 8 | 9 | 10 | 11 | 12 | 13 | 14 | 15 | 16 | 17 | 18 | 19 | 20 | 21 | 22 | 23 | 24 | 25 | 26 | 27 | 28 | 29 | 30 | 31 | 32 | 33 | 34 | 35 | 36 | 37 | 38 | 39 | 40 | 41 | 42 | 43 | 44 | 45 | 46 | 47 | 48 | 49 | 50 | 51 | 52 | 53 | 54 | 55 | 56 | 57 | 58 | 59 | 60 | 61 | 62 | 63 | 64 | -------------------------------------------------------------------------------- /kmiptest/testdata/v1.3/CS-AC-M-7-13.xml: -------------------------------------------------------------------------------- 1 | 2 | 12 | 13 | 14 | 15 | 16 | 17 | 18 | 19 | 20 | 21 | 22 | 23 | 24 | 25 | 26 | 27 | 28 | 29 | 30 | 31 | 32 | 33 | 34 | 35 | 36 | 37 | 38 | 39 | 40 | 41 | 42 | 43 | 44 | 45 | 46 | 47 | 48 | 49 | 50 | 51 | 52 | 53 | 54 | 55 | 56 | 57 | 58 | 59 | 60 | 61 | 62 | 63 | 64 | 65 | 66 | 67 | 68 | 69 | 70 | 71 | 72 | 73 | 74 | 75 | 76 | 77 | 78 | 79 | 80 | 81 | 82 | 83 | 84 | 85 | 86 | 87 | 88 | -------------------------------------------------------------------------------- /kmiptest/testdata/v1.3/CS-RNG-M-1-13.xml: -------------------------------------------------------------------------------- 1 | 2 | 12 | 13 | 14 | 15 | 16 | 17 | 18 | 19 | 20 | 21 | 22 | 23 | 24 | 25 | 26 | 27 | 28 | 29 | 30 | 31 | 32 | 33 | 34 | 35 | 36 | 37 | 38 | 39 | 40 | 41 | 42 | 43 | 44 | 45 | 46 | 47 | 48 | -------------------------------------------------------------------------------- /kmiptest/testdata/v1.3/CS-RNG-O-1-13.xml: -------------------------------------------------------------------------------- 1 | 2 | 12 | 13 | 14 | 15 | 16 | 17 | 18 | 19 | 20 | 21 | 22 | 23 | 24 | 25 | 26 | 27 | 28 | 29 | 30 | 31 | 32 | 33 | 34 | 35 | 36 | 37 | 38 | 39 | 40 | 41 | 42 | 43 | 44 | 45 | 46 | 47 | 48 | -------------------------------------------------------------------------------- /kmiptest/testdata/v1.3/CS-RNG-O-2-13.xml: -------------------------------------------------------------------------------- 1 | 2 | 12 | 13 | 14 | 15 | 16 | 17 | 18 | 19 | 20 | 21 | 22 | 23 | 24 | 25 | 26 | 27 | 28 | 29 | 30 | 31 | 32 | 33 | 34 | 35 | 36 | 37 | 38 | 39 | 40 | 41 | 42 | 43 | 44 | 45 | 46 | 47 | 48 | -------------------------------------------------------------------------------- /kmiptest/testdata/v1.3/CS-RNG-O-3-13.xml: -------------------------------------------------------------------------------- 1 | 2 | 12 | 13 | 14 | 15 | 16 | 17 | 18 | 19 | 20 | 21 | 22 | 23 | 24 | 25 | 26 | 27 | 28 | 29 | 30 | 31 | 32 | 33 | 34 | 35 | 36 | 37 | 38 | 39 | 40 | 41 | 42 | 43 | 44 | 45 | 46 | 47 | 48 | -------------------------------------------------------------------------------- /kmiptest/testdata/v1.3/CS-RNG-O-4-13.xml: -------------------------------------------------------------------------------- 1 | 2 | 12 | 13 | 14 | 15 | 16 | 17 | 18 | 19 | 20 | 21 | 22 | 23 | 24 | 25 | 26 | 27 | 28 | 29 | 30 | 31 | 32 | 33 | 34 | 35 | 36 | 37 | 38 | 39 | 40 | 41 | 42 | 43 | 44 | 45 | 46 | 47 | -------------------------------------------------------------------------------- /kmiptest/testdata/v1.3/SASED-M-1-13.xml: -------------------------------------------------------------------------------- 1 | 2 | 12 | 13 | 14 | 15 | 16 | 17 | 18 | 19 | 20 | 21 | 22 | 23 | 24 | 25 | 26 | 27 | 28 | 29 | 30 | 31 | 32 | 33 | 34 | 35 | 36 | 37 | 38 | 39 | 40 | 41 | 42 | 43 | 44 | 45 | 46 | 47 | 48 | 49 | 50 | 51 | 52 | 53 | 54 | 55 | 56 | 57 | 58 | 59 | 60 | 61 | 62 | -------------------------------------------------------------------------------- /kmiptest/testdata/v1.3/TL-M-1-13.xml: -------------------------------------------------------------------------------- 1 | 2 | 12 | 13 | 14 | 15 | 16 | 17 | 18 | 19 | 20 | 21 | 22 | 23 | 24 | 25 | 26 | 27 | 28 | 29 | 30 | 31 | 32 | 33 | 34 | 35 | 36 | 37 | 38 | 39 | 40 | 41 | 42 | 43 | 44 | 45 | 46 | 47 | 48 | 49 | 50 | 51 | 52 | 53 | 54 | 55 | 56 | 57 | 58 | 59 | 60 | 61 | 62 | 63 | 64 | 65 | 66 | 67 | 68 | 69 | 70 | 71 | -------------------------------------------------------------------------------- /kmiptest/testdata/v1.4/CS-AC-M-7-14.xml: -------------------------------------------------------------------------------- 1 | 2 | 3 | 12 | 13 | 14 | 15 | 16 | 17 | 18 | 19 | 20 | 21 | 22 | 23 | 24 | 25 | 26 | 27 | 28 | 29 | 30 | 31 | 32 | 33 | 34 | 35 | 36 | 37 | 38 | 39 | 40 | 41 | 42 | 43 | 44 | 45 | 46 | 47 | 48 | 49 | 50 | 51 | 52 | 53 | 54 | 55 | 56 | 57 | 58 | 59 | 60 | 61 | 62 | 63 | 64 | 65 | 66 | 67 | 68 | 69 | 70 | 71 | 72 | 73 | 74 | 75 | 76 | 77 | 78 | 79 | 80 | 81 | 82 | 83 | 84 | 85 | 86 | 87 | -------------------------------------------------------------------------------- /kmiptest/testdata/v1.4/CS-RNG-M-1-14.xml: -------------------------------------------------------------------------------- 1 | 2 | 3 | 12 | 13 | 14 | 15 | 16 | 17 | 18 | 19 | 20 | 21 | 22 | 23 | 24 | 25 | 26 | 27 | 28 | 29 | 30 | 31 | 32 | 33 | 34 | 35 | 36 | 37 | 38 | 39 | 40 | 41 | 42 | 43 | 44 | 45 | 46 | 47 | -------------------------------------------------------------------------------- /kmiptest/testdata/v1.4/CS-RNG-O-1-14.xml: -------------------------------------------------------------------------------- 1 | 2 | 3 | 12 | 13 | 14 | 15 | 16 | 17 | 18 | 19 | 20 | 21 | 22 | 23 | 24 | 25 | 26 | 27 | 28 | 29 | 30 | 31 | 32 | 33 | 34 | 35 | 36 | 37 | 38 | 39 | 40 | 41 | 42 | 43 | 44 | 45 | 46 | 47 | -------------------------------------------------------------------------------- /kmiptest/testdata/v1.4/CS-RNG-O-2-14.xml: -------------------------------------------------------------------------------- 1 | 2 | 3 | 12 | 13 | 14 | 15 | 16 | 17 | 18 | 19 | 20 | 21 | 22 | 23 | 24 | 25 | 26 | 27 | 28 | 29 | 30 | 31 | 32 | 33 | 34 | 35 | 36 | 37 | 38 | 39 | 40 | 41 | 42 | 43 | 44 | 45 | 46 | 47 | -------------------------------------------------------------------------------- /kmiptest/testdata/v1.4/CS-RNG-O-3-14.xml: -------------------------------------------------------------------------------- 1 | 2 | 3 | 12 | 13 | 14 | 15 | 16 | 17 | 18 | 19 | 20 | 21 | 22 | 23 | 24 | 25 | 26 | 27 | 28 | 29 | 30 | 31 | 32 | 33 | 34 | 35 | 36 | 37 | 38 | 39 | 40 | 41 | 42 | 43 | 44 | 45 | 46 | 47 | -------------------------------------------------------------------------------- /kmiptest/testdata/v1.4/CS-RNG-O-4-14.xml: -------------------------------------------------------------------------------- 1 | 2 | 3 | 12 | 13 | 14 | 15 | 16 | 17 | 18 | 19 | 20 | 21 | 22 | 23 | 24 | 25 | 26 | 27 | 28 | 29 | 30 | 31 | 32 | 33 | 34 | 35 | 36 | 37 | 38 | 39 | 40 | 41 | 42 | 43 | 44 | 45 | 46 | -------------------------------------------------------------------------------- /kmiptest/testdata/v1.4/SASED-M-1-14.xml: -------------------------------------------------------------------------------- 1 | 2 | 3 | 12 | 13 | 14 | 15 | 16 | 17 | 18 | 19 | 20 | 21 | 22 | 23 | 24 | 25 | 26 | 27 | 28 | 29 | 30 | 31 | 32 | 33 | 34 | 35 | 36 | 37 | 38 | 39 | 40 | 41 | 42 | 43 | 44 | 45 | 46 | 47 | 48 | 49 | 50 | 51 | 52 | 53 | 54 | 55 | 56 | 57 | 58 | 59 | 60 | 61 | -------------------------------------------------------------------------------- /kmiptest/testdata/v1.4/TC-CS-CORVAL-1-14.xml: -------------------------------------------------------------------------------- 1 | 2 | 3 | 12 | 13 | 14 | 15 | 16 | 17 | 18 | 19 | 20 | 21 | 22 | 23 | 24 | 25 | 26 | 27 | 28 | 29 | 30 | 31 | 32 | 33 | 34 | 35 | 36 | 37 | 38 | 39 | 40 | 41 | 42 | 43 | 44 | 45 | 46 | 47 | 48 | 49 | 50 | 51 | 52 | 53 | 54 | 55 | 56 | 57 | 58 | 59 | 60 | -------------------------------------------------------------------------------- /kmiptest/testdata/v1.4/TC-Q-CAP-1-14.xml: -------------------------------------------------------------------------------- 1 | 2 | 3 | 12 | 13 | 14 | 15 | 16 | 17 | 18 | 19 | 20 | 21 | 22 | 23 | 24 | 25 | 26 | 27 | 28 | 29 | 30 | 31 | 32 | 33 | 34 | 35 | 36 | 37 | 38 | 39 | 40 | 41 | 42 | 43 | 44 | 45 | 46 | 47 | 48 | 49 | 50 | 51 | 52 | 53 | 54 | 55 | 56 | 57 | -------------------------------------------------------------------------------- /kmiptest/testdata/v1.4/TC-Q-PROF-1-14.xml: -------------------------------------------------------------------------------- 1 | 2 | 3 | 12 | 13 | 14 | 15 | 16 | 17 | 18 | 19 | 20 | 21 | 22 | 23 | 24 | 25 | 26 | 27 | 28 | 29 | 30 | 31 | 32 | 33 | 34 | 35 | 36 | 37 | 38 | 39 | 40 | 41 | 42 | 43 | 44 | 45 | 46 | 47 | 48 | 49 | 50 | 51 | 52 | 53 | 54 | 55 | -------------------------------------------------------------------------------- /kmiptest/testdata/v1.4/TC-Q-RNGS-1-14.xml: -------------------------------------------------------------------------------- 1 | 2 | 3 | 12 | 13 | 14 | 15 | 16 | 17 | 18 | 19 | 20 | 21 | 22 | 23 | 24 | 25 | 26 | 27 | 28 | 29 | 30 | 31 | 32 | 33 | 34 | 35 | 36 | 37 | 38 | 39 | 40 | 41 | 42 | 43 | 44 | 45 | 46 | 47 | 48 | 49 | -------------------------------------------------------------------------------- /kmiptest/testdata/v1.4/TC-Q-S2C-1-14.xml: -------------------------------------------------------------------------------- 1 | 2 | 3 | 12 | 13 | 14 | 15 | 16 | 17 | 18 | 19 | 20 | 21 | 22 | 23 | 24 | 25 | 26 | 27 | 28 | 29 | 30 | 31 | 32 | 33 | 34 | 35 | 36 | 37 | 38 | 39 | 40 | 41 | 42 | 43 | 44 | 45 | 46 | 47 | 48 | 49 | 50 | 51 | 52 | 53 | 54 | 55 | 56 | 57 | 58 | 59 | 60 | 61 | 62 | 63 | 64 | 65 | 66 | 67 | 68 | 69 | 70 | 71 | -------------------------------------------------------------------------------- /kmiptest/testdata/v1.4/TL-M-1-14.xml: -------------------------------------------------------------------------------- 1 | 2 | 3 | 12 | 13 | 14 | 15 | 16 | 17 | 18 | 19 | 20 | 21 | 22 | 23 | 24 | 25 | 26 | 27 | 28 | 29 | 30 | 31 | 32 | 33 | 34 | 35 | 36 | 37 | 38 | 39 | 40 | 41 | 42 | 43 | 44 | 45 | 46 | 47 | 48 | 49 | 50 | 51 | 52 | 53 | 54 | 55 | 56 | 57 | 58 | 59 | 60 | 61 | 62 | 63 | 64 | 65 | 66 | 67 | 68 | 69 | 70 | -------------------------------------------------------------------------------- /payloads/activate.go: -------------------------------------------------------------------------------- 1 | package payloads 2 | 3 | import ( 4 | "github.com/ovh/kmip-go" 5 | ) 6 | 7 | func init() { 8 | kmip.RegisterOperationPayload[ActivateRequestPayload, ActivateResponsePayload](kmip.OperationActivate) 9 | } 10 | 11 | var _ kmip.OperationPayload = (*ActivateRequestPayload)(nil) 12 | 13 | // This operation requests the server to activate a Managed Cryptographic Object. The request SHALL NOT specify a Template object. 14 | // The operation SHALL only be performed on an object in the Pre-Active state and has the effect of changing its state to Active, 15 | // and setting its Activation Date to the current date and time. 16 | type ActivateRequestPayload struct { 17 | // Determines the object being activated. 18 | // If omitted, then the ID Placeholder value is used by the server as the Unique Identifier. 19 | UniqueIdentifier string `ttlv:",omitempty"` 20 | } 21 | 22 | // Operation implements kmip.OperationPayload. 23 | func (a *ActivateRequestPayload) Operation() kmip.Operation { 24 | return kmip.OperationActivate 25 | } 26 | 27 | // Response for the activate operation. 28 | type ActivateResponsePayload struct { 29 | // The Unique Identifier of the object. 30 | UniqueIdentifier string 31 | } 32 | 33 | var _ kmip.OperationPayload = (*ActivateResponsePayload)(nil) 34 | 35 | // Operation implements kmip.OperationPayload. 36 | func (a *ActivateResponsePayload) Operation() kmip.Operation { 37 | return kmip.OperationActivate 38 | } 39 | -------------------------------------------------------------------------------- /payloads/add_attribute.go: -------------------------------------------------------------------------------- 1 | package payloads 2 | 3 | import "github.com/ovh/kmip-go" 4 | 5 | func init() { 6 | kmip.RegisterOperationPayload[AddAttributeRequestPayload, AddAttributeResponsePayload](kmip.OperationAddAttribute) 7 | } 8 | 9 | // This operation requests the server to add a new attribute instance to be associated with a Managed Object and set its value. 10 | // The request contains the Unique Identifier of the Managed Object to which the attribute pertains, along with the attribute name and value. 11 | // For single-instance attributes, this is how the attribute value is created. 12 | // For multi-instance attributes, this is how the first and subsequent values are created. 13 | // Existing attribute values SHALL only be changed by the Modify Attribute operation. Read-Only attributes SHALL NOT be added using the Add Attribute operation. 14 | // The Attribute Index SHALL NOT be specified in the request. The response returns a new Attribute Index and the Attribute Index MAY be omitted if the index 15 | // of the added attribute instance is 0. Multiple Add Attribute requests MAY be included in a single batched request to add multiple attributes. 16 | type AddAttributeRequestPayload struct { 17 | // The Unique Identifier of the object. 18 | // If omitted, then the ID Placeholder value is used by the server as the Unique Identifier. 19 | UniqueIdentifier string `ttlv:",omitempty"` 20 | // Specifies the attribute to be added as an attribute for the object. 21 | Attribute kmip.Attribute 22 | } 23 | 24 | func (pl *AddAttributeRequestPayload) Operation() kmip.Operation { 25 | return kmip.OperationAddAttribute 26 | } 27 | 28 | // Response for the add-attribute operation. 29 | type AddAttributeResponsePayload struct { 30 | // The Unique Identifier of the object. 31 | UniqueIdentifier string 32 | // The added attribute associated with the object. 33 | Attribute kmip.Attribute 34 | } 35 | 36 | func (pl *AddAttributeResponsePayload) Operation() kmip.Operation { 37 | return kmip.OperationAddAttribute 38 | } 39 | -------------------------------------------------------------------------------- /payloads/archive_recover.go: -------------------------------------------------------------------------------- 1 | package payloads 2 | 3 | import "github.com/ovh/kmip-go" 4 | 5 | func init() { 6 | kmip.RegisterOperationPayload[ArchiveRequestPayload, ArchiveResponsePayload](kmip.OperationArchive) 7 | kmip.RegisterOperationPayload[RecoverRequestPayload, RecoverResponsePayload](kmip.OperationRecover) 8 | } 9 | 10 | // This operation is used to specify that a Managed Object MAY be archived. The actual time when the object is archived, 11 | // the location of the archive, or level of archive hierarchy is determined by the policies within the key management system 12 | // and is not specified by the client. The request contains the Unique Identifier of the Managed Object. 13 | // Special authentication and authorization SHOULD be enforced to perform this request. 14 | // Only the object owner or an authorized security officer SHOULD be allowed to issue this request. This request is only an indication from a client that, 15 | // from its point of view, the key management system MAY archive the object. 16 | type ArchiveRequestPayload struct { 17 | // Determines the object being archived. 18 | // If omitted, then the ID Placeholder value is used by the server as the Unique Identifier. 19 | UniqueIdentifier string `ttlv:",omitempty"` 20 | } 21 | 22 | func (pl *ArchiveRequestPayload) Operation() kmip.Operation { 23 | return kmip.OperationArchive 24 | } 25 | 26 | // Respsonse for the archive operation. 27 | type ArchiveResponsePayload struct { 28 | // The Unique Identifier of the object. 29 | UniqueIdentifier string 30 | } 31 | 32 | func (pl *ArchiveResponsePayload) Operation() kmip.Operation { 33 | return kmip.OperationArchive 34 | } 35 | 36 | // This operation is used to obtain access to a Managed Object that has been archived. 37 | // This request MAY need asynchronous polling to obtain the response due to delays caused by retrieving the object from the archive. 38 | // Once the response is received, the object is now on-line, and MAY be obtained (e.g., via a Get operation). 39 | // Special authentication and authorization SHOULD be enforced to perform this request. 40 | type RecoverRequestPayload struct { 41 | UniqueIdentifier string `ttlv:",omitempty"` 42 | } 43 | 44 | func (pl *RecoverRequestPayload) Operation() kmip.Operation { 45 | return kmip.OperationRecover 46 | } 47 | 48 | // Response for the recover operation. 49 | type RecoverResponsePayload struct { 50 | // The Unique Identifier of the object. 51 | UniqueIdentifier string 52 | } 53 | 54 | func (pl *RecoverResponsePayload) Operation() kmip.Operation { 55 | return kmip.OperationRecover 56 | } 57 | -------------------------------------------------------------------------------- /payloads/create.go: -------------------------------------------------------------------------------- 1 | package payloads 2 | 3 | import "github.com/ovh/kmip-go" 4 | 5 | func init() { 6 | kmip.RegisterOperationPayload[CreateRequestPayload, CreateResponsePayload](kmip.OperationCreate) 7 | } 8 | 9 | // This operation requests the server to generate a new symmetric key as a Managed Cryptographic Object. 10 | // This operation is not used to create a Template object (see Register operation, Section 4.3). 11 | // 12 | // The request contains information about the type of object being created, and some of the attributes to be assigned to the object 13 | // (e.g., Cryptographic Algorithm, Cryptographic Length, etc.). This information MAY be specified by the names of Template objects that already exist. 14 | // 15 | // The response contains the Unique Identifier of the created object. The server SHALL copy the Unique Identifier returned by this operation into the ID Placeholder variable. 16 | type CreateRequestPayload struct { 17 | // Determines the type of object to be created. 18 | ObjectType kmip.ObjectType 19 | // Specifies desired attributes using to be associated with the new object templates and/or individual attributes. 20 | // 21 | // The Template Managed Object is deprecated as of version 1.3 of this specification and MAY be removed from subsequent versions of the specification. 22 | // Individual Attributes SHOULD be used in operations which currently support use of a Name within a Template-Attribute to reference a Template. 23 | TemplateAttribute kmip.TemplateAttribute 24 | } 25 | 26 | func (a *CreateRequestPayload) Operation() kmip.Operation { 27 | return kmip.OperationCreate 28 | } 29 | 30 | // Response for the create operation. 31 | type CreateResponsePayload struct { 32 | // Type of object created. 33 | ObjectType kmip.ObjectType 34 | // The Unique Identifier of the newly created object. 35 | UniqueIdentifier string 36 | // An OPTIONAL list of object attributes with values that were not specified in the request, but have been implicitly set by the key management server. 37 | // 38 | // The Template Managed Object is deprecated as of version 1.3 of this specification and MAY be removed from subsequent versions of the specification. 39 | // Individual Attributes SHOULD be used in operations which currently support use of a Name within a Template-Attribute to reference a Template. 40 | Attributes *kmip.TemplateAttribute 41 | } 42 | 43 | func (a *CreateResponsePayload) Operation() kmip.Operation { 44 | return kmip.OperationCreate 45 | } 46 | -------------------------------------------------------------------------------- /payloads/delete_attribute.go: -------------------------------------------------------------------------------- 1 | package payloads 2 | 3 | import "github.com/ovh/kmip-go" 4 | 5 | func init() { 6 | kmip.RegisterOperationPayload[DeleteAttributeRequestPayload, DeleteAttributeResponsePayload](kmip.OperationDeleteAttribute) 7 | } 8 | 9 | // This operation requests the server to delete an attribute associated with a Managed Object. 10 | // The request contains the Unique Identifier of the Managed Object whose attribute is to be deleted, the attribute name, 11 | // and the OPTIONAL Attribute Index of the attribute. If no Attribute Index is specified in the request, 12 | // then the Attribute Index SHALL be assumed to be 0. Attributes that are always REQUIRED to have a value SHALL never be deleted by this operation. 13 | // Attempting to delete a non-existent attribute or specifying an Attribute Index for which there exists no Attribute Value SHALL result in an error. 14 | // The response returns the deleted Attribute and the Attribute Index MAY be omitted if the index of the deleted attribute instance is 0. Multiple Delete Attribute 15 | // requests MAY be included in a single batched request to delete multiple attributes. 16 | type DeleteAttributeRequestPayload struct { 17 | // Determines the object whose attributes are being deleted. If omitted, then the ID Placeholder value is used by the server as the Unique Identifier. 18 | UniqueIdentifier string `ttlv:",omitempty"` 19 | // Specifies the name of the attribute associated with the object to be deleted. 20 | AttributeName kmip.AttributeName 21 | // Specifies the Index of the Attribute. 22 | AttributeIndex *int32 23 | } 24 | 25 | func (pl *DeleteAttributeRequestPayload) Operation() kmip.Operation { 26 | return kmip.OperationDeleteAttribute 27 | } 28 | 29 | // Response for the delete-attribute operation. 30 | type DeleteAttributeResponsePayload struct { 31 | // The Unique Identifier of the object. 32 | UniqueIdentifier string 33 | // The deleted attribute associated with the object. 34 | Attribute kmip.Attribute 35 | } 36 | 37 | func (pl *DeleteAttributeResponsePayload) Operation() kmip.Operation { 38 | return kmip.OperationDeleteAttribute 39 | } 40 | -------------------------------------------------------------------------------- /payloads/destroy.go: -------------------------------------------------------------------------------- 1 | package payloads 2 | 3 | import ( 4 | "github.com/ovh/kmip-go" 5 | ) 6 | 7 | func init() { 8 | kmip.RegisterOperationPayload[DestroyRequestPayload, DestroyResponsePayload](kmip.OperationDestroy) 9 | } 10 | 11 | var _ kmip.OperationPayload = (*DestroyRequestPayload)(nil) 12 | 13 | // This operation is used to indicate to the server that the key material for the specified Managed Object SHALL be destroyed. 14 | // The meta-data for the key material MAY be retained by the server (e.g., used to ensure that an expired or revoked private signing key is no longer available). 15 | // Special authentication and authorization SHOULD be enforced to perform this request. 16 | // Only the object owner or an authorized security officer SHOULD be allowed to issue this request. If the Unique Identifier specifies a Template object, 17 | // then the object itself, including all meta-data, SHALL be destroyed. Cryptographic Objects MAY only be destroyed if they are in either Pre-Active or Deactivated state. 18 | type DestroyRequestPayload struct { 19 | // Determines the object being destroyed. If omitted, then the ID Placeholder value is used by the server as the Unique Identifier. 20 | UniqueIdentifier string `ttlv:",omitempty"` 21 | } 22 | 23 | // Operation implements kmip.OperationPayload. 24 | func (a *DestroyRequestPayload) Operation() kmip.Operation { 25 | return kmip.OperationDestroy 26 | } 27 | 28 | // Response for the destroy operation. 29 | type DestroyResponsePayload struct { 30 | // The Unique Identifier of the object. 31 | UniqueIdentifier string 32 | } 33 | 34 | var _ kmip.OperationPayload = (*DestroyResponsePayload)(nil) 35 | 36 | // Operation implements kmip.OperationPayload. 37 | func (a *DestroyResponsePayload) Operation() kmip.Operation { 38 | return kmip.OperationDestroy 39 | } 40 | -------------------------------------------------------------------------------- /payloads/discover.go: -------------------------------------------------------------------------------- 1 | package payloads 2 | 3 | import ( 4 | "github.com/ovh/kmip-go" 5 | ) 6 | 7 | func init() { 8 | kmip.RegisterOperationPayload[DiscoverVersionsRequestPayload, DiscoverVersionsResponsePayload](kmip.OperationDiscoverVersions) 9 | } 10 | 11 | // This operation is used by the client to determine a list of protocol versions that is supported by the server. The request payload contains an OPTIONAL 12 | // list of protocol versions that is supported by the client. The protocol versions SHALL be ranked in decreasing order of preference. 13 | // 14 | // The response payload contains a list of protocol versions that are supported by the server. The protocol versions are ranked in decreasing order of preference. 15 | // If the client provides the server with a list of supported protocol versions in the request payload, the server SHALL return 16 | // only the protocol versions that are supported by both the client and server. The server SHOULD list all the protocol versions supported by both client and server. 17 | // If the protocol version specified in the request header is not specified in the request payload and the server does not support any 18 | // protocol version specified in the request payload, the server SHALL return an empty list in the response payload. If no protocol versions are specified in the request payload, 19 | // the server SHOULD return all the protocol versions that are supported by the server. 20 | type DiscoverVersionsRequestPayload struct { 21 | // The list of protocol versions supported by the client ordered in decreasing order of preference. 22 | ProtocolVersion []kmip.ProtocolVersion 23 | } 24 | 25 | func (*DiscoverVersionsRequestPayload) Operation() kmip.Operation { 26 | return kmip.OperationDiscoverVersions 27 | } 28 | 29 | // Response for the discover-versions operation. 30 | type DiscoverVersionsResponsePayload struct { 31 | // The list of protocol versions supported by the server ordered in decreasing order of preference. 32 | ProtocolVersion []kmip.ProtocolVersion 33 | } 34 | 35 | func (*DiscoverVersionsResponsePayload) Operation() kmip.Operation { 36 | return kmip.OperationDiscoverVersions 37 | } 38 | -------------------------------------------------------------------------------- /payloads/get_attribute_list.go: -------------------------------------------------------------------------------- 1 | package payloads 2 | 3 | import "github.com/ovh/kmip-go" 4 | 5 | func init() { 6 | kmip.RegisterOperationPayload[GetAttributeListRequestPayload, GetAttributeListResponsePayload](kmip.OperationGetAttributeList) 7 | } 8 | 9 | // This operation requests a list of the attribute names associated with a Managed Object. The object is specified by its Unique Identifier. 10 | type GetAttributeListRequestPayload struct { 11 | // Determines the object whose attribute names are being requested. If omitted, then the ID Placeholder value is used by the server as the Unique Identifier. 12 | UniqueIdentifier string `ttlv:",omitempty"` 13 | } 14 | 15 | func (pl *GetAttributeListRequestPayload) Operation() kmip.Operation { 16 | return kmip.OperationGetAttributeList 17 | } 18 | 19 | // Response for the get-attribute-list operation. 20 | type GetAttributeListResponsePayload struct { 21 | // The Unique Identifier of the object. 22 | UniqueIdentifier string 23 | // The names of the available attributes associated with the object. 24 | AttributeName []kmip.AttributeName 25 | } 26 | 27 | func (pl *GetAttributeListResponsePayload) Operation() kmip.Operation { 28 | return kmip.OperationGetAttributeList 29 | } 30 | -------------------------------------------------------------------------------- /payloads/get_attributes.go: -------------------------------------------------------------------------------- 1 | package payloads 2 | 3 | import ( 4 | "github.com/ovh/kmip-go" 5 | ) 6 | 7 | func init() { 8 | kmip.RegisterOperationPayload[GetAttributesRequestPayload, GetAttributesResponsePayload](kmip.OperationGetAttributes) 9 | } 10 | 11 | var _ kmip.OperationPayload = (*GetAttributesRequestPayload)(nil) 12 | 13 | // This operation requests one or more attributes associated with a Managed Object. 14 | // The object is specified by its Unique Identifier, and the attributes are specified by their name in the request. 15 | // If a specified attribute has multiple instances, then all instances are returned. If a specified attribute does not exist (i.e., has no value), 16 | // then it SHALL NOT be present in the returned response. If no requested attributes exist, then the response SHALL consist only of the Unique Identifier. 17 | // If no attribute name is specified in the request, all attributes SHALL be deemed to match the Get Attributes request. 18 | // The same attribute name SHALL NOT be present more than once in a request. 19 | type GetAttributesRequestPayload struct { 20 | // Determines the object whose attributes are being requested. If omitted, then the ID Placeholder value is used by the server as the Unique Identifier. 21 | UniqueIdentifier string `ttlv:",omitempty"` 22 | // Specifies the name of an attribute associated with the object. 23 | AttributeName []kmip.AttributeName 24 | } 25 | 26 | // Operation implements kmip.OperationPayload. 27 | func (a *GetAttributesRequestPayload) Operation() kmip.Operation { 28 | return kmip.OperationGetAttributes 29 | } 30 | 31 | type GetAttributesResponsePayload struct { 32 | // The Unique Identifier of the object. 33 | UniqueIdentifier string 34 | // The requested attribute associated with the object. 35 | Attribute []kmip.Attribute 36 | } 37 | 38 | var _ kmip.OperationPayload = (*GetAttributesResponsePayload)(nil) 39 | 40 | // Operation implements kmip.OperationPayload. 41 | func (a *GetAttributesResponsePayload) Operation() kmip.Operation { 42 | return kmip.OperationGetAttributes 43 | } 44 | -------------------------------------------------------------------------------- /payloads/get_usage.go: -------------------------------------------------------------------------------- 1 | package payloads 2 | 3 | import "github.com/ovh/kmip-go" 4 | 5 | func init() { 6 | kmip.RegisterOperationPayload[GetUsageAllocationRequestPayload, GetUsageAllocationResponsePayload](kmip.OperationGetUsageAllocation) 7 | } 8 | 9 | // This operation requests the server to obtain an allocation from the current Usage Limits value to allow the client to use the Managed Cryptographic Object 10 | // for applying cryptographic protection. The allocation only applies to Managed Cryptographic Objects that are able to be used for applying protection 11 | // (e.g., symmetric keys for encryption, private keys for signing, etc.) and is only valid if the Managed Cryptographic Object has a Usage Limits attribute. 12 | // Usage for processing cryptographically protected information (e.g., decryption, verification, etc.) is not limited and is not able to be allocated. 13 | // A Managed Cryptographic Object that has a Usage Limits attribute SHALL NOT be used by a client for applying cryptographic protection unless an allocation 14 | // has been obtained using this operation. The operation SHALL only be requested during the time that protection is enabled for these objects 15 | // (i.e., after the Activation Date and before the Protect Stop Date). If the operation is requested for an object that has no Usage Limits attribute, 16 | // or is not an object that MAY be used for applying cryptographic protection, then the server SHALL return an error. 17 | // 18 | // The field in the request specifies the number of units that the client needs to protect. 19 | // If the requested amount is not available or if the Managed Object is not able to be used for applying cryptographic protection at this time, 20 | // then the server SHALL return an error. The server SHALL assume that the entire allocated amount is going to be consumed. 21 | // Once the entire allocated amount has been consumed, the client SHALL NOT continue to use the Managed Cryptographic Object for applying cryptographic protection 22 | // until a new allocation is obtained. 23 | type GetUsageAllocationRequestPayload struct { 24 | // Determines the object whose usage allocation is being requested. If omitted, then the ID Placeholder is substituted by the server. 25 | UniqueIdentifier string `ttlv:",omitempty"` 26 | // The number of Usage Limits Units to be protected. 27 | UsageLimitsCount int64 28 | } 29 | 30 | func (pl *GetUsageAllocationRequestPayload) Operation() kmip.Operation { 31 | return kmip.OperationGetUsageAllocation 32 | } 33 | 34 | // Response for the get-usage-allocation operation. 35 | type GetUsageAllocationResponsePayload struct { 36 | // The Unique Identifier of the object. 37 | UniqueIdentifier string 38 | } 39 | 40 | func (pl *GetUsageAllocationResponsePayload) Operation() kmip.Operation { 41 | return kmip.OperationGetUsageAllocation 42 | } 43 | -------------------------------------------------------------------------------- /payloads/modify_attribute.go: -------------------------------------------------------------------------------- 1 | package payloads 2 | 3 | import "github.com/ovh/kmip-go" 4 | 5 | func init() { 6 | kmip.RegisterOperationPayload[ModifyAttributeRequestPayload, ModifyAttributeResponsePayload](kmip.OperationModifyAttribute) 7 | } 8 | 9 | // This operation requests the server to modify the value of an existing attribute instance associated with a Managed Object. 10 | // The request contains the Unique Identifier of the Managed Object whose attribute is to be modified, the attribute name, 11 | // the OPTIONAL Attribute Index, and the new value. If no Attribute Index is specified in the request, 12 | // then the Attribute Index SHALL be assumed to be 0. Only existing attributes MAY be changed via this operation. 13 | // New attributes SHALL only be added by the Add Attribute operation. Only the specified instance of the attribute SHALL be modified. 14 | // Specifying an Attribute Index for which there exists no Attribute object SHALL result in an error. 15 | // 16 | // The response returns the modified Attribute (new value) and the Attribute Index MAY be omitted if the index of the modified attribute instance is 0. 17 | // 18 | // Multiple Modify Attribute requests MAY be included in a single batched request to modify multiple attributes. 19 | type ModifyAttributeRequestPayload struct { 20 | // The Unique Identifier of the object. If omitted, then the ID Placeholder value is used by the server as the Unique Identifier. 21 | UniqueIdentifier string `ttlv:",omitempty"` 22 | // Specifies the attribute associated with the object to be modified. 23 | Attribute kmip.Attribute 24 | } 25 | 26 | func (pl *ModifyAttributeRequestPayload) Operation() kmip.Operation { 27 | return kmip.OperationModifyAttribute 28 | } 29 | 30 | // Response for the Modify-Attribute operation. 31 | type ModifyAttributeResponsePayload struct { 32 | // The Unique Identifier of the object. 33 | UniqueIdentifier string 34 | // The modified attribute associated with the object with the new value. 35 | Attribute kmip.Attribute 36 | } 37 | 38 | func (pl *ModifyAttributeResponsePayload) Operation() kmip.Operation { 39 | return kmip.OperationModifyAttribute 40 | } 41 | -------------------------------------------------------------------------------- /payloads/obtain_lease.go: -------------------------------------------------------------------------------- 1 | package payloads 2 | 3 | import ( 4 | "time" 5 | 6 | "github.com/ovh/kmip-go" 7 | ) 8 | 9 | func init() { 10 | kmip.RegisterOperationPayload[ObtainLeaseRequestPayload, ObtainLeaseResponsePayload](kmip.OperationObtainLease) 11 | } 12 | 13 | // This operation requests the server to obtain a new Lease Time for a specified Managed Object. 14 | // The Lease Time is an interval value that determines when the client's internal cache of information about the object expires and needs to be renewed. 15 | // If the returned value of the lease time is zero, then the server is indicating that no lease interval is effective, and the client MAY use the object without any lease time limit. 16 | // If a client's lease expires, then the client SHALL NOT use the associated cryptographic object until a new lease is obtained. 17 | // If the server determines that a new lease SHALL NOT be issued for the specified cryptographic object, then the server SHALL respond to the Obtain Lease request with an error. 18 | // 19 | // The response payload for the operation contains the current value of the Last Change Date attribute for the object. 20 | // This MAY be used by the client to determine if any of the attributes cached by the client need to be refreshed, 21 | // by comparing this time to the time when the attributes were previously obtained. 22 | type ObtainLeaseRequestPayload struct { 23 | // Determines the object for which the lease is being obtained. If omitted, then the ID Placeholder value is used by the server as the Unique Identifier. 24 | UniqueIdentifier string `ttlv:",omitempty"` 25 | } 26 | 27 | func (pl *ObtainLeaseRequestPayload) Operation() kmip.Operation { 28 | return kmip.OperationObtainLease 29 | } 30 | 31 | // Response for the ObtainLease operation. 32 | type ObtainLeaseResponsePayload struct { 33 | // The Unique Identifier of the object. 34 | UniqueIdentifier string 35 | // An interval (in seconds) that specifies the amount of time that the object MAY be used until a new lease needs to be obtained. 36 | LeaseTime time.Duration 37 | // The date and time indicating when the latest change was made to the contents or any attribute of the specified object. 38 | LastChangeDate time.Time 39 | } 40 | 41 | func (pl *ObtainLeaseResponsePayload) Operation() kmip.Operation { 42 | return kmip.OperationObtainLease 43 | } 44 | -------------------------------------------------------------------------------- /payloads/payloads_test.go: -------------------------------------------------------------------------------- 1 | package payloads_test 2 | 3 | import ( 4 | "testing" 5 | 6 | "github.com/ovh/kmip-go" 7 | "github.com/ovh/kmip-go/payloads" 8 | "github.com/ovh/kmip-go/ttlv" 9 | 10 | _ "unsafe" 11 | 12 | "github.com/stretchr/testify/assert" 13 | "github.com/stretchr/testify/require" 14 | ) 15 | 16 | func TestPayloadsTypes(t *testing.T) { 17 | for op := range ttlv.EnumValues[kmip.Operation]() { 18 | assert.Equal(t, op, newRequestPayload(op).Operation()) 19 | assert.Equal(t, op, newResponsePayload(op).Operation()) 20 | } 21 | } 22 | 23 | //go:linkname newRequestPayload github.com/ovh/kmip-go.newRequestPayload 24 | func newRequestPayload(op kmip.Operation) kmip.OperationPayload 25 | 26 | //go:linkname newResponsePayload github.com/ovh/kmip-go.newResponsePayload 27 | func newResponsePayload(op kmip.Operation) kmip.OperationPayload 28 | 29 | func TestRegisterRequestPayload_Encode_Decode(t *testing.T) { 30 | secret := []byte("foobar") 31 | req := &payloads.RegisterRequestPayload{ 32 | ObjectType: kmip.ObjectTypeSecretData, 33 | TemplateAttribute: kmip.TemplateAttribute{}, 34 | Object: &kmip.SecretData{ 35 | SecretDataType: kmip.SecretDataTypePassword, 36 | KeyBlock: kmip.KeyBlock{KeyFormatType: kmip.KeyFormatTypeRaw, KeyValue: &kmip.KeyValue{Plain: &kmip.PlainKeyValue{KeyMaterial: kmip.KeyMaterial{Bytes: &secret}}}}, 37 | }, 38 | } 39 | 40 | enc := ttlv.NewTTLVEncoder() 41 | enc.TagAny(kmip.TagRequestPayload, req) 42 | ttlvReq := enc.Bytes() 43 | decodedReq := &payloads.RegisterRequestPayload{} 44 | dec, err := ttlv.NewTTLVDecoder(ttlvReq) 45 | require.NoError(t, err) 46 | err = dec.TagAny(kmip.TagRequestPayload, decodedReq) 47 | require.NoError(t, err) 48 | require.EqualValues(t, req, decodedReq) 49 | } 50 | -------------------------------------------------------------------------------- /payloads/revoke.go: -------------------------------------------------------------------------------- 1 | package payloads 2 | 3 | import ( 4 | "time" 5 | 6 | "github.com/ovh/kmip-go" 7 | ) 8 | 9 | func init() { 10 | kmip.RegisterOperationPayload[RevokeRequestPayload, RevokeResponsePayload](kmip.OperationRevoke) 11 | } 12 | 13 | var _ kmip.OperationPayload = (*RevokeRequestPayload)(nil) 14 | 15 | // This operation requests the server to revoke a Managed Cryptographic Object or an Opaque Object. The request SHALL NOT specify a Template object. 16 | // The request contains a reason for the revocation (e.g., “key compromise”, “cessation of operation”, etc.). 17 | // Special authentication and authorization SHOULD be enforced to perform this request. Only the object owner or an authorized security officer 18 | // SHOULD be allowed to issue this request. The operation has one of two effects. If the revocation reason is “key compromise” or “CA compromise”, 19 | // then the object is placed into the “compromised” state; the Date is set to the current date and time; and the Compromise Occurrence Date is 20 | // set to the value (if provided) in the Revoke request and if a value is not provided in the Revoke request then Compromise Occurrence Date SHOULD 21 | // be set to the Initial Date for the object. If the revocation reason is neither “key compromise” nor “CA compromise”, 22 | // the object is placed into the “deactivated” state, and the Deactivation Date is set to the current date and time. 23 | type RevokeRequestPayload struct { 24 | // Determines the object being revoked. If omitted, then the ID Placeholder value is used by the server as the Unique Identifier. 25 | UniqueIdentifier string `ttlv:",omitempty"` 26 | // Specifies the reason for revocation. 27 | RevocationReason kmip.RevocationReason 28 | // SHOULD be specified if the Revocation Reason is 'key compromise' or ‘CA compromise’ and SHALL NOT be specified for other Revocation Reason enumerations. 29 | CompromiseOccurrenceDate *time.Time 30 | } 31 | 32 | // Operation implements kmip.OperationPayload. 33 | func (a *RevokeRequestPayload) Operation() kmip.Operation { 34 | return kmip.OperationRevoke 35 | } 36 | 37 | // Response for the Revoke operation. 38 | type RevokeResponsePayload struct { 39 | // The Unique Identifier of the object. 40 | UniqueIdentifier string 41 | } 42 | 43 | var _ kmip.OperationPayload = (*RevokeResponsePayload)(nil) 44 | 45 | // Operation implements kmip.OperationPayload. 46 | func (a *RevokeResponsePayload) Operation() kmip.Operation { 47 | return kmip.OperationRevoke 48 | } 49 | -------------------------------------------------------------------------------- /requests.go: -------------------------------------------------------------------------------- 1 | package kmip 2 | 3 | import ( 4 | "encoding/binary" 5 | "math" 6 | "time" 7 | 8 | "github.com/ovh/kmip-go/ttlv" 9 | ) 10 | 11 | type RequestMessage struct { 12 | Header RequestHeader 13 | BatchItem []RequestBatchItem 14 | } 15 | 16 | func NewRequestMessage(version ProtocolVersion, payloads ...OperationPayload) RequestMessage { 17 | bc := len(payloads) 18 | if bc > math.MaxInt32 { 19 | panic("too many payloads") 20 | } 21 | timestamp := time.Now().Truncate(time.Second) 22 | msg := RequestMessage{ 23 | Header: RequestHeader{ 24 | ProtocolVersion: version, 25 | TimeStamp: ×tamp, 26 | BatchCount: int32(bc), 27 | }, 28 | } 29 | 30 | for i, pl := range payloads { 31 | item := RequestBatchItem{ 32 | Operation: pl.Operation(), 33 | RequestPayload: pl, 34 | } 35 | if len(payloads) > 1 { 36 | //nolint:gosec // this cast is safe as we just want to append a number to a byte slice 37 | item.UniqueBatchItemID = binary.BigEndian.AppendUint64(item.UniqueBatchItemID, uint64(i)) 38 | } 39 | msg.BatchItem = append(msg.BatchItem, item) 40 | } 41 | 42 | return msg 43 | } 44 | 45 | type RequestHeader struct { 46 | ProtocolVersion ProtocolVersion `ttlv:",set-version"` 47 | MaximumResponseSize int32 `ttlv:",omitempty"` 48 | 49 | ClientCorrelationValue string `ttlv:",omitempty,version=v1.4.."` 50 | ServerCorrelationValue string `ttlv:",omitempty,version=v1.4.."` 51 | AsynchronousIndicator *bool 52 | AttestationCapableIndicator *bool `ttlv:",version=v1.2.."` 53 | AttestationType []AttestationType `ttlv:",version=v1.2.."` 54 | Authentication *Authentication 55 | BatchErrorContinuationOption BatchErrorContinuationOption `ttlv:",omitempty"` 56 | BatchOrderOption *bool 57 | TimeStamp *time.Time 58 | BatchCount int32 59 | } 60 | 61 | type RequestBatchItem struct { 62 | Operation Operation 63 | UniqueBatchItemID []byte `ttlv:",omitempty"` 64 | RequestPayload OperationPayload 65 | MessageExtension *MessageExtension 66 | } 67 | 68 | func (pv *RequestBatchItem) TagEncodeTTLV(e *ttlv.Encoder, tag int) { 69 | e.Struct(tag, func(e *ttlv.Encoder) { 70 | e.Any(pv.Operation) 71 | if len(pv.UniqueBatchItemID) > 0 { 72 | e.ByteString(TagUniqueBatchItemID, pv.UniqueBatchItemID) 73 | } 74 | e.TagAny(TagRequestPayload, pv.RequestPayload) 75 | e.Any(pv.MessageExtension) 76 | }) 77 | } 78 | 79 | func (pv *RequestBatchItem) TagDecodeTTLV(d *ttlv.Decoder, tag int) error { 80 | return d.Struct(tag, func(d *ttlv.Decoder) error { 81 | if err := d.Any(&pv.Operation); err != nil { 82 | return err 83 | } 84 | if err := d.Opt(TagUniqueBatchItemID, &pv.UniqueBatchItemID); err != nil { 85 | return err 86 | } 87 | pv.RequestPayload = newRequestPayload(pv.Operation) 88 | if err := d.TagAny(TagRequestPayload, &pv.RequestPayload); err != nil { 89 | return err 90 | } 91 | return d.Opt(TagMessageExtension, &pv.MessageExtension) 92 | }) 93 | } 94 | -------------------------------------------------------------------------------- /responses.go: -------------------------------------------------------------------------------- 1 | package kmip 2 | 3 | import ( 4 | "fmt" 5 | "time" 6 | 7 | "github.com/ovh/kmip-go/ttlv" 8 | ) 9 | 10 | type ResponseMessage struct { 11 | Header ResponseHeader 12 | BatchItem []ResponseBatchItem 13 | } 14 | 15 | type ResponseHeader struct { 16 | ProtocolVersion ProtocolVersion `ttlv:",set-version"` 17 | TimeStamp time.Time 18 | Nonce *Nonce `ttlv:",version=v1.2.."` 19 | AttestationType []AttestationType `ttlv:",version=v1.2.."` 20 | ClientCorrelationValue string `ttlv:",omitempty,version=v1.4.."` 21 | ServerCorrelationValue string `ttlv:",omitempty,version=v1.4.."` 22 | BatchCount int32 23 | } 24 | 25 | type ResponseBatchItem struct { 26 | Operation Operation `ttlv:",omitempty"` 27 | UniqueBatchItemID []byte `ttlv:",omitempty"` 28 | ResultStatus ResultStatus 29 | ResultReason ResultReason `ttlv:",omitempty"` 30 | ResultMessage string `ttlv:",omitempty"` 31 | AsynchronousCorrelationValue []byte `ttlv:",omitempty"` 32 | ResponsePayload OperationPayload 33 | MessageExtension *MessageExtension 34 | } 35 | 36 | func (bi *ResponseBatchItem) Err() error { 37 | if bi.ResultStatus != ResultStatusSuccess { 38 | msg := bi.ResultMessage 39 | return fmt.Errorf("Operation failed (status=%q, reason=%q) %s", ttlv.EnumStr(bi.ResultStatus), ttlv.EnumStr(bi.ResultReason), msg) 40 | } 41 | return nil 42 | } 43 | 44 | func (pv *ResponseBatchItem) TagEncodeTTLV(e *ttlv.Encoder, tag int) { 45 | e.Struct(TagBatchItem, func(e *ttlv.Encoder) { 46 | if pv.Operation != 0 { 47 | e.Any(pv.Operation) 48 | } 49 | if len(pv.UniqueBatchItemID) > 0 { 50 | e.ByteString(TagUniqueBatchItemID, pv.UniqueBatchItemID) 51 | } 52 | e.Any(pv.ResultStatus) 53 | if pv.ResultStatus != ResultStatusSuccess || pv.ResultReason != 0 { 54 | e.Any(pv.ResultReason) 55 | } 56 | if pv.ResultMessage != "" { 57 | e.TextString(TagResultMessage, pv.ResultMessage) 58 | } 59 | if len(pv.AsynchronousCorrelationValue) > 0 { 60 | e.ByteString(TagAsynchronousCorrelationValue, pv.AsynchronousCorrelationValue) 61 | } 62 | e.TagAny(TagResponsePayload, pv.ResponsePayload) 63 | if pv.MessageExtension != nil { 64 | e.Any(pv.MessageExtension) 65 | } 66 | }) 67 | } 68 | 69 | func (pv *ResponseBatchItem) TagDecodeTTLV(d *ttlv.Decoder, tag int) error { 70 | return d.Struct(tag, func(d *ttlv.Decoder) error { 71 | if err := d.Opt(TagOperation, &pv.Operation); err != nil { 72 | return err 73 | } 74 | if err := d.Opt(TagUniqueBatchItemID, &pv.UniqueBatchItemID); err != nil { 75 | return err 76 | } 77 | 78 | if err := d.TagAny(TagResultStatus, &pv.ResultStatus); err != nil { 79 | return err 80 | } 81 | 82 | if err := d.Opt(TagResultReason, &pv.ResultReason); err != nil { 83 | return err 84 | } 85 | if err := d.Opt(TagResultMessage, &pv.ResultMessage); err != nil { 86 | return err 87 | } 88 | if err := d.Opt(TagAsynchronousCorrelationValue, &pv.AsynchronousCorrelationValue); err != nil { 89 | return err 90 | } 91 | if pv.Operation > 0 && d.Tag() == TagResponsePayload { 92 | pv.ResponsePayload = newResponsePayload(pv.Operation) 93 | return d.TagAny(TagResponsePayload, &pv.ResponsePayload) 94 | } 95 | return d.Opt(TagMessageExtension, &pv.MessageExtension) 96 | }) 97 | } 98 | -------------------------------------------------------------------------------- /ttlv/decoder_test.go: -------------------------------------------------------------------------------- 1 | package ttlv 2 | 3 | import ( 4 | "reflect" 5 | "testing" 6 | "time" 7 | ) 8 | 9 | func init() { 10 | RegisterTag("Foo", 0x1234) 11 | RegisterTag("Bar", 0x2222) 12 | RegisterTag("Toto", 0x6666, reflect.TypeFor[Baz]()) 13 | RegisterTag("MyStruct", 1, reflect.TypeFor[MyStruct]()) 14 | 15 | RegisterEnum[TheEnum](0x9999, nil) 16 | // ttlv.RegisterType[MyStruct]() 17 | } 18 | 19 | type TheEnum uint32 20 | 21 | const ( 22 | Enum12 TheEnum = 12 23 | ) 24 | 25 | type Foo int32 26 | type Baz int32 27 | 28 | type MyStruct struct { 29 | Skipped bool `ttlv:"-"` 30 | Enum TheEnum 31 | Int int32 `ttlv:"Foo"` 32 | Foo int32 33 | Oof Foo 34 | Baz Baz 35 | Bool bool `ttlv:"0x1111"` 36 | Bar []string `ttlv:""` 37 | Time time.Time `ttlv:"0x3333"` 38 | Duration time.Duration `ttlv:"0x4444"` 39 | // Any any `ttlv:"0x0000"` 40 | OptionalPtr *string `ttlv:"0x0002"` 41 | Optional string `ttlv:"0x0002,omitempty"` 42 | } 43 | 44 | var v = &MyStruct{ 45 | Enum: Enum12, 46 | Int: 12, 47 | Foo: 13, 48 | Oof: 14, 49 | Baz: 15, 50 | Bool: true, 51 | Bar: []string{"foo", "bar"}, 52 | Time: time.Now().Round(time.Second), 53 | Duration: time.Hour, 54 | // Any: "abcd", 55 | } 56 | 57 | func (m *MyStruct) encodeTTLV(e *Encoder) { 58 | e.Struct(1, func(e *Encoder) { 59 | e.Enum(0, 0x9999, uint32(m.Enum)) 60 | // e.Any(m.Enum) 61 | e.Integer(0x1234, m.Int) 62 | e.Integer(0x1234, m.Foo) 63 | e.Integer(0x1234, int32(m.Oof)) 64 | e.Integer(0x6666, int32(m.Baz)) 65 | 66 | e.Bool(0x1111, m.Bool) 67 | for _, s := range m.Bar { 68 | e.TextString(0x2222, s) 69 | } 70 | e.DateTime(0x3333, m.Time) 71 | e.Interval(0x4444, m.Duration) 72 | // e.TagAny(0x0000, m.Any) 73 | }) 74 | } 75 | 76 | func TestDecoderReflect(t *testing.T) { 77 | // data := MarshalTTLV(&v) 78 | enc := NewTTLVEncoder() 79 | v.encodeTTLV(&enc) 80 | data := enc.Bytes() 81 | // f := decodeFunc(reflect.TypeFor[*MyStruct]()) 82 | // dec, err := NewDecoder(data) 83 | // if err != nil { 84 | // panic(err) 85 | // } 86 | val := MyStruct{} 87 | if err := UnmarshalTTLV(data, &val); err != nil { 88 | panic(err) 89 | } 90 | // if err := f(&dec, 1, reflect.ValueOf(&val)); err != nil { 91 | // panic(err) 92 | // } 93 | // assert.Equal(t, v, &val) 94 | } 95 | -------------------------------------------------------------------------------- /ttlv/encoder_test.go: -------------------------------------------------------------------------------- 1 | package ttlv_test 2 | 3 | import ( 4 | "encoding/hex" 5 | "reflect" 6 | "strings" 7 | "testing" 8 | "time" 9 | 10 | "github.com/ovh/kmip-go/ttlv" 11 | 12 | "github.com/stretchr/testify/assert" 13 | ) 14 | 15 | func init() { 16 | ttlv.RegisterTag("Foo", 0x1234) 17 | ttlv.RegisterTag("Bar", 0x2222) 18 | ttlv.RegisterTag("Toto", 0x6666, reflect.TypeFor[Baz]()) 19 | 20 | ttlv.RegisterEnum[TheEnum](0x9999, nil) 21 | // ttlv.RegisterType[MyStruct]() 22 | } 23 | 24 | type TheEnum uint32 25 | 26 | const ( 27 | Enum12 TheEnum = 12 28 | ) 29 | 30 | type Foo int32 31 | type Baz int32 32 | 33 | type MyStruct struct { 34 | Skipped bool `ttlv:"-"` 35 | Enum TheEnum 36 | Int int32 `ttlv:"Foo"` 37 | Foo int32 38 | Oof Foo 39 | Baz Baz 40 | Bool bool `ttlv:"0x1111"` 41 | Bar []string `ttlv:""` 42 | Time time.Time `ttlv:"0x3333"` 43 | Duration time.Duration `ttlv:"0x4444"` 44 | Any any `ttlv:"0x0001"` 45 | OptionalPtr *string `ttlv:"0x0002"` 46 | Optional string `ttlv:"0x0002,omitempty"` 47 | } 48 | 49 | var v = &MyStruct{ 50 | Enum: Enum12, 51 | Int: 12, 52 | Foo: 13, 53 | Oof: 14, 54 | Baz: 15, 55 | Bool: true, 56 | Bar: []string{"foo", "bar"}, 57 | Time: time.Now(), 58 | Duration: time.Hour, 59 | Any: "abcd", 60 | } 61 | 62 | func (m *MyStruct) encodeTTLV(e *ttlv.Encoder) { 63 | e.Struct(1, func(e *ttlv.Encoder) { 64 | e.Enum(0, 0x9999, uint32(m.Enum)) 65 | // e.Any(m.Enum) 66 | e.Integer(0x1234, m.Int) 67 | e.Integer(0x1234, m.Foo) 68 | e.Integer(0x1234, int32(m.Oof)) 69 | e.Integer(0x6666, int32(m.Baz)) 70 | 71 | e.Bool(0x1111, m.Bool) 72 | for _, s := range m.Bar { 73 | e.TextString(0x2222, s) 74 | } 75 | e.DateTime(0x3333, m.Time) 76 | e.Interval(0x4444, m.Duration) 77 | e.TagAny(0x0001, m.Any) 78 | }) 79 | } 80 | 81 | // func TestEncoderReflect(t *testing.T) { 82 | // enc := Encoder{} 83 | // v := &MyStruct{Int: 12, Bool: true, Str: []string{"foo", "bar"}} 84 | // enc.reflectEncode(1, reflect.ValueOf(v)) 85 | 86 | // expectedEnc := Encoder{} 87 | // v.encodeTTLV(&expectedEnc) 88 | 89 | // assert.Equal(t, strings.ToUpper(hex.EncodeToString(expectedEnc.buf)), strings.ToUpper(hex.EncodeToString(enc.buf))) 90 | // } 91 | 92 | func TestEncoderReflectFunc(t *testing.T) { 93 | enc := ttlv.MarshalTTLV(v) 94 | 95 | expectedEnc := ttlv.NewTTLVEncoder() 96 | v.encodeTTLV(&expectedEnc) 97 | 98 | assert.Equal(t, strings.ToUpper(hex.EncodeToString(expectedEnc.Bytes())), strings.ToUpper(hex.EncodeToString(enc))) 99 | } 100 | 101 | func BenchmarkEncodeManual(b *testing.B) { 102 | enc := ttlv.NewTTLVEncoder() 103 | b.ResetTimer() 104 | for range b.N { 105 | enc.Clear() 106 | v.encodeTTLV(&enc) 107 | } 108 | } 109 | 110 | func BenchmarkEncodeAny(b *testing.B) { 111 | enc := ttlv.NewTTLVEncoder() 112 | b.ResetTimer() 113 | for range b.N { 114 | enc.Clear() 115 | enc.TagAny(1, v) 116 | } 117 | } 118 | -------------------------------------------------------------------------------- /ttlv/encoding_text_test.go: -------------------------------------------------------------------------------- 1 | package ttlv 2 | 3 | import ( 4 | "math/big" 5 | "testing" 6 | "time" 7 | 8 | "github.com/stretchr/testify/assert" 9 | ) 10 | 11 | func TestTextEncoding(t *testing.T) { 12 | dt := time.Date(2024, time.September, 16, 15, 7, 42, 0, time.UTC) 13 | w := newTextWriter() 14 | w.Struct(0x6666, func(w writer) { 15 | w.Integer(0x2341, 12) 16 | w.LongInteger(0x7634, 1234567890) 17 | w.Bool(0x7777, true) 18 | w.Struct(0x3333, func(w writer) {}) 19 | w.TextString(0x8888, "hello world") 20 | w.ByteString(0x9999, []byte{1, 2, 3, 4}) 21 | w.DateTime(0x3456, dt) 22 | w.Interval(0x8724, 3*time.Minute+42*time.Second) 23 | w.BigInteger(0x872573, big.NewInt(-123456789101112)) 24 | w.Bitmask(0, 0x5642, 1|2) 25 | w.Enum(0, 0x67342, 12) 26 | }) 27 | expect := `Toto (Structure): 28 | 0x002341 (Integer): 12 29 | 0x007634 (LongInteger): 1234567890 30 | 0x007777 (Boolean): true 31 | 0x003333 (Structure): 32 | ... empty ... 33 | 0x008888 (TextString): hello world 34 | 0x009999 (ByteString): 01020304 35 | 0x003456 (DateTime): 2024-09-16T15:07:42Z 36 | 0x008724 (Interval): 3m42s 37 | 0x872573 (BigInteger): -123456789101112 38 | 0x005642 (Integer): 0x00000001 | 0x00000002 39 | 0x067342 (Enumeration): 0x0000000C` 40 | assert.Equal(t, expect, string(w.Bytes())) 41 | w.Clear() 42 | assert.Empty(t, w.Bytes()) 43 | //TODO: Test named tags, named enums and named bitmasks 44 | } 45 | -------------------------------------------------------------------------------- /ttlv/io.go: -------------------------------------------------------------------------------- 1 | package ttlv 2 | 3 | import ( 4 | "io" 5 | "slices" 6 | ) 7 | 8 | func computeNeededBytes(buf []byte) int { 9 | if len(buf) < 8 { 10 | return 8 11 | } 12 | dec := ttlvReader{buf: buf} 13 | return 8 + dec.paddedLen() 14 | } 15 | 16 | // Stream is a helper type to wrap io.ReadWrite stream to serialize and deserialize 17 | // binary TTLV encoded golang types to / from the stream. 18 | type Stream struct { 19 | inner io.ReadWriteCloser 20 | max int 21 | } 22 | 23 | // NewStream creates a new TTLV stream around the given I/O stream. 24 | // If maxSize is greater than 0, then it will limit the maximum allowed 25 | // size in bytes for a message to receive. 26 | func NewStream(inner io.ReadWriteCloser, maxSize int) Stream { 27 | return Stream{ 28 | inner: inner, 29 | max: maxSize, 30 | } 31 | } 32 | 33 | // Close wloses the inner stream. 34 | func (s *Stream) Close() error { 35 | if s.inner == nil { 36 | return nil 37 | } 38 | return s.inner.Close() 39 | } 40 | 41 | // Send serializes to TTLV binary the given `msg` then writes it to the inner stream. 42 | func (s *Stream) Send(msg any) error { 43 | data := MarshalTTLV(msg) 44 | _, err := s.inner.Write(data) 45 | return err 46 | } 47 | 48 | // Recv reads the next TTLV binary payload from the inner stream, then deserialize it into the value pointed 49 | // by `msg`. Note that `msg` must be a pointer. 50 | func (s *Stream) Recv(msg any) error { 51 | read := 0 52 | buf := make([]byte, 512) 53 | need := 8 54 | for { 55 | if need > cap(buf) { 56 | buf = slices.Grow(buf, need-cap(buf)) 57 | } 58 | n, err := s.inner.Read(buf[read:need]) 59 | if err != nil { 60 | return err 61 | } 62 | if n == 0 { 63 | if read == 0 { 64 | return io.ErrUnexpectedEOF 65 | } 66 | return io.EOF 67 | } 68 | read += n 69 | need = computeNeededBytes(buf[:read]) 70 | if s.max > 0 && need > s.max { 71 | return Errorf("Message is too big. Max allowed size is %d bytes", s.max) 72 | } 73 | if read >= need { 74 | return UnmarshalTTLV(buf[:need], msg) 75 | } 76 | } 77 | } 78 | 79 | // Roundtrip simply perform a Send() followed by a Recv(), 80 | // sending `req` then receiving `resp`. 81 | func (s *Stream) Roundtrip(req, resp any) error { 82 | if err := s.Send(req); err != nil { 83 | return err 84 | } 85 | return s.Recv(resp) 86 | } 87 | -------------------------------------------------------------------------------- /ttlv/reflect.go: -------------------------------------------------------------------------------- 1 | package ttlv 2 | 3 | import ( 4 | "reflect" 5 | "strconv" 6 | "strings" 7 | ) 8 | 9 | type fieldInfo struct { 10 | tag string 11 | omitempty bool 12 | vrange *versionRange 13 | setVersion bool 14 | } 15 | 16 | func getFieldInfo(fldT reflect.StructField) fieldInfo { 17 | tagVal, _ := fldT.Tag.Lookup("ttlv") 18 | return parseFieldInfo(tagVal) 19 | } 20 | 21 | func parseFieldInfo(s string) fieldInfo { 22 | parts := strings.Split(s, ",") 23 | ann := fieldInfo{tag: parts[0]} 24 | 25 | for _, part := range parts[1:] { 26 | if part == "omitempty" { 27 | ann.omitempty = true 28 | continue 29 | } 30 | if part == "set-version" { 31 | ann.setVersion = true 32 | continue 33 | } 34 | parts := strings.Split(part, "=") 35 | if len(parts) != 2 { 36 | panic("invalid sub-tag " + part) 37 | } 38 | if parts[0] == "version" { 39 | vrange, err := parseVersionRange(parts[1]) 40 | if err != nil { 41 | panic("Invalid sub-tag version range: " + err.Error()) 42 | } 43 | ann.vrange = &vrange 44 | continue 45 | } 46 | panic("invalid sub-tag " + part) 47 | } 48 | 49 | return ann 50 | } 51 | 52 | func getFieldTag(fldT reflect.StructField, tagVal string) int { 53 | if tagVal == "" { 54 | // if fldT.Type.Implements(reflect.TypeFor[Encodable]()) { 55 | // // FIXME: How to pass a custom tag if any ? 56 | // fieldsEncode = append(fieldsEncode, func(e *Encoder, v reflect.Value) { 57 | // if encodable := v.Field(i).Interface(); encodable != nil { 58 | // encodable.(Encodable).EncodeTTLV(e) 59 | // } 60 | // }) 61 | // continue 62 | // } 63 | if tg, err := getTagByName(fldT.Name); err == nil { 64 | // Check if we already know a tag with the same name as the field 65 | return tg 66 | } else if tg, err := getTagForType(fldT.Type); err == nil { 67 | // if not check if we know the default tag for this type (either explicitly registered, or fallback to type name) 68 | return tg 69 | } 70 | return 0 71 | } 72 | 73 | if strings.HasPrefix(tagVal, "0x") { 74 | n, err := strconv.ParseInt(tagVal[2:], 16, 0) 75 | if err != nil { 76 | panic(err) 77 | } 78 | if n <= 0 { 79 | panic("the tag must be strictly positive") 80 | } 81 | if n > 0xFFFFFF { 82 | panic("the tag cannot be bigger than 3 bytes") 83 | } 84 | return int(n) 85 | } 86 | 87 | numTag, err := getTagByName(tagVal) 88 | if err != nil { 89 | panic(err) 90 | } 91 | return numTag 92 | } 93 | -------------------------------------------------------------------------------- /ttlv/types.go: -------------------------------------------------------------------------------- 1 | package ttlv 2 | 3 | import "fmt" 4 | 5 | // Type is a TTLV encoding type as defined in the [KMIP 1.4 specification, section 9.1.1.2] 6 | // for TTLV encoding. 7 | // 8 | // [KMIP 1.4 specification, section 9.1.1.2]: http://docs.oasis-open.org/kmip/spec/v1.4/os/kmip-spec-v1.4-os.html#_Toc490660914 9 | type Type uint8 10 | 11 | const ( 12 | TypeStructure Type = 0x01 + iota 13 | TypeInteger 14 | TypeLongInteger 15 | TypeBigInteger 16 | TypeEnumeration 17 | TypeBoolean 18 | TypeTextString 19 | TypeByteString 20 | TypeDateTime 21 | TypeInterval 22 | ) 23 | 24 | func (ty Type) String() string { 25 | if n, ok := typesName[ty]; ok { 26 | return n 27 | } 28 | return fmt.Sprintf("Unknown(%02X)", uint8(ty)) 29 | } 30 | 31 | // typeFromName returns the type for the given normalized name string. 32 | // It returns (0, false) if the name is not valid, otherwise it 33 | // returns the type and true. 34 | // 35 | // The function is case sensitive, and the type mus mathc the camel case as 36 | // defined in the standard. 37 | func typeFromName(name string) (Type, bool) { 38 | ty, ok := nameTypes[name] 39 | return ty, ok 40 | } 41 | 42 | var ( 43 | typesName = map[Type]string{ 44 | TypeStructure: "Structure", 45 | TypeInteger: "Integer", 46 | TypeLongInteger: "LongInteger", 47 | TypeBigInteger: "BigInteger", 48 | TypeEnumeration: "Enumeration", 49 | TypeBoolean: "Boolean", 50 | TypeTextString: "TextString", 51 | TypeByteString: "ByteString", 52 | TypeDateTime: "DateTime", 53 | TypeInterval: "Interval", 54 | } 55 | nameTypes = revMap(typesName) 56 | ) 57 | -------------------------------------------------------------------------------- /ttlv/utils.go: -------------------------------------------------------------------------------- 1 | package ttlv 2 | 3 | import ( 4 | "fmt" 5 | "math/big" 6 | "math/bits" 7 | "strconv" 8 | "strings" 9 | ) 10 | 11 | func padForLen(l, padSize int) int { 12 | return (padSize - l%padSize) % padSize 13 | } 14 | 15 | func bigIntToBytes(value *big.Int, padding int) (b []byte, padVal byte, padLen int) { 16 | if padding < 1 { 17 | padding = 1 18 | } 19 | b = value.Bytes() 20 | padVal = byte(0) 21 | padLen = padForLen(len(b), padding) 22 | if value.Sign() < 0 { 23 | padVal = 0xFF 24 | carry := byte(1) 25 | for i := len(b) - 1; i >= 0; i-- { 26 | b[i] = ^b[i] + carry 27 | if carry > 0 && b[i] != 0 { 28 | carry = 0 29 | } 30 | } 31 | } else if value.Sign() == 0 { 32 | return []byte{}, 0, padding 33 | } 34 | if (b[0]>>7)&1 != (padVal&1) && padLen == 0 { 35 | padLen = padding 36 | } 37 | return b, padVal, padLen 38 | } 39 | 40 | func bytesToBigInt(v []byte) *big.Int { 41 | if bits.LeadingZeros8(v[0]) > 0 { 42 | // Positive integer 43 | bv := big.NewInt(0).SetBytes(v) 44 | return bv 45 | } 46 | // Negative integer 47 | bv := big.NewInt(0) 48 | carry := byte(1) 49 | for i := len(v) - 1; i >= 0; i-- { 50 | v[i] = ^(v[i] - carry) 51 | if carry > 0 && v[i] != 0 { 52 | carry = 0 53 | } 54 | } 55 | bv.SetBytes(v) 56 | bv.Neg(bv) 57 | return bv 58 | } 59 | 60 | // revMap reverses the given map. Values in the map must be unique or the function will panic. 61 | func revMap[K, V comparable](m map[K]V) map[V]K { 62 | res := make(map[V]K, len(m)) 63 | for k, v := range m { 64 | if _, ok := res[v]; ok { 65 | panic(fmt.Sprintf("Duplicate map key: %+v", v)) 66 | } 67 | res[v] = k 68 | } 69 | return res 70 | } 71 | 72 | func parseInt(val string, bits int) (int64, error) { 73 | if strings.HasPrefix(val, "0x") { 74 | ui, err := strconv.ParseUint(val[2:], 16, bits) 75 | //nolint:gosec // this cast is safe as we are parsing a hex value 76 | return int64(ui), err 77 | } else { 78 | return strconv.ParseInt(val, 10, bits) 79 | } 80 | } 81 | 82 | func parseUint(val string, bits int) (uint64, error) { 83 | if strings.HasPrefix(val, "0x") { 84 | ui, err := strconv.ParseUint(val[2:], 16, bits) 85 | return ui, err 86 | } else { 87 | return strconv.ParseUint(val, 10, bits) 88 | } 89 | } 90 | -------------------------------------------------------------------------------- /ttlv/value.go: -------------------------------------------------------------------------------- 1 | package ttlv 2 | 3 | import ( 4 | "fmt" 5 | "math/big" 6 | "time" 7 | ) 8 | 9 | // Enum is a generic TTLV enum value. 10 | type Enum uint32 11 | 12 | // Struct is a generic TTLV struct container. 13 | type Struct []Value 14 | 15 | func (v Struct) TagEncodeTTLV(e *Encoder, tag int) { 16 | e.Struct(tag, func(e *Encoder) { 17 | for _, f := range v { 18 | f.EncodeTTLV(e) 19 | } 20 | }) 21 | } 22 | 23 | func (v *Struct) TagDecodeTTLV(d *Decoder, tag int) error { 24 | return d.Struct(tag, func(d *Decoder) error { 25 | for d.Tag() != 0 { 26 | field := Value{} 27 | if err := field.DecodeTTLV(d); err != nil { 28 | return err 29 | } 30 | *v = append(*v, field) 31 | } 32 | return nil 33 | }) 34 | } 35 | 36 | // Value is a generic TTLV tagged value. 37 | type Value struct { 38 | // The value's TTLV tag. 39 | Tag int 40 | // The TTLV value. 41 | Value any 42 | } 43 | 44 | func (v *Value) DecodeTTLV(d *Decoder) error { 45 | return v.TagDecodeTTLV(d, d.Tag()) 46 | } 47 | 48 | func (v *Value) TagDecodeTTLV(d *Decoder, tag int) error { 49 | var err error 50 | ty := d.Type() 51 | switch ty { 52 | case TypeInteger: 53 | v.Value, err = d.Integer(tag) 54 | case TypeLongInteger: 55 | v.Value, err = d.LongInteger(tag) 56 | case TypeBigInteger: 57 | v.Value, err = d.BigInteger(tag) 58 | case TypeBoolean: 59 | v.Value, err = d.Bool(tag) 60 | case TypeByteString: 61 | v.Value, err = d.ByteString(tag) 62 | case TypeDateTime: 63 | v.Value, err = d.DateTime(tag) 64 | case TypeEnumeration: 65 | var enum uint32 66 | enum, err = d.Enum(0, tag) 67 | v.Value = Enum(enum) 68 | case TypeInterval: 69 | v.Value, err = d.Interval(tag) 70 | case TypeTextString: 71 | v.Value, err = d.TextString(tag) 72 | case TypeStructure: 73 | val := Struct{} 74 | err = val.TagDecodeTTLV(d, tag) 75 | v.Value = val 76 | default: 77 | return fmt.Errorf("Unsupported TTLV type %s", ty.String()) 78 | } 79 | if err != nil { 80 | return err 81 | } 82 | v.Tag = tag 83 | return nil 84 | } 85 | 86 | func (v Value) EncodeTTLV(e *Encoder) { 87 | v.TagEncodeTTLV(e, v.Tag) 88 | } 89 | 90 | func (v Value) TagEncodeTTLV(e *Encoder, tag int) { 91 | switch val := v.Value.(type) { 92 | case int32: 93 | e.Integer(tag, val) 94 | case int64: 95 | e.LongInteger(tag, val) 96 | case *big.Int: 97 | e.BigInteger(tag, val) 98 | case bool: 99 | e.Bool(tag, val) 100 | case []byte: 101 | e.ByteString(tag, val) 102 | case time.Time: 103 | e.DateTime(tag, val) 104 | case Enum: 105 | e.Enum(0, tag, uint32(val)) 106 | case time.Duration: 107 | e.Interval(tag, val) 108 | case string: 109 | e.TextString(tag, val) 110 | case Struct: 111 | val.TagEncodeTTLV(e, tag) 112 | default: 113 | panic(fmt.Sprintf("Unsupported type %T", val)) 114 | } 115 | } 116 | -------------------------------------------------------------------------------- /ttlv/value_test.go: -------------------------------------------------------------------------------- 1 | package ttlv 2 | 3 | import ( 4 | "math/big" 5 | "testing" 6 | "time" 7 | 8 | "github.com/stretchr/testify/assert" 9 | ) 10 | 11 | func TestDecodeValue(t *testing.T) { 12 | for _, tc := range []struct { 13 | name string 14 | enc func() Encoder 15 | marshal func(any) []byte 16 | unmarshal func([]byte, any) error 17 | }{ 18 | {"TTLV", NewTTLVEncoder, MarshalTTLV, UnmarshalTTLV}, 19 | {"XML", NewXMLEncoder, MarshalXML, UnmarshalXML}, 20 | } { 21 | 22 | t.Run(tc.name, func(t *testing.T) { 23 | now := time.Now().Round(time.Second) 24 | enc := tc.enc() 25 | enc.Struct(12, func(e *Encoder) { 26 | enc.Integer(1, 1) 27 | enc.BigInteger(2, big.NewInt(2)) 28 | enc.LongInteger(3, 3) 29 | enc.Bool(4, true) 30 | enc.ByteString(5, []byte{5}) 31 | enc.TextString(6, "6") 32 | enc.DateTime(7, now) 33 | enc.Interval(8, 8*time.Second) 34 | enc.Enum(0, 9, 9) 35 | }) 36 | 37 | bytes := enc.Bytes() 38 | 39 | val := Value{} 40 | err := tc.unmarshal(bytes, &val) 41 | assert.NoError(t, err) 42 | marsh := tc.marshal(val) 43 | assert.Equal(t, bytes, marsh) 44 | }) 45 | } 46 | } 47 | --------------------------------------------------------------------------------