diff --git a/LICENSE.txt b/LICENSE.txt new file mode 100644 index 0000000..261eeb9 --- /dev/null +++ b/LICENSE.txt @@ -0,0 +1,201 @@ + Apache License + Version 2.0, January 2004 + http://www.apache.org/licenses/ + + TERMS AND CONDITIONS FOR USE, REPRODUCTION, AND DISTRIBUTION + + 1. Definitions. + + "License" shall mean the terms and conditions for use, reproduction, + and distribution as defined by Sections 1 through 9 of this document. + + "Licensor" shall mean the copyright owner or entity authorized by + the copyright owner that is granting the License. + + "Legal Entity" shall mean the union of the acting entity and all + other entities that control, are controlled by, or are under common + control with that entity. For the purposes of this definition, + "control" means (i) the power, direct or indirect, to cause the + direction or management of such entity, whether by contract or + otherwise, or (ii) ownership of fifty percent (50%) or more of the + outstanding shares, or (iii) beneficial ownership of such entity. + + "You" (or "Your") shall mean an individual or Legal Entity + exercising permissions granted by this License. + + "Source" form shall mean the preferred form for making modifications, + including but not limited to software source code, documentation + source, and configuration files. + + "Object" form shall mean any form resulting from mechanical + transformation or translation of a Source form, including but + not limited to compiled object code, generated documentation, + and conversions to other media types. + + "Work" shall mean the work of authorship, whether in Source or + Object form, made available under the License, as indicated by a + copyright notice that is included in or attached to the work + (an example is provided in the Appendix below). + + "Derivative Works" shall mean any work, whether in Source or Object + form, that is based on (or derived from) the Work and for which the + editorial revisions, annotations, elaborations, or other modifications + represent, as a whole, an original work of authorship. For the purposes + of this License, Derivative Works shall not include works that remain + separable from, or merely link (or bind by name) to the interfaces of, + the Work and Derivative Works thereof. + + "Contribution" shall mean any work of authorship, including + the original version of the Work and any modifications or additions + to that Work or Derivative Works thereof, that is intentionally + submitted to Licensor for inclusion in the Work by the copyright owner + or by an individual or Legal Entity authorized to submit on behalf of + the copyright owner. For the purposes of this definition, "submitted" + means any form of electronic, verbal, or written communication sent + to the Licensor or its representatives, including but not limited to + communication on electronic mailing lists, source code control systems, + and issue tracking systems that are managed by, or on behalf of, the + Licensor for the purpose of discussing and improving the Work, but + excluding communication that is conspicuously marked or otherwise + designated in writing by the copyright owner as "Not a Contribution." + + "Contributor" shall mean Licensor and any individual or Legal Entity + on behalf of whom a Contribution has been received by Licensor and + subsequently incorporated within the Work. + + 2. Grant of Copyright License. Subject to the terms and conditions of + this License, each Contributor hereby grants to You a perpetual, + worldwide, non-exclusive, no-charge, royalty-free, irrevocable + copyright license to reproduce, prepare Derivative Works of, + publicly display, publicly perform, sublicense, and distribute the + Work and such Derivative Works in Source or Object form. + + 3. Grant of Patent License. Subject to the terms and conditions of + this License, each Contributor hereby grants to You a perpetual, + worldwide, non-exclusive, no-charge, royalty-free, irrevocable + (except as stated in this section) patent license to make, have made, + use, offer to sell, sell, import, and otherwise transfer the Work, + where such license applies only to those patent claims licensable + by such Contributor that are necessarily infringed by their + Contribution(s) alone or by combination of their Contribution(s) + with the Work to which such Contribution(s) was submitted. If You + institute patent litigation against any entity (including a + cross-claim or counterclaim in a lawsuit) alleging that the Work + or a Contribution incorporated within the Work constitutes direct + or contributory patent infringement, then any patent licenses + granted to You under this License for that Work shall terminate + as of the date such litigation is filed. + + 4. Redistribution. You may reproduce and distribute copies of the + Work or Derivative Works thereof in any medium, with or without + modifications, and in Source or Object form, provided that You + meet the following conditions: + + (a) You must give any other recipients of the Work or + Derivative Works a copy of this License; and + + (b) You must cause any modified files to carry prominent notices + stating that You changed the files; and + + (c) You must retain, in the Source form of any Derivative Works + that You distribute, all copyright, patent, trademark, and + attribution notices from the Source form of the Work, + excluding those notices that do not pertain to any part of + the Derivative Works; and + + (d) If the Work includes a "NOTICE" text file as part of its + distribution, then any Derivative Works that You distribute must + include a readable copy of the attribution notices contained + within such NOTICE file, excluding those notices that do not + pertain to any part of the Derivative Works, in at least one + of the following places: within a NOTICE text file distributed + as part of the Derivative Works; within the Source form or + documentation, if provided along with the Derivative Works; or, + within a display generated by the Derivative Works, if and + wherever such third-party notices normally appear. The contents + of the NOTICE file are for informational purposes only and + do not modify the License. You may add Your own attribution + notices within Derivative Works that You distribute, alongside + or as an addendum to the NOTICE text from the Work, provided + that such additional attribution notices cannot be construed + as modifying the License. + + You may add Your own copyright statement to Your modifications and + may provide additional or different license terms and conditions + for use, reproduction, or distribution of Your modifications, or + for any such Derivative Works as a whole, provided Your use, + reproduction, and distribution of the Work otherwise complies with + the conditions stated in this License. + + 5. Submission of Contributions. Unless You explicitly state otherwise, + any Contribution intentionally submitted for inclusion in the Work + by You to the Licensor shall be under the terms and conditions of + this License, without any additional terms or conditions. + Notwithstanding the above, nothing herein shall supersede or modify + the terms of any separate license agreement you may have executed + with Licensor regarding such Contributions. + + 6. Trademarks. This License does not grant permission to use the trade + names, trademarks, service marks, or product names of the Licensor, + except as required for reasonable and customary use in describing the + origin of the Work and reproducing the content of the NOTICE file. + + 7. Disclaimer of Warranty. Unless required by applicable law or + agreed to in writing, Licensor provides the Work (and each + Contributor provides its Contributions) on an "AS IS" BASIS, + WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or + implied, including, without limitation, any warranties or conditions + of TITLE, NON-INFRINGEMENT, MERCHANTABILITY, or FITNESS FOR A + PARTICULAR PURPOSE. You are solely responsible for determining the + appropriateness of using or redistributing the Work and assume any + risks associated with Your exercise of permissions under this License. + + 8. Limitation of Liability. In no event and under no legal theory, + whether in tort (including negligence), contract, or otherwise, + unless required by applicable law (such as deliberate and grossly + negligent acts) or agreed to in writing, shall any Contributor be + liable to You for damages, including any direct, indirect, special, + incidental, or consequential damages of any character arising as a + result of this License or out of the use or inability to use the + Work (including but not limited to damages for loss of goodwill, + work stoppage, computer failure or malfunction, or any and all + other commercial damages or losses), even if such Contributor + has been advised of the possibility of such damages. + + 9. Accepting Warranty or Additional Liability. While redistributing + the Work or Derivative Works thereof, You may choose to offer, + and charge a fee for, acceptance of support, warranty, indemnity, + or other liability obligations and/or rights consistent with this + License. However, in accepting such obligations, You may act only + on Your own behalf and on Your sole responsibility, not on behalf + of any other Contributor, and only if You agree to indemnify, + defend, and hold each Contributor harmless for any liability + incurred by, or claims asserted against, such Contributor by reason + of your accepting any such warranty or additional liability. + + END OF TERMS AND CONDITIONS + + APPENDIX: How to apply the Apache License to your work. + + To apply the Apache License to your work, attach the following + boilerplate notice, with the fields enclosed by brackets "[]" + replaced with your own identifying information. (Don't include + the brackets!) The text should be enclosed in the appropriate + comment syntax for the file format. We also recommend that a + file or class name and description of purpose be included on the + same "printed page" as the copyright notice for easier + identification within third-party archives. + + Copyright [yyyy] [name of copyright owner] + + Licensed under the Apache License, Version 2.0 (the "License"); + you may not use this file except in compliance with the License. + You may obtain a copy of the License at + + http://www.apache.org/licenses/LICENSE-2.0 + + Unless required by applicable law or agreed to in writing, software + distributed under the License is distributed on an "AS IS" BASIS, + WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + See the License for the specific language governing permissions and + limitations under the License. diff --git a/client/client_tsb.go b/client/client_tsb.go new file mode 100644 index 0000000..b50c7e2 --- /dev/null +++ b/client/client_tsb.go @@ -0,0 +1,1023 @@ +/* +Copyright (c) 2023 Securosys SA, authors: Tomasz Madej + +Licensed under the Apache License, Version 2.0 (the "License"); +you may not use this file except in compliance with the License. +You may obtain a copy of the License at + + https://www.apache.org/licenses/LICENSE-2.0 + +Unless required by applicable law or agreed to in writing, software +distributed under the License is distributed on an "AS IS" BASIS, +WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +See the License for the specific language governing permissions and +limitations under the License. + +The above copyright notice and this permission notice shall be included +in all copies or substantial portions of the Software. +*/ + +package client + +import ( + "bytes" + "crypto/tls" + "crypto/x509" + "encoding/json" + "fmt" + "io/ioutil" + "log" + "net/http" + "strconv" + "time" + + helpers "securosys.ch/helpers" +) + +// HostURL - Default Securosys TSB URL +const HostURL string = "" + +// TSBClient struct +type TSBClient struct { + HostURL string + HTTPClient *http.Client + Auth AuthStruct +} +type AuthStruct struct { + AuthType string `json:"auth"` + CertPath string `json:"certpath"` + KeyPath string `json:"keypath"` + BearerToken string `json:"bearertoken"` + BasicToken string `json:"basictoken"` + Username string `json:"username"` + Password string `json:"password"` +} + +// Function inicialize new client for accessing TSB +func NewTSBClient(data map[string]string) (*TSBClient, error) { + c := TSBClient{ + HTTPClient: &http.Client{Timeout: 9999999 * time.Second}, + // Default Hashicups URL + HostURL: data["restapi"], + Auth: AuthStruct{ + AuthType: data["auth"], + CertPath: data["certpath"], + KeyPath: data["keypath"], + BearerToken: data["bearertoken"], + BasicToken: data["basictoken"], + Username: data["username"], + Password: data["password"], + }, + } + + return &c, nil +} + +// Function thats send request modify key to TSB +func (c *TSBClient) Modify(label string, password string, policy helpers.Policy) (string, error) { + policyJson, _ := json.Marshal(policy) + policyString := string(`,"policy":` + string(policyJson)) + + charsPasswordJson, _ := json.Marshal(helpers.StringToCharArray(password)) + passwordString := "" + if len(charsPasswordJson) > 2 { + passwordString = `"keyPassword": ` + string(charsPasswordJson) + `,` + + } + + var jsonStr = []byte(`{ + "modifyRequest":{ + ` + passwordString + ` + "modifyKeyName": "` + label + `" + ` + policyString + `} + }`) + + req, err := http.NewRequest("POST", c.HostURL+"/v1/synchronousModify", bytes.NewBuffer(jsonStr)) + if err != nil { + return "", err + } + _, errRes, _ := c.doRequest(req) + if errRes != nil { + return "", errRes + } + return label, nil + +} + +// Function thats send asynchronous request modify key to TSB +func (c *TSBClient) AsyncModify(label string, password string, policy helpers.Policy, customMetaData map[string]string) (string, error) { + var additionalMetaDataInfo map[string]string = make(map[string]string) + metaDataB64, metaDataSignature, err := helpers.PrepareMetaData("Modify", additionalMetaDataInfo, customMetaData) + policyJson, _ := json.Marshal(policy) + policyString := string(`,"policy":` + string(policyJson)) + + charsPasswordJson, _ := json.Marshal(helpers.StringToCharArray(password)) + passwordString := "" + if len(charsPasswordJson) > 2 { + passwordString = `"keyPassword": ` + string(charsPasswordJson) + `,` + + } + var jsonStr = []byte(`{ + "modifyRequest":{ + "modifyKeyName": "` + label + `", + ` + passwordString + ` + "metaData": "` + metaDataB64 + `", + "metaDataSignature": "` + metaDataSignature + `" + ` + policyString + `} + }`) + req, err := http.NewRequest("POST", c.HostURL+"/v1/modify", bytes.NewBuffer(jsonStr)) + if err != nil { + return "", err + } + body, errRes, _ := c.doRequest(req) + if errRes != nil { + return "", errRes + } + var result map[string]interface{} + errJSON := json.Unmarshal(body, &result) + if errJSON != nil { + return "", errJSON + } + return result["modifyKeyRequestId"].(string), nil + +} + +// Function thats send wrap request to TSB +func (c *TSBClient) Wrap(wrapKeyName string, wrapKeyPassword string, keyToBeWrapped string, keyToBeWrappedPassword string, wrapMethod string) (map[string]interface{}, error) { + keyToBeWrappedPasswordJson, _ := json.Marshal(helpers.StringToCharArray(keyToBeWrappedPassword)) + wrapKeyPasswordJson, _ := json.Marshal(helpers.StringToCharArray(wrapKeyPassword)) + keyToBeWrappedPasswordString := "" + if len(keyToBeWrappedPasswordJson) > 2 { + keyToBeWrappedPasswordString = `"keyToBeWrappedPassword": ` + string(keyToBeWrappedPasswordJson) + `,` + + } + wrapKeyPasswordString := "" + if len(wrapKeyPasswordJson) > 2 { + wrapKeyPasswordString = `"wrapKeyPassword": ` + string(wrapKeyPasswordJson) + `,` + + } + + var jsonStr = []byte(`{ + "wrapKeyRequest": { + "keyToBeWrapped": "` + keyToBeWrapped + `", + ` + keyToBeWrappedPasswordString + ` + "wrapKeyName": "` + wrapKeyName + `", + ` + wrapKeyPasswordString + ` + "wrapMethod":"` + wrapMethod + `" + } + }`) + + req, err := http.NewRequest("POST", c.HostURL+"/v1/wrap", bytes.NewBuffer(jsonStr)) + if err != nil { + return nil, err + } + body, errRes, _ := c.doRequest(req) + if errRes != nil { + return nil, errRes + } + var response map[string]interface{} + json.Unmarshal(body, &response) + return response, nil + +} + +// Function thats send encrypt request to TSB +func (c *TSBClient) Encrypt(label string, password string, payload string, cipherAlgorithm string, tagLength int, additionalAuthenticationData string) (map[string]interface{}, error) { + charsPasswordJson, _ := json.Marshal(helpers.StringToCharArray(password)) + additionalAuthenticationDataString := `"` + additionalAuthenticationData + `"` + if additionalAuthenticationData == "" { + additionalAuthenticationDataString = "null" + } + tagLengthString := "" + if tagLength != -1 && cipherAlgorithm == "AES_GSM" { + tagLengthString = `"tagLength":` + strconv.Itoa(tagLength) + `,` + } + passwordString := "" + if len(charsPasswordJson) > 2 { + passwordString = `"keyPassword": ` + string(charsPasswordJson) + `,` + + } + + var jsonStr = []byte(`{ + "encryptRequest": { + "payload": "` + payload + `", + ` + passwordString + ` + "encryptKeyName": "` + label + `", + "cipherAlgorithm": "` + cipherAlgorithm + `", + ` + tagLengthString + ` + "additionalAuthenticationData":` + additionalAuthenticationDataString + ` + } + }`) + req, err := http.NewRequest("POST", c.HostURL+"/v1/encrypt", bytes.NewBuffer(jsonStr)) + if err != nil { + return nil, err + } + body, errRes, _ := c.doRequest(req) + if errRes != nil { + return nil, errRes + } + var response map[string]interface{} + json.Unmarshal(body, &response) + if !helpers.ContainsKey(response, "encryptedPayload") || !helpers.ContainsKey(response, "initializationVector") { + return nil, fmt.Errorf("Error on encrypt response. Need encryptedPayload, initializationVector found %s", string(body[:])) + } + return response, nil + +} + +// Function thats send block request to TSB +func (c *TSBClient) Block(label string, password string) (map[string]interface{}, error) { + charsPasswordJson, _ := json.Marshal(helpers.StringToCharArray(password)) + passwordString := "" + if len(charsPasswordJson) > 2 { + passwordString = `"keyPassword": ` + string(charsPasswordJson) + `,` + + } + + var jsonStr = []byte(`{ + "blockRequest": { + ` + passwordString + ` + "blockKeyName": "` + label + `" + } + }`) + + req, err := http.NewRequest("POST", c.HostURL+"/v1/synchronousBlock", bytes.NewBuffer(jsonStr)) + if err != nil { + return nil, err + } + body, errRes, _ := c.doRequest(req) + if errRes != nil { + return nil, errRes + } + var response map[string]interface{} + json.Unmarshal(body, &response) + return response, nil + +} + +// Function thats send asynchronous block request to TSB +func (c *TSBClient) AsyncBlock(label string, password string, customMetaData map[string]string) (string, error) { + charsPasswordJson, _ := json.Marshal(helpers.StringToCharArray(password)) + var additionalMetaDataInfo map[string]string = make(map[string]string) + metaDataB64, metaDataSignature, err := helpers.PrepareMetaData("Block", additionalMetaDataInfo, customMetaData) + passwordString := "" + if len(charsPasswordJson) > 2 { + passwordString = `"keyPassword": ` + string(charsPasswordJson) + `,` + + } + + var jsonStr = []byte(`{ + "blockRequest": { + "blockKeyName": "` + label + `", + ` + passwordString + ` + "metaData": "` + metaDataB64 + `", + "metaDataSignature": "` + metaDataSignature + `" + + } + }`) + + req, err := http.NewRequest("POST", c.HostURL+"/v1/block", bytes.NewBuffer(jsonStr)) + if err != nil { + return "", err + } + body, errRes, _ := c.doRequest(req) + if errRes != nil { + return "", errRes + } + var result map[string]interface{} + errJSON := json.Unmarshal(body, &result) + if errJSON != nil { + return "", errJSON + } + return result["blockKeyRequestId"].(string), nil + +} + +// Function thats send unblock request to TSB +func (c *TSBClient) UnBlock(label string, password string) (map[string]interface{}, error) { + charsPasswordJson, _ := json.Marshal(helpers.StringToCharArray(password)) + passwordString := "" + if len(charsPasswordJson) > 2 { + passwordString = `"keyPassword": ` + string(charsPasswordJson) + `,` + + } + + var jsonStr = []byte(`{ + "unblockRequest": { + ` + passwordString + ` + "unblockKeyName": "` + label + `" + } + }`) + + req, err := http.NewRequest("POST", c.HostURL+"/v1/synchronousUnblock", bytes.NewBuffer(jsonStr)) + if err != nil { + return nil, err + } + body, errRes, _ := c.doRequest(req) + if errRes != nil { + return nil, errRes + } + var response map[string]interface{} + json.Unmarshal(body, &response) + return response, nil + +} + +// Function thats send asynchronous unblock request to TSB +func (c *TSBClient) AsyncUnBlock(label string, password string, customMetaData map[string]string) (string, error) { + charsPasswordJson, _ := json.Marshal(helpers.StringToCharArray(password)) + var additionalMetaDataInfo map[string]string = make(map[string]string) + metaDataB64, metaDataSignature, err := helpers.PrepareMetaData("UnBlock", additionalMetaDataInfo, customMetaData) + passwordString := "" + if len(charsPasswordJson) > 2 { + passwordString = `"keyPassword": ` + string(charsPasswordJson) + `,` + + } + + var jsonStr = []byte(`{ + "unblockRequest": { + "unblockKeyName": "` + label + `", + ` + passwordString + ` + "metaData": "` + metaDataB64 + `", + "metaDataSignature": "` + metaDataSignature + `" + } + }`) + + req, err := http.NewRequest("POST", c.HostURL+"/v1/unblock", bytes.NewBuffer(jsonStr)) + if err != nil { + return "", err + } + body, errRes, _ := c.doRequest(req) + if errRes != nil { + return "", errRes + } + var result map[string]interface{} + errJSON := json.Unmarshal(body, &result) + if errJSON != nil { + return "", errJSON + } + return result["unblockKeyRequestId"].(string), nil +} + +// Function thats sends sign request to TSB +func (c *TSBClient) Sign(label string, password string, payload string, payloadType string, signatureAlgorithm string) (map[string]interface{}, error) { + charsPasswordJson, _ := json.Marshal(helpers.StringToCharArray(password)) + passwordString := "" + if len(charsPasswordJson) > 2 { + passwordString = `"keyPassword": ` + string(charsPasswordJson) + `,` + + } + + var jsonStr = []byte(`{ + "signRequest": { + "payload": "` + payload + `", + "payloadType": "` + payloadType + `", + ` + passwordString + ` + "signKeyName": "` + label + `", + "signatureAlgorithm": "` + signatureAlgorithm + `" + } + }`) + + req, err := http.NewRequest("POST", c.HostURL+"/v1/synchronousSign", bytes.NewBuffer(jsonStr)) + if err != nil { + return nil, err + } + body, errRes, _ := c.doRequest(req) + if errRes != nil { + return nil, errRes + } + var response map[string]interface{} + json.Unmarshal(body, &response) + if !helpers.ContainsKey(response, "signature") { + return nil, fmt.Errorf("Error on sign response. Need signature found %s", string(body[:])) + } + + return response, nil + +} + +// Function thats sends asynchronous sign request to TSB +func (c *TSBClient) AsyncSign(label string, password string, payload string, payloadType string, signatureAlgorithm string, customMetaData map[string]string) (string, error) { + charsPasswordJson, _ := json.Marshal(helpers.StringToCharArray(password)) + var additionalMetaDataInfo map[string]string = make(map[string]string) + additionalMetaDataInfo["payload"] = payload + additionalMetaDataInfo["payload type"] = payloadType + additionalMetaDataInfo["signature algorithm"] = signatureAlgorithm + + metaDataB64, metaDataSignature, err := helpers.PrepareMetaData("Sign", additionalMetaDataInfo, customMetaData) + passwordString := "" + if len(charsPasswordJson) > 2 { + passwordString = `"keyPassword": ` + string(charsPasswordJson) + `,` + + } + + var jsonStr = []byte(`{ + "signRequest": { + "payload": "` + payload + `", + "payloadType": "` + payloadType + `", + ` + passwordString + ` + "signKeyName": "` + label + `", + "signatureAlgorithm": "` + signatureAlgorithm + `", + "metaData": "` + metaDataB64 + `", + "metaDataSignature": "` + metaDataSignature + `" + } + }`) + + req, err := http.NewRequest("POST", c.HostURL+"/v1/sign", bytes.NewBuffer(jsonStr)) + if err != nil { + return "", err + } + body, errRes, _ := c.doRequest(req) + if errRes != nil { + return "", errRes + } + var result map[string]interface{} + errJSON := json.Unmarshal(body, &result) + if errJSON != nil { + return "", errJSON + } + return result["signRequestId"].(string), nil + +} + +// Function thats sends verify request to TSB +func (c *TSBClient) Verify(label string, password string, payload string, signatureAlgorithm string, signature string) (map[string]interface{}, error) { + charsPasswordJson, _ := json.Marshal(helpers.StringToCharArray(password)) + passwordString := "" + if len(charsPasswordJson) > 2 { + passwordString = `"masterKeyPassword": ` + string(charsPasswordJson) + `,` + + } + + var jsonStr = []byte(`{ + "verifySignatureRequest": { + "payload": "` + payload + `", + ` + passwordString + ` + "signKeyName": "` + label + `", + "signatureAlgorithm": "` + signatureAlgorithm + `", + "signature": "` + signature + `" + } + }`) + + req, err := http.NewRequest("POST", c.HostURL+"/v1/verify", bytes.NewBuffer(jsonStr)) + if err != nil { + return nil, err + } + body, errRes, _ := c.doRequest(req) + if errRes != nil { + return nil, errRes + } + var response map[string]interface{} + json.Unmarshal(body, &response) + return response, nil + +} + +// Function thats sends asynchronous decrypt request to TSB +func (c *TSBClient) AsyncDecrypt(label string, password string, cipertext string, vector string, cipherAlgorithm string, tagLength int, additionalAuthenticationData string, customMetaData map[string]string) (string, error) { + charsPasswordJson, _ := json.Marshal(helpers.StringToCharArray(password)) + + var additionalMetaDataInfo map[string]string = make(map[string]string) + additionalMetaDataInfo["encrypted payload"] = cipertext + additionalMetaDataInfo["cipher algorithm"] = cipherAlgorithm + additionalMetaDataInfo["tag length"] = strconv.Itoa(tagLength) + additionalMetaDataInfo["additional authentication data"] = additionalAuthenticationData + additionalMetaDataInfo["initialization vector"] = vector + + metaDataB64, metaDataSignature, err := helpers.PrepareMetaData("Decrypt", additionalMetaDataInfo, customMetaData) + if err != nil { + return "", err + } + vectorString := `"` + vector + `"` + if vector == "" { + vectorString = "null" + } + additionalAuthenticationDataString := `"` + additionalAuthenticationData + `"` + if additionalAuthenticationData == "" { + additionalAuthenticationDataString = "null" + } + tagLengthString := "" + if tagLength != -1 && cipherAlgorithm == "AES_GSM" { + tagLengthString = `"tagLength":` + strconv.Itoa(tagLength) + `,` + } + passwordString := "" + if len(charsPasswordJson) > 2 { + passwordString = `"keyPassword": ` + string(charsPasswordJson) + `,` + + } + + var jsonStr = []byte(`{ + "decryptRequest": { + "encryptedPayload": "` + cipertext + `", + ` + passwordString + ` + "decryptKeyName": "` + label + `", + "metaData": "` + metaDataB64 + `", + "metaDataSignature": "` + metaDataSignature + `", + "cipherAlgorithm": "` + cipherAlgorithm + `", + "initializationVector": ` + vectorString + `, + ` + tagLengthString + ` + "additionalAuthenticationData":` + additionalAuthenticationDataString + ` + } + }`) + req, err := http.NewRequest("POST", c.HostURL+"/v1/decrypt", bytes.NewBuffer(jsonStr)) + if err != nil { + return "", err + } + body, errRes, _ := c.doRequest(req) + if errRes != nil { + return "", errRes + } + var result map[string]interface{} + errJSON := json.Unmarshal(body, &result) + if errJSON != nil { + return "", errJSON + } + return result["decryptRequestId"].(string), nil + // return response, nil + +} + +// Function thats sends decrypt request to TSB +func (c *TSBClient) Decrypt(label string, password string, cipertext string, vector string, cipherAlgorithm string, tagLength int, additionalAuthenticationData string) (map[string]interface{}, error) { + charsPasswordJson, _ := json.Marshal(helpers.StringToCharArray(password)) + vectorString := `"` + vector + `"` + if vector == "" { + vectorString = "null" + } + additionalAuthenticationDataString := `"` + additionalAuthenticationData + `"` + if additionalAuthenticationData == "" { + additionalAuthenticationDataString = "null" + } + tagLengthString := "" + if tagLength != -1 && cipherAlgorithm == "AES_GSM" { + tagLengthString = `"tagLength":` + strconv.Itoa(tagLength) + `,` + } + passwordString := "" + if len(charsPasswordJson) > 2 { + passwordString = `"keyPassword": ` + string(charsPasswordJson) + `,` + + } + + var jsonStr = []byte(`{ + "decryptRequest": { + "encryptedPayload": "` + cipertext + `", + ` + passwordString + ` + "decryptKeyName": "` + label + `", + "cipherAlgorithm": "` + cipherAlgorithm + `", + "initializationVector": ` + vectorString + `, + ` + tagLengthString + ` + "additionalAuthenticationData":` + additionalAuthenticationDataString + ` + } + }`) + req, err := http.NewRequest("POST", c.HostURL+"/v1/synchronousDecrypt", bytes.NewBuffer(jsonStr)) + if err != nil { + return nil, err + } + body, errRes, _ := c.doRequest(req) + if errRes != nil { + return nil, errRes + } + var response map[string]interface{} + json.Unmarshal(body, &response) + if !helpers.ContainsKey(response, "payload") { + return nil, fmt.Errorf("Error on decrypt response. Need payload found %s", string(body[:])) + } + return response, nil + +} + +// Function thats sends export request to TSB +func (c *TSBClient) ExportKey(label string, password string) (map[string]interface{}, error) { + charsPasswordJson, _ := json.Marshal(helpers.StringToCharArray(password)) + passwordString := "" + if len(charsPasswordJson) > 2 { + passwordString = `"password": ` + string(charsPasswordJson) + `,` + + } + + var jsonStr = []byte(`{ + ` + passwordString + ` + "label": "` + label + `" + + }`) + + req, err := http.NewRequest("POST", c.HostURL+"/v1/key/export/plain", bytes.NewBuffer(jsonStr)) + if err != nil { + return nil, err + } + body, errRes, _ := c.doRequest(req) + if errRes != nil { + return nil, errRes + } + + var response map[string]interface{} + json.Unmarshal(body, &response) + + return response, nil + +} + +// Function thats sends get request to TSB +func (c *TSBClient) GetRequest(id string) (*helpers.RequestResponse, error, int) { + req, err := http.NewRequest("GET", c.HostURL+"/v1/request/"+id, bytes.NewBuffer(nil)) + if err != nil { + return nil, err, 500 + } + body, errRes, code := c.doRequest(req) + if errRes != nil { + return nil, errRes, code + } + var requestResponse helpers.RequestResponse + errJSON := json.Unmarshal(body, &requestResponse) + if errJSON != nil { + return nil, errJSON, code + } + return &requestResponse, nil, code +} + +// Function thats sends import key request to TSB +func (c *TSBClient) ImportKey(label string, privateKey string, publicKey string, secretKey string, certificate string, attributes map[string]bool, keytype string, policy helpers.Policy) (map[string]interface{}, error) { + policyJson, _ := json.Marshal(policy) + policyString := string(`,"policy":` + string(policyJson)) + var privateKeyString string + if privateKey == "" { + privateKeyString = "" + } else { + privateKeyString = `"privateKey": "` + privateKey + `",` + } + var publicKeyString string + if publicKey == "" { + publicKeyString = "" + } else { + publicKeyString = `"publicKey": "` + publicKey + `",` + } + var secretKeyString string + if secretKey == "" { + secretKeyString = "" + } else { + secretKeyString = `"secretKey": "` + secretKey + `",` + } + var certificateString string + if certificate == "" { + certificateString = "" + } else { + certificateString = `"certificate": "` + certificate + `",` + } + var jsonStr = []byte(`{ + "label": "` + label + `", + "algorithm": "` + keytype + `", + ` + privateKeyString + ` + ` + publicKeyString + ` + ` + secretKeyString + ` + ` + certificateString + ` + "attributes": ` + helpers.PrepareAttributes(attributes) + policyString + `}`) + + req, err := http.NewRequest("POST", c.HostURL+"/v1/key/import/plain", bytes.NewBuffer(jsonStr)) + if err != nil { + return nil, err + } + body, errRes, _ := c.doRequest(req) + if errRes != nil { + return nil, errRes + } + var response map[string]interface{} + json.Unmarshal(body, &response) + return response, nil + +} + +// Function thats sends get key attribute request to TSB +func (c *TSBClient) GetKey(label string, password string) (helpers.KeyAttributes, error) { + + charsPasswordJson, _ := json.Marshal(helpers.StringToCharArray(password)) + passwordString := "" + if len(charsPasswordJson) > 2 { + passwordString = `"password": ` + string(charsPasswordJson) + `,` + + } + var jsonStr = []byte(`{ + ` + passwordString + ` + "label":"` + label + `" + }`) + + req, err := http.NewRequest("POST", c.HostURL+"/v1/key/attributes", bytes.NewBuffer(jsonStr)) + var key helpers.KeyAttributes + if err != nil { + return key, err + } + body, errRes, _ := c.doRequest(req) + if errRes != nil { + return key, errRes + } + var response interface{} + json.Unmarshal(body, &response) + data := response.(map[string]interface{}) + jsonData := data["json"].(map[string]interface{}) + key.Algorithm = jsonData["algorithm"].(string) + key.AlgorithmOid = jsonData["algorithmOid"].(string) + key.CurveOid = "" + if fmt.Sprintf("%T", jsonData["curveOid"]) == "string" { + key.CurveOid = jsonData["curveOid"].(string) + } + key.Attributes = map[string]bool{} + attributes := jsonData["attributes"].(map[string]interface{}) + for k, e := range attributes { + if fmt.Sprintf("%T", e) == "bool" { + key.Attributes[k] = e.(bool) + } + } + if fmt.Sprintf("%T", jsonData["keySize"]) == "float64" { + key.KeySize = jsonData["keySize"].(float64) + } + key.Xml = data["xml"].(string) + key.XmlSignature = data["xmlSignature"].(string) + key.AttestationKeyName = data["attestationKeyName"].(string) + key.Label = jsonData["label"].(string) + policyString, _ := json.Marshal(jsonData["policy"]) + json.Unmarshal(policyString, &key.Policy) + if fmt.Sprintf("%T", jsonData["publicKey"]) == "string" { + key.PublicKey = jsonData["publicKey"].(string) + } + return key, nil + +} + +// Function thats sends delete key request to TSB +func (c *TSBClient) RemoveKey(key helpers.KeyEntry) error { + for _, version := range key.Versions { + time.Sleep(500) + req, _ := http.NewRequest("DELETE", c.HostURL+"/v1/key/"+version.KeyLabel, nil) + c.doRequest(req) + } + + return nil + +} +func (c *TSBClient) RemoveKeyVersion(keys map[string]helpers.KeyVersion, version string) error { + time.Sleep(500) + req, _ := http.NewRequest("DELETE", c.HostURL+"/v1/key/"+keys[version].KeyLabel, nil) + c.doRequest(req) + + return nil + +} + +// Function thats sends delete request to TSB +func (c *TSBClient) RemoveRequest(id string) error { + req, err := http.NewRequest("DELETE", c.HostURL+"/v1/request/"+id, nil) + if err != nil { + return err + } + _, errReq, code := c.doRequest(req) + if code == 404 || code == 500 { + return nil + } + if errReq != nil { + return errReq + } + return nil + +} + +// Function thats sends update key password request to TSB +func (c *TSBClient) UpdateKeyPassword(label string, password string, newPassword string) (string, error) { + charsPasswordJson, _ := json.Marshal(helpers.StringToCharArray(password)) + charsNewPasswordJson, _ := json.Marshal(helpers.StringToCharArray(newPassword)) + passwordString := "" + if len(charsPasswordJson) > 2 { + passwordString = `"password": ` + string(charsPasswordJson) + `,` + + } + newPasswordString := "" + if len(charsNewPasswordJson) > 2 { + newPasswordString = `"newPassword": ` + string(charsNewPasswordJson) + `,` + + } + var jsonStr = []byte(`{ + ` + passwordString + newPasswordString + ` + "label": "` + label + `" + }`) + + req, err := http.NewRequest("POST", c.HostURL+"/v1/key/changePassword", bytes.NewBuffer(jsonStr)) + if err != nil { + return "", err + } + _, errRes, _ := c.doRequest(req) + if errRes != nil { + return "", errRes + } + return label, nil + +} + +// Function thats sends create key request to TSB +func (c *TSBClient) CreateOrUpdateKey(label string, password string, attributes map[string]bool, keytype string, keySize float64, policy *helpers.Policy, curveOid string, modify bool) (string, error) { + + charsPasswordJson, _ := json.Marshal(helpers.StringToCharArray(password)) + policyJson, _ := json.Marshal(&policy) + policyString := string(`,"policy":` + string(policyJson)) + if attributes["extractable"] { + policyString = string(`,"policy":null`) + } + var keySizeAttr string + if keySize == 0 { + keySizeAttr = "" + } else { + keySizeAttr = `"keySize": ` + fmt.Sprintf("%g", keySize) + `,` + } + var curveOidString string + if curveOid == "" { + curveOidString = "" + } else { + curveOidString = `"curveOid": "` + curveOid + `",` + } + passwordString := "" + if len(charsPasswordJson) > 2 { + passwordString = `"password": ` + string(charsPasswordJson) + `,` + + } + var jsonStr = []byte(`{ + "label": "` + label + `", + "algorithm": "` + keytype + `", + ` + passwordString + ` + ` + keySizeAttr + ` + ` + curveOidString + ` + "attributes": ` + helpers.PrepareAttributes(attributes) + policyString + `}`) + + req, err := http.NewRequest("POST", c.HostURL+"/v1/key", bytes.NewBuffer(jsonStr)) + if err != nil { + return "", err + } + body, err, _ := c.doRequest(req) + if err != nil { + var result map[string]interface{} + errJSON := json.Unmarshal(body, &result) + if errJSON != nil { + return "", errJSON + } + return "", err + } + return label, nil +} +func (c *TSBClient) CheckConnection() (string, error) { + req, err := http.NewRequest("GET", c.HostURL+"/v1/keystore/statistics", nil) + if err != nil { + return "", err + } + body, errReq, _ := c.doRequest(req) + if errReq != nil { + return string(body[:]), errReq + } + return string(body[:]), nil + +} + +// Function thats sends asynchronous unwrap request to TSB +func (c *TSBClient) AsyncUnWrap(wrappedKey string, label string, attributes map[string]bool, unwrapKeyName string, unwrapKeyPassword string, wrapMethod string, policy *helpers.Policy, customMetaData map[string]string) (string, error) { + charsPasswordJson, _ := json.Marshal(helpers.StringToCharArray(unwrapKeyPassword)) + var additionalMetaDataInfo map[string]string = make(map[string]string) + additionalMetaDataInfo["wrapped key"] = wrappedKey + additionalMetaDataInfo["new key label"] = label + additionalMetaDataInfo["wrap method"] = wrapMethod + additionalMetaDataInfo["attributes"] = fmt.Sprintf("%v", attributes) + var policyString string + if policy == nil { + policyString = string(`,"policy":null`) + } else { + policyJson, _ := json.Marshal(*policy) + policyString = string(`,"policy":` + string(policyJson)) + } + + if attributes["extractable"] { + policyString = string(`,"policy":null`) + } + //Only for asychronous unwrap + policyString = string(``) + metaDataB64, metaDataSignature, err := helpers.PrepareMetaData("UnWrap", additionalMetaDataInfo, customMetaData) + passwordString := "" + if len(charsPasswordJson) > 2 { + passwordString = `"unwrapKeyPassword": ` + string(charsPasswordJson) + `,` + + } + var jsonStr = []byte(`{ + "unwrapKeyRequest": { + "wrappedKey": "` + wrappedKey + `", + "label": "` + label + `", + "unwrapKeyName": "` + unwrapKeyName + `", + ` + passwordString + ` + "wrapMethod": "` + wrapMethod + `", + "attributes": ` + helpers.PrepareAttributes(attributes) + `, + "metaData": "` + metaDataB64 + `", + "metaDataSignature": "` + metaDataSignature + `"` + policyString + ` + }}`) + req, err := http.NewRequest("POST", c.HostURL+"/v1/unwrap", bytes.NewBuffer(jsonStr)) + if err != nil { + return "", err + } + body, errRes, _ := c.doRequest(req) + if errRes != nil { + return "", errRes + } + var result map[string]interface{} + errJSON := json.Unmarshal(body, &result) + if errJSON != nil { + return "", errJSON + } + return result["unwrapRequestId"].(string), nil +} + +// Function thats sends unwrap request to TSB +func (c *TSBClient) UnWrap(wrappedKey string, label string, attributes map[string]bool, unwrapKeyName string, unwrapKeyPassword string, wrapMethod string, policy *helpers.Policy) (string, error) { + charsPasswordJson, _ := json.Marshal(helpers.StringToCharArray(unwrapKeyPassword)) + var policyString string + if policy == nil { + policyString = string(`,"policy":null`) + } else { + policyJson, _ := json.Marshal(policy) + policyString = string(`,"policy":` + string(policyJson)) + } + if attributes["extractable"] { + policyString = string(`,"policy":null`) + } + passwordString := "" + if len(charsPasswordJson) > 2 { + passwordString = `"unwrapKeyPassword": ` + string(charsPasswordJson) + `,` + + } + + var jsonStr = []byte(`{ + "unwrapKeyRequest": { + "wrappedKey": "` + wrappedKey + `", + "label": "` + label + `", + "unwrapKeyName": "` + unwrapKeyName + `", + ` + passwordString + ` + "wrapMethod": "` + wrapMethod + `", + "attributes": ` + helpers.PrepareAttributes(attributes) + policyString + ` + }}`) + req, err := http.NewRequest("POST", c.HostURL+"/v1/synchronousUnwrap", bytes.NewBuffer(jsonStr)) + if err != nil { + return "", err + } + body, err, _ := c.doRequest(req) + if err != nil { + var result map[string]interface{} + errJSON := json.Unmarshal(body, &result) + if errJSON != nil { + return "", errJSON + } + return "", err + } + return label, nil +} + +// Function that making all requests. Using config for Authorization to TSB +func (c *TSBClient) doRequest(req *http.Request) ([]byte, error, int) { + // req.Header.Set("Authorization", c.Token) + if c.Auth.AuthType == "TOKEN" { + req.Header.Set("Authorization", "Bearer "+c.Auth.BearerToken) + } + if c.Auth.AuthType == "BASIC" { + if c.Auth.BasicToken == "" { + req.SetBasicAuth(c.Auth.Username, c.Auth.Password) + } else { + req.Header.Set("Authorization", "Basic "+(c.Auth.BasicToken)) + } + } + if c.Auth.AuthType == "CERT" { + + caCert, _ := ioutil.ReadFile(c.Auth.CertPath) + + caCertPool := x509.NewCertPool() + caCertPool.AppendCertsFromPEM(caCert) + clientTLSCert, err := tls.LoadX509KeyPair(c.Auth.CertPath, c.Auth.KeyPath) + if err != nil { + log.Fatalf("Error loading certificate and key file: %v", err) + return nil, err, 0 + } + + c.HTTPClient.Transport = &http.Transport{ + TLSClientConfig: &tls.Config{ + RootCAs: caCertPool, + InsecureSkipVerify: true, + Certificates: []tls.Certificate{clientTLSCert}, + }, + } + } + req.Header.Set("Content-Type", "application/json") + + res, err := c.HTTPClient.Do(req) + if err != nil { + return nil, err, res.StatusCode + } + defer res.Body.Close() + + body, err := ioutil.ReadAll(res.Body) + if err != nil { + return nil, err, res.StatusCode + } + if res.StatusCode != http.StatusOK && res.StatusCode != http.StatusCreated { + return body, fmt.Errorf("status: %d, body: %s", res.StatusCode, body), res.StatusCode + } + + return body, err, res.StatusCode +} diff --git a/integration_junit_report.xml b/integration_junit_report.xml new file mode 100644 index 0000000..c834e42 --- /dev/null +++ b/integration_junit_report.xml @@ -0,0 +1,54 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + diff --git a/junit_report.xml b/junit_report.xml new file mode 100644 index 0000000..8d5e7f5 --- /dev/null +++ b/junit_report.xml @@ -0,0 +1,297 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + diff --git a/testHelpers/tsb-integration-test-client.crt b/testHelpers/tsb-integration-test-client.crt new file mode 100644 index 0000000..d048390 --- /dev/null +++ b/testHelpers/tsb-integration-test-client.crt @@ -0,0 +1,28 @@ +-----BEGIN CERTIFICATE----- +MIIE2TCCAsECFF/dPcdVcWEyXcfOLibtzCXpDJUfMA0GCSqGSIb3DQEBCwUAMCkx +JzAlBgNVBAMMHmludGVncmF0aW9uLXRlc3QuY2xvdWRzaHNtLmNvbTAeFw0yMzEy +MDUxNjQ2MTRaFw0zMzEyMDIxNjQ2MTRaMCkxJzAlBgNVBAMMHmludGVncmF0aW9u +LXRlc3QuY2xvdWRzaHNtLmNvbTCCAiIwDQYJKoZIhvcNAQEBBQADggIPADCCAgoC +ggIBAM5q4LtZu8HRPOYcBzEBbnKEEIy1YLOvl9FR0hgOQkAfkxke1B+GszhW0JYV +VF59hVzlR2jsDuQXK90JnU7WTeGBr4fKop83jCw9raDc2hYKPHLpWyLgkmN/eE86 +EzigJzGmNEVcj/JqPIMZ/rWwQEPv1OTDSW8T2Vx4WJCG6/z6YR8MWBxnt7K31SSG +g1OIYPhoo2995wW92XuyNgkH4BV0K4maiuuJv5QvwyMABaao6D2ilWlBWfsD+XSB +EobtIuzgGjn20WnmKWN7pdy2AwSkuiZjCjCgkrNMXe1cKq1kbBCjWQitJKChJ0Gp +jBJ1TT7jDY6RtJY20v1UpgAdM3Qo2cCA48WcECAG+tFa4Eb7oHuejQDirkr60BgQ +B2mZZ/6husyvFN6Ah92DeEulW7JCUeu18Xr62C/P4AN+FRuuiMVBhTz7erLJNLs+ +5koZpINSs3bxrtVmhHrIgwX9FCOQphHX+YyCkBgCYZEUk6eCO6TEt8bsm18HvXRd +/7f0IRYJLfRhIPvXuzOVWj8ZtL5mIkqdh/bMZFbVrs/foD4TduLYIiNP7BousbKZ +SiOmSFkz+2jK3bnyv+7HH6mlPRqdOg7x+Dnoo4nhdCSMDf9m34uEH+sIcNW4cfsF +/g721TqljZjkj37t1mnY+D99to/Fnpf8zb8Bm6Ss68N9bOfrAgMBAAEwDQYJKoZI +hvcNAQELBQADggIBAJlSI9oPUW3KBCUqp0Oirz1de/MpZHPywPhOtxyKsh7kmd+O +whE65m93ntYoIghVCxhB+TZFxaS/vBxWBualArlTGpAhQdBw6J3Zlrq52h8a9S1/ +31MjbRI0Jf0sq8yQDZIb9+86F41SoboGIRh/zuYqPXbCnP4AgXk2ZXmDZrKmcTpZ +Tfo6WyBjI+TOEaEHOtbdhpipuI1BMKi+0UkX8HENi6Ba03Ddr3d0u1QeImyMTWaq +erwgixETzJ8gi0f3A0IK2u9prcC/I5KLRdXTnsFJmV4fF74QKOL1FWTIpNFqpEwV +E6YqpepZEtnhoozEm4J3LoSceoftDdKisaI61Ogm61T/r9jAV0k3LXUOkTAP2IIB +ZwVAqc1gyf5+S9IYK622+yHN7iZAzjPg71psGHY2kdkBEf5mNK2zLzUkKoOFTM88 +teHt8xNxwgsyTEdqNStl9LwJXVzqJUONXI3Ih66PBB+hwxTcIQ+e+OQGGHbRsAHV +wXehCmoUD+EA1kFQ9lOOBfRj9qhShSwplAQSuXMjeByMYxac+kP5u77HbaJN1w5R +2Rf73C3lufqpmJNpZ+qjuJS3AlIVSTrp9I90jqw1GjyPvfm/QVNogvzKaRaePs12 +/vdmT1VUW7ulTAEMJdEynXLptiiE0N34MWCIcAe5bULwieOS8O11/5om7zWD +-----END CERTIFICATE----- diff --git a/testHelpers/tsb-integration-test-client.key b/testHelpers/tsb-integration-test-client.key new file mode 100644 index 0000000..e5771d5 --- /dev/null +++ b/testHelpers/tsb-integration-test-client.key @@ -0,0 +1,52 @@ +-----BEGIN PRIVATE KEY----- +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 +9SqGGPLpcoLFpe/9SPdY4ULh42GAOg== +-----END PRIVATE KEY-----