HMAC validation in GoLang - go

I have a HMAC validation code in PHP which is like
$calculatedHmac = base64_encode(hash_hmac('sha256', json_encode($input), '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', true));
if($calculatedHmac === $this->app['request']->header('X-Shopify-Hmac-Sha256'))
{
echo "Correct HMAC";
}
Now, I am trying to do the same validation in GoLang. The corresponding code:
hash := hmac.New(sha256.New, []byte('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'))
inputData, _ := json.Marshal(input)
fmt.Println(string(inputData))
hash.Write(inputData)
base64EncodedStr := base64.StdEncoding.EncodeToString(hash.Sum(nil))
hexEncodedStr := hex.EncodeToString(hash.Sum(nil))
if requestHmac == base64EncodedStr {
logger.GetEntryWithContext(ctx).Infow("JsonMarshal.Base64 worked", nil)
} else if hexEncodedStr == requestHmac {
logger.GetEntryWithContext(ctx).Infow("JsonMarshal.Hex Encoding worked", nil)
}
Anything that I am doing wrong???
Input data:
Actual HMAC: EUF2feuOuaQzh0bhSE8eCaI23BM0Qh+yBtmxNWyt8JQ=
JSON Input:
{"address1":"Ghar ka Address","address2":null,"auto_configure_tax_inclusivity":null,"checkout_api_supported":false,"city":"Bangalore ","cookie_consent_level":"implicit","country":"IN","country_code":"IN","country_name":"India","county_taxes":true,"created_at":"2021-03-06T11:54:22+05:30","currency":"INR","customer_email":"amit.vickey#razorpay.com","domain":"rzp-sample-store.myshopify.com","eligible_for_card_reader_giveaway":false,"eligible_for_payments":false,"email":"amit.vickey#razorpay.com","enabled_presentment_currencies":["INR"],"finances":true,"force_ssl":true,"google_apps_domain":null,"google_apps_login_enabled":null,"has_discounts":false,"has_gift_cards":false,"has_storefront":true,"iana_timezone":"Asia/Calcutta","id":55091921055,"latitude":12.9142148,"longitude":77.61210129999999,"money_format":"Rs. {{amount}}","money_in_emails_format":"Rs. {{amount}}","money_with_currency_format":"Rs. {{amount}}","money_with_currency_in_emails_format":"Rs. {{amount}}","multi_location_enabled":true,"myshopify_domain":"rzp-sample-store.myshopify.com","name":"RZP Sample Store","password_enabled":true,"phone":"","plan_display_name":"Developer Preview","plan_name":"partner_test","pre_launch_enabled":false,"primary_locale":"en","primary_location_id":61106618527,"province":"Karnataka","province_code":"KA","requires_extra_payments_agreement":false,"setup_required":false,"shop_owner":"Amit Vickey","source":null,"tax_shipping":null,"taxes_included":false,"timezone":"(GMT+05:30) Asia/Calcutta","updated_at":"2021-03-06T12:06:18+05:30","visitor_tracking_consent_preference":"allow_all","weight_unit":"kg","zip":"560076"}

Related

Handle user answer

