Discussing Shopify Functions development, deployment, and usage in Shopify apps.
I'm writing a shopify function for applying discounts to the cart items. I want to send some data through the cart attributes to the shopify function which involves a RSA signature of the cart items using a private key. I want to verify this signature in the Shopify function using the public key. I'm implementing this function in typescript. Started with jsonwebtoken library before I understood it does not work with Javy runtime. Javy runtime from Shopify converts the corresponding javascript (from typescript) into webassembly (.wasm). Javy runtime has restrictions - for example, I'm not able to use any of the node native crypto modules. I can't use any library that has async/await model, can't use functions like atob, Buffer etc due to Javy restrictions. I tried plain javascript libraries like jsrsasign but it increases the size of the .wasm file to more than the allowed 256KB for Shopify functions. I'm not able to find any library that I can use within the restrictions. If I implement the hashing my myself, I'm not sure whether I would remain within the instructions count limit.
What are the options for me to get this done sticking to typescript? I'm like kind of stuck on what to do.
Solved! Go to the solution
This is an accepted solution.
What is the instruction count without the message signing?
RSA/SHA256 is pretty heavy, what if you just use a SHA-1 HMAC?
Nick Wesselman | Shopify
- Was my reply helpful? Click Like to let me know!
- Was your question answered? Mark it as an Accepted Solution
- To learn more visit the Shopify Help Center or the Shopify Blog
Hi @sriram-1 --
Message signing is the only secure way of doing this, and currently there isn't a mechanism for JavaScript/Javy to use crypto in a way that fits within our instruction limits. As mentioned in our docs, cryptographic hashes, such as SHA implemented in JavaScript, are unlikely to execute within the allowed instruction count.
I'd recommend use of Rust for now. We have discussed providing a crypto implementation in Javy. You can upvote the feature request here.
-Nick
Nick Wesselman | Shopify
- Was my reply helpful? Click Like to let me know!
- Was your question answered? Mark it as an Accepted Solution
- To learn more visit the Shopify Help Center or the Shopify Blog
@Anonymous I actually did a Go implementation of this function and compiled it to .wasm using tinygo. I was able to get the file size under 256KB but the instruction count went to 71M. Thus, instruction count limit exceeded in Javascript as well as Go implementations. How sure can I be that this will not be the case if I implement it in Rust? I mean to understand what's special about Rust implementation that can avoid this scenario.
I know of several partners successfully doing crypto operations in Rust.
I'm surprised TinyGo is that high though. What is your instruction count without the message signature verification? What hashing algorithm are you using?
Nick Wesselman | Shopify
- Was my reply helpful? Click Like to let me know!
- Was your question answered? Mark it as an Accepted Solution
- To learn more visit the Shopify Help Center or the Shopify Blog
You might check the compiler options I'm using here, if you haven't already:
https://github.com/nickwesselman/polyglot-functions/blob/main/app/extensions/order-discount-golang/b...
Nick Wesselman | Shopify
- Was my reply helpful? Click Like to let me know!
- Was your question answered? Mark it as an Accepted Solution
- To learn more visit the Shopify Help Center or the Shopify Blog
The instruction count came close to 72M. And yes, I'm using the same compiler options that you pointed me to.
$ cat input1.json | shopify app function run
Benchmark Results
Name: function.wasm
Linear Memory Usage: 512KB
Instructions: 71.539465M
Size: 242KB
I'm doing json.Unmarshal and json.Marshal in my main() to handle the input to and output of the function.
And, this is all the crypto operations i'm doing in the Go file. Using RSA, SHA256
import (
"crypto"
"crypto/rsa"
"crypto/sha256"
"crypto/x509"
"encoding/base64"
"encoding/json"
"encoding/pem"
)
// block, _ := pem.Decode(pemBytes) and x509.ParsePKCS1PublicKey(block.Bytes)
pubKey, err := parsePublicKey([]byte(PUBLIC_KEY))
encodedHeader, encodedPayload, encodedSignature := <derived from token>
payloadBytes, err := base64.RawURLEncoding.DecodeString(encodedPayload)
payload := string(payloadBytes)
signatureBytes, err := base64.RawURLEncoding.DecodeString(encodedSignature)
data := encodedHeader + "." + encodedPayload
h := sha256.New()
h.Write([]byte(data))
d := h.Sum(nil)
err = rsa.VerifyPKCS1v15(pubKey, crypto.SHA256, d, signatureBytes)
if err != nil {
return "", fmt.Errorf("signature verification failed: %v", err)
}
return payload, nil
This is an accepted solution.
What is the instruction count without the message signing?
RSA/SHA256 is pretty heavy, what if you just use a SHA-1 HMAC?
Nick Wesselman | Shopify
- Was my reply helpful? Click Like to let me know!
- Was your question answered? Mark it as an Accepted Solution
- To learn more visit the Shopify Help Center or the Shopify Blog
Thanks for this suggestion, it worked. The instruction count came down to 1M. The binary size also has come down.