commit 570f110ede99657137c5b8357c8b924765d80b03
parent 018ea9e7e0bcadbbd849f3f90cbbd08cee1c48c2
Author: Mikolai Gütschow <mikolai.guetschow@tu-dresden.de>
Date: Tue, 12 Aug 2025 12:52:36 +0200
introduce Sign-Msg
Diffstat:
2 files changed, 223 insertions(+), 167 deletions(-)
diff --git a/draft-guetschow-taler-protocol.md b/draft-guetschow-taler-protocol.md
@@ -57,6 +57,7 @@ Use at your own risk!
- `"abc"` denotes the literal string `abc` encoded as ASCII [RFC20]
- `a | b` denotes the concatenation of a with b
+- `len(a)` denotes the length in bytes of the byte string a
- `padZero(y, a)` denotes the byte string a, zero-padded to the length of y bytes
- `bits(x)`/`bytes(x)` denotes the minimal number of bits/bytes necessary to represent the multiple precision integer x
- `uint(y, x)` denotes the `y` least significant bits of the integer `x` encoded in network byte order (big endian)
@@ -364,6 +365,31 @@ Relative timestamps are represented as `uint64(x)` where `x` is given in microse
The special value `0xFFFFFFFFFFFFFFFF` represents "forever".
-->
+### Signatures
+
+All messages to be signed in Taler start with a header containing their size and
+a fixed signing context (purpose) as registered by GANA in the
+[GNUnet Signature Purposes](https://gana.gnunet.org/gnunet-signatures/gnunet_signatures.html)
+registry. All Taler-related purposes start at 1000 and can be found in the
+[registry sources](https://git.taler.net/gana.git/tree/gnunet-signatures/registry.rec#n221).
+
+~~~
+Sign-Msg(purpose, msg) -> out
+
+Inputs:
+ purpose signature purpose as registered at GANA
+ msg message content (excl. header) to be signed
+
+Output:
+ out complete message (incl. header) to be signed
+~~~
+
+`out` is formed as follows:
+
+~~~
+out = uint32(len(msg)) | uint32(purpose) | msg
+~~~
+
// todo: explain persist, check
## Withdrawal
@@ -417,11 +443,11 @@ persist *(coin, blind_secret)
where `msg0` is formed as follows:
~~~
-msg0 = uint32(160) | uint32(1200) /* TALER_SIGNATURE_WALLET_RESERVE_WITHDRAW */
- | sum(*denom.value) | sum(*denom.fee_withdraw)
- | SHA-512( *(SHA-512(denom.pub) | uint32(0x1) | blind_coin) )
- | uint256(0x0)
- | uint32(0x0) | uint32(0x0)
+msg0 = Sign-Msg(WALLET_RESERVE_WITHDRAW,
+ ( sum(*denom.value) | sum(*denom.fee_withdraw)
+ | SHA-512( *(SHA-512(denom.pub) | uint32(0x1) | blind_coin) )
+ | uint256(0x0)
+ | uint32(0x0) | uint32(0x0) ))
~~~
The wallet derives coins and blinding secrets using `GenerateCoin` from a master secret and an integer index.
@@ -508,16 +534,14 @@ where `msg0`, `*msg1`, and `msg2` are formed as follows:
~~~
h_contract = SHA-512(canonicalJSON(contract))
-msg0 = uint32(72) | uint32(1101) /* TALER_SIGNATURE_MERCHANT_CONTRACT */
- | h_contract
-*msg1 = uint32(456) | uint32(1201) /* TALER_SIGNATURE_WALLET_COIN_DEPOSIT */
- | h_contract | uint256(0x0)
- | uint512(0x0) | contract.h_wire | *coin.h_denom
- | contract.timestamp | contract.refund_deadline
- | *fraction + *denom.fee_deposit
- | *denom.fee_deposit | merchant.pub | uint512(0x0)
-msg2 = uint32(72) | uint32(1104) /* TALER_SIGNATURE_MERCHANT_PAYMENT_OK */
- | h_contract
+msg0 = Sign-Msg(MERCHANT_CONTRACT, h_contract)
+*msg1 = Sign-Msg(WALLET_COIN_DEPOSIT,
+ ( h_contract | uint256(0x0)
+ | uint512(0x0) | contract.h_wire | *coin.h_denom
+ | contract.timestamp | contract.refund_deadline
+ | *fraction + *denom.fee_deposit
+ | *denom.fee_deposit | merchant.pub | uint512(0x0) ))
+msg2 = Sign-Msg(MERCHANT_PAYMENT_OK, h_contract)
~~~
(without age restriction, policy and wallet data hash)
@@ -562,12 +586,12 @@ check EdDSA-Verify(exchange.pub, msg3, sig3) |
where `msg3` is formed as follows:
~~~
-msg3 = uint32(344) | uint32(1033) /* TALER_SIGNATURE_EXCHANGE_CONFIRM_DEPOSIT */
- | h_contract | contract.h_wire | uint512(0x0)
- | exchange_timestamp | contract.wire_deadline
- | contract.refund_deadline
- | sum(*deposit.fraction - *denom.fee_deposit)
- | SHA-512(*deposit.sig1) | merchant.pub
+msg3 = Sign-Msg(EXCHANGE_CONFIRM_DEPOSIT,
+ ( h_contract | contract.h_wire | uint512(0x0)
+ | exchange_timestamp | contract.wire_deadline
+ | contract.refund_deadline
+ | sum(*deposit.fraction - *denom.fee_deposit)
+ | SHA-512(*deposit.sig1) | merchant.pub ))
~~~
# Security Considerations
diff --git a/draft-guetschow-taler-protocol.xml b/draft-guetschow-taler-protocol.xml
@@ -29,7 +29,7 @@
</address>
</author>
- <date year="2025" month="August" day="11"/>
+ <date year="2025" month="August" day="12"/>
<workgroup>independent</workgroup>
@@ -68,6 +68,7 @@ Use at your own risk!</t>
<t><list style="symbols">
<t><spanx style="verb">"abc"</spanx> denotes the literal string <spanx style="verb">abc</spanx> encoded as ASCII <xref target="RFC20"></xref></t>
<t><spanx style="verb">a | b</spanx> denotes the concatenation of a with b</t>
+ <t><spanx style="verb">len(a)</spanx> denotes the length in bytes of the byte string a</t>
<t><spanx style="verb">padZero(y, a)</spanx> denotes the byte string a, zero-padded to the length of y bytes</t>
<t><spanx style="verb">bits(x)</spanx>/<spanx style="verb">bytes(x)</spanx> denotes the minimal number of bits/bytes necessary to represent the multiple precision integer x</t>
<t><spanx style="verb">uint(y, x)</spanx> denotes the <spanx style="verb">y</spanx> least significant bits of the integer <spanx style="verb">x</spanx> encoded in network byte order (big endian)</t>
@@ -396,6 +397,32 @@ Relative timestamps are represented as `uint64(x)` where `x` is given in microse
The special value `0xFFFFFFFFFFFFFFFF` represents "forever".
--></t>
+</section>
+<section anchor="signatures"><name>Signatures</name>
+
+<t>All messages to be signed in Taler start with a header containing their size and
+a fixed signing context (purpose) as registered by GANA in the
+<eref target="https://gana.gnunet.org/gnunet-signatures/gnunet_signatures.html">GNUnet Signature Purposes</eref>
+registry. All Taler-related purposes start at 1000 and can be found in the
+<eref target="https://git.taler.net/gana.git/tree/gnunet-signatures/registry.rec#n221">registry sources</eref>.</t>
+
+<figure><artwork><![CDATA[
+Sign-Msg(purpose, msg) -> out
+
+Inputs:
+ purpose signature purpose as registered at GANA
+ msg message content (excl. header) to be signed
+
+Output:
+ out complete message (incl. header) to be signed
+]]></artwork></figure>
+
+<t><spanx style="verb">out</spanx> is formed as follows:</t>
+
+<figure><artwork><![CDATA[
+out = uint32(len(msg)) | uint32(purpose) | msg
+]]></artwork></figure>
+
<t>// todo: explain persist, check</t>
</section>
@@ -451,11 +478,11 @@ persist *(coin, blind_secret)
<t>where <spanx style="verb">msg0</spanx> is formed as follows:</t>
<figure><artwork><![CDATA[
-msg0 = uint32(160) | uint32(1200) /* TALER_SIGNATURE_WALLET_RESERVE_WITHDRAW */
- | sum(*denom.value) | sum(*denom.fee_withdraw)
- | SHA-512( *(SHA-512(denom.pub) | uint32(0x1) | blind_coin) )
- | uint256(0x0)
- | uint32(0x0) | uint32(0x0)
+msg0 = Sign-Msg(WALLET_RESERVE_WITHDRAW,
+ ( sum(*denom.value) | sum(*denom.fee_withdraw)
+ | SHA-512( *(SHA-512(denom.pub) | uint32(0x1) | blind_coin) )
+ | uint256(0x0)
+ | uint32(0x0) | uint32(0x0) ))
]]></artwork></figure>
<t>The wallet derives coins and blinding secrets using <spanx style="verb">GenerateCoin</spanx> from a master secret and an integer index.
@@ -543,16 +570,14 @@ check EdDSA-Verify(merchant.pub, msg2, sig2) |
<figure><artwork><![CDATA[
h_contract = SHA-512(canonicalJSON(contract))
-msg0 = uint32(72) | uint32(1101) /* TALER_SIGNATURE_MERCHANT_CONTRACT */
- | h_contract
-*msg1 = uint32(456) | uint32(1201) /* TALER_SIGNATURE_WALLET_COIN_DEPOSIT */
- | h_contract | uint256(0x0)
- | uint512(0x0) | contract.h_wire | *coin.h_denom
- | contract.timestamp | contract.refund_deadline
- | *fraction + *denom.fee_deposit
- | *denom.fee_deposit | merchant.pub | uint512(0x0)
-msg2 = uint32(72) | uint32(1104) /* TALER_SIGNATURE_MERCHANT_PAYMENT_OK */
- | h_contract
+msg0 = Sign-Msg(MERCHANT_CONTRACT, h_contract)
+*msg1 = Sign-Msg(WALLET_COIN_DEPOSIT,
+ ( h_contract | uint256(0x0)
+ | uint512(0x0) | contract.h_wire | *coin.h_denom
+ | contract.timestamp | contract.refund_deadline
+ | *fraction + *denom.fee_deposit
+ | *denom.fee_deposit | merchant.pub | uint512(0x0) ))
+msg2 = Sign-Msg(MERCHANT_PAYMENT_OK, h_contract)
]]></artwork></figure>
<t>(without age restriction, policy and wallet data hash)</t>
@@ -598,12 +623,12 @@ check EdDSA-Verify(exchange.pub, msg3, sig3) |
<t>where <spanx style="verb">msg3</spanx> is formed as follows:</t>
<figure><artwork><![CDATA[
-msg3 = uint32(344) | uint32(1033) /* TALER_SIGNATURE_EXCHANGE_CONFIRM_DEPOSIT */
- | h_contract | contract.h_wire | uint512(0x0)
- | exchange_timestamp | contract.wire_deadline
- | contract.refund_deadline
- | sum(*deposit.fraction - *denom.fee_deposit)
- | SHA-512(*deposit.sig1) | merchant.pub
+msg3 = Sign-Msg(EXCHANGE_CONFIRM_DEPOSIT,
+ ( h_contract | contract.h_wire | uint512(0x0)
+ | exchange_timestamp | contract.wire_deadline
+ | contract.refund_deadline
+ | sum(*deposit.fraction - *denom.fee_deposit)
+ | SHA-512(*deposit.sig1) | merchant.pub ))
]]></artwork></figure>
</section>
@@ -714,7 +739,7 @@ msg3 = uint32(344) | uint32(1033) /* TALER_SIGNATURE_EXCHANGE_CONFIRM_DEPOSIT */
-<?line 581?>
+<?line 612?>
<section anchor="change-log"><name>Change log</name>
@@ -732,129 +757,136 @@ Education and Research (BMBF) within the project Concrete Contracts.</t>
</back>
<!-- ##markdown-source:
-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
+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
-->