go-paseto: Keep getting "bad signature" from parsing

I’m doing some test locally

After my client logs in, and the backend generate signed variable and send back to client in frontend with the following code:

token := paseto.NewToken()
token.SetIssuer("test@example.com")
token.SetIssuedAt(time.Now())
token.SetNotBefore(time.Now())
token.SetExpiration(time.Now().Add(time.Minute + 30))

secretKey, err := paseto.NewV4AsymmetricSecretKeyFromBytes([]byte(<Some random 64 characters>))

publicKey := secretKey.Public().ExportHex() // DO share this one
signed := token.V4Sign(secretKey, nil)

The auth middleware I have to check if key/token valid:

pubKey, err := paseto.NewV4AsymmetricPublicKeyFromHex(publicKey)
parser := paseto.NewParser()
parsedToken, err := parser.ParseV4Public(pubKey, signedToken, nil)
if err != nil {
	log.Println("err: ", err)
	return "", errors.New(err.Error())
}

This is where I keep getting bad signature

Can anyone help me here?

One quick note: I notice two hex values are different between using

pubkey := secretKey.Public().ExportHex()

and

pubkey := hex.EncodeToString([]byte(os.Getenv("TOKEN_SYMMETRIC_PUBLIC_KEY")))

But I have tried both public keys to verify and both give me bad signature

About this issue

  • Original URL
  • State: closed
  • Created 2 years ago
  • Comments: 21 (11 by maintainers)

Most upvoted comments

I figured out what you meant but I do have a quick question. I’m doing the key as an environment variable as you suggested, but wouldn’t another user overwrite the value when the user signs in?

The decision to make here is how many keys you want to manage operationally. If using an environment variable, the approach would be to generate a key once for the app to use, and then to keep using that same key both to mint new tokens and to verify incoming ones.

If you wanted more granularity (such as a key per user), then you’d need a different approach to storing the keys than an environment variable (such as using a KMS or DB).

More granular keys can offer advantages (e.g. if you had a key per user, you’d be able to revoke all tokens for a user by deleting and regenerating their key), but are operationally it can be more difficult to manage than a single key.

Hmmmm, interesting, doesn’t that mean I will have to store the symmetric key into the database first? And when it needs to verify, I will have to query the key corresponding to the user in the database?

You’d need to store the symmetric key you use, yes, but this is true of both modes. The difference between the symmetric vs. asymmetric approach is that you only need to deal with one type of key in the symmetric approach (the same key is used to mint new tokens as is to verify them), whereas asymmetric you have one key to mint and another to verify.

The key doesn’t necessarily need to be per user, it could be set at the application level instead (e.g. set it through an environment variable). Equally you could go so far as to generate a new key for each token you issue, so long as you have a means to store that key and a sensible way to look it up when it comes to verify the token; a key per user falls somewhere in the middle of these extremes. How you set this up has different security and operational trade-offs, and depending on your use case you may decide a different option is the better suited 🙂

The function you’re looking for is ParseV4Local on the parser 🙂

I named the methods Parse... to keep it a little general, since each method will do the appropriate cryptographic operation (decrypt/verify) as well as rule validation on the claims for any supplied rules.

Possibly these names are now over generalised though, and might help if I renamed these to DecryptV4Local, VerifyV4Public etc…

The value that you’re using is still not a valid ed25519 private key 😄 The reason I suggested using hex was so that you could have a convenient string representation of the key in your file, but you still need the underlying value to be a valid key. Hope that makes sense!

If you need help generating a new valid private key, you can run this once and save the output to use as a key later:

paseto.NewV4AsymmetricSecretKey().ExportHex()

A side question, in your example in README, you have publicKey := secretKey.Public() // DO share this one, so after I did publicKey := secretKey.Public().ExportHex() like above, not only the signed token, but also I put the publicKey in 2 cookies and send it back to the browser, so when a user is trying to access APIs that require authMiddle, I grab the publicKey from the request in the cookie in my code and use paseto.NewV4AsymmetricPublicKeyFromHex() to get the pubKey and pass into parser.ParseV4Public(), this is the right logic to do, right?

Unless you need the client side to be able to inspect the values inside the token, or a party other than your server to be able to verify your token (which doesn’t seem to be the case here?), I’d recommend instead using the symmetric (aka “local”) PASETO mode.

For asymmetric modes, whilst the public key can be known by others, you still need the public key to be trustworthy (i.e. you get it directly from the service you expect to have signed the token, rather than consuming it at the same time as the token that you are verifying). The setup you’ve described would actually allow users to generate their own tokens using whatever fields they like, so long as they generate their own keypair and give you their public key.

To use an analogy: imagine that someone gave you a signed document and you wanted to make sure it was correct; instead of checking your own records to verify the signature, you asked the person that handed you this document to also tell you whether or not the signature was correct. If they wanted to trick you, they could simply say it was correct 😄 When you consume the public key (i.e. the thing that verifies the token) from the same place as you get the token, you’re essentially doing this with the token.

I see. Thank you so much for the detailed explanation. I will test it out later again and keep you posted!

The reason I ask is because an ed25519 key (at least in Go’s private format) can’t just be an arbitrary string of characters. The 64 bytes consist of a seed value (which is generally random) and a public key (which is mathematically related to the seed). If you pass in an arbitrary string of bytes/characters here, the public portion of the key won’t be mathematically related to the seed, and so it won’t be possible to validate signatures which were built on this value.

If you’re wanting to pass in your own random bytes, you can use the seed constructors which accept 32 bytes to use as the seed (these will calculate the the public key/rest of the private key for you).

I’d also caution against using a string of printable characters even using the seed constructor in anything other than testing, because it’ll significantly reduce the security of signatures by restricting the seed to only bytes which happen to be printable characters (instead it is better to import as hex if you need to store the key in a text file, so you can retain the full range of the passed in bytes).

Also, you’ve mentioned a symmetric key in the above, so just wanted to point out that we’re currently discussing the asymmetric PASETO mode (which signs but does not encrypt the contents of the token). If you’re actually wanting to use a symmetric key, “local” mode is suited for that (and will encrypt the token) 🙂