2

I sent a signed transaction through the internet to a cloud function that needs to verify that the signer is the valid one.

My code:

const bHMainKeys = Keypair.fromSecret(privateKey)

if (transaction.source !== bHMainKeys.publicKey() || !transaction.signatures.some((s) => bHMainKeys.verify(transaction.signatureBase(), s))) {
  return res.status(500).send({ error: 'Invalid Transaction' })
}

The problem is that it's giving me false all the time.

Is this the correct way to use verify?

2 Answers 2

2

You will need to verify the signature against the signing key (account ID). You can take a look at how the compliance server implements signature verification.

Copying over the code (Golang) here for convenience:

// VerifySignature verifies if signature is valid. It makes a network connection
// to sender server in order to obtain stellar.toml file and signing key.
func (r *AuthRequest) VerifySignature(sender string) error {
    signatureBytes, err := base64.StdEncoding.DecodeString(r.Signature)
    if err != nil {
        return errors.New("Signature is not base64 encoded")
    }

    senderStellarToml, err := stellartoml.GetStellarTomlByAddress(sender)
    if err != nil {
        return errors.Wrap(err, "Cannot get stellar.toml of sender domain")
    }

    if senderStellarToml.SigningKey == "" {
        return errors.New("No SIGNING_KEY in stellar.toml of sender")
    }

    kp, err := keypair.Parse(senderStellarToml.SigningKey)
    if err != nil {
        return errors.New("SigningKey is invalid")
    }

    err = kp.Verify([]byte(r.DataJSON), signatureBytes)
    if err != nil {
        return errors.New("Signature is invalid")
    }

    return nil
}
0

There are two problems:

  1. You are verifying the wrong data. use transaction.hash() instead of transaction.signatureBase(). According to the transaction.signatureBase() docs:

Returns the "signature base" of this transaction, which is the value that, when hashed, should be signed to create a signature that validators on the Stellar Network will accept

  1. It states in the documentation that the argument is of type Buffer and your s variable is an xdr.DecoratedSignature. You need to extract the signature buffer inside of it. That is, use s._attributes.signature instead of just s (I know this seems hacky, but the SDK doesn't provide a Signature type, which would probably be more appropriate here)

Your hideous one-liner with these changes:

const bHMainKeys = Keypair.fromSecret(privateKey)

if (transaction.source !== bHMainKeys.publicKey() || !transaction.signatures.some((s) => bHMainKeys.verify(transaction.hash(), s._attributes.signature))) {
  return res.status(500).send({ error: 'Invalid Transaction' })
}

Also bear in mind that if the account has other signers, a signature by a different key may be valid on the network. In order to account for this you need to query horizon for the signers on the account, then check if the signature is valid for their keys as well. The javascript SDK does not provide one correct verification method for this. Here's how Stellar Laboratory does it:

https://github.com/stellar/laboratory/blob/4abbd25659c070b88071dfc73513f006e6925b7c/src/actions/xdrViewer.js#L63

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service and acknowledge you have read our privacy policy.

Not the answer you're looking for? Browse other questions tagged or ask your own question.