The task is that user input a sum of deposit and I could handle it in this context, but isn't like a simple command. Example:
My code:
func main () {
testing()
NewBot, BotError = tgBotApi.NewBotAPI(configuration.BOT_TOKEN)
if BotError != nil {
fmt.Println(BotError.Error())
}
NewBot.Debug = true
fmt.Println("OK", time.Now().Unix(), time.Now(), time.Now().Weekday())
setWebhook(NewBot)
updates := NewBot.ListenForWebhook("/" + configuration.BOT_TOKEN)
//go successfulPaymentListen()
go http.ListenAndServeTLS(fmt.Sprintf("%s:%s", configuration.BOT_HOST, configuration.BOT_PORT), configuration.CERT_FILE, configuration.CERT_KEY, nil)
for update := range updates {
if update.Message != nil {
recognizeCommand(update)
} else if update.CallbackQuery != nil {
if update.CallbackQuery.Data == "/addFunds crypto" {
get_data.AddFundsChooseCurrencyCrypto(update, NewBot)
} else if update.CallbackQuery.Data == "/addFunds qiwi" {
get_data.AddFundsChooseCurrencyQiwi(update, NewBot)
} else if strings.Split(update.CallbackQuery.Data, " ")[2] != "" {
get_data.AddFundsChooseCurrencyCurrentCrypto(update, NewBot, strings.Split(update.CallbackQuery.Data, " ")[2])
//This function is below
}
}
}
}
get_data.AddFundsChooseCurrencyCurrentCrypto:
func AddFundsChooseCurrencyCurrentCrypto(update tgBotApi.Update, NewBot *tgBotApi.BotAPI, currency string) {
chatUser := int64(update.CallbackQuery.From.ID)
msg := tgBotApi.NewMessage(chatUser, "Input a sum of deposit:")
NewBot.Send(msg)
//There is I have to handle user answer, but I can't override ListenWebHook
}
The problem is that I need ListenWebHook localy( in the function AddFundsChooseCurrencyCurrentCrypto) instead of main function
------------------------ UPDATE ------------------------
I have tried this code:
func AddFundsChooseCurrencyCurrentCrypto(update tgBotApi.Update, NewBot *tgBotApi.BotAPI, currency string) {
chatUser := int64(update.CallbackQuery.From.ID)
msg := tgBotApi.NewMessage(chatUser, "Input a sum of deposit:")
NewBot.Send(msg)
NewBotContext, BotError := tgBotApi.NewBotAPI(configuration.BOT_TOKEN)
if BotError != nil {
log.Panic(BotError.Error())
}
updates := NewBotContext.ListenForWebhook("/" + configuration.BOT_TOKEN)
for update := range updates {
fmt.Println(update)
}
}
But error:
panic: http: multiple registrations for /mytokenbot
goroutine 1 [running]:
net/http.(*ServeMux).Handle(0xe38620, 0xc25304, 0x2f, 0xc7dbe0, 0xc00018bec0)
You've tried to register the same url '/mytokenbot' twice with your router. You can find the error in net/http:
https://golang.org/src/net/http/server.go#L2433
In the mux Handle function.
So just look through your code for the register function with servemux, and check how you might be calling it twice.

Check if a value is an int

So Im sending this payload to my app :
{
"name" : "Matias Barrios",
"age" : 123
}
The problem I am facing is that when I test if name is a string it works perfectly. But test if age is an int is always returning false, no matter what I do.
if gjson.Get(spec, "name").Exists() {
if _, ok := gjson.Get(spec, "name").Value().(string); !ok {
n := validator_error{Path: "_.name", Message: "should be a string"}
errors = append(errors,n)
}
}
if gjson.Get(spec, "age").Exists() {
if _, ok := gjson.Get(spec, "age").Value().(int); !ok {
n := validator_error{Path: "_.age", Message: "should be an int"}
errors = append(errors,n)
}
}
Could someone tell me where is the error here?
Note - I am using this https://github.com/tidwall/gjson to get the values out of the JSON.
It seems like this lib for json numbers it return float64
bool, for JSON booleans
float64, for JSON numbers
string, for JSON string literals
nil, for JSON null
As in github.com/tidwall/gjson result type can only hold float64 for json numbers.
Though you can use Int() instead of Value() to get an integer value.
fmt.Println(gjson.Get(spec, "age").Int()) // 123
You can use the result.Type field to check the type:
res := gjson.Get(spec, "age")
if res.Type != gjson.Number {
n := validator_error{Path: "_.age", Message: "should be an int"}
}
If you require that the value is a whole number 123 not 123.5:
res := gjson.Get(spec, "age")
if res.Type != gjson.Number || math.Floor(res.Num) != res.Num {
n := validator_error{Path: "_.age", Message: "should be an int"}
}

Parsing plist xml

