Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

docs(textual): Require signing over raw bytes #12910

Merged
merged 10 commits into from
Sep 5, 2022
26 changes: 25 additions & 1 deletion docs/architecture/adr-050-sign-mode-textual.md
Original file line number Diff line number Diff line change
Expand Up @@ -5,6 +5,7 @@
- Dec 06, 2021: Initial Draft.
- Feb 07, 2022: Draft read and concept-ACKed by the Ledger team.
- May 16, 2022: Change status to Accepted.
- Aug 11, 2022: Require signing over tx raw bytes.

## Status

Expand Down Expand Up @@ -115,6 +116,7 @@ Tip: <string>
*Public Key: <base64_string>
*Sequence: <uint64>
*End of other signers
*Hash of raw bytes: <hex_string> // Hex encoding of bytes defined in #10, to prevent tx hash malleability.
```

### 8. Encoding of the Transaction Body
Expand Down Expand Up @@ -158,7 +160,26 @@ Grantee: cosmos1ghi...jkl
End of transaction messages
```

### 9. Signing Payload and Wire Format
### 10. Require signing over the `TxBody` and `AuthInfo` raw bytes

Recall that the transaction bytes merklelized on chain are the Protobuf binary serialization of [TxRaw](https://github.com/cosmos/cosmos-sdk/blob/v0.46.0/proto/cosmos/tx/v1beta1/tx.proto#L33), which contains the `body_bytes` and `auth_info_bytes`. Moreover, the transaction hash is defined as the SHA256 hash of the `TxRaw` bytes. We require that the user signs over these bytes in SIGN_MODE_TEXTUAL, more specifically over the following string:

```
*Hash of raw bytes: <HEX(sha256(len(body_bytes) ++ <body_bytes> ++ len(auth_info_bytes) ++ auth_info_bytes))>
amaury1093 marked this conversation as resolved.
Show resolved Hide resolved
```

where:
- `++` denotes concatenation,
- `HEX` is the hexadecimal representation of the bytes, all in capital letters, no `0x` prefix,
- and `len()` is encoded as a Big-Endian uint64.

This is to prevent transaction hash malleability. The point #1 about bijectivity assures that transaction `body` and `auth_info` values are not malleable, but the transaction hash still might be malleable with point #1 only, because the SIGN_MODE_TEXTUAL strings don't follow the byte ordering defined in `body_bytes` and `auth_info_bytes`. Without this hash, a malicious validator or exchange could intercept a transaction, modify its transaction hash _after_ the user signed it using SIGN_MODE_TEXTUAL (by tweaking the byte ordering inside `body_bytes` or `auth_info_bytes`), and then submit it to Tendermint.
amaury1093 marked this conversation as resolved.
Show resolved Hide resolved

By including this hash in the SIGN_MODE_TEXTUAL signing payload, we keep the same level of guarantees as [SIGN_MODE_DIRECT](./adr-020-protobuf-transaction-encoding.md).

These bytes are only shown in expert mode, hence the leading `*`.

### 11. Signing Payload and Wire Format

This string array is encoded as a single `\n`-delimited string before transmitted to the hardware device, and this long string is the signing payload signed by the hardware wallet.

Expand Down Expand Up @@ -232,6 +253,7 @@ Amount: 10 atom // Conversion from uatom to atom using value renderer
End of transaction messages
Fee: 0.002 atom
*Gas: 100'000
*Hash of raw bytes: <hex_string>
```

#### Example 2: Multi-Msg Transaction with 3 signers
Expand Down Expand Up @@ -320,6 +342,7 @@ Tip: 200 ibc/CDC4587874B85BEA4FCEC3CEA5A1195139799A1FEE711A07D972537E18FDA39D
*Sign mode: Direct Aux
*Sequence: 42
*End of other signers
*Hash of raw bytes: <hex_string>
```

#### Example 5: Complex Transaction with Nested Messages
Expand Down Expand Up @@ -466,6 +489,7 @@ Fee: 0.002 atom
*Sign mode: Direct
*Sequence: 42
*End of other signers
*Hash of raw bytes: <hex_string>
```

## Consequences
Expand Down