diff options
-rw-r--r-- | anastasis-db.png | bin | 34685 -> 38626 bytes | |||
-rw-r--r-- | anastasis.rst | 54 | ||||
-rw-r--r-- | checklist-demo-upgrade.rst | 51 | ||||
-rw-r--r-- | checklist-release.rst | 70 | ||||
-rw-r--r-- | core/api-merchant.rst | 67 | ||||
-rw-r--r-- | core/taler-uri.rst | 6 | ||||
-rw-r--r-- | developers-manual.rst (renamed from onboarding.rst) | 210 | ||||
-rw-r--r-- | index.rst | 2 | ||||
-rw-r--r-- | libeufin/api-sandbox.rst | 2 | ||||
-rw-r--r-- | libeufin/ebics.rst | 61 | ||||
-rw-r--r-- | taler-nfc-guide.rst | 2 |
11 files changed, 413 insertions, 112 deletions
diff --git a/anastasis-db.png b/anastasis-db.png Binary files differindex 5afa96ba..168506d4 100644 --- a/anastasis-db.png +++ b/anastasis-db.png diff --git a/anastasis.rst b/anastasis.rst index 3dcc4ce4..e8d10845 100644 --- a/anastasis.rst +++ b/anastasis.rst @@ -326,7 +326,7 @@ malicious policy, a user can still retrieve an older version of the policy to recover access to their data. This append-only storage for policies still leaves a strong adversary with the option of uploading many policies to exhaust the Anastasis server's capacity. We limit this attack by requiring a -policy upload to include a reference to a **payment secret** from a payment +policy upload to include a reference to a **payment identifier** from a payment made by the user. Thus, a policy upload requires both knowledge of the **identity** and making a payment. This effectively prevents and adversary from using the append-only policy storage from exhausting Anastasis server @@ -400,7 +400,7 @@ Receiving Terms of Service // Amount required per policy upload. Note that the amount is NOT charged additionally // to the monthly_storage_fee. Instead, when a payment is made, the amount is // divided by the policy_upload_fee (and rounded down) to determine how many - // uploads can be made under the associated **payment secret**. + // uploads can be made under the associated **payment identifier**. policy_upload_ratio: Amount; // maximum policy upload size supported @@ -454,6 +454,9 @@ Operations by the client are identified and authorized by $ACCOUNT_PUB, which should be kept secret from third parties. $ACCOUNT_PUB should be an account public key using the Crockford base32-encoding. +In the following, UUID is always defined and used according to `RFC 4122`_. + +.. _`RFC 4122`: https://tools.ietf.org/html/rfc4122 .. http:get:: /policy/$ACCOUNT_PUB[?version=$NUMBER] @@ -636,12 +639,11 @@ public key using the Crockford base32-encoding. // escrow methods identified by UUID. encrypted_master_key: [32]; //bytearray - // List of escrow methods identified by their uuid + // List of escrow methods identified by their uuid. uuid: string[]; } - .. _manage-truth: @@ -661,13 +663,8 @@ charge per truth operation using GNU Taler. .. http:post:: /truth/$UUID - Upload an EncryptedTruth_-Object according to the policy the client created before (see RecoveryDocument_). + Upload a Truth_-Object according to the policy the client created before (see RecoveryDocument_). If request has been seen before, the server should do nothing, and otherwise store the new object. - While the document's structure is described in JSON below, the upload - should just be the bytestream of the raw data (i.e. 32 bytes nonce followed - by 16 bytes tag followed by the encrypted truth). - The Anastasis server cannot fully validate the format, but MAY impose - minimum and maximum size limits. :status 204 No content: Truth stored successfully. @@ -690,24 +687,6 @@ charge per truth operation using GNU Taler. **Details:** - .. _EncryptedTruth: - .. ts:def:: EncryptedTruth - - interface EncryptedTruth { - // Nonce used to compute the (iv,key) pair for encryption of the - // encrypted_compressed_truth. - nonce: [32]; //bytearray - - // Authentication tag - aes_gcm_tag: [16]; //bytearray - - // Variable-size truth. After decryption, - // this contains a gzip compressed JSON-encoded `Truth`. - // The nonce of the HKDF for this encryption must include the - // string "ECT". - encrypted_compressed_truth: []; //bytearray of undefined length - } - .. _Truth: .. ts:def:: Truth @@ -719,14 +698,21 @@ charge per truth operation using GNU Taler. // Key share method, i.e. "security question", "SMS", "e-mail", ... method: string; - // ground truth, i.e. H(challenge answer), + // Nonce used to compute the (iv,key) pair for encryption of the + // encrypted_truth. + nonce: [32]; //bytearray + + // Authentication tag of encrypted_truth + aes_gcm_tag: [16]; //bytearray + + // Variable-size truth. After decryption, + // this contains the ground truth, i.e. H(challenge answer), // phone number, e-mail address, picture, fingerprint, ... - // **base32 encoded** + // **base32 encoded**. // - // The truth MUST NOT be revealed to the user, even - // after successful authentication (of course the user - // was originally aware when establishing the truth). - truth: string; + // The nonce of the HKDF for this encryption must include the + // string "ECT". + encrypted_truth: []; //bytearray of undefined length // mime type of truth, i.e. text/ascii, image/jpeg, etc. truth_mime: string; diff --git a/checklist-demo-upgrade.rst b/checklist-demo-upgrade.rst new file mode 100644 index 00000000..87d35b6a --- /dev/null +++ b/checklist-demo-upgrade.rst @@ -0,0 +1,51 @@ +################################ +GNU Taler Demo Upgrade Checklist +################################ + +.. |check| raw:: html + + <input type="checkbox"> + +Post-upgrade checks: + +- |check| Run ``taler-deployment-arm -I`` to verify that all services are running. +- |check| Run the headless wallet to check that services are actually working: + + .. code-block:: sh + + taler-wallet-cli integrationtest -e https://exchange.demo.taler.net/ -m https://backend.demo.taler.net/ -b https://bank.demo.taler.net -w "KUDOS:10" -s "KUDOS:5" + +Basics: + +- |check| Visit https://demo.taler.net/ to see if the landing page is displayed correctly +- |check| Visit the wallet installation page, install the wallet, and see if the presence + indicator is updated correctly. +- |check| Visit https://bank.demo.taler.net/, register a new user and withdraw coins into the + browser wallet. + + +Blog demo: + +- |check| Visit https://shop.demo.taler.net/ and purchase an article. +- |check| Verify that the balance in the wallet was updated correctly. +- |check| Go back to https://shop.demo.taler.net/ and click on the same article + link. Verify that the article is shown and **no** repeated payment is + requested. +- |check| Open the fulfillment page from the previous step in an anonymous browsing session + (without the wallet installed) and verify that it requests a payment again. +- |check| Delete cookies on https://shop.demo.taler.net/ and click on the same article again. + Verify that the wallet detects that the article has already purchased and successfully + redirects to the article without spending more money. + +Donation demo: + +- |check| Make a donation on https://donations.demo.taler.net +- |check| Make another donation with the same parameters and verify + that the payment is requested again, instead of showing the previous + fulfillment page. + +Survey/Tipping: + +- |check| Visit https://survey.demo.taler.net/ and receive a tip. +- |check| Verify that the survey stats page (https://survey.demo.taler.net/survey-stats) is working, + and that the survey reserve has sufficient funds. diff --git a/checklist-release.rst b/checklist-release.rst new file mode 100644 index 00000000..90ca6cb6 --- /dev/null +++ b/checklist-release.rst @@ -0,0 +1,70 @@ +########################### +GNU Taler Release Checklist +########################### + + +Release checklists for GNU Taler: + +Wallet: + +- [ ] build wallet +- [ ] verify wallet works against 'test.taler.net' +- [ ] tag repo. +- [ ] upgrade 'demo.taler.net' to 'test.taler.net' +- [ ] upload new wallet release to app store +- [ ] Update bug tracker (mark release, resolved -> closed) +- [ ] Send announcement to taler@gnu.org +- [ ] Send announcement to info-gnu@gnu.org (major releases only) +- [ ] Send announcement to coordinator@translationproject.org + +For exchange: + +- [ ] check no compiler warnings at "-Wall" +- [ ] ensure Coverity static analysis passes +- [ ] make check. +- [ ] upgrade 'demo.taler.net' to 'test.taler.net' +- [ ] make dist, make check on result of 'make dist'. +- [ ] Change version number in configure.ac. +- [ ] make dist for release. +- [ ] tag repo. +- [ ] Upload triplet to ftp-upload.gnu.org/incoming/ftp or /incoming/alpha +- [ ] Update bug tracker (mark release, resolved -> closed) +- [ ] Send announcement to taler@gnu.org +- [ ] Send announcement to info-gnu@gnu.org (major releases only) +- [ ] Send announcement to coordinator@translationproject.org + +For merchant (C backend): + +- [ ] check no compiler warnings at "-Wall" +- [ ] ensure Coverity static analysis passes +- [ ] make check. +- [ ] upgrade 'demo.taler.net' to 'test.taler.net' +- [ ] make dist, make check on result of 'make dist'. +- [ ] Change version number in configure.ac. +- [ ] make dist for release. +- [ ] tag repo. +- [ ] Upload triplet to ftp-upload.gnu.org/incoming/ftp or /incoming/alpha +- [ ] Update bug tracker (mark release, resolved -> closed) +- [ ] Send announcement to taler@gnu.org +- [ ] Send announcement to info-gnu@gnu.org (major releases only) +- [ ] Send announcement to coordinator@translationproject.org + +For bank: + +- TBD + +For Python merchant frontend: + +- TBD + +For PHP merchant frontend: + +- TBD + +For auditor: + +- TBD + +For libebics: + +- TBD diff --git a/core/api-merchant.rst b/core/api-merchant.rst index 56054761..7fc4ac63 100644 --- a/core/api-merchant.rst +++ b/core/api-merchant.rst @@ -115,8 +115,8 @@ Receiving Payments // Was the payment refunded (even partially) refunded: boolean; - // Amount that was refunded - refund_amount: Amount; + // Amount that was refunded, only present if refunded is true. + refund_amount?: Amount; // Contract terms contract_terms: ContractTerms; @@ -130,6 +130,9 @@ Receiving Payments // URI that the wallet must process to complete the payment. taler_pay_uri: string; + // Alternative order ID which was paid for already in the same session. + // Only given if the same product was purchased before in the same session. + already_paid_order_id?: string; } @@ -745,9 +748,6 @@ The contract terms must have the following structure: // before transfering it to the merchant. amount: Amount; - // The URL where the wallet has to send coins. - pay_url: string; - // The URL for this purchase. Every time is is visited, the merchant // will send back to the customer the same proposal. Clearly, this URL // can be bookmarked and shared by users. @@ -783,10 +783,18 @@ The contract terms must have the following structure: // After this deadline, the merchant won't accept payments for the contact pay_deadline: Timestamp; + // Transfer deadline for the exchange. Must be in the + // deposit permissions of coins used to pay for this order. + wire_transfer_deadline: Timestamp; + // Merchant's public key used to sign this proposal; this information // is typically added by the backend Note that this can be an ephemeral key. merchant_pub: EddsaPublicKey; + // Base URL of the (public!) merchant backend API. + // Must be an absolute URL that ends with a slash. + merchant_base_url: string; + // More info about the merchant, see below merchant: Merchant; @@ -1118,3 +1126,52 @@ both by the user's browser and their wallet. // The order of the signatures matches the planchets list. reserve_sigs: EddsaSignature[]; } + + +.. http:get:: /public/poll-payment + + Check the payment status of an order. + + **Request:** + + :query order_id: order id that should be used for the payment + :query h_contract: hash of the contract (used to authenticate customer) + :query session_id: *Optional*. Session ID that the payment must be bound to. If not specified, the payment is not session-bound. + :query timeout: *Optional*. Timeout in seconds to wait for a payment if the answer would otherwise be negative (long polling). + + **Response:** + + Returns a `PollPaymentResponse`, whose format can differ based on the status of the payment. + + .. ts:def:: PollPaymentResponse + + type CheckPaymentResponse = PollPaymentPaidResponse | PollPaymentUnpaidResponse + + .. ts:def:: PollPaymentPaidResponse + + interface PollPaymentPaidResponse { + // value is always true; + paid: boolean; + + // Was the payment refunded (even partially) + refunded: boolean; + + // Amount that was refunded, only present if refunded is true. + refund_amount?: Amount; + + } + + .. ts:def:: PollPaymentUnpaidResponse + + interface PollPaymentUnpaidResponse { + // value is always false; + paid: boolean; + + // URI that the wallet must process to complete the payment. + taler_pay_uri: string; + + // Alternative order ID which was paid for already in the same session. + // Only given if the same product was purchased before in the same session. + already_paid_order_id?: string; + + } diff --git a/core/taler-uri.rst b/core/taler-uri.rst index caa32bb4..a228d461 100644 --- a/core/taler-uri.rst +++ b/core/taler-uri.rst @@ -19,16 +19,16 @@ Payments are requested with the ``pay`` action. The parameters are a hierarchic .. code:: none - 'taler://pay/' merchant-host '/' merchant-query '/' merchant-instance '/' order-id [ '/' session-id ] + 'taler://pay/' merchant-host '/' merchant-public-prefix '/' merchant-instance '/' order-id [ '/' session-id ] -The components ``merchant-host``, ``merchant-query`` and ``order-id`` identify the URL that is used to claim the contract +The components ``merchant-host``, ``merchant-prefix`` and ``order-id`` identify the URL that is used to claim the contract for this payment request. To make the URI shorter (which is important for QR code payments), ``-`` (minus) can be substituted to get a default value for some components: * the default for ``merchant-instance`` is ``default`` -* the default for ``merchant-query`` is ``/public/proposal`` +* the default for ``merchant-public-prefix`` is ``public`` The following is a minimal example for a payment request from the demo merchant, using the default instance and no session-bound payment: diff --git a/onboarding.rst b/developers-manual.rst index 5f9defb9..f008b807 100644 --- a/onboarding.rst +++ b/developers-manual.rst @@ -1,5 +1,17 @@ -Developer Onboarding Manual -########################### +Developer's Manual +################## + +.. toctree:: + :hidden: + + checklist-release + checklist-demo-upgrade + + +.. note:: + + This manual contains information for developers working on GNU Taler + and related components. It is not intended for a general audience. .. contents:: Table of Contents @@ -28,6 +40,52 @@ A complete list of all the existing repositories is currently found at `<https://git.taler.net/>`_. +Committing code +--------------- + +To obtain Git access, you need to send us your SSH public key. You can +find instructions on how to do so in the `Git book <https://git-scm.com/book/en/v2/Git-on-the-Server-Generating-Your-SSH-Public-Key>`_. +If you have been granted write access, you fist of all must change the URL of +the respective repository to: + +:: + + git://git@git.taler.net/<repository> + +For an existing checkout, this can be done by editing the ``.git/config`` file. + +The server is configured to reject all commits that have not been signed with +GnuPG. If you do not yet have a GnuPG key, you must create one, as explained +in the `GNU Privacy Handbook <https://www.gnupg.org/gph/en/manual/c14.html>`_. +You do not need to share the respective public key with us to make commits. +However, we recommend that you upload it to key servers, put it on your +business card and personally meet with other GNU hackers to have it signed +such that others can verify your commits later. + +To sign all commits, you should run + +:: + + $ git config --global commit.gpgsign true + +You can also sign individual commits only by adding the ``-S`` option to the +``git commit`` command. If you accidentally already made commits but forgot +to sign them, you can retroactively add signatures using: + +:: + + $ git rebase -S + + +Whether you commit to a personal branch, a feature branch or to master should +depend on your level of comfort and the nature of the change. As a general +rule, the code in master must always build and tests should always pass, at +least on your own system. However, we all make mistakes and you should expect +to receive friendly reminders if your change did not live up to this simple +standard. We plan to move to a system where the CI guarantees this invariant +in the future. + + Taler Deployment on gv.taler.net ================================ @@ -54,108 +112,126 @@ user can be switched to become active (see next section), and vice versa. - ``demo-blue`` - ``demo-green`` -Compile and switch color. -------------------------- - -If the setup is already bootstrapped, then it should only be needed to -login as ’demo-X’ (with X being the inactive color); and then: -:: +Demo Upgrade Procedure +====================== - $ source activate - $ taler-deployment-build +Upgrading the ``demo`` environment should be done with care, and ideally be +coordinated on the mailing list before. It is our goal for ``demo`` to always +run a "working version" that is compatible with various published wallets. -and then switch the color by logging in as the *demo* user, and switch -the color with the following command: +Before deploying on ``demo``, the same version of all components **must** +be deployed *and* tested on ``int``. -:: +Please use the :doc:`demo upgrade checklist <checklist-demo-upgrade>` to make +sure everything is working. - $ taler-deployment-switch-demo-X -Full bootstrap. ---------------- +Tagging components +------------------ -In order to bootstrap a Taler installation under a empty home directory, -do: +All Taler components must be tagged with git before they are deployed on the +``demo`` environment, using a tag of the following form: :: - $ cd $HOME - $ git clone git://git.taler.net/deployment + demo-YYYY-MM-DD-SS + YYYY = year + MM = month + DD = day + SS = serial -Then run the prepare script that will (1) download all the repositories -(2) build the codebases, (3) configure the system, and (4) generate the -needed data. -:: +Environment Layout +------------------ - $ ./deployment/bin/taler-deployment-prepare [test | int | demo] +Environments have the following layout: -.. +:: - **Note** + $HOME/ + deployment (deployment.git checkout) + envcfg.py (configuration of the Taler environment) + activate (bash file, sourced to set environment variables) + logs/ (log files) + local/ (locally installed software) + sources/ (sources repos of locally build components) + sockets/ (unix domain sockets of running components) + taler-data (on-disk state, public and private keys) + .config/taler.conf (main Taler configuration file) - If the DB schema of merchant/exchange/auditor changed, at this point - it MIGHT be necessary to reset all the tables. To this regard, - consider running one of the following commands: +On ``demo-blue`` and ``demo-green``, ``taler-data`` is a symlink pointing to ``$HOME/demo/shared-data`` +instead of a directory. - :: - # To reset the merchant DB. - $ taler-merchant-dbinit -r +Using envcfg.py +--------------- - # To reset the exchange DB. - $ taler-exchange-dbinit -r +The ``$HOME/envcfg.py`` file contains (1) the name of the environment and (2) the version +of all components we build (in the form of a git rev). - # To reset the exchange DB. - $ taler-auditor-dbinit -r +The ``envcfg.py`` for demo looks like this: -If all the steps succeeded, then it should be possible to launch all the -services. Give: +.. code-block:: python -:: + env = "demo" + tag = "demo-2019-10-05-01: + tag_gnunet = tag + tag_libmicrohttpd = tag + tag_exchange = tag + tag_merchant = tag + tag_bank = tag + tag_twister = tag + tag_landing = tag + tag_donations = tag + tag_blog = tag + tag_survey = tag + tag_backoffice = tag - $ taler-deployment-start +Currently only the variables ``env`` and ``tag_${component}`` are used. - # or restart, if you want to kill old processes and - # start new ones. - $ taler-deployment-restart +When deploying to ``demo``, the ``envcfg.py`` should be committed to ``deployment.git/envcfg/envcfg-demo-YYYY-MM-DD-SS.py``. -Verify that all services are up and running: -:: +Bootstrapping an Environment +---------------------------- - $ taler-deployment-arm -I - $ tail logs/<component>-<date>.log +.. code-block:: sh -How to upgrade the code. ------------------------- + $ git clone https://git.taler.net/deployment.git ~/deployment + $ cp ~/deployment/envcfg/$ENVCFGFILE ~/envcfg.py + $ ./deployment/bin/taler-deployment bootstrap + $ source ~/activate + $ taler-deployment build + $ taler-deployment-keyup + $ taler-deployment-sign + $ taler-deployment-start -Some repositories, especially the ones from the released components, -have a *stable* branch, that keeps older and more stable code. -Therefore, upon each release we must rebase those stable branches on the -master. -The following commands do that: +Upgrading an Existing Environment +--------------------------------- .. code-block:: sh - $ cd $REPO + $ rm -rf ~/sources ~/local + $ git -C ~/deployment pull + $ cp ~/deployment/envcfg/$ENVCFGFILE ~/envcfg.py + $ taler-deployment bootstrap + $ taler-deployment build + $ taler-deployment-keyup + $ taler-deployment-sign + $ taler-deployment-start - $ git pull origin master stable - $ git checkout stable - # option a: resolve conflicts resulting from hotfixes - $ git rebase master - $ ... +Switching Demo Colors +--------------------- - # option b: force stable to master - $ git update-ref refs/heads/stable master +As the ``demo`` user, to switch to color ``${COLOR}``, +run the following script from ``deployment/bin``: - $ git push # possibly with --force +.. code-block:: sh - # continue development - $ git checkout master + $ taler-deployment-switch-demo-${COLOR} Environments and Builders on taler.net @@ -292,6 +368,8 @@ Releases Release Process and Checklists ------------------------------ +Please use the :doc:`release checklist <checklist-release>` + This document describes the process for releasing a new version of the various Taler components to the official GNU mirrors. @@ -58,7 +58,7 @@ Documentation Overview taler-merchant-api-tutorial taler-bank-manual taler-backoffice-manual - onboarding + developers-manual.rst anastasis libeufin/index global-licensing diff --git a/libeufin/api-sandbox.rst b/libeufin/api-sandbox.rst index 249f917a..246bc8cf 100644 --- a/libeufin/api-sandbox.rst +++ b/libeufin/api-sandbox.rst @@ -104,7 +104,7 @@ HTTP API // Identification token of the bank. Not required to obey to any particular standard. recipient: string; - // Electronic signature version. Admitted values: A004, A005, A006. + // Electronic signature version. Admitted values: A005, A006. version: string; // Length in bits of the key exponent. diff --git a/libeufin/ebics.rst b/libeufin/ebics.rst index 605bd971..58c22d24 100644 --- a/libeufin/ebics.rst +++ b/libeufin/ebics.rst @@ -342,6 +342,28 @@ The following order types are, for now, not relevant for LibEuFin: HAC order type. +EBICS Message Format +==================== + +The following elements are the allowed root elements of EBICS request/response messages: + +* ``ebicsHEVRequest`` / ``ebicsHEVResponse``: Always unauthenticated and unencrypted. Used + **only** for query/response of the host's EBICS version. +* ``ebicsUnsecuredRequest``: Request without signature or encryption (beyond TLS). Used for INI and HIA. +* ``ebicsKeyManagementResponse``: Unauthenticated response. Used for key management responses and + sometimes **also** to deliver error messages that are not signed by the bank (such as "invalid request"). +* ``ebicsNoPubKeyDigestsRequest``: Signed request that *does not* contain the hash of the bank's + public key that the client expects. Used for key management, specifically only the HPB order type. +* ``ebicsRequest`` / ``ebicsResponse`` +* ``ebicsUnsignedRequest``: Not used anymore. Was used in FTAM migration with the HSA order type. + + +Order ID Allocation +=================== + +In practice, the Order ID seems to be allocated via number of counters at the level of the **PartnerID**. + + EBICS Transaction ================= @@ -432,7 +454,7 @@ RSA key pairs are used for three purposes: One subscriber *may* use three different key pairs for these purposes. The identification and authentication key pair may be the same as the encryption key pair. -The bank-technical key pair may not be used for any other purpose.. +The bank-technical key pair may not be used for any other purpose. Real-time Transactions @@ -491,6 +513,43 @@ HypoVereinsbank See `this document <https://www.hypovereinsbank.de/content/dam/hypovereinsbank/shared/pdf/Auftragsarten_Internet_Nov2018_181118.pdf>`__. + +Cryptography +============ + +EBICS uses the XML Signature standard for signatures. It does *not* use XML encryption. + +The EBICS specification doesn't directly reference the standardized URIs for the various +signing algorithms. Some of these URIs are defined in `<https://tools.ietf.org/html/rfc6931>`__. + +* A005 is http://www.w3.org/2001/04/xmldsig-more#rsa-sha256 + + * the ``RSASSA-PKCS1-v1_5`` signature scheme contains the ``EMSA-PKCS1-v1_5`` encoding scheme + mentioned in the EBICS spec. + +* A006 is `<http://www.w3.org/2007/05/xmldsig-more#rsa-pss>`__ as defined in RFC 6931. + +XML Signatures +-------------- + +XML Signatures can be a combination of: + +* detached (referencing another document) +* enveloping (signs over ``Object`` tags *within* the ``Signature`` elements) +* enveloped (signs over arbitrary data (via XPath expression) in other parts of the document + that contains the signature). + +In EBICS, only **enveloped** signatures are used. + +In the XML Signature standard, the element for a signature is ``Signature``. EBICS violates this +standard by always mandating ``AuthSignature`` as the name. ``AuthSignature`` is an alias to +the ``SignatureType`` xsd type in the XML Schema. + +Canonicalization vs transforms: + * Canonicalization refers to the processing of the ``SignedInfo`` element. + * Transformations apply to the data that ``Reference`` elements reference. Canonicalization + algorithms can be used as a transformation on referenced data. + Standards and Resources ======================= diff --git a/taler-nfc-guide.rst b/taler-nfc-guide.rst index 029b84f4..20239352 100644 --- a/taler-nfc-guide.rst +++ b/taler-nfc-guide.rst @@ -177,7 +177,7 @@ instruction ID (TID). Currently, the following TIDs are used: * - TID (reader to wallet) - Description * - ``0x01`` - - Dereference the UTF-8 ecoded ``taler://`` URI in the remainder of the command data. + - Dereference the UTF-8 encoded ``taler://`` URI in the remainder of the command data. * - ``0x02`` - Accept the UTF-8 encoded JSON object in the remainder of the command data as a request tunneling response. |