Comments (4)
I've tracked this far enough to tell that the client is signing a different message than the chain is validating. The cosmos-sdk appears to use the dreaded "sign the canonical serialization" pattern, in which the bytes being sent over the wire are not the same bytes that the signer uses when computing the signature (and hopefully the verifier can reconstruct the same byte sequence). My biggest concern about this pattern is that the recipient may choose to act upon a decoded form of the received bytes, even though the signature they verified was on something different. If there is any variation in the encoding or parsing, an attacker might be able to replace a signed message on the wire with something very similar, but that has different semantics.
In this case, the sender (our ag-cosmos-helper
, which acts like nscli
in the cosmos-sdk Name Service tutorial) produces a message with the following JSON structure:
{
account_number: "0",
chain_id: "agoric",
fee: { amount: [], gas: "200000" },
memo: "",
msgs: [
{
Ack: 1,
Messages: [],
Nums: [],
Peer: (pubkey)
Submitter: (pubkey)
}],
sequence: "1"
}
In MakeSignature
, the body being signed (msg.Bytes()
) is:
00000000 7b 22 61 63 63 6f 75 6e 74 5f 6e 75 6d 62 65 72 |{"account_number|
00000010 22 3a 22 30 22 2c 22 63 68 61 69 6e 5f 69 64 22 |":"0","chain_id"|
00000020 3a 22 61 67 6f 72 69 63 22 2c 22 66 65 65 22 3a |:"agoric","fee":|
00000030 7b 22 61 6d 6f 75 6e 74 22 3a 5b 5d 2c 22 67 61 |{"amount":[],"ga|
00000040 73 22 3a 22 32 30 30 30 30 30 22 7d 2c 22 6d 65 |s":"200000"},"me|
00000050 6d 6f 22 3a 22 22 2c 22 6d 73 67 73 22 3a 5b 7b |mo":"","msgs":[{|
00000060 22 41 63 6b 22 3a 31 2c 22 4d 65 73 73 61 67 65 |"Ack":1,"Message|
00000070 73 22 3a 5b 5d 2c 22 4e 75 6d 73 22 3a 5b 5d 2c |s":[],"Nums":[],|
00000080 22 50 65 65 72 22 3a 22 63 6f 73 6d 6f 73 31 66 |"Peer":"cosmos1f|
00000090 33 30 76 65 6d 76 36 72 75 7a 38 38 74 34 73 78 |30vemv6ruz88t4sx|
000000a0 66 6a 68 74 67 75 67 36 79 76 6e 6a 6e 33 6a 64 |fjhtgug6yvnjn3jd|
000000b0 34 74 61 78 6a 22 2c 22 53 75 62 6d 69 74 74 65 |4taxj","Submitte|
000000c0 72 22 3a 22 63 6f 73 6d 6f 73 31 66 33 30 76 65 |r":"cosmos1f30ve|
000000d0 6d 76 36 72 75 7a 38 38 74 34 73 78 66 6a 68 74 |mv6ruz88t4sxfjht|
000000e0 67 75 67 36 79 76 6e 6a 6e 33 6a 64 34 74 61 78 |gug6yvnjn3jd4tax|
000000f0 6a 22 7d 5d 2c 22 73 65 71 75 65 6e 63 65 22 3a |j"}],"sequence":|
00000100 22 31 22 7d |"1"}|
The actual transmitted bytes (maybe "amino" encoded?) are smaller (txBytes
inside CompleteAndBroadcastTxCli
):
00000000 c3 01 f0 62 5d ee 0a 4b 07 0f de 94 0a 2d 63 6f |...b]..K.....-co|
00000010 73 6d 6f 73 31 66 33 30 76 65 6d 76 36 72 75 7a |smos1f30vemv6ruz|
00000020 38 38 74 34 73 78 66 6a 68 74 67 75 67 36 79 76 |88t4sxfjhtgug6yv|
00000030 6e 6a 6e 33 6a 64 34 74 61 78 6a 20 01 2a 14 4c |njn3jd4taxj .*.L|
00000040 5e cc ed 9a 1f 04 73 ae b0 32 65 75 a3 88 d1 19 |^.....s..2eu....|
00000050 39 4e 32 12 04 10 c0 9a 0c 1a 6a 0a 26 eb 5a e9 |9N2.......j.&.Z.|
00000060 87 21 02 39 46 8a 4b 4e c8 85 0b 36 6e 0c da bf |.!.9F.KN...6n...|
00000070 5a 9a ab 7b be 5b cd 06 03 60 6d 52 14 c0 91 e0 |Z..{.[...`mR....|
00000080 d6 4e b1 12 40 02 4a 58 7a a2 2f 1c 1f 97 f4 35 |[email protected]./....5|
00000090 25 90 79 e6 94 c7 1d 1f 97 0d 8a 64 2e b1 73 2a |%.y........d..s*|
000000a0 00 49 c0 4a 5c 25 28 a0 33 47 d8 ce f5 9f 92 cc |.I.J\%(.3G......|
000000b0 d7 37 c1 a1 92 b4 0c bb 11 0b e6 85 70 e6 94 04 |.7..........p...|
000000c0 fb ab 51 c5 58 |..Q.X|
When this arrives on the chain node, the x/auth/ante.go NewAnteHandler
calls processSig
with a signBytes
that differs in the way the empty Messages and Nums lists are encoded (they show up as null
instead of []
):
00000000 7b 22 61 63 63 6f 75 6e 74 5f 6e 75 6d 62 65 72 |{"account_number|
00000010 22 3a 22 30 22 2c 22 63 68 61 69 6e 5f 69 64 22 |":"0","chain_id"|
00000020 3a 22 61 67 6f 72 69 63 22 2c 22 66 65 65 22 3a |:"agoric","fee":|
00000030 7b 22 61 6d 6f 75 6e 74 22 3a 5b 5d 2c 22 67 61 |{"amount":[],"ga|
00000040 73 22 3a 22 32 30 30 30 30 30 22 7d 2c 22 6d 65 |s":"200000"},"me|
00000050 6d 6f 22 3a 22 22 2c 22 6d 73 67 73 22 3a 5b 7b |mo":"","msgs":[{|
00000060 22 41 63 6b 22 3a 31 2c 22 4d 65 73 73 61 67 65 |"Ack":1,"Message|
00000070 73 22 3a 6e 75 6c 6c 2c 22 4e 75 6d 73 22 3a 6e |s":null,"Nums":n|
00000080 75 6c 6c 2c 22 50 65 65 72 22 3a 22 63 6f 73 6d |ull,"Peer":"cosm|
00000090 6f 73 31 66 33 30 76 65 6d 76 36 72 75 7a 38 38 |os1f30vemv6ruz88|
000000a0 74 34 73 78 66 6a 68 74 67 75 67 36 79 76 6e 6a |t4sxfjhtgug6yvnj|
000000b0 6e 33 6a 64 34 74 61 78 6a 22 2c 22 53 75 62 6d |n3jd4taxj","Subm|
000000c0 69 74 74 65 72 22 3a 22 63 6f 73 6d 6f 73 31 66 |itter":"cosmos1f|
000000d0 33 30 76 65 6d 76 36 72 75 7a 38 38 74 34 73 78 |30vemv6ruz88t4sx|
000000e0 66 6a 68 74 67 75 67 36 79 76 6e 6a 6e 33 6a 64 |fjhtgug6yvnjn3jd|
000000f0 34 74 61 78 6a 22 7d 5d 2c 22 73 65 71 75 65 6e |4taxj"}],"sequen|
00000100 63 65 22 3a 22 31 22 7d |ce":"1"}|
The difference between this and what MakeSignature
used is, of course, enough to make the signature invalid, and the message is rejected.
I suspect this has something to do with interactions between our msgs.go
definitions of GetSignBytes()
, MsgDeliverInbound
, and NewMsgDeliverInbound()
. I imagine the amino encoding isn't representing "empty" in the same way as the JSON serialization does it.
from agoric-sdk.
@michaelfig and I tracked this down to the Amino decoder providing us with a MsgDeliverInbound
struct that contained Messages= nil
instead of an empty array. This might be by design.. we're looking at the go-amino bugs to figure it out. The upshot is that when we send Messages: []
from the client, the chain gets Messages: nil
, which then serializes into Messages:null
in GetSignBytes
. That difference breaks the signatures.
We ruled out json.Marshal
causing the problem by asking it to serialize a structure with an empty array, and it created "Messages":[]
as expected. json.Marshal
only put null
in the output when it got nil
as an input, which is why we think it's the amino pathway. It might be either the encoder or the decoder, we can't yet distinguish between the two.
@michaelfig landed a patch (e214661) to compensate for this by replacing the nil
with an empty array in GetSignBytes
, which makes the signatures work again. So we can close this bug, although MFig is going to file a bug on go-amino to see if we can get the problem addressed upstream.
from agoric-sdk.
I have raised this issue at tendermint/go-amino#275
from agoric-sdk.
in the old repo. this was cosmic-swingset issue 5
from agoric-sdk.
Related Issues (20)
- Confusion about which `assert` to endow a compartment with
- Improve publishing DX for deploying contracts
- bundleStore: break bundles into deduplicated modules HOT 1
- kernel support for module-deduplication in bundleStore
- cosmic-swingset support for module installation: agd publish
- retry helper to durably watch idempotent promises
- integration tests missing from master
- agoric-cli wallet send options are too strongly positional
- inconsistent `watch` handling of arguments for watcher
- Self resolving vows cause infinite loop
- Genesis import fails for large exports
- Otel slog sender does not handle `cosmic-swingset-upgrade-finish` HOT 2
- MainNet proposals should dynamically provide oracle addresses HOT 1
- test support: ibc bridge mocks for @agoric/orchestration HOT 1
- Make maxVatsOnline customizable to optimize resource usage HOT 1
- orchestration bundles: remove unused protobuf encoders/decoders in `CosmosOrchestrationAccount` HOT 2
- Extract metrics from slogs and send them to otel/datadog
- confusing "vat terminated" error for messages sent to upgrade-abandoned objects HOT 1
- Upgrading scaled price authority causes vaults upgrade to not complete
- A typo in vat-data-bindings.js
Recommend Projects
-
React
A declarative, efficient, and flexible JavaScript library for building user interfaces.
-
Vue.js
🖖 Vue.js is a progressive, incrementally-adoptable JavaScript framework for building UI on the web.
-
Typescript
TypeScript is a superset of JavaScript that compiles to clean JavaScript output.
-
TensorFlow
An Open Source Machine Learning Framework for Everyone
-
Django
The Web framework for perfectionists with deadlines.
-
Laravel
A PHP framework for web artisans
-
D3
Bring data to life with SVG, Canvas and HTML. 📊📈🎉
-
Recommend Topics
-
javascript
JavaScript (JS) is a lightweight interpreted programming language with first-class functions.
-
web
Some thing interesting about web. New door for the world.
-
server
A server is a program made to process requests and deliver data to clients.
-
Machine learning
Machine learning is a way of modeling and interpreting data that allows a piece of software to respond intelligently.
-
Visualization
Some thing interesting about visualization, use data art
-
Game
Some thing interesting about game, make everyone happy.
Recommend Org
-
Facebook
We are working to build community through open source technology. NB: members must have two-factor auth.
-
Microsoft
Open source projects and samples from Microsoft.
-
Google
Google ❤️ Open Source for everyone.
-
Alibaba
Alibaba Open Source for everyone
-
D3
Data-Driven Documents codes.
-
Tencent
China tencent open source team.
from agoric-sdk.