$ tcpdump -i lo port 8000 -w pcap
TCP Dump
Public key:
Message hash:
Signature:
Let’s observe Internet Computer calls on the wire with tcpdump
and
tcpflow
. Assuming dfx start
is running on the default port of 8000:
We call our hello.wasm
canister:
$ dfx canister call hello hi
Next, we hit Ctrl-C to stop tcpdump
, and run:
$ tcpflow -r pcap
This produces a few files. One has a name like:
127.000.000.001.59628-127.000.000.001.08000
where the first port number is selected by the system.
This log shows the TCP requests made to the local net. It turns out to use the HTTPS interface of the Internet Computer, starting with a status check:
GET /api/v2/status HTTP/1.1 content-type: application/cbor accept: */* host: 127.0.0.1:8000
Satisfied with the check, it then sends a POST request to call the hi
method:
POST /api/v2/canister/rrkah-fqaaa-aaaaa-aaaaq-cai/call HTTP/1.1 content-type: application/cbor accept: */* host: 127.0.0.1:8000 content-length: 346
We hex dump the contents:
00000000: d9d9 f7a3 6763 6f6e 7465 6e74 a76c 7265 ....gcontent.lre 00000010: 7175 6573 745f 7479 7065 6463 616c 6c65 quest_typedcalle 00000020: 6e6f 6e63 6550 a397 34ae 37c9 ebba f7ae nonceP..4.7..... 00000030: cd7f 9b10 ce0e 6e69 6e67 7265 7373 5f65 ......ningress_e 00000040: 7870 6972 791b 16e2 e030 d9c3 b342 6673 xpiry....0...Bfs 00000050: 656e 6465 7258 1d4f f2c7 9f70 067d 24bb enderX.O...p.}$. 00000060: ba4a 1647 37e0 eddd 6280 2ac6 0353 1fa0 .J.G7...b.*..S.. 00000070: fc85 5b02 6b63 616e 6973 7465 725f 6964 ..[.kcanister_id 00000080: 4a00 0000 0000 0000 0101 016b 6d65 7468 J..........kmeth 00000090: 6f64 5f6e 616d 6562 6869 6361 7267 4644 od_namebhicargFD 000000a0: 4944 4c00 006d 7365 6e64 6572 5f70 7562 IDL..msender_pub 000000b0: 6b65 7958 5830 5630 1006 072a 8648 ce3d keyXX0V0...*.H.= 000000c0: 0201 0605 2b81 0400 0a03 4200 043c c849 ....+.....B..<.I 000000d0: c77d 5ead 3aea f2ea 821d c85d 6bb1 0483 .}^.:......]k... 000000e0: bbe9 7875 d010 ada2 629e 4a86 3e81 5793 ..xu....b.J.>.W. 000000f0: de69 ae4f fce4 6d52 c4b1 4ed1 a3ae 40e8 .i.O..mR..N...@. 00000100: 5b53 b5cb 6c7e d6de 89d8 0c43 056a 7365 [S..l~.....C.jse 00000110: 6e64 6572 5f73 6967 5840 c1e2 68ea d7f7 nder_sigX@..h... 00000120: f8b5 e42c bb77 c1d7 5f2e e8a5 8f32 94d9 ...,.w.._....2.. 00000130: 23fc 9bc1 cc83 6719 9be5 8a4d c285 beb4 #.....g....M.... 00000140: ed6f a1be abb0 682e c37a a6b7 7263 6071 .o....h..z..rc`q 00000150: 41a6 9573 3e6b a53d d002 A..s>k.=..
The d9d9f7
hints that it is a CBOR message, which we
decode with cbor.me
:
55799( { "content": { "request_type": "call" , "nonce": h'A39734AE37C9EBBAF7AECD7F9B10CE0E' , "ingress_expiry": 1649126913987556162 , "sender": h'4FF2C79F70067D24BBBA4A164737E0EDDD62802AC603531FA0FC855B02' , "canister_id": h'00000000000000010101' , "method_name": "hi" , "arg": h'4449444C0000' } , "sender_pubkey": h'3056301006072A8648CE3D020106052B8104000A034200043CC849C77D5EAD3AEAF2EA821DC85D6BB10483BBE97875D010ADA2629E4A863E815793DE69AE4FFCE46D52C4B14ED1A3AE40E85B53B5CB6C7ED6DE89D80C4305' , "sender_sig": h'C1E268EAD7F7F8B5E42CBB77C1D75F2EE8A58F3294D923FC9BC1CC8367199BE58A4DC285BEB4ED6FA1BEABB0682EC37AA6B77263607141A695733E6BA53DD002'})
We see that:
-
request_type
iscall
. This is overkill becausehi
is a query, not an update. On the other hand, we now get to see an update in action, which is more interesting than a query. -
arg
is the Candid message4449444C0000
, the empty message. -
sender
is the (raw) principal ID of the sender; since a user sent the request, it is the SHA-224 hash of thesender_pubkey
field followed by 0x02. -
sender_pubkey
is a DER-encoded key. -
sender_sig
is ther
ands
fields of the signature, both 32 bytes long.
The sender_pubkey
is the the "zoo"
key we explored.
Recall we can
decode
sender_pubkey
online to see some boilerplate followed by 0x04 indicating an uncompressed point,
then the x and y coordinates that take 32 bytes each (so the last 130 hex digits represent the raw key).
The message being signed is the
Request
ID prepended with the domain separator \x0Aic-request
.
Because we made a call
instead of a query, we see dfx
poll for a response
via read_state
:
POST /api/v2/canister/rrkah-fqaaa-aaaaa-aaaaq-cai/read_state HTTP/1.1 content-type: application/cbor accept: */* host: 127.0.0.1:8000 content-length: 337
The decoded contents contain:
"paths": [[h'726571756573745F737461747573', h'E3676A8C10705FCCE9772601485F6CEA00283166411E5FBE827F796B3C779D06']]},
The first blob is request_status
, and the second is the request ID of the
request we just sent.
We can compute the message hash that is signed by the sender_pubkey
:
$ (printf "\x0aic-request"; echo E3676A8C10705FCCE9772601485F6CEA00283166411E5FBE827F796B3C779D06 | xxd -r -p ) | sha256sum ab01a4005d74c5f86b8b1e7a0e2e7c79286c6f6b6a910585cd822f210139fb2b -
Use the widget at the top of this page to verify the signature.
We examine another file produced by tcpflow
, logging the responses from the
local net:
127.000.000.001.08000-127.000.000.001.59628
55799({"certificate": h'D9D9F7A264747265658301820458209BB201590CC0CC99791C2E597995C481E1AAAD237C25465084D27FFAC3EEE0D3830182045820ED5A370EA06DD5C64BBDB9049F9645D703430F0827E39C7AEBF1FE44599120EE83024474696D65820349FC9EA8AA8FFFB7F116697369676E61747572655830B26FAB0E4093F839194990434A1374DCD14DBE4F90EA7A9280B26C8751CC1B9BDD7477AECD054C61A60BA8A6AC6BF94F'})
We see a CBOR message within this CBOR message. The inner message decodes to:
55799({"tree": [1, [4, h'9BB201590CC0CC99791C2E597995C481E1AAAD237C25465084D27FFAC3EEE0D3'], [1, [4, h'ED5A370EA06DD5C64BBDB9049F9645D703430F0827E39C7AEBF1FE44599120EE'], [2, h'74696D65', [3, h'FC9EA8AA8FFFB7F116']]]], "signature": h'B26FAB0E4093F839194990434A1374DCD14DBE4F90EA7A9280B26C8751CC1B9BDD7477AECD054C61A60BA8A6AC6BF94F'})
This certificate is proof that the local net has not yet received the request at a certain time. Eventually, we get a bigger certifiicate and decoding the CBOR message within this CBOR message gives:
55799({"tree": [1, [1, [4, h'883F097A8B478D53D2A3D8C09D9F3463E299705D78B782A4457815FD966120F0'], [1, [2, h'726571756573745F737461747573', [1, [4, h'16353093030AD98645FEC08958223DDD3ED211DB1C8619BB465EDD88712F1CDF'], [2, h'E3676A8C10705FCCE9772601485F6CEA00283166411E5FBE827F796B3C779D06', [1, [2, h'7265706C79', [3, h'48656C6C6F2C20576F726C64210A']], [2, h'737461747573', [3, h'7265706C696564']]]]]], [4, h'BD1E8E6FCF5F6150EF65C180344A88CCEA77C12967D52A41E65B6A2D6E7504BB']]], [1, [4, h'ED5A370EA06DD5C64BBDB9049F9645D703430F0827E39C7AEBF1FE44599120EE'], [2, h'74696D65', [3, h'A58CAFE291FFB7F116']]]], "signature": h'81E173DAFAA7D9D480107BFC9A38F7060F23FD9E5B7F97A0CA64BB2A065821A80742BBA81E7FF9F9EA0A6D632FA13887'})
Many of the blobs are strings encoded in ASCII, and it turns out this
certificate is proof that our call resulted in a reply
of "Hello,
World!\n"
.