How to parse xml in such silly format:
<key>KEY1</key><string>VALUE OF KEY1</string>
<key>KEY2</key><string>VALUE OF KEY2</string>
<key>KEY3</key><integer>42</integer>
<key>KEY3</key><array>
<integer>1</integer>
<integer>2</integer>
</array>
Parsing would be very simple if all values would have same type - for example strings. But in my case each value could be string, data, integer, boolean, array or dict.
This xml looks nearly like json, but unfortunately format is fixed, and I cannot change it. And I would prefer solution without any external packages.
Use a lower-level parsing interface provided by encoding/xml which allows you to iterate over individual tokens in the XML stream (such as "start element", "end element" etc).
See the Token() method of the encoding/xml's Decoder type.
Since the data is not well structured, and you can't modify the format, you can't use xml.Unmarshal, so you can process the XML elements by creating a new Decoder, then iterate over the tokens and use DecodeElement to process them one by one. In my sample code below, it puts everything in a map. The code is also on github here...
package main
import (
"encoding/xml"
"strings"
"fmt"
)
type PlistArray struct {
Integer []int `xml:"integer"`
}
const in = "<key>KEY1</key><string>VALUE OF KEY1</string><key>KEY2</key><string>VALUE OF KEY2</string><key>KEY3</key><integer>42</integer><key>KEY3</key><array><integer>1</integer><integer>2</integer></array>"
func main() {
result := map[string]interface{}{}
dec := xml.NewDecoder(strings.NewReader(in))
dec.Strict = false
var workingKey string
for {
token, _ := dec.Token()
if token == nil {
break
}
switch start := token.(type) {
case xml.StartElement:
fmt.Printf("startElement = %+v\n", start)
switch start.Name.Local {
case "key":
var k string
err := dec.DecodeElement(&k, &start)
if err != nil {
fmt.Println(err.Error())
}
workingKey = k
case "string":
var s string
err := dec.DecodeElement(&s, &start)
if err != nil {
fmt.Println(err.Error())
}
result[workingKey] = s
workingKey = ""
case "integer":
var i int
err := dec.DecodeElement(&i, &start)
if err != nil {
fmt.Println(err.Error())
}
result[workingKey] = i
workingKey = ""
case "array":
var ai PlistArray
err := dec.DecodeElement(&ai, &start)
if err != nil {
fmt.Println(err.Error())
}
result[workingKey] = ai
workingKey = ""
default:
fmt.Errorf("Unrecognized token")
}
}
}
fmt.Printf("%+v", result)
}

Custom xml decoder issue

I have multiple test cases which pass, however this one fails. What am I missing here that is causing the decoder read the content of my target keys incorrectly?
const respGenericFault1 = `<?xml version='1.0' encoding='UTF-8'?>
<SOAP-ENV:Envelope
xmlns:SOAP-ENV="http://schemas.xmlsoap.org/soap/envelope/"
xmlns:xsi="http://www.w3.org/1999/XMLSchema-instance"
xmlns:xsd="http://www.w3.org/1999/XMLSchema">
<SOAP-ENV:Body>
<SOAP-ENV:Fault>
<faultcode xsi:type="xsd:string">SOAP-ENV:Client</faultcode>
<faultstring xsi:type="xsd:string">Failed to validate</faultstring>
</SOAP-ENV:Fault>
</SOAP-ENV:Body>
</SOAP-ENV:Envelope>`
type Fault struct {
FaultCode, FaultString string
}
func (f Fault) Error() string {
return "Fault Code: '" + f.FaultCode + "' FaultString: '" + f.FaultString + "'"
}
func ParseFault(b []byte) error {
reader := bytes.NewReader(b)
d := xml.NewDecoder(reader)
var start xml.StartElement
fault := Fault{}
found := false
// iterate through the tokens
for {
tok, _ := d.Token()
if tok == nil {
break
}
// switch on token type
switch t := tok.(type) {
case xml.StartElement:
start = t.Copy()
fmt.Println(start.Name.Local)
case xml.CharData:
key := strings.ToLower(start.Name.Local)
// fault was found, capture the values and mark as found
if key == "faultcode" {
found = true
fault.FaultCode = string(t)
fmt.Printf("%#v\n", string(t))
} else if key == "faultstring" {
found = true
fault.FaultString = string(t)
}
}
}
if found {
return fault
}
return nil
}
func main() {
err := ParseFault([]byte(respGenericFault1))
fmt.Printf("%#v\n", err)
}
Here is the playground url: http://play.golang.org/p/7PFPNsmast
Your code successfully captures the faultstring and faultcode, but then unintentionally overwrites it with xml.CharData containing the whitespace between tags.
Here is a fixed version: http://play.golang.org/p/s1aFFYtwcX . Comment out line 52 to see the failure mode.
Alternatively, you can use encoding/xml Unmarshal to parse the XML directly into a struct. See http://play.golang.org/p/lOsZRUJ63B

