How to verify a SignedTransaction thoroughly?

SignedTransaction is defined as following:

pub struct SignedTransaction {
    public_key: PublicKey,
    signature: Signature,
    raw_txn_bytes: Vec<u8>,

We can easily verify that the raw_txn_bytes is signed by public_key. However, there could be someone X who has manipulated raw_txn_bytes and signed it with X’s own key pair. Then, X could have updated public_key and signature to his own version. To prevent the attack, we have to verify that signature is from the original author of raw_txn_bytes.

Currently, I see the client does not do this type of verification.

We can decode raw_txn_bytes to find out sender account address. In normal cases, that address should match sha3(public_key). However there are exceptions such as sender == 0x0, which is libra genesis account.

What is the correct way to verify that the signature is from the real sender?

Hi @the729, I’m not sure if I’m entirely understanding your question, so please let me know if I’m answering the wrong thing.

So let’s say a sender A is composing a transaction. Let’s call their keypair A/A’ where A is the public key and A’ is the secret key. They will compose a transaction and serialize it into raw bytes (which go into raw_txn_bytes). These bytes are then hashed and signed using the secret key A’. Now the transaction is submitted to the validator network along with public key A. When executing, the validators look up the corresponding public key for the sender account and verify that it matches A (note that the auth key is stored on chain per-account). If it doesn’t, then the transaction fails. Now the transaction is executed and stored.

Ideally, we would return the proof to the auth key along with the proofs that are returned to a client, but since there is still some ongoing work for key rotations, we are not yet doing this. So once we were, you would take the proof to the auth key, verify that it matches the sender key, and then verify the signature. This will hopefully be in place soon

Your question actually raised some internal discussion. The determination was that this is likely unnecessary. We may at some point allow you to query the auth key at version V via a separate query. But if we allow incorrectly signed transactions to be committed that’s a ddos mechanism (and an obviously incorrect system) so we might be able to safely allow clients to assume that committed transactions are correctly signed. It’s also unclear that we want people to do operations like “get T_i, verify the signature, then do X” generally one should use events to determine the effects of a tx

@kph Thank you for answering. Let me repeat the protocol to see if I understand correctly.

So from the validator’s point of view, the only source of trust is the auth key of an account, which is sha3 hash of the actual public key A. When the validator receives an incoming signed transaction, it validates the transaction in 2 steps before execution:

  • step 1: verify hash(public key) == auth key in store
  • step 2: verify signature

You are suggesting that, after your discussion, the client can safely skip step 1, and only verify step 2. However, it sounds to me that if step 1 can be skiped on the client side, then step 2 can also be safely skiped. After all, if a signature is incorrect, the transaction should never be included, otherwise it “an obviously incorrect system”.

1 Like

Indeed, the client could skip both steps. And in fact, likely should skip them both. The client doesn’t actually know the auth key that was used unless it first queries the state of the account at the same version as the transaction was executed (because the address does not necessarily correspond to the auth key because a user could change their auth key).