Golang AES-CBC 256 to decrypt using CryptoJS

Been working for days trying to get Golang AES-CBC to CryptoJS working (or vice-versa), I fixed most of the errors but not getting decryption even though i have confirmed the key, iv, ciphertext is the same on both ends.
There must be someone who knows, there is no working example anywhere on the net for this...
//golang
if a == "test64bytes" {
output = "AAAAAAAABBBBBBBBCCCCCCCCDDDDDDDDAAAAAAAABBBBBBBBCCCCCCCCDDDDDDDD"
}
// encrypt ajax response
iv := decodeBase64("AAAAAAAAAAAAAAAAAAAAAA==")
ciphertext := []byte(output)
ckey := decodeBase64(string(PLAINkey[0:32]))
c, err := aes.NewCipher(ckey)
cfbdec := cipher.NewCBCDecrypter(c, iv)
plaintext := make([]byte, len(ciphertext))
cfbdec.CryptBlocks(plaintext, ciphertext)
crypt := string(encodeBase64(plaintext))
fmt.Fprintf(res, "%v", crypt)
fmt.Println(encodeBase64(ckey))
fmt.Println(encodeBase64(iv))
fmt.Println(crypt)
// javascript
if (xmlhttp.readyState==4 && xmlhttp.status==200)
{
var enc = {};
enc["key"] = CryptoJS.enc.Base64.parse(keyseed.substring(0,32));
enc["iv"] = CryptoJS.enc.Base64.parse("AAAAAAAAAAAAAAAAAAAAAA==");
enc["ciphertext"] = CryptoJS.enc.Base64.parse(xmlhttp.responseText);
enc["salt"] = "";
console.log("RESPONSE:", xmlhttp.responseText, atob(xmlhttp.responseText));
// check i'm using same data
console.log(CryptoJS.enc.Base64.stringify(enc["key"]));
console.log(CryptoJS.enc.Base64.stringify(enc["iv"]));
console.log(CryptoJS.enc.Base64.stringify(enc["ciphertext"]));
var options = { keySize: 256 / 8, mode: CryptoJS.mode.CBC, padding: CryptoJS.pad.Pkcs7, iv: enc["iv"] };
de = CryptoJS.AES.decrypt(enc, enc["key"], options);
document.getElementById(target).innerHTML = de.toString();
console.log(de.toString(CryptoJS.enc.Utf8));
console.log("DECRYPTION FINISHED");
}
After methodically trying all possible AES configurations I can now decrypt my text..
...using a blank iv ("AAAAAAAAAAAAAAAAAAAAAA==") for this example. If you use a different one it will become the first block of plaintext when encrypting...
Go > CryptoJS
// Go
plaintext := []byte("THIS NEEDS TO BE MULTIPLE OF BLOCK LENGTH (16) I THINK")
// encrypt ajax response
iv := decodeBase64("AAAAAAAAAAAAAAAAAAAAAA==")
ckey := decodeBase64(string(PLAINkey[0:32]))
c, err := aes.NewCipher(ckey)
cfbdec := cipher.NewCBCEncrypter(c, iv)
ciphertext := make([]byte, len(plaintext))
cfbdec.CryptBlocks(ciphertext, plaintext)
crypt := string(encodeBase64(ciphertext))
fmt.Fprintf(res, "%v", crypt)
// JavaScript Ajax
if (xmlhttp.readyState==4 && xmlhttp.status==200)
{
var symkey = keyseed.substring(0,32);
var cipherParams = CryptoJS.lib.CipherParams.create({ ciphertext: CryptoJS.enc.Base64.parse(xmlhttp.responseText) });
var options = { mode: CryptoJS.mode.CBC, padding: CryptoJS.pad.NoPadding, iv: CryptoJS.enc.Base64.parse("AAAAAAAAAAAAAAAAAAAAAA==") };
de = CryptoJS.AES.decrypt(cipherParams, CryptoJS.enc.Base64.parse(symkey), options);
document.getElementById(target).innerHTML = de.toString(CryptoJS.enc.Utf8);
console.log("DECRYPTION FINISHED");
}

Resources