| < draft-privacypass-rate-limit-tokens-01.txt | draft-privacypass-rate-limit-tokens-02.txt > | |||
|---|---|---|---|---|
| Network Working Group S. Hendrickson | Network Working Group S. Hendrickson | |||
| Internet-Draft Google LLC | Internet-Draft Google LLC | |||
| Intended status: Informational J. Iyengar | Intended status: Informational J. Iyengar | |||
| Expires: 7 October 2022 Fastly | Expires: 3 November 2022 Fastly | |||
| T. Pauly | T. Pauly | |||
| Apple Inc. | Apple Inc. | |||
| S. Valdez | S. Valdez | |||
| Google LLC | Google LLC | |||
| C. A. Wood | C. A. Wood | |||
| Cloudflare | Cloudflare | |||
| 5 April 2022 | 2 May 2022 | |||
| Rate-Limited Token Issuance Protocol | Rate-Limited Token Issuance Protocol | |||
| draft-privacypass-rate-limit-tokens-01 | draft-privacypass-rate-limit-tokens-02 | |||
| Abstract | Abstract | |||
| This document specifies a variant of the Privacy Pass issuance | This document specifies a variant of the Privacy Pass issuance | |||
| protocol that allows for tokens to be rate-limited on a per-origin | protocol that allows for tokens to be rate-limited on a per-origin | |||
| basis. This enables origins to use tokens for use cases that need to | basis. This enables origins to use tokens for use cases that need to | |||
| restrict access from anonymous clients. | restrict access from anonymous clients. | |||
| Discussion Venues | Discussion Venues | |||
| skipping to change at page 1, line 47 ¶ | skipping to change at page 1, line 47 ¶ | |||
| Internet-Drafts are working documents of the Internet Engineering | Internet-Drafts are working documents of the Internet Engineering | |||
| Task Force (IETF). Note that other groups may also distribute | Task Force (IETF). Note that other groups may also distribute | |||
| working documents as Internet-Drafts. The list of current Internet- | working documents as Internet-Drafts. The list of current Internet- | |||
| Drafts is at https://datatracker.ietf.org/drafts/current/. | Drafts is at https://datatracker.ietf.org/drafts/current/. | |||
| Internet-Drafts are draft documents valid for a maximum of six months | Internet-Drafts are draft documents valid for a maximum of six months | |||
| and may be updated, replaced, or obsoleted by other documents at any | and may be updated, replaced, or obsoleted by other documents at any | |||
| time. It is inappropriate to use Internet-Drafts as reference | time. It is inappropriate to use Internet-Drafts as reference | |||
| material or to cite them other than as "work in progress." | material or to cite them other than as "work in progress." | |||
| This Internet-Draft will expire on 7 October 2022. | This Internet-Draft will expire on 3 November 2022. | |||
| Copyright Notice | Copyright Notice | |||
| Copyright (c) 2022 IETF Trust and the persons identified as the | Copyright (c) 2022 IETF Trust and the persons identified as the | |||
| document authors. All rights reserved. | document authors. All rights reserved. | |||
| This document is subject to BCP 78 and the IETF Trust's Legal | This document is subject to BCP 78 and the IETF Trust's Legal | |||
| Provisions Relating to IETF Documents (https://trustee.ietf.org/ | Provisions Relating to IETF Documents (https://trustee.ietf.org/ | |||
| license-info) in effect on the date of publication of this document. | license-info) in effect on the date of publication of this document. | |||
| Please review these documents carefully, as they describe your rights | Please review these documents carefully, as they describe your rights | |||
| skipping to change at page 2, line 33 ¶ | skipping to change at page 2, line 33 ¶ | |||
| 1.2. Properties and Requirements . . . . . . . . . . . . . . . 4 | 1.2. Properties and Requirements . . . . . . . . . . . . . . . 4 | |||
| 2. Terminology . . . . . . . . . . . . . . . . . . . . . . . . . 6 | 2. Terminology . . . . . . . . . . . . . . . . . . . . . . . . . 6 | |||
| 3. Configuration . . . . . . . . . . . . . . . . . . . . . . . . 8 | 3. Configuration . . . . . . . . . . . . . . . . . . . . . . . . 8 | |||
| 4. Token Challenge Requirements . . . . . . . . . . . . . . . . 9 | 4. Token Challenge Requirements . . . . . . . . . . . . . . . . 9 | |||
| 5. Issuance Protocol . . . . . . . . . . . . . . . . . . . . . . 10 | 5. Issuance Protocol . . . . . . . . . . . . . . . . . . . . . . 10 | |||
| 5.1. State Requirements . . . . . . . . . . . . . . . . . . . 10 | 5.1. State Requirements . . . . . . . . . . . . . . . . . . . 10 | |||
| 5.1.1. Client State . . . . . . . . . . . . . . . . . . . . 11 | 5.1.1. Client State . . . . . . . . . . . . . . . . . . . . 11 | |||
| 5.1.2. Attester State . . . . . . . . . . . . . . . . . . . 12 | 5.1.2. Attester State . . . . . . . . . . . . . . . . . . . 12 | |||
| 5.1.3. Issuer State . . . . . . . . . . . . . . . . . . . . 12 | 5.1.3. Issuer State . . . . . . . . . . . . . . . . . . . . 12 | |||
| 5.2. Issuance HTTP Headers . . . . . . . . . . . . . . . . . . 13 | 5.2. Issuance HTTP Headers . . . . . . . . . . . . . . . . . . 13 | |||
| 5.3. Client-to-Attester Request . . . . . . . . . . . . . . . 13 | 5.3. Client-to-Attester Request . . . . . . . . . . . . . . . 14 | |||
| 5.4. Attester-to-Issuer Request . . . . . . . . . . . . . . . 16 | 5.4. Attester-to-Issuer Request . . . . . . . . . . . . . . . 17 | |||
| 5.5. Issuer-to-Attester Response . . . . . . . . . . . . . . . 18 | 5.5. Issuer-to-Attester Response . . . . . . . . . . . . . . . 18 | |||
| 5.6. Attester-to-Client Response . . . . . . . . . . . . . . . 18 | 5.6. Attester-to-Client Response . . . . . . . . . . . . . . . 19 | |||
| 6. Encrypting Origin Names . . . . . . . . . . . . . . . . . . . 19 | 6. Encrypting Origin Token Requests and Responses . . . . . . . 20 | |||
| 7. Anonymous Issuer Origin ID Computation . . . . . . . . . . . 21 | 6.1. Client to Issuer Encapsulation . . . . . . . . . . . . . 20 | |||
| 7.1. Client Behavior . . . . . . . . . . . . . . . . . . . . . 23 | 6.2. Issuer to Client Encapsulation . . . . . . . . . . . . . 22 | |||
| 7.1.1. Request Key . . . . . . . . . . . . . . . . . . . . . 23 | 7. Anonymous Issuer Origin ID Computation . . . . . . . . . . . 23 | |||
| 7.1.2. Request Signature . . . . . . . . . . . . . . . . . . 23 | 7.1. Client Behavior . . . . . . . . . . . . . . . . . . . . . 25 | |||
| 7.2. Attester Behavior (Client Request Validation) . . . . . . 24 | 7.1.1. Request Key . . . . . . . . . . . . . . . . . . . . . 25 | |||
| 7.3. Issuer Behavior . . . . . . . . . . . . . . . . . . . . . 25 | 7.1.2. Request Signature . . . . . . . . . . . . . . . . . . 25 | |||
| 7.4. Attester Behavior (Index Computation) . . . . . . . . . . 25 | 7.2. Attester Behavior (Client Request Validation) . . . . . . 26 | |||
| 8. Security Considerations . . . . . . . . . . . . . . . . . . . 26 | 7.3. Issuer Behavior . . . . . . . . . . . . . . . . . . . . . 26 | |||
| 8.1. Channel Security . . . . . . . . . . . . . . . . . . . . 26 | 7.4. Attester Behavior (Index Computation) . . . . . . . . . . 27 | |||
| 8.2. Token Request Unlinkability and Unforgeability . . . . . 26 | 8. Security Considerations . . . . . . . . . . . . . . . . . . . 28 | |||
| 8.3. Information Disclosure . . . . . . . . . . . . . . . . . 27 | 8.1. Channel Security . . . . . . . . . . . . . . . . . . . . 28 | |||
| 9. Privacy Considerations . . . . . . . . . . . . . . . . . . . 28 | 8.2. Token Request Unlinkability and Unforgeability . . . . . 28 | |||
| 9.1. Client Token State and Origin Tracking . . . . . . . . . 28 | 8.3. Information Disclosure . . . . . . . . . . . . . . . . . 29 | |||
| 9.2. Origin Verification . . . . . . . . . . . . . . . . . . . 29 | 9. Privacy Considerations . . . . . . . . . . . . . . . . . . . 30 | |||
| 9.3. Client Identification with Unique Keys . . . . . . . . . 29 | 9.1. Client Token State and Origin Tracking . . . . . . . . . 30 | |||
| 9.4. Origin Identification . . . . . . . . . . . . . . . . . . 29 | 9.2. Origin Verification . . . . . . . . . . . . . . . . . . . 30 | |||
| 9.5. Collusion Among Different Entities . . . . . . . . . . . 30 | 9.3. Client Identification with Unique Keys . . . . . . . . . 30 | |||
| 10. Deployment Considerations . . . . . . . . . . . . . . . . . . 30 | 9.4. Origin Identification . . . . . . . . . . . . . . . . . . 31 | |||
| 10.1. Token Key Management . . . . . . . . . . . . . . . . . . 30 | 9.5. Collusion Among Different Entities . . . . . . . . . . . 31 | |||
| 11. IANA considerations . . . . . . . . . . . . . . . . . . . . . 30 | 10. Deployment Considerations . . . . . . . . . . . . . . . . . . 32 | |||
| 11.1. Token Type . . . . . . . . . . . . . . . . . . . . . . . 31 | 10.1. Token Key Management . . . . . . . . . . . . . . . . . . 32 | |||
| 11.2. HTTP Headers . . . . . . . . . . . . . . . . . . . . . . 31 | 11. IANA considerations . . . . . . . . . . . . . . . . . . . . . 32 | |||
| 12. References . . . . . . . . . . . . . . . . . . . . . . . . . 31 | 11.1. Token Type . . . . . . . . . . . . . . . . . . . . . . . 32 | |||
| 12.1. Normative References . . . . . . . . . . . . . . . . . . 31 | 11.1.1. ECDSA-based Token Type . . . . . . . . . . . . . . . 33 | |||
| 12.2. Informative References . . . . . . . . . . . . . . . . . 33 | 11.1.2. Ed25519-based Token Type . . . . . . . . . . . . . . 34 | |||
| Appendix A. Acknowledgements . . . . . . . . . . . . . . . . . . 34 | 11.2. HTTP Headers . . . . . . . . . . . . . . . . . . . . . . 35 | |||
| Appendix B. Test Vectors . . . . . . . . . . . . . . . . . . . . 34 | 12. References . . . . . . . . . . . . . . . . . . . . . . . . . 35 | |||
| B.1. Origin Name Encryption Test Vector . . . . . . . . . . . 34 | 12.1. Normative References . . . . . . . . . . . . . . . . . . 35 | |||
| B.2. Anonymous Origin ID Test Vector . . . . . . . . . . . . . 35 | 12.2. Informative References . . . . . . . . . . . . . . . . . 37 | |||
| Authors' Addresses . . . . . . . . . . . . . . . . . . . . . . . 36 | Appendix A. Acknowledgements . . . . . . . . . . . . . . . . . . 38 | |||
| Appendix B. Test Vectors . . . . . . . . . . . . . . . . . . . . 38 | ||||
| B.1. Origin Name Encryption Test Vector . . . . . . . . . . . 38 | ||||
| B.2. Anonymous Origin ID Test Vector . . . . . . . . . . . . . 39 | ||||
| Authors' Addresses . . . . . . . . . . . . . . . . . . . . . . . 40 | ||||
| 1. Introduction | 1. Introduction | |||
| This document specifies a variant of the Privacy Pass issuance | This document specifies a variant of the Privacy Pass issuance | |||
| protocol (as defined in [ARCH]) that allows for tokens to be rate- | protocol (as defined in [ARCH]) that allows for tokens to be rate- | |||
| limited on a per-origin basis. This enables origins to use tokens | limited on a per-origin basis. This enables origins to use tokens | |||
| for use cases that need to restrict access from anonymous clients. | for use cases that need to restrict access from anonymous clients. | |||
| The base Privacy Pass issuance protocol [ISSUANCE] defines stateless | The base Privacy Pass issuance protocol [ISSUANCE] defines stateless | |||
| anonymous tokens, which can either be publicly verifiable or not. | anonymous tokens, which can either be publicly verifiable or not. | |||
| skipping to change at page 7, line 28 ¶ | skipping to change at page 7, line 28 ¶ | |||
| Additionally, this document defines several terms that are unique to | Additionally, this document defines several terms that are unique to | |||
| the rate-limited issuance protocol: | the rate-limited issuance protocol: | |||
| * Issuer Policy Window: The period over which an Issuer will track | * Issuer Policy Window: The period over which an Issuer will track | |||
| access policy, defined in terms of seconds and represented as a | access policy, defined in terms of seconds and represented as a | |||
| uint64. The state that the Attester keeps for a Client is | uint64. The state that the Attester keeps for a Client is | |||
| specific to a policy window. The effective policy window for a | specific to a policy window. The effective policy window for a | |||
| specific Client starts when the Client first sends a request | specific Client starts when the Client first sends a request | |||
| associated with an Issuer. | associated with an Issuer. | |||
| * Origin Name Key: The public key used to encrypt values such as | * Issuer Encapsulation Key: The public key used to encrypt values | |||
| Origin Name in requests from Clients to the Issuer, so that | such as Origin Name in requests from Clients to the Issuer, so | |||
| Attesters cannot learn the Origin Name value. Each Origin Name | that Attesters cannot learn the Origin Name value. Each Issuer | |||
| Key is used across all requests on the Issuer, for different | Encapsulation Key is used across all requests on the Issuer, for | |||
| Origins. | different Origins. | |||
| * Anonymous Origin ID: An identifier that is generated by the Client | * Anonymous Origin ID: An identifier that is generated by the Client | |||
| and marked on requests to the Attester, which represents a | and marked on requests to the Attester, which represents a | |||
| specific Origin anonymously. The Client generates a stable | specific Origin anonymously. The Client generates a stable | |||
| Anonymous Origin ID for each Origin Name, to allow the Attester to | Anonymous Origin ID for each Origin Name, to allow the Attester to | |||
| count token access without learning the Origin Name. | count token access without learning the Origin Name. | |||
| * Client Key: A public key chosen by the Client and shared only with | * Client Key: A public key chosen by the Client and shared only with | |||
| the Attester; see Section 8.2 for more details about this | the Attester; see Section 8.2 for more details about this | |||
| restriction. | restriction. | |||
| skipping to change at page 8, line 21 ¶ | skipping to change at page 8, line 21 ¶ | |||
| Issuers MUST provide three parameters for configuration: | Issuers MUST provide three parameters for configuration: | |||
| 1. Issuer Policy Window: a uint64 of seconds as defined in | 1. Issuer Policy Window: a uint64 of seconds as defined in | |||
| Section 2. | Section 2. | |||
| 2. Issuer Request URI: a token request URL for generating access | 2. Issuer Request URI: a token request URL for generating access | |||
| tokens. For example, an Issuer URL might be | tokens. For example, an Issuer URL might be | |||
| https://issuer.example.net/token-request. This parameter uses | https://issuer.example.net/token-request. This parameter uses | |||
| resource media type "text/plain". | resource media type "text/plain". | |||
| 3. Origin Name Key: a NameKey structure as defined below to use when | 3. Issuer Encapsulation Key: a EncapsulationKey structure as defined | |||
| encrypting the Origin Name in issuance requests. This parameter | below to use when encapsulating information, such as the origin | |||
| uses resource media type "application/issuer-name-key". The Npk | name, to the Issuer in issuance requests. This parameter uses | |||
| resource media type "application/issuer-encap-key". The Npk | ||||
| parameter corresponding to the HpkeKdfId can be found in [HPKE]. | parameter corresponding to the HpkeKdfId can be found in [HPKE]. | |||
| opaque HpkePublicKey[Npk]; // defined in RFC9180 | opaque HpkePublicKey[Npk]; // defined in RFC9180 | |||
| uint16 HpkeKemId; // defined in RFC9180 | uint16 HpkeKemId; // defined in RFC9180 | |||
| uint16 HpkeKdfId; // defined in RFC9180 | uint16 HpkeKdfId; // defined in RFC9180 | |||
| uint16 HpkeAeadId; // defined in RFC9180 | uint16 HpkeAeadId; // defined in RFC9180 | |||
| struct { | struct { | |||
| uint8 key_id; | uint8 key_id; | |||
| HpkeKemId kem_id; | HpkeKemId kem_id; | |||
| HpkePublicKey public_key; | HpkePublicKey public_key; | |||
| HpkeKdfId kdf_id; | HpkeKdfId kdf_id; | |||
| HpkeAeadId aead_id; | HpkeAeadId aead_id; | |||
| } NameKey; | } EncapsulationKey; | |||
| The Issuer parameters can be obtained from an Issuer via a directory | The Issuer parameters can be obtained from an Issuer via a directory | |||
| object, which is a JSON object whose field names and values are raw | object, which is a JSON object whose field names and values are raw | |||
| values and URLs for the parameters. | values and URLs for the parameters. | |||
| +======================+=======================================+ | +======================+=======================================+ | |||
| | Field Name | Value | | | Field Name | Value | | |||
| +======================+=======================================+ | +======================+=======================================+ | |||
| | issuer-policy-window | Issuer Policy Window as a JSON number | | | issuer-policy-window | Issuer Policy Window as a JSON number | | |||
| +----------------------+---------------------------------------+ | +----------------------+---------------------------------------+ | |||
| | issuer-request-uri | Issuer Request URI resource URL as a | | | issuer-request-uri | Issuer Request URI resource URL as a | | |||
| | | JSON string | | | | JSON string | | |||
| +----------------------+---------------------------------------+ | +----------------------+---------------------------------------+ | |||
| | origin-name-key-uri | Origin Name Key URI resource URL as a | | | issuer-encap-key-uri | Issuer Encapsulation Key URI resource | | |||
| | | JSON string | | | | URL as a JSON string | | |||
| +----------------------+---------------------------------------+ | +----------------------+---------------------------------------+ | |||
| Table 1 | Table 1 | |||
| As an example, the Issuer's JSON directory could look like: | As an example, the Issuer's JSON directory could look like: | |||
| { | { | |||
| "issuer-token-window": 86400, | "issuer-token-window": 86400, | |||
| "issuer-request-uri": "https://issuer.example.net/token-request" | "issuer-request-uri": "https://issuer.example.net/token-request" | |||
| "origin-name-key-uri": "https://issuer.example.net/name-key", | "issuer-encap-key-uri": "https://issuer.example.net/encap-key", | |||
| } | } | |||
| Issuer directory resources have the media type "application/json" and | Issuer directory resources have the media type "application/json" and | |||
| are located at the well-known location /.well-known/token-issuer- | are located at the well-known location /.well-known/token-issuer- | |||
| directory. | directory. | |||
| 4. Token Challenge Requirements | 4. Token Challenge Requirements | |||
| Clients receive challenges for tokens, as described in [AUTHSCHEME]. | Clients receive challenges for tokens, as described in [AUTHSCHEME]. | |||
| skipping to change at page 9, line 47 ¶ | skipping to change at page 9, line 47 ¶ | |||
| the Client to the Issuer. If the TokenChallenge.origin_info field | the Client to the Issuer. If the TokenChallenge.origin_info field | |||
| contains a single origin name, that origin name is used. If the | contains a single origin name, that origin name is used. If the | |||
| origin_info field contains multiple origin names, the client selects | origin_info field contains multiple origin names, the client selects | |||
| the single origin name that presented the challenge. If the | the single origin name that presented the challenge. If the | |||
| origin_info field is empty, the encrypted message is the empty string | origin_info field is empty, the encrypted message is the empty string | |||
| "". | "". | |||
| The HTTP authentication challenge also SHOULD contain the following | The HTTP authentication challenge also SHOULD contain the following | |||
| additional attribute: | additional attribute: | |||
| * "origin-name-key", which contains a base64url encoding of a | * "issuer-encap-key", which contains a base64url encoding of a | |||
| NameKey as defined in Section 3 to use when encrypting the Origin | EncapsulationKey as defined in Section 3 to use when encrypting | |||
| Name in issuance requests. | the Origin Name in issuance requests. | |||
| 5. Issuance Protocol | 5. Issuance Protocol | |||
| This section describes the Issuance protocol for a Client to request | This section describes the Issuance protocol for a Client to request | |||
| and receive a token from an Issuer. Token issuance involves a | and receive a token from an Issuer. Token issuance involves a | |||
| Client, Attester, and Issuer, with the following steps: | Client, Attester, and Issuer, with the following steps: | |||
| 1. The Client sends a token request containing a token request, | 1. The Client sends a token request containing a token request, | |||
| encrypted origin name, and one-time-use public key and signature | encrypted origin name, and one-time-use public key and signature | |||
| to the Attester | to the Attester | |||
| skipping to change at page 10, line 40 ¶ | skipping to change at page 10, line 40 ¶ | |||
| The Issuance protocol has a number of underlying cryptographic | The Issuance protocol has a number of underlying cryptographic | |||
| dependencies for operation: | dependencies for operation: | |||
| * RSA Blind Signatures [BLINDSIG], for issuing and constructing | * RSA Blind Signatures [BLINDSIG], for issuing and constructing | |||
| Tokens. This support is the same as used in the base publicly | Tokens. This support is the same as used in the base publicly | |||
| verifiable token issuance protocol [ISSUANCE] | verifiable token issuance protocol [ISSUANCE] | |||
| * [HPKE], for encrypting the origin server name in transit between | * [HPKE], for encrypting the origin server name in transit between | |||
| Client and Issuer across the Attester. | Client and Issuer across the Attester. | |||
| * [ECDSA] signatures with key blinding, as described in | * Signatures with key blinding, as described in [KEYBLINDING], for | |||
| [KEYBLINDING], for verifying correctness of Client requests. | verifying correctness of Client requests. | |||
| Clients and Issuers are required to implement all of these | Clients and Issuers are required to implement all of these | |||
| dependencies, whereas Attesters are required to implement ECDSA | dependencies, whereas Attesters are required to implement signature | |||
| signature with key blinding support. | with key blinding support. | |||
| 5.1. State Requirements | 5.1. State Requirements | |||
| The Issuance protocol requires each participating endpoint to | The Issuance protocol requires each participating endpoint to | |||
| maintain some necessary state, as described in this section. | maintain some necessary state, as described in this section. | |||
| 5.1.1. Client State | 5.1.1. Client State | |||
| A Client is required to have the following information, derived from | A Client is required to have the following information, derived from | |||
| a given TokenChallenge: | a given TokenChallenge: | |||
| * Origin Name, a hostname referring to the Origin [RFC6454]. This | * Origin Name, a hostname referring to the Origin [RFC6454]. This | |||
| is the name of the Origin that issued the token challenge. One or | is the name of the Origin that issued the token challenge. One or | |||
| more names can be listed in the TokenChallenge.origin_info field. | more names can be listed in the TokenChallenge.origin_info field. | |||
| Rate-limited token issuance relies on the client selecting a | Rate-limited token issuance relies on the client selecting a | |||
| single origin name from this list if multiple are present. | single origin name from this list if multiple are present. | |||
| * Token Key, a blind signature public key corresponding to the | * Token Key, a blind signature public key corresponding to the | |||
| Issuer identified by the TokenChallenge.issuer_name. | Issuer identified by the TokenChallenge.issuer_name. | |||
| * Origin Name Key, a public key used to encrypt request information | * Issuer Encapsulation Key, a public key used to encrypt request | |||
| corresponding to the Issuer identified by | information corresponding to the Issuer identified by | |||
| TokenChallenge.issuer_name. | TokenChallenge.issuer_name. | |||
| Clients maintain a stable Client Key that they use for all | Clients maintain a stable Client Key that they use for all | |||
| communication with a specific Attester. Client Key is a public key, | communication with a specific Attester. Client Key is a public key, | |||
| where the corresponding private key Client Secret is known only to | where the corresponding private key Client Secret is known only to | |||
| the client. | the client. | |||
| If the client loses this (Client Key, Client Secret), they may | If the client loses this (Client Key, Client Secret), they may | |||
| generate a new tuple. The Attester will enforce if a client is | generate a new tuple. The Attester will enforce if a client is | |||
| allowed to use this new Client Key. See Section 5.1.2 for details on | allowed to use this new Client Key. See Section 5.1.2 for details on | |||
| skipping to change at page 12, line 14 ¶ | skipping to change at page 12, line 14 ¶ | |||
| 5.1.2. Attester State | 5.1.2. Attester State | |||
| An Attester is required to maintain state for every authenticated | An Attester is required to maintain state for every authenticated | |||
| Client. The mechanism of identifying a Client is specific to each | Client. The mechanism of identifying a Client is specific to each | |||
| Attester, and is not defined in this document. As examples, the | Attester, and is not defined in this document. As examples, the | |||
| Attester could use device-specific certificates or account | Attester could use device-specific certificates or account | |||
| authentication to identify a Client. | authentication to identify a Client. | |||
| Attesters must enforce that Clients don't change their Client Key | Attesters must enforce that Clients don't change their Client Key | |||
| frequently, to ensure Clients can't regularily evade the per-client | frequently, to ensure Clients can't regularly evade the per-client | |||
| policy as seen by the issuer. Attesters MUST NOT allow Clients to | policy as seen by the issuer. Attesters MUST NOT allow Clients to | |||
| change their Client Key more than once within a policy window, or in | change their Client Key more than once within a policy window, or in | |||
| the subsequent policy window after a previous Client Key change. | the subsequent policy window after a previous Client Key change. | |||
| Alternative schemes where the Attester stores the encrypted (Client | Alternative schemes where the Attester stores the encrypted (Client | |||
| Key, Client Secret) tuple on behalf of the client are possble but not | Key, Client Secret) tuple on behalf of the client are possible but | |||
| described here. | not described here. | |||
| Attesters are expected to know the Issuer Policy Window for any | Attesters are expected to know the Issuer Policy Window for any | |||
| Issuer Name to which they allow access. This information can be | Issuer Name to which they allow access. This information can be | |||
| retrieved using the URIs defined in Section 3. | retrieved using the URIs defined in Section 3. | |||
| For each Client-Issuer pair, an Attester maintains a policy window | For each Client-Issuer pair, an Attester maintains a policy window | |||
| start and end time for each Issuer from which a Client requests a | start and end time for each Issuer from which a Client requests a | |||
| token. | token. | |||
| For each tuple of (Client Key, Anonymous Origin ID, policy window), | For each tuple of (Client Key, Anonymous Origin ID, policy window), | |||
| skipping to change at page 12, line 50 ¶ | skipping to change at page 12, line 50 ¶ | |||
| 5.1.3. Issuer State | 5.1.3. Issuer State | |||
| Issuers maintain a stable Issuer Origin Secret that they use in | Issuers maintain a stable Issuer Origin Secret that they use in | |||
| calculating values returned to the Attester for each origin. If this | calculating values returned to the Attester for each origin. If this | |||
| value changes, it will open up a possibility for Clients to request | value changes, it will open up a possibility for Clients to request | |||
| extra tokens for an Origin without being limited, within a policy | extra tokens for an Origin without being limited, within a policy | |||
| window. See Section 10.1 for details about generating and rotating | window. See Section 10.1 for details about generating and rotating | |||
| the Issuer Origin Secret. | the Issuer Origin Secret. | |||
| Issuers are expected to have the private key that corresponds to | Issuers are expected to have the private key that corresponds to | |||
| Origin Name Key, which allows them to decrypt the Origin Name values | Issuer Encapsulation Key, which allows them to decrypt the Origin | |||
| in requests. | Name values in requests. | |||
| Issuers also need to know the set of valid Token Key public keys and | Issuers also need to know the set of valid Token Key public keys and | |||
| corresponding private key, for each Origin Name that is served by the | corresponding private key, for each Origin Name that is served by the | |||
| Issuer. Origins SHOULD update their view of the Token Key regularly | Issuer. Origins SHOULD update their view of the Token Key regularly | |||
| to ensure that Client requests do not fail after Token Key rotation. | to ensure that Client requests do not fail after Token Key rotation. | |||
| 5.2. Issuance HTTP Headers | 5.2. Issuance HTTP Headers | |||
| The Issuance protocol defines four new HTTP headers that are used in | The Issuance protocol defines four new HTTP headers that are used in | |||
| requests and responses between Clients, Attesters, and Issuers (see | requests and responses between Clients, Attesters, and Issuers (see | |||
| skipping to change at page 13, line 37 ¶ | skipping to change at page 13, line 37 ¶ | |||
| Sec-Token-Client = sf-binary | Sec-Token-Client = sf-binary | |||
| The "Sec-Token-Request-Blind" is an Item Structured Header [RFC8941]. | The "Sec-Token-Request-Blind" is an Item Structured Header [RFC8941]. | |||
| Its value MUST be a Byte Sequence. This header is sent on Client-to- | Its value MUST be a Byte Sequence. This header is sent on Client-to- | |||
| Attester requests (Section 5.3), and contains a per-request nonce | Attester requests (Section 5.3), and contains a per-request nonce | |||
| value. Its ABNF is: | value. Its ABNF is: | |||
| Sec-Token-Request-Blind = sf-binary | Sec-Token-Request-Blind = sf-binary | |||
| The "Sec-Token-Request-Key" is an Item Structured Header [RFC8941]. | ||||
| Its value MUST be a Byte Sequence. This header is sent on Client-to- | ||||
| Attester requests (Section 5.3), and contains a per-request public | ||||
| key. Its ABNF is: | ||||
| Sec-Token-Request-Key = sf-binary | ||||
| The "Sec-Token-Limit" is an Item Structured Header [RFC8941]. Its | The "Sec-Token-Limit" is an Item Structured Header [RFC8941]. Its | |||
| value MUST be an Integer. This header is sent on Issuer-to-Attester | value MUST be an Integer. This header is sent on Issuer-to-Attester | |||
| responses (Section 5.5), and contains the number of times a Client | responses (Section 5.5), and contains the number of times a Client | |||
| can retrieve a token for the requested Origin within a policy window, | can retrieve a token for the requested Origin within a policy window, | |||
| as set by the Issuer. Its ABNF is: | as set by the Issuer. Its ABNF is: | |||
| Sec-Token-Limit = sf-integer | Sec-Token-Limit = sf-integer | |||
| 5.3. Client-to-Attester Request | 5.3. Client-to-Attester Request | |||
| skipping to change at page 14, line 31 ¶ | skipping to change at page 14, line 38 ¶ | |||
| nonce = random(32) | nonce = random(32) | |||
| context = SHA256(challenge) | context = SHA256(challenge) | |||
| token_input = concat(0x0003, nonce, context, key_id) | token_input = concat(0x0003, nonce, context, key_id) | |||
| blinded_msg, blind_inv = rsabssa_blind(pkI, token_input) | blinded_msg, blind_inv = rsabssa_blind(pkI, token_input) | |||
| The Client then uses Client Key to generate its one-time-use request | The Client then uses Client Key to generate its one-time-use request | |||
| public key request_key and blind request_blind as described in | public key request_key and blind request_blind as described in | |||
| Section 7.1. | Section 7.1. | |||
| The Client then encrypts the origin name using Origin Name Key, | The Client then constructs a InnerTokenRequest value, denoted | |||
| producing encrypted_origin_name as described in Section 6. | origin_token_request, combining blinded_msg, request_key, and the | |||
| origin name as follows: | ||||
| struct { | ||||
| uint8_t blinded_msg[Nk]; | ||||
| uint8_t request_key[Npk]; | ||||
| uint8_t padded_origin_name<0..2^16-1>; | ||||
| } InnerTokenRequest; | ||||
| This structure is initialized and then encrypted using Issuer | ||||
| Encryption Key, producing encrypted_token_request, as described in | ||||
| Section 6. | ||||
| Finally, the Client uses Client Secret to produce request_signature | Finally, the Client uses Client Secret to produce request_signature | |||
| as described in Section 7.1.2. | as described in Section 7.1.2. | |||
| The Client then constructs a TokenRequest structure. This | The Client then constructs a TokenRequest structure. This | |||
| TokenRequest structure is based on the publicly verifiable token | TokenRequest structure is based on the publicly verifiable token | |||
| issuance path in [ISSUANCE], adding fields for the encrypted origin | issuance path in [ISSUANCE], adding fields for the encrypted origin | |||
| name and request signature. | name and request signature. | |||
| struct { | struct { | |||
| uint16_t token_type = 0x0003; | uint16_t token_type = 0x0003; | |||
| uint8_t token_key_id; | uint8_t token_key_id; | |||
| uint8_t blinded_msg[Nk]; | uint8_t issuer_encap_key_id[32]; | |||
| uint8_t request_key[49]; | uint8_t encrypted_token_request<1..2^16-1>; | |||
| uint8_t origin_name_key_id[32]; | uint8_t request_signature[Nsig]; | |||
| uint8_t encrypted_origin_name<1..2^16-1>; | ||||
| uint8_t request_signature[96]; | ||||
| } TokenRequest; | } TokenRequest; | |||
| The structure fields are defined as follows: | The structure fields are defined as follows: | |||
| * "token_type" is a 2-octet integer, which matches the type in the | * "token_type" is a 2-octet integer, which matches the type in the | |||
| challenge. | challenge. | |||
| * "token_key_id" is the least significant byte of the Token Key key | * "token_key_id" is the least significant byte of the Token Key key | |||
| ID, which is generated as SHA256(public_key), where public_key is | ID, which is generated as SHA256(public_key), where public_key is | |||
| a DER-encoded SubjectPublicKeyInfo object carrying Token Key. | a DER-encoded SubjectPublicKeyInfo object carrying Token Key. | |||
| * "blinded_msg" is the Nk-octet request defined above. | * "issuer_encap_key_id" is a collision-resistant hash that | |||
| identifies the Issuer Encryption Key, generated as | ||||
| * "request_key" is computed as described in Section 7.1.1. | SHA256(EncapsulationKey). | |||
| * "origin_name_key_id" is a collision-resistant hash that identifies | ||||
| the Origin Name Key, generated as SHA256(NameKey). | ||||
| * "encrypted_origin_name" is an encrypted structure that contains | * "encrypted_token_request" is an encrypted structure that contains | |||
| Origin Name, calculated as described in Section 6. | an InnerTokenRequest value, calculated as described in Section 6. | |||
| * "request_signature" is computed as described in Section 7.1.2. | * "request_signature" is computed as described in Section 7.1.2. | |||
| The Client then generates an HTTP POST request to send through the | The Client then generates an HTTP POST request to send through the | |||
| Attester to the Issuer, with the TokenRequest as the body. The media | Attester to the Issuer, with the TokenRequest as the body. The media | |||
| type for this request is "message/token-request". The Client | type for this request is "message/token-request". The Client | |||
| includes the "Sec-Token-Origin" header, whose value is Anonymous | includes the "Sec-Token-Origin" header, whose value is Anonymous | |||
| Origin ID; the "Sec-Token-Client" header, whose value is Client Key; | Origin ID; the "Sec-Token-Client" header, whose value is Client Key; | |||
| and the "Sec-Token-Request-Blind" header, whose value is | the "Sec-Token-Request-Blind" header, whose value is request_blind; | |||
| request_blind. The Client sends this request to the Attester's proxy | and the "Sec-Token-Request-Key" header, whose value is request_key. | |||
| URI. An example request is shown below, where Nk = 512, the Issuer | The Client sends this request to the Attester's proxy URI. An | |||
| Name is "issuer.net", and the Attester URI template is | example request is shown below, where the Issuer Name is "issuer.net" | |||
| "https://attester.net/token-request{?issuer}" | and the Attester URI template is "https://attester.net/token- | |||
| request{?issuer}" | ||||
| :method = POST | :method = POST | |||
| :scheme = https | :scheme = https | |||
| :authority = attester.net | :authority = attester.net | |||
| :path = /token-request?issuer=issuer.net | :path = /token-request?issuer=issuer.net | |||
| accept = message/token-response | accept = message/token-response | |||
| cache-control = no-cache, no-store | cache-control = no-cache, no-store | |||
| content-type = message/token-request | content-type = message/token-request | |||
| content-length = <Length of TokenRequest> | content-length = <Length of TokenRequest> | |||
| sec-token-origin = Anonymous Origin ID | sec-token-origin = Anonymous Origin ID | |||
| sec-token-client = Client Key | sec-token-client = Client Key | |||
| sec-token-request-blind = request_blind | sec-token-request-blind = request_blind | |||
| sec-token-request-key = request_key | ||||
| <Bytes containing the TokenRequest> | <Bytes containing the TokenRequest> | |||
| If the Attester detects a token_type in the TokenRequest that it does | If the Attester detects a token_type in the TokenRequest that it does | |||
| not recognize or support, it MUST reject the request with an HTTP 400 | not recognize or support, it MUST reject the request with an HTTP 400 | |||
| error. | error. | |||
| The Attester also checks to validate that the origin_name_key_id in | The Attester also checks to validate that the issuer_encap_key_id in | |||
| the client's TokenRequest matches a known Origin Name Key public key | the client's TokenRequest matches a known Issuer Encapsulation Key | |||
| for the Issuer. For example, the Attester can fetch this key using | public key for the Issuer. For example, the Attester can fetch this | |||
| the API defined in Section 3. This check is done to help ensure that | key using the API defined in Section 3. This check is done to help | |||
| the Client has not been given a unique key that could allow the | ensure that the Client has not been given a unique key that could | |||
| Issuer to fingerprint or target the Client. If the key does not | allow the Issuer to fingerprint or target the Client. If the key | |||
| match, the Attester rejects the request with an HTTP 400 error. Note | does not match, the Attester rejects the request with an HTTP 400 | |||
| that this can lead to failures in the event of Issuer Origin Name Key | error. Note that this can lead to failures in the event of Issuer | |||
| rotation; see Section 9 for considerations. | Issuer Encapsulation Key rotation; see Section 9 for considerations. | |||
| The Attester finally validates the Client's stable mapping request as | The Attester finally validates the Client's stable mapping request as | |||
| described in Section 7.2. If this fails, the Attester MUST return an | described in Section 7.2. If this fails, the Attester MUST return an | |||
| HTTP 400 error to the Client. | HTTP 400 error to the Client. | |||
| If the Attester accepts the request, it will look up the state stored | If the Attester accepts the request, it will look up the state stored | |||
| for this Client. It will look up the count of previously generate | for this Client. It will look up the count of previously generate | |||
| tokens for this Client using the same Anonymous Origin ID. See | tokens for this Client using the same Anonymous Origin ID. See | |||
| Section 5.1.2 for more details. | Section 5.1.2 for more details. | |||
| skipping to change at page 17, line 28 ¶ | skipping to change at page 17, line 39 ¶ | |||
| Attesters, either via mutual TLS or another form of application-layer | Attesters, either via mutual TLS or another form of application-layer | |||
| authentication. They MAY additionally use mechanisms such as TLS | authentication. They MAY additionally use mechanisms such as TLS | |||
| certificate pinning, to mitigate the risk of channel compromise; see | certificate pinning, to mitigate the risk of channel compromise; see | |||
| Section 8 for additional about this channel. | Section 8 for additional about this channel. | |||
| Upon receipt of the forwarded request, the Issuer validates the | Upon receipt of the forwarded request, the Issuer validates the | |||
| following conditions: | following conditions: | |||
| * The TokenRequest contains a supported token_type | * The TokenRequest contains a supported token_type | |||
| * The TokenRequest.token_key_id and TokenRequest.origin_name_key_id | * The TokenRequest.token_key_id and TokenRequest.issuer_encap_key_id | |||
| correspond to known Token Keys and Origin Name Keys held by the | correspond to known Token Keys and Issuer Encapsulation Keys held | |||
| Issuer. | by the Issuer. | |||
| * The TokenRequest.encrypted_origin_name can be decrypted using the | ||||
| Issuer's private key (the private key associated with Origin Name | ||||
| Key), and matches an Origin Name that is served by the Issuer. | ||||
| This name might be the empty string "", as described in Section 6, | ||||
| in which case the Issuer applies a cross-origin policy if | ||||
| supported. If a cross-origin policy is not supported, this | ||||
| condition is not met. | ||||
| * The TokenRequest.blinded_msg is of the correct size | * The TokenRequest.encrypted_token_request can be decrypted using | |||
| the Issuer's private key (the private key associated with Issuer | ||||
| Encapsulation Key), and contains a valid InnerTokenRequest whose | ||||
| unpadded origin name matches an Origin Name that is served by the | ||||
| Issuer. The Origin name associated with the InnerTokenRequest | ||||
| value might be the empty string "", as described in Section 6, in | ||||
| which case the Issuer applies a cross-origin policy if supported. | ||||
| If a cross-origin policy is not supported, this condition is not | ||||
| met. | ||||
| If any of these conditions is not met, the Issuer MUST return an HTTP | If any of these conditions is not met, the Issuer MUST return an HTTP | |||
| 400 error to the Attester, which will forward the error to the | 400 error to the Attester, which will forward the error to the | |||
| client. | client. | |||
| The Issuer determines the correct Issuer Key by using the decrypted | The Issuer determines the correct Issuer Key by using the decrypted | |||
| Origin Name value and TokenRequest.token_key_id. If there is no | Origin Name value and TokenRequest.token_key_id. If there is no | |||
| Token Key whose truncated key ID matches TokenRequest.token_key_id, | Token Key whose truncated key ID matches TokenRequest.token_key_id, | |||
| the Issuer MUST return an HTTP 401 error to Attester, which will | the Issuer MUST return an HTTP 401 error to Attester, which will | |||
| forward the error to the client. The Attester learns that the | forward the error to the client. The Attester learns that the | |||
| client's view of the Origin key was invalid in the process. | client's view of the Origin key was invalid in the process. | |||
| 5.5. Issuer-to-Attester Response | 5.5. Issuer-to-Attester Response | |||
| If the Issuer is willing to give a token to the Client, the Issuer | If the Issuer is willing to give a token to the Client, the Issuer | |||
| decrypts TokenRequest.encrypted_origin_name to discover "origin". If | decrypts TokenRequest.encrypted_token_request to discover a | |||
| this fails, the Issuer rejects the request with a 400 error. | InnerTokenRequest value. If this fails, the Issuer rejects the | |||
| Otherwise, the Issuer validates and processes the token request with | request with a 400 error. Otherwise, the Issuer validates and | |||
| Issuer Origin Secret corresponding to the designated Origin as | processes the token request with Issuer Origin Secret corresponding | |||
| described in Section 7.3. If this fails, the Issuer rejects the | to the designated Origin as described in Section 7.3. If this fails, | |||
| request with a 400 error. Otherwise, the output is index_key. | the Issuer rejects the request with a 400 error. Otherwise, the | |||
| output is index_key. | ||||
| The Issuer completes the issuance flow by computing a blinded | The Issuer completes the issuance flow by computing a blinded | |||
| response as follows: | response as follows: | |||
| blind_sig = rsabssa_blind_sign(skP, TokenRequest.blinded_msg) | blind_sig = rsabssa_blind_sign(skP, InnerTokenRequest.blinded_msg) | |||
| skP is the private key corresponding to Token Key, known only to the | skP is the private key corresponding to Token Key, known only to the | |||
| Issuer. | Issuer. The Issuer then encrypts blind_sig to the Client as | |||
| described in Section 6.2, yielding encrypted_token_response. | ||||
| The Issuer generates an HTTP response with status code 200 whose body | The Issuer generates an HTTP response with status code 200 whose body | |||
| consists of blind_sig, with the content type set as "message/token- | consists of blind_sig, with the content type set as "message/token- | |||
| response", the index_key set in the "Sec-Token-Origin" header, and | response", the index_key set in the "Sec-Token-Origin" header, and | |||
| the limit of tokens allowed for a Client for the Origin within a | the limit of tokens allowed for a Client for the Origin within a | |||
| policy window set in the "Sec-Token-Limit" header. This limit SHOULD | policy window set in the "Sec-Token-Limit" header. This limit SHOULD | |||
| NOT be unique to a specific Origin, such that the Attester could use | NOT be unique to a specific Origin, such that the Attester could use | |||
| the value to infer which Origin the Client is accessing (see | the value to infer which Origin the Client is accessing (see | |||
| Section 9). | Section 9). | |||
| :status = 200 | :status = 200 | |||
| content-type = message/token-response | content-type = message/token-response | |||
| content-length = <Length of blind_sig> | content-length = <Length of blind_sig> | |||
| sec-token-origin = index_key | sec-token-origin = index_key | |||
| sec-token-limit = Token limit | sec-token-limit = Token limit | |||
| <Bytes containing the blind_sig> | <Bytes containing the encrypted_token_response> | |||
| 5.6. Attester-to-Client Response | 5.6. Attester-to-Client Response | |||
| Upon receipt of a successful response from the Issuer, the Attester | Upon receipt of a successful response from the Issuer, the Attester | |||
| extracts the "Sec-Token-Origin" header, and uses the value to | extracts the "Sec-Token-Origin" header, and uses the value to | |||
| determine Anonymous Issuer Origin ID as described in Section 7.4. | determine Anonymous Issuer Origin ID as described in Section 7.4. | |||
| If the "Sec-Token-Origin" is missing, or if the same Anonymous Issuer | If the "Sec-Token-Origin" is missing, or if the same Anonymous Issuer | |||
| Origin ID was previously received in a response for a different | Origin ID was previously received in a response for a different | |||
| Anonymous Origin ID within the same policy window, the Attester MUST | Anonymous Origin ID within the same policy window, the Attester MUST | |||
| skipping to change at page 19, line 19 ¶ | skipping to change at page 19, line 32 ¶ | |||
| with an HTTP 429 (Too Many Requests) error. | with an HTTP 429 (Too Many Requests) error. | |||
| For all other cases, the Attester forwards all HTTP responses | For all other cases, the Attester forwards all HTTP responses | |||
| unmodified to the Client as the response to the original request for | unmodified to the Client as the response to the original request for | |||
| this issuance. | this issuance. | |||
| When the Attester detects successful token issuance, it MUST | When the Attester detects successful token issuance, it MUST | |||
| increment the counter in its state for the number of tokens issued to | increment the counter in its state for the number of tokens issued to | |||
| the Client for the Anonymous Origin ID. | the Client for the Anonymous Origin ID. | |||
| Upon receipt, the Client handles the response and, if successful, | Upon receipt, the Client decrypts the blind_sig from | |||
| processes the body as follows: | encrypted_token_response as described in Section 6.2. If successful, | |||
| the Client then processes the response as follows: | ||||
| authenticator = rsabssa_finalize(pkI, token_input, blind_sig, blind_inv) | authenticator = rsabssa_finalize(pkI, token_input, blind_sig, blind_inv) | |||
| If this succeeds, the Client then constructs a token as described in | If this succeeds, the Client then constructs a token as described in | |||
| [AUTHSCHEME] as follows: | [AUTHSCHEME] as follows: | |||
| struct { | struct { | |||
| uint16_t token_type = 0x0003 | uint16_t token_type = 0x0003 | |||
| uint8_t nonce[32]; | uint8_t nonce[32]; | |||
| uint8_t context[32]; | uint8_t context[32]; | |||
| uint8_t token_key_id[Nid]; | uint8_t token_key_id[Nid]; | |||
| uint8_t authenticator[Nk] | uint8_t authenticator[Nk] | |||
| } Token; | } Token; | |||
| 6. Encrypting Origin Names | 6. Encrypting Origin Token Requests and Responses | |||
| Given a NameKey (Origin Name Key), Clients produce | Clients encapsulate token request information to the Issuer using the | |||
| encrypted_origin_name and authenticate the contents of the | Issuer Encapsulation Key. Issuers decrypt the token request using | |||
| TokenRequest using the following values: | their corresponding private key. This process yields the decrypted | |||
| token request as well as a shared encryption context between Client | ||||
| and Issuer. Issuers encapsulate their token response to the Client | ||||
| using an ephemeral key derived from this shared encryption context. | ||||
| This process ensures that the Attester learns neither the token | ||||
| request or response information. | ||||
| Client to Issuer encapsulation is described in Section 6.1, and | ||||
| Issuer to Client encapsulation is described in Section 6.2. | ||||
| 6.1. Client to Issuer Encapsulation | ||||
| Given a EncapsulationKey (Issuer Encapsulation Key), Clients produce | ||||
| encrypted_token_request using the following values: | ||||
| * the one octet key identifier from the Name Key, keyID, with the | * the one octet key identifier from the Name Key, keyID, with the | |||
| corresponding KEM identified by kemID, the public key from the | corresponding KEM identified by kemID, the public key from the | |||
| configuration, pkI, and; | configuration, pkI, and; | |||
| * a selected combination of KDF, identified by kdfID, and AEAD, | * a selected combination of KDF, identified by kdfID, and AEAD, | |||
| identified by aeadID. | identified by aeadID. | |||
| Beyond the key configuration inputs, Clients also require the | Beyond the key configuration inputs, Clients also require the | |||
| following inputs defined in Section 5.3: token_key_id, blinded_msg, | following inputs defined in Section 5.3: token_key_id, blinded_msg, | |||
| request_key, and origin_name_key_id. | request_key, origin_name, and issuer_encap_key_id. | |||
| Together, these are used to encapsulate Origin Name (origin_name) and | Together, these are used to encapsulate an InnerTokenRequest and | |||
| produce Encrypted Origin Name (encrypted_origin). | produce an encrypted token request (encrypted_token_request). | |||
| origin_name contains the name of the origin that initiated the | origin_name contains the name of the origin that initiated the | |||
| challenge, as taken from the TokenChallenge.origin_info field. If | challenge, as taken from the TokenChallenge.origin_info field. If | |||
| the TokenChallenge.origin_info field is empty, origin_name is set to | the TokenChallenge.origin_info field is empty, origin_name is set to | |||
| the empty string "". | the empty string "". | |||
| The process for generating encrypted_origin from origin_name is as | The process for generating encrypted_token_request from blinded_msg, | |||
| follows: | request_key, and origin_name values is as follows: | |||
| 1. Compute an [HPKE] context using pkI, yielding context and | 1. Compute an [HPKE] context using pkI, yielding context and | |||
| encapsulation key enc. | encapsulation key enc. | |||
| 2. Construct associated data, aad, by concatenating the values of | 2. Construct associated data, aad, by concatenating the values of | |||
| keyID, kemID, kdfID, aeadID, and all other values of the | keyID, kemID, kdfID, aeadID, and all other values of the | |||
| TokenRequest structure. | TokenRequest structure. | |||
| 3. Pad origin_name with N zero bytes, where N = 31 - ((L - 1) % 32) | 3. Pad origin_name with N zero bytes, where N = 31 - ((L - 1) % 32) | |||
| and L is the length of origin_name. Denote this padding process | and L is the length of origin_name. If L is 0, N = 32. Denote | |||
| as the function pad. | this padding process as the function pad. | |||
| 4. Encrypt (seal) the padded origin_name with aad as associated data | 4. Encrypt (seal) the padded origin_name with aad as associated data | |||
| using context, yielding ciphertext ct. | using context, yielding ciphertext ct. | |||
| 5. Concatenate the values of aad, enc, and ct, yielding | 5. Concatenate the values of aad, enc, and ct, yielding | |||
| encrypted_origin_name. | encrypted_token_request. | |||
| Note that enc is of fixed-length, so there is no ambiguity in parsing | Note that enc is of fixed-length, so there is no ambiguity in parsing | |||
| this structure. | this structure. | |||
| In pseudocode, this procedure is as follows: | In pseudocode, this procedure is as follows: | |||
| enc, context = SetupBaseS(pkI, "TokenRequest") | enc, context = SetupBaseS(pkI, "InnerTokenRequest") | |||
| aad = concat(encode(1, keyID), | aad = concat(encode(1, keyID), | |||
| encode(2, kemID), | encode(2, kemID), | |||
| encode(2, kdfID), | encode(2, kdfID), | |||
| encode(2, aeadID), | encode(2, aeadID), | |||
| encode(2, token_type), | encode(2, token_type), | |||
| encode(1, token_key_id), | encode(1, token_key_id), | |||
| encode(Nk, blinded_msg), | encode(32, issuer_encap_key_id)) | |||
| encode(49, request_key), | padded_origin_name = pad(origin_name) | |||
| encode(32, origin_name_key_id)) | input = concat(encode(Nk, blinded_msg), | |||
| ct = context.Seal(aad, pad(origin_name)) | encode(49, request_key), | |||
| encrypted_origin_name = concat(enc, ct) | encode(len(padded_origin_name), padded_origin_name)) | |||
| Issuers reverse this procedure to recover the (padded) Origin Name by | ct = context.Seal(aad, input) | |||
| computing the AAD as described above and decrypting | encrypted_token_request = concat(enc, ct) | |||
| encrypted_origin_name with their private key skI (the private key | ||||
| corresponding to pkI), and stripping off padding bytes. In | ||||
| pseudocode, this procedure is as follows: | ||||
| enc, ct = parse(encrypted_origin_name) | Issuers reverse this procedure to recover the InnerTokenRequest value | |||
| by computing the AAD as described above and decrypting | ||||
| encrypted_token_request with their private key skI (the private key | ||||
| corresponding to pkI). The origin_name value is recovered from | ||||
| InnerTokenRequest.padded_origin_name by stripping off padding bytes. | ||||
| In pseudocode, this procedure is as follows: | ||||
| enc, ct = parse(encrypted_token_request) | ||||
| aad = concat(encode(1, keyID), | aad = concat(encode(1, keyID), | |||
| encode(2, kemID), | encode(2, kemID), | |||
| encode(2, kdfID), | encode(2, kdfID), | |||
| encode(2, aeadID), | encode(2, aeadID), | |||
| encode(2, token_type), | encode(2, token_type), | |||
| encode(1, token_key_id), | encode(1, token_key_id), | |||
| encode(Nk, blinded_msg), | encode(32, issuer_encap_key_id)) | |||
| encode(49, request_key), | context = SetupBaseR(enc, skI, "TokenRequest") | |||
| encode(32, origin_name_key_id)) | origin_token_request, error = context.Open(aad, ct) | |||
| enc, context = SetupBaseR(enc, skI, "TokenRequest") | The InnerTokenRequest.blinded_msg and InnerTokenRequest.request_key | |||
| origin_name, error = context.Open(aad, ct) | values, along with the unpadded origin_name value, are used by the | |||
| Issuer as described in Section 5.4. | ||||
| The resulting value of origin_name is used by the Issuer to determine | 6.2. Issuer to Client Encapsulation | |||
| which rate-limit values to send to the Attester for enforcement. If | ||||
| the decrypted origin_name is the empty string "", the Issuer applies | Given an HPKE context context computed in Section 6.1, Issuers | |||
| a cross-origin rate-limit policy, if supported. If the decrypted | encapsulate their token response blind_sig, yielding an encrypted | |||
| origin_name is the empty string "" and the Issuer does not support | token response encrypted_token_response, to the Client as follows: | |||
| cross-origin rate limiting, then it MUST abort the protocol as | ||||
| described in Section 5.4. | 1. Export a secret secret from context, using the string | |||
| "OriginTokenResponse" as context. The length of this secret is | ||||
| max(Nn, Nk), where Nn and Nk are the length of AEAD key and nonce | ||||
| associated with context. | ||||
| 2. Generate a random value of length max(Nn, Nk) bytes, called | ||||
| response_nonce. | ||||
| 3. Extract a pseudorandom key prk using the Extract function | ||||
| provided by the KDF algorithm associated with context. The ikm | ||||
| input to this function is secret; the salt input is the | ||||
| concatenation of enc (from enc_request) and response_nonce | ||||
| 4. Use the Expand function provided by the same KDF to extract an | ||||
| AEAD key key, of length Nk - the length of the keys used by the | ||||
| AEAD associated with context. Generating key uses a label of | ||||
| "key". | ||||
| 5. Use the same Expand function to extract a nonce nonce of length | ||||
| Nn - the length of the nonce used by the AEAD. Generating nonce | ||||
| uses a label of "nonce". | ||||
| 6. Encrypt blind_sig, passing the AEAD function Seal the values of | ||||
| key, nonce, empty aad, and a pt input of request, which yields | ||||
| ct. | ||||
| 7. Concatenate response_nonce and ct, yielding an Encapsulated | ||||
| Response enc_response. Note that response_nonce is of fixed- | ||||
| length, so there is no ambiguity in parsing either response_nonce | ||||
| or ct. | ||||
| In pseudocode, this procedure is as follows: | ||||
| secret = context.Export("OriginTokenResponse", Nk) | ||||
| response_nonce = random(max(Nn, Nk)) | ||||
| salt = concat(enc, response_nonce) | ||||
| prk = Extract(salt, secret) | ||||
| aead_key = Expand(prk, "key", Nk) | ||||
| aead_nonce = Expand(prk, "nonce", Nn) | ||||
| ct = Seal(aead_key, aead_nonce, "", blind_sig) | ||||
| encrypted_token_response = concat(response_nonce, ct) | ||||
| Clients decrypt encrypted_token_response by reversing this process. | ||||
| That is, they first parse enc_response into response_nonce and ct. | ||||
| They then follow the same process to derive values for aead_key and | ||||
| aead_nonce. | ||||
| The client uses these values to decrypt ct using the Open function | ||||
| provided by the AEAD. Decrypting might produce an error, as follows: | ||||
| blind_sig, error = Open(aead_key, aead_nonce, "", ct) | ||||
| 7. Anonymous Issuer Origin ID Computation | 7. Anonymous Issuer Origin ID Computation | |||
| This section describes the Client, Attester, and Issuer behavior in | This section describes the Client, Attester, and Issuer behavior in | |||
| computing Anonymous Issuer Origin ID, the stable mapping based on | computing Anonymous Issuer Origin ID, the stable mapping based on | |||
| client identity and origin name. At a high level, this functionality | client identity and origin name. At a high level, this functionality | |||
| computes y = F(x, k), where x is a per-Client secret and k is a per- | computes y = F(x, k), where x is a per-Client secret and k is a per- | |||
| Origin secret, subject to the following constraints: | Origin secret, subject to the following constraints: | |||
| * The Attester only learns y if the Client in possession of x | * The Attester only learns y if the Client in possession of x | |||
| skipping to change at page 22, line 21 ¶ | skipping to change at page 24, line 13 ¶ | |||
| <---------------------- | <---------------------- | |||
| The protocol for computing this functionality is divided into | The protocol for computing this functionality is divided into | |||
| sections for each of the participants. Section 7.1 describes Client | sections for each of the participants. Section 7.1 describes Client | |||
| behavior for initiating the computation with its per-Client secret, | behavior for initiating the computation with its per-Client secret, | |||
| Section 7.2 describes Attester behavior for verifying Client | Section 7.2 describes Attester behavior for verifying Client | |||
| requests, Section 7.3 describes Issuer behavior for computing the | requests, Section 7.3 describes Issuer behavior for computing the | |||
| mapping with its per-Origin secret, and Section 7.4 describes the | mapping with its per-Origin secret, and Section 7.4 describes the | |||
| final Attester step for computing the client-origin index. | final Attester step for computing the client-origin index. | |||
| The index computation is based on ECDSA [ECDSA] instantiated with | The index computation is based on a signature scheme with key | |||
| P-384 and SHA-384 and extended with key blinding support as described | blinding and unblinding support, denoted BKS, as described in | |||
| in [KEYBLINDING]. It uses the following functions: | [KEYBLINDING]. Such a scheme has the following functions: | |||
| * ECDSA-KeyGen(): Generate a random ECDSA private and public key | * BKS-KeyGen(): Generate a random private and public key pair (sk, | |||
| pair (sk, pk). | pk). | |||
| * ECDSA-BlindPublicKey(pk, r): Produce a blinded public key based on | * BKS-BlindKeyGen(): Generate a random blinding key bk. | |||
| the input public key pk and blind r according to [KEYBLINDING], | ||||
| Section 6.1. | ||||
| * ECDSA-Verify(pk, msg, sig): Verify the DER-encoded [X690] ECDSA- | * BKS-BlindPublicKey(pk, bk): Produce a blinded public key based on | |||
| Sig-Value signature sig over input message msg against the ECDSA | the input public key pk and blind key bk according to | |||
| public key pk, producing a boolean value indicating success. | [KEYBLINDING], Section 6.1. | |||
| * ECDSA-BlindKeySign(sk_sign, sk_blind, msg): Sign input message msg | * BKS-Verify(pk, msg, sig): Verify signature sig over input message | |||
| with signing key sk_sign and blind sk_blind according to | msg against the public key pk, producing a boolean value | |||
| [KEYBLINDING], Section 6.2, and serializes the resulting signature | indicating success. | |||
| pair (r, s) in "raw" form, i.e., as the concatenation of two | ||||
| 48-byte, big endian scalars. | ||||
| * ECDSA-SerializePrivatekey(sk): Serialize an ECDSA private key | * BKS-BlindKeySign(sk_sign, sk_blind, msg): Sign input message msg | |||
| using the Field-Element-to-Octet-String conversion according to | with signing key sk_sign and blind key sk_blind according to | |||
| [SECG]. | [KEYBLINDING], Section 6.2, and produce a signature of size Nsig | |||
| bytes. | ||||
| * ECDSA-DeserializePrivatekey(buf): Attempt to deserialize an ECDSA | * BKS-SerializePrivatekey(sk): Serialize a private key to a byte | |||
| private key from a 48-byte string buf using Octet-String-to-Field- | string of length Nsk. | |||
| Element from [SECG]. This function can fail if buf does not | ||||
| represent a valid private key. | ||||
| * ECDSA-SerializePublicKey(pk): Serialize an ECDSA public key using | * BKS-DeserializePrivatekey(buf): Attempt to deserialize a private | |||
| the compressed Elliptic-Curve-Point-to-Octet-String method | key from an Nsk-byte string buf. This function can fail if buf | |||
| according to [SECG]. | does not represent a valid private key. | |||
| * ECDSA-DeserializePublicKey(buf): Attempt to deserialize a public | * BKS-SerializePublicKey(pk): Serialize a public key to a byte | |||
| key using the compressed Octet-String-to-Elliptic-Curve-Point | string of length Npk. | |||
| method according to [SECG], and then performs partial public-key | ||||
| validation as defined in section 5.6.2.3.4 of [KEYAGREEMENT]. | * BKS-DeserializePublicKey(buf): Attempt to deserialize a public key | |||
| This validation includes checking that the coordinates are in the | of length Npk. This function can fail if buf does not represent a | |||
| correct range, that the point is on the curve, and that the point | valid public key. | |||
| is not the point at infinity. | ||||
| Additionally, each BKS scheme has a corresponding hash function, | ||||
| denoted Hash. The implementation of each of these functions depends | ||||
| on the issuance protocol token type. See Section 11.1 for more | ||||
| details. | ||||
| 7.1. Client Behavior | 7.1. Client Behavior | |||
| This section describes the Client behavior for generating an one- | This section describes the Client behavior for generating an one- | |||
| time-use request key and signature. Clients provide their Client | time-use request key and signature. Clients provide their Client | |||
| Secret as input to the request key generation step, and the rest of | Secret as input to the request key generation step, and the rest of | |||
| the token request inputs to the signature generation step. | the token request inputs to the signature generation step. | |||
| 7.1.1. Request Key | 7.1.1. Request Key | |||
| Clients produce request_key by masking Client Key and Client Secret | Clients produce request_key by masking Client Key and Client Secret | |||
| with a randomly chosen blind. Let pk_sign and sk_sign denote Client | with a randomly chosen blind. Let pk_sign and sk_sign denote Client | |||
| Key and Client Secret, respectively. This process is done as | Key and Client Secret, respectively. This process is done as | |||
| follows: | follows: | |||
| 1. Generate a random ECDSA private key, sk_blind. | 1. Generate a random blind key, sk_blind. | |||
| 2. Blind pk_sign with sk_blind to compute a blinded public key, | 2. Blind pk_sign with sk_blind to compute a blinded public key, | |||
| request_key. | request_key. | |||
| 3. Output the blinded public key. | 3. Output the blinded public key. | |||
| In pseudocode, this is as follows: | In pseudocode, this is as follows: | |||
| sk_blind = ECDSA-KeyGen() | sk_blind = BKS-BlindKeyGen() | |||
| blinded_key = ECDSA-BlindPublicKey(pk_sign, sk_blind) | blinded_key = BKS-BlindPublicKey(pk_sign, sk_blind) | |||
| request_key = ECDSA-SerializePublicKey(blinded_key) | request_key = BKS-SerializePublicKey(blinded_key) | |||
| request_blind = ECDSA-SerializePrivatekey(sk_blind) | request_blind = BKS-SerializePrivatekey(sk_blind) | |||
| 7.1.2. Request Signature | 7.1.2. Request Signature | |||
| Clients produce signature of their request based on the following | Clients produce signature of their request based on the following | |||
| inputs defined in Section 5.3: token_key_id, blinded_msg, | inputs defined in Section 5.3: token_key_id, blinded_msg, | |||
| request_key, name_key_id, encrypted_origin_name. This process | request_key, issuer_encap_key_id, encrypted_token_request. This | |||
| requires the blind value sk_blind produced during the Section 7.1.1 | process requires the blind value sk_blind produced during the | |||
| process. As above, let pk and sk denote Client Key and Client | Section 7.1.1 process. As above, let pk and sk denote Client Key and | |||
| Secret, respectively. Given these values, this signature process | Client Secret, respectively. Given these values, this signature | |||
| works as follows: | process works as follows: | |||
| 1. Concatenate all signature inputs to yield a message to sign. | 1. Concatenate all signature inputs to yield a message to sign. | |||
| 2. Compute an ECDSA signature with the blind sk_blind over the input | 2. Compute a signature with the blind sk_blind over the input | |||
| message using Client Secret, sk_sign as the signing key. | message using Client Secret, sk_sign as the signing key. | |||
| 3. Output the signature. | 3. Output the signature. | |||
| In pseudocode, this is as follows: | In pseudocode, this is as follows: | |||
| context = concat(0x0003, // token_type | context = concat(token_type, | |||
| token_key_id, | token_key_id, | |||
| blinded_msg, | issuer_encap_key_id, | |||
| request_key, | encrypted_token_request) | |||
| name_key_id, | request_signature = BKS-BlindKeySign(sk_sign, sk_blind, context) | |||
| encrypted_origin_name) | ||||
| request_signature = ECDSA-BlindKeySign(sk_sign, sk_blind, context) | ||||
| 7.2. Attester Behavior (Client Request Validation) | 7.2. Attester Behavior (Client Request Validation) | |||
| Given a TokenRequest request containing request_key, | Given a TokenRequest request containing request_key, | |||
| request_signature, and request_blind, as well as Client Key pk_blind, | request_signature, and request_blind, as well as Client Key pk_blind, | |||
| Attesters verify the signature as follows: | Attesters verify the signature as follows: | |||
| 1. Check that request_key is a valid ECDSA public key. If this | 1. Check that request_key is a valid public key. If this fails, | |||
| fails, abort. | abort. | |||
| 2. Check that request_blind is a valid ECDSA private key. If this | 2. Check that request_blind is a valid private key. If this fails, | |||
| fails, abort. | abort. | |||
| 3. Blind the Client Key pk_sign by blind sk_blind, yielding a | 3. Blind the Client Key pk_sign by blind sk_blind, yielding a | |||
| blinded key. If this does not match request_key, abort. | blinded key. If this does not match request_key, abort. | |||
| 4. Verify request_signature over the contents of the request, | 4. Verify request_signature over the contents of the request, | |||
| excluding the signature itself, using request_key. If signature | excluding the signature itself, using request_key. If signature | |||
| verification fails, abort. | verification fails, abort. | |||
| In pseudocode, this is as follows: | In pseudocode, this is as follows: | |||
| blind_key = ECDSA-DeserializePublicKey(request_key) | blind_key = BKS-DeserializePublicKey(request_key) | |||
| sk_blind = ECDSA-DeserializePrivatekey(request_blind) | sk_blind = BKS-DeserializePrivatekey(request_blind) | |||
| pk_blind = ECDSA-BlindPublicKey(pk_sign, sk_blind) | pk_blind = BKS-BlindPublicKey(pk_sign, sk_blind) | |||
| if pk_blind != blind_key: | if pk_blind != blind_key: | |||
| raise InvalidParameterError | raise InvalidParameterError | |||
| context = parse(request[..len(request)-96]) // this matches context computed during signing | context = parse(request[..len(request)-Nsig]) // this matches context computed during signing | |||
| valid = ECDSA-Verify(blind_key, context, request_signature) | valid = BKS-Verify(blind_key, context, request_signature) | |||
| if not valid: | if not valid: | |||
| raise InvalidSignatureError | raise InvalidSignatureError | |||
| 7.3. Issuer Behavior | 7.3. Issuer Behavior | |||
| Given an Issuer Origin Secret (denoted sk_origin) and a TokenRequest, | Given an Issuer Origin Secret (denoted sk_origin) and a TokenRequest, | |||
| from which request_key and request_signature are parsed, Issuers | from which request_key and request_signature are parsed, Issuers | |||
| verify the request signature and compute a response as follows: | verify the request signature and compute a response as follows: | |||
| 1. Check that request_key is a valid ECDSA public key. If this | 1. Check that request_key is a valid public key. If this fails, | |||
| fails, abort. | abort. | |||
| 2. Verify request_signature over the contents of the request, | 2. Verify request_signature over the contents of the request, | |||
| excluding the signature itself, using request_key. If signature | excluding the signature itself, using request_key. If signature | |||
| verification fails, abort. | verification fails, abort. | |||
| 3. Blind request_key by Issuer Origin Secret, sk_origin, yielding an | 3. Blind request_key by Issuer Origin Secret, sk_origin, yielding an | |||
| index key. | index key. | |||
| 4. Output the index key. | 4. Output the index key. | |||
| In pseudocode, this is as follows: | In pseudocode, this is as follows: | |||
| blind_key = ECDSA-DeserializePublicKey(request_key) | blind_key = BKS-DeserializePublicKey(request_key) | |||
| context = parse(request[..len(request)-96]) // this matches context computed during signing | context = parse(request[..len(request)-Nsig]) // this matches context computed during signing | |||
| valid = ECDSA-Verify(blind_key, context, request_signature) | valid = BKS-Verify(blind_key, context, request_signature) | |||
| if not valid: | if not valid: | |||
| raise InvalidSignatureError | raise InvalidSignatureError | |||
| evaluated_key = ECDSA-BlindPublicKey(request_key, sk_origin) | evaluated_key = BKS-BlindPublicKey(request_key, sk_origin) | |||
| index_key = ECDSA-SerializePublicKey(evaluated_key) | index_key = BKS-SerializePublicKey(evaluated_key) | |||
| 7.4. Attester Behavior (Index Computation) | 7.4. Attester Behavior (Index Computation) | |||
| Given an Issuer response index_key, Client blind sk_blind, and Client | Given an Issuer response index_key, Client blind sk_blind, and Client | |||
| Key (denoted pk_sign), Attesters complete the Anonymous Issuer Origin | Key (denoted pk_sign), Attesters complete the Anonymous Issuer Origin | |||
| ID computation as follows: | ID computation as follows: | |||
| 1. Check that index_key is a valid ECDSA public key. If this fails, | 1. Check that index_key is a valid public key. If this fails, | |||
| abort. | abort. | |||
| 2. Unblind the index_key using the Client blind sk_blind, yielding | 2. Unblind the index_key using the Client blind sk_blind, yielding | |||
| the index result. | the index result. | |||
| 3. Run HKDF [RFC5869] with SHA-384 using the index result as the | 3. Run HKDF [RFC5869] with the hash function corresponding to the | |||
| secret, Client Key pk_sign as the salt, and ASCII string | BKS scheme, using the index result as the secret, Client Key | |||
| "anon_issuer_origin_id" as the info string, yielding Anonymous | pk_sign as the salt, and ASCII string "anon_issuer_origin_id" as | |||
| Issuer Origin ID. | the info string, yielding Anonymous Issuer Origin ID. | |||
| In pseudocode, this is as follows: | In pseudocode, this is as follows: | |||
| evaluated_key = ECDSA-DeserializePublicKey(index_key) | evaluated_key = BKS-DeserializePublicKey(index_key) | |||
| unblinded_key = ECDSA-UnblindPublicKey(evaluated_key, sk_blind) | unblinded_key = BKS-UnblindPublicKey(evaluated_key, sk_blind) | |||
| index_result = ECDSA-SerializePublicKey(unblinded_key) | index_result = BKS-SerializePublicKey(unblinded_key) | |||
| pk_encoded = ECDSA-SerializePublicKey(pk_sign) | pk_encoded = BKS-SerializePublicKey(pk_sign) | |||
| anon_issuer_origin_id = HKDF-SHA384(secret=index_result, | anon_issuer_origin_id = HKDF-Hash(secret=index_result, | |||
| salt=pk_encoded, | salt=pk_encoded, info="anon_issuer_origin_id") | |||
| info="anon_issuer_origin_id") | ||||
| 8. Security Considerations | 8. Security Considerations | |||
| This section describes security considerations relevant to the use of | This section describes security considerations relevant to the use of | |||
| this protocol. | this protocol. | |||
| 8.1. Channel Security | 8.1. Channel Security | |||
| An attacker that can act as an intermediate between Attester and | An attacker that can act as an intermediate between Attester and | |||
| Issuer communication can influence or disrupt the ability for the | Issuer communication can influence or disrupt the ability for the | |||
| skipping to change at page 29, line 23 ¶ | skipping to change at page 31, line 5 ¶ | |||
| policies in [RFC6454]. Clients MAY further limit which | policies in [RFC6454]. Clients MAY further limit which | |||
| authentication challenges they are willing to respond to, for example | authentication challenges they are willing to respond to, for example | |||
| by only accepting challenges when the origin is a web site to which | by only accepting challenges when the origin is a web site to which | |||
| the user navigated. | the user navigated. | |||
| 9.3. Client Identification with Unique Keys | 9.3. Client Identification with Unique Keys | |||
| Client activity could be linked if an Origin and Issuer collude to | Client activity could be linked if an Origin and Issuer collude to | |||
| have unique keys targeted at specific Clients or sets of Clients. | have unique keys targeted at specific Clients or sets of Clients. | |||
| To mitigate the risk of a targeted Origin Name Key, the Attester can | To mitigate the risk of a targeted Issuer Encapsulation Key, the | |||
| observe and validate the token_key_id presented by the Client to the | Attester can observe and validate the token_key_id presented by the | |||
| Issuer. As described in Section 5, Attesters MUST validate that the | Client to the Issuer. As described in Section 5, Attesters MUST | |||
| token_key_id in the Client's TokenRequest matches a known public key | validate that the token_key_id in the Client's TokenRequest matches a | |||
| for the Issuer. The Attester needs to support key rotation, but | known public key for the Issuer. The Attester needs to support key | |||
| ought to disallow very rapid key changes, which could indicate that | rotation, but ought to disallow very rapid key changes, which could | |||
| an Origin is colluding with an Issuer to try to rotate the key for | indicate that an Origin is colluding with an Issuer to try to rotate | |||
| each new Client in order to link the client activity. | the key for each new Client in order to link the client activity. | |||
| 9.4. Origin Identification | 9.4. Origin Identification | |||
| As stated in Section 1.2, the design of this protocol is such that | As stated in Section 1.2, the design of this protocol is such that | |||
| Attesters cannot learn the identity of origins that Clients are | Attesters cannot learn the identity of origins that Clients are | |||
| accessing. The Origin Name itself is encrypted in the request | accessing. The Origin Name itself is encrypted in the request | |||
| between the Client and the Issuer, so the Attester cannot directly | between the Client and the Issuer, so the Attester cannot directly | |||
| learn the value. However, in order to prevent the Attester from | learn the value. However, in order to prevent the Attester from | |||
| inferring the value, additional constraints need to be added: | inferring the value, additional constraints need to be added: | |||
| skipping to change at page 31, line 4 ¶ | skipping to change at page 32, line 28 ¶ | |||
| graceful updates. The RECOMMENDED rotation interval is two times the | graceful updates. The RECOMMENDED rotation interval is two times the | |||
| length of the policy window for that information. During generation, | length of the policy window for that information. During generation, | |||
| issuers must ensure the token_key_id (the 8-bit prefix of | issuers must ensure the token_key_id (the 8-bit prefix of | |||
| SHA256(Token Key)) is different from all other token_key_id values | SHA256(Token Key)) is different from all other token_key_id values | |||
| for that origin currently in rotation. One way to ensure this | for that origin currently in rotation. One way to ensure this | |||
| uniqueness is via rejection sampling, where a new key is generated | uniqueness is via rejection sampling, where a new key is generated | |||
| until its token_key_id is unique among all currently in rotation for | until its token_key_id is unique among all currently in rotation for | |||
| the origin. | the origin. | |||
| 11. IANA considerations | 11. IANA considerations | |||
| 11.1. Token Type | 11.1. Token Type | |||
| This document updates the "Token Type" Registry ([AUTHSCHEME]) with | This document updates the "Token Type" Registry ([AUTHSCHEME]) with | |||
| the following value: | the following value: | |||
| +======+=============+==========+========+========+===+===+=========+ | +======+=============+==========+========+========+===+===+=========+ | |||
| |Value |Name |Publicly |Public |Private |Nk |Nid|Reference| | |Value |Name |Publicly |Public |Private |Nk |Nid|Reference| | |||
| | | |Verifiable|Metadata|Metadata| | | | | | | |Verifiable|Metadata|Metadata| | | | | |||
| +======+=============+==========+========+========+===+===+=========+ | +======+=============+==========+========+========+===+===+=========+ | |||
| |0x0003|Rate-Limited |Y |N |N |512|32 |This | | |0x0003|Rate-Limited |Y |N |N |512|32 |This | | |||
| | |Blind RSA | | | | | |document | | | |Blind | | | | | |document | | |||
| | |RSA(SHA-384, | | | | | | | | ||||
| | |2048-bit) | | | | | | | | ||||
| | |with | | | | | | | | ||||
| | |ECDSA(P-384, | | | | | | | | ||||
| | |SHA-384) | | | | | | | | ||||
| +------+-------------+----------+--------+--------+---+---+---------+ | ||||
| |0x0004|Rate-Limited |Y |N |N |512|32 |This | | ||||
| | |Blind | | | | | |document | | ||||
| | |RSA(SHA-384, | | | | | | | | ||||
| | |2048-bit) | | | | | | | | ||||
| | |with | | | | | | | | ||||
| | |Ed25519(SHA- | | | | | | | | ||||
| | |512) | | | | | | | | ||||
| +------+-------------+----------+--------+--------+---+---+---------+ | +------+-------------+----------+--------+--------+---+---+---------+ | |||
| Table 2: Token Types | Table 2: Token Types | |||
| The details of the signature scheme with key blinding and unblinding | ||||
| functions for each token type above are described in the following | ||||
| sections. | ||||
| 11.1.1. ECDSA-based Token Type | ||||
| This section describes the implementation details of the signature | ||||
| scheme with key blinding and unblinding functions introduced in | ||||
| Section 7 using [ECDSA] with P-384 as the underlying elliptic curve | ||||
| and SHA-384 as the corresponding hash function. | ||||
| * BKS-KeyGen(): Generate a random ECDSA private and public key pair | ||||
| (sk, pk). | ||||
| * BKS-BlindKeyGen(): Generate a random ECDSA private key bk. | ||||
| * BKS-BlindPublicKey(pk, bk): Produce a blinded public key based on | ||||
| the input public key pk and blind bk according to [KEYBLINDING], | ||||
| Section 6.1. | ||||
| * BKS-Verify(pk, msg, sig): Verify the DER-encoded [X690] BKS-Sig- | ||||
| Value signature sig over input message msg against the ECDSA | ||||
| public key pk, producing a boolean value indicating success. | ||||
| * BKS-BlindKeySign(sk_sign, sk_blind, msg): Sign input message msg | ||||
| with signing key sk_sign and blind sk_blind according to | ||||
| [KEYBLINDING], Section 6.2, and serializes the resulting signature | ||||
| pair (r, s) in "raw" form, i.e., as the concatenation of two | ||||
| 48-byte, big endian scalars, yielding an Nsig=96 byte signature. | ||||
| * BKS-SerializePrivatekey(sk): Serialize an ECDSA private key using | ||||
| the Field-Element-to-Octet-String conversion according to [SECG]. | ||||
| * BKS-DeserializePrivatekey(buf): Attempt to deserialize an ECDSA | ||||
| private key from a 48-byte string buf using Octet-String-to-Field- | ||||
| Element from [SECG]. This function can fail if buf does not | ||||
| represent a valid private key. | ||||
| * BKS-SerializePublicKey(pk): Serialize an ECDSA public key using | ||||
| the compressed Elliptic-Curve-Point-to-Octet-String method | ||||
| according to [SECG]. | ||||
| * BKS-DeserializePublicKey(buf): Attempt to deserialize a public key | ||||
| using the compressed Octet-String-to-Elliptic-Curve-Point method | ||||
| according to [SECG], and then performs partial public-key | ||||
| validation as defined in section 5.6.2.3.4 of [KEYAGREEMENT]. | ||||
| This validation includes checking that the coordinates are in the | ||||
| correct range, that the point is on the curve, and that the point | ||||
| is not the point at infinity. | ||||
| 11.1.2. Ed25519-based Token Type | ||||
| This section describes the implementation details of the signature | ||||
| scheme with key blinding and unblinding functions introduced in | ||||
| Section 7 using Ed25519 as described in [RFC8032]. | ||||
| * BKS-KeyGen(): Generate a random Ed25519 private and public key | ||||
| pair (sk, pk), where sk is randomly generated 32 bytes (See | ||||
| [RFC4086] for information about randomness generation) and pk is | ||||
| computed according to [RFC8032], Section 5.1.5. | ||||
| * BKS-BlindKeyGen(): Generate and output 32 random bytes. | ||||
| * BKS-BlindPublicKey(pk, bk): Produce a blinded public key based on | ||||
| the input public key pk and blind bk according to [KEYBLINDING], | ||||
| Section 5.1. | ||||
| * BKS-Verify(pk, msg, sig): Verify the signature sig over input | ||||
| message msg against the Ed25519 public key pk, as defined in | ||||
| [RFC8032], Section 5.1.7, producing a boolean value indicating | ||||
| success. | ||||
| * BKS-BlindKeySign(sk_sign, sk_blind, msg): Sign input message msg | ||||
| with signing key sk_sign and blind sk_blind according to | ||||
| [KEYBLINDING], Section 5.2, yielding an Nsig=64 byte signature. | ||||
| * BKS-SerializePrivatekey(sk): Identity function which outputs sk as | ||||
| an Nsk=32 byte buffer. | ||||
| * BKS-DeserializePrivatekey(buf): Identity function which outputs | ||||
| buf interpreted as sk. | ||||
| * BKS-SerializePublicKey(pk): Identity function which outputs pk as | ||||
| an Npk=32 byte buffer. | ||||
| * BKS-DeserializePublicKey(buf): Identity function which outputs buf | ||||
| interpreted as pk. | ||||
| 11.2. HTTP Headers | 11.2. HTTP Headers | |||
| This document registers four new headers for use on the token | This document registers four new headers for use on the token | |||
| issuance path in the "Permanent Message Header Field Names" | issuance path in the "Permanent Message Header Field Names" | |||
| <https://www.iana.org/assignments/message-headers>. | <https://www.iana.org/assignments/message-headers>. | |||
| +-------------------------+----------+--------+---------------+ | +-------------------------+----------+--------+---------------+ | |||
| | Header Field Name | Protocol | Status | Reference | | | Header Field Name | Protocol | Status | Reference | | |||
| +-------------------------+----------+--------+---------------+ | +-------------------------+----------+--------+---------------+ | |||
| | Sec-Token-Origin | http | std | This document | | | Sec-Token-Origin | http | std | This document | | |||
| +-------------------------+----------+--------+---------------+ | +-------------------------+----------+--------+---------------+ | |||
| | Sec-Token-Client | http | std | This document | | | Sec-Token-Client | http | std | This document | | |||
| +-------------------------+----------+--------+---------------+ | +-------------------------+----------+--------+---------------+ | |||
| | Sec-Token-Request-Blind | http | std | This document | | | Sec-Token-Request-Blind | http | std | This document | | |||
| +-------------------------+----------+--------+---------------+ | +-------------------------+----------+--------+---------------+ | |||
| | Sec-Token-Request-Key | http | std | This document | | ||||
| +-------------------------+----------+--------+---------------+ | ||||
| | Sec-Token-Limit | http | std | This document | | | Sec-Token-Limit | http | std | This document | | |||
| +-------------------------+----------+--------+---------------+ | +-------------------------+----------+--------+---------------+ | |||
| Figure 1: Registered HTTP Header | Figure 1: Registered HTTP Header | |||
| 12. References | 12. References | |||
| 12.1. Normative References | 12.1. Normative References | |||
| [ARCH] Davidson, A., Iyengar, J., and C. A. Wood, "Privacy Pass | [ARCH] Davidson, A., Iyengar, J., and C. A. Wood, "Privacy Pass | |||
| skipping to change at page 33, line 19 ¶ | skipping to change at page 37, line 19 ¶ | |||
| [RFC6454] Barth, A., "The Web Origin Concept", RFC 6454, | [RFC6454] Barth, A., "The Web Origin Concept", RFC 6454, | |||
| DOI 10.17487/RFC6454, December 2011, | DOI 10.17487/RFC6454, December 2011, | |||
| <https://www.rfc-editor.org/rfc/rfc6454>. | <https://www.rfc-editor.org/rfc/rfc6454>. | |||
| [RFC6570] Gregorio, J., Fielding, R., Hadley, M., Nottingham, M., | [RFC6570] Gregorio, J., Fielding, R., Hadley, M., Nottingham, M., | |||
| and D. Orchard, "URI Template", RFC 6570, | and D. Orchard, "URI Template", RFC 6570, | |||
| DOI 10.17487/RFC6570, March 2012, | DOI 10.17487/RFC6570, March 2012, | |||
| <https://www.rfc-editor.org/rfc/rfc6570>. | <https://www.rfc-editor.org/rfc/rfc6570>. | |||
| [RFC8032] Josefsson, S. and I. Liusvaara, "Edwards-Curve Digital | ||||
| Signature Algorithm (EdDSA)", RFC 8032, | ||||
| DOI 10.17487/RFC8032, January 2017, | ||||
| <https://www.rfc-editor.org/rfc/rfc8032>. | ||||
| [RFC8174] Leiba, B., "Ambiguity of Uppercase vs Lowercase in RFC | [RFC8174] Leiba, B., "Ambiguity of Uppercase vs Lowercase in RFC | |||
| 2119 Key Words", BCP 14, RFC 8174, DOI 10.17487/RFC8174, | 2119 Key Words", BCP 14, RFC 8174, DOI 10.17487/RFC8174, | |||
| May 2017, <https://www.rfc-editor.org/rfc/rfc8174>. | May 2017, <https://www.rfc-editor.org/rfc/rfc8174>. | |||
| [RFC8941] Nottingham, M. and P-H. Kamp, "Structured Field Values for | [RFC8941] Nottingham, M. and P-H. Kamp, "Structured Field Values for | |||
| HTTP", RFC 8941, DOI 10.17487/RFC8941, February 2021, | HTTP", RFC 8941, DOI 10.17487/RFC8941, February 2021, | |||
| <https://www.rfc-editor.org/rfc/rfc8941>. | <https://www.rfc-editor.org/rfc/rfc8941>. | |||
| [SECG] "Elliptic Curve Cryptography, Standards for Efficient | [SECG] "Elliptic Curve Cryptography, Standards for Efficient | |||
| Cryptography Group, ver. 2", 2009, | Cryptography Group, ver. 2", 2009, | |||
| skipping to change at page 34, line 5 ¶ | skipping to change at page 38, line 5 ¶ | |||
| 12.2. Informative References | 12.2. Informative References | |||
| [BASIC-ISSUANCE] | [BASIC-ISSUANCE] | |||
| Celi, S., Davidson, A., Faz-Hernandez, A., Valdez, S., and | Celi, S., Davidson, A., Faz-Hernandez, A., Valdez, S., and | |||
| C. A. Wood, "Privacy Pass Issuance Protocol", Work in | C. A. Wood, "Privacy Pass Issuance Protocol", Work in | |||
| Progress, Internet-Draft, draft-ietf-privacypass-protocol- | Progress, Internet-Draft, draft-ietf-privacypass-protocol- | |||
| 04, 5 April 2022, <https://datatracker.ietf.org/doc/html/ | 04, 5 April 2022, <https://datatracker.ietf.org/doc/html/ | |||
| draft-ietf-privacypass-protocol-04>. | draft-ietf-privacypass-protocol-04>. | |||
| [RFC4086] Eastlake 3rd, D., Schiller, J., and S. Crocker, | ||||
| "Randomness Requirements for Security", BCP 106, RFC 4086, | ||||
| DOI 10.17487/RFC4086, June 2005, | ||||
| <https://www.rfc-editor.org/rfc/rfc4086>. | ||||
| Appendix A. Acknowledgements | Appendix A. Acknowledgements | |||
| The authors of this document would like to acknowledge feedback from | The authors of this document would like to acknowledge feedback from | |||
| contributors to the Privacy Pass working group for their help in | contributors to the Privacy Pass working group for their help in | |||
| improving this document. The authors also thank Frank Denis and | improving this document. The authors also thank Frank Denis and | |||
| David Schinazi for their contributions. | David Schinazi for their contributions. | |||
| Appendix B. Test Vectors | Appendix B. Test Vectors | |||
| This section includes test vectors for Origin Name encryption in | This section includes test vectors for Origin Name encryption in | |||
| skipping to change at page 34, line 30 ¶ | skipping to change at page 38, line 35 ¶ | |||
| The test vector below for the procedure in Section 6 lists the | The test vector below for the procedure in Section 6 lists the | |||
| following values: | following values: | |||
| * origin_name: The Origin Name to encrypt, represented as a | * origin_name: The Origin Name to encrypt, represented as a | |||
| hexadecimal string. | hexadecimal string. | |||
| * kem_id, kdf_id, aead_id: The HPKE algorithms comprising the | * kem_id, kdf_id, aead_id: The HPKE algorithms comprising the | |||
| ciphersuite DHKEM(X25519, HKDF-SHA256), HKDF-SHA256, AES-128-GCM. | ciphersuite DHKEM(X25519, HKDF-SHA256), HKDF-SHA256, AES-128-GCM. | |||
| * origin_name_key_seed: The seed used to derive the private key | * issuer_encap_key_seed: The seed used to derive the private key | |||
| corresponding to Origin Name Key via the DeriveKeyPair function as | corresponding to Issuer Encapsulation Key via the DeriveKeyPair | |||
| defined in Section 7.1.3. of [HPKE], represented as a hexadecimal | function as defined in Section 7.1.3. of [HPKE], represented as a | |||
| string. | ||||
| * origin_name_key: The public Origin Name Key, represented as a | ||||
| hexadecimal string. | hexadecimal string. | |||
| * token_type: The type of the protocol specified in this document, | * issuer_encap_key: The public Issuer Encapsulation Key, represented | |||
| 0x0003. | as a hexadecimal string. | |||
| * token_type: The type of the protocol specified in this document. | ||||
| * token_key_id: The ID of Token Key computed as in Section 5.3, a | * token_key_id: The ID of Token Key computed as in Section 5.3, a | |||
| single octet. | single octet. | |||
| * blinded_msg: A random blinded_msg value, represented as a | * blinded_msg: A random blinded_msg value, represented as a | |||
| hexadecimal string. | hexadecimal string. | |||
| * request_key: A random request_key value, represented as a | * request_key: A random request_key value, represented as a | |||
| hexadecimal string. | hexadecimal string. | |||
| * origin_name_key_id: The Origin Name Key ID computed as in | * issuer_encap_key_id: The Issuer Encapsulation Key ID computed as | |||
| Section 5.3, represented as a hexadecimal string. | in Section 5.3, represented as a hexadecimal string. | |||
| * encrypted_origin_name: The encrypted Origin Name, represented as a | * encrypted_token_request: The encrypted InnerTokenRequest, | |||
| hexadecimal string. | represented as a hexadecimal string. | |||
| origin_name: 746573742e6578616d706c65 | origin_name: 746573742e6578616d706c65 | |||
| kem_id: 32 | kem_id: 32 | |||
| kdf_id: 1 | kdf_id: 1 | |||
| aead_id: 1 | aead_id: 1 | |||
| origin_name_key_seed: | issuer_encap_key_seed: | |||
| ccc9e9744e7461acc6f3967757e7564b78f04ae5c9cd33f8c0c9b51ddff61f4f | d2653816496f400baec656f213f1345092f4406af4f2a63e164956c4c3d240ca | |||
| origin_name_key: 010020c9a7a358ab74c0664b0cffd11a5f7090108086153d77e2824 | issuer_encap_key: 010020d7b6a2c10e75c4239feb9897e8d23f3f3c377d78e7903611 | |||
| 72d254991fdbe1b00010001 | 53167736a24a9c5400010001 | |||
| token_type: 3 | token_type: 3 | |||
| token_key_id: 104 | token_key_id: 125 | |||
| blinded_msg: 01a7031dbbcca4ac6acaa4650219406fd9fbceb8c264e2b3be22823a679 | blinded_msg: 89da551a48270b053e53c9eb741badf89e43cb7e66366bb936e11fb2aa0 | |||
| 6571230f29ec01fe2dd633bbd4c208addfa0a6dbb149aeb4aca9070bafe849301076a40b | d30866986a790378bb9fc6a7cf5c32b7b7584d448ffa4ced3be650e354b3136428a52ec0 | |||
| 442469066ef78525f16a4e8cb3596ac0c548932aa421d7294d98e86187100598e9b8a968 | b27c4103c5855c2b9b4f521ad0713c800d7e6925b6c62e1a6f58b31d13335f468cf509b7 | |||
| c56d2c0277132c3efdc04b6030b9800642db686254784927cfd941f8a3a1f71893e33af4 | 46a16e79b23862d277d0880706c3fb84b127d94faf8d6d2f3e124e681994441b19be084e | |||
| d73f71403108c882afed2129b050663839d77bbd38f9479e7242a2d31f2fbce72259aab4 | c5c159bcd0abab433bbc308d90ea2cabdf4216e1b07155be66a048d686e383ca1e517ab8 | |||
| acecf7da7168347a214c6e9bb77974812b363355ac8bf4703085de54e2af85e970a3a83e | 0025bb4849d98beb8c3d05d045c1167cb74f4451d8f85695babb604418385464f21f9a81 | |||
| 2ee3d658951167fae57ab313569dcb7551c916cb0bda9c6293843fe75849eb6a33953145 | 5fb850ed83fd16a966130427e5637816501f7a79c0010e06adeba55781ceb50f56eae152 | |||
| 43bccb7e5146b618c637ffe8fc1bae2fddb102ce6d9a9c5eee0d267849a9bfb12e633fbf | ebd06f3cef80dc7ab121d | |||
| ffc80b76b017ae16f70ee6c3d841b9a4b1bf88264a2d62ae564941f943001ed95b869d2f | request_key: 0161d905e4e37f515cb61f863b60e5896aa9e4a17dbe238e752a144c64a | |||
| d81e82efc399507a0ed5450be2832076a40ce1dbd1e555fb0cf360e65c4087d101d87919 | 5412e244f0b1f75e010831e185cac023d33cb20 | |||
| 9180dfcecdd0a3a2340b893fd130e5d6ece273b4a646f157b344f110973403a14a5f46d3 | issuer_encap_key_id: | |||
| b2ec4457e2164ce850fc395c71d4adeab69f7ec617b6b062c1afb8da558970dfea3196e9 | dd2c6de3091f1873643233d229a7a0e9defe0f9fe43f6a7c42ae3a6b16f77837 | |||
| b2acb58dcfd23f54fc2084373cd9a78f7c84047c6f64e7464999597267e9b6a0f95bf13f | encrypted_token_request: 82ef7c068506bcabc27d068a51c7ead2cbaf600b76a15e4 | |||
| 634c2a23f861fc83a09d08309ad00af4abf8cf48f1f7d286f65750f20faa1310849e8aee | d9df99a0da676da5a073fcc8f5ac77b25064d7379037b4e1b186977cface31eceb611978 | |||
| 44c4ec6abb1c846a16a222116ad94 | c73c9aef38c9a0e8ae846881624fa6d454523a0a91d22b02b022891d0469deebd66a912a | |||
| request_key: 8a9ffd91064e498f0b14b4b82d65255b2859da8d349e2bb362ca7c8211e | a1ab3391b203e92e0a681f0a10c2a2d59b668daf1e5219ed16227d707fa0e8e29188bd58 | |||
| 23fcd5cd01813ef6eb4b994d532c3520e1aa89e | 7ab38b3584564ce9b6538ba82e301cfed4231a2fa4f64a67285a1b9bf648e25f3eb1644c | |||
| origin_name_key_id: | 88d43552bdea6e4bfcbaef0de3ac245e0432be6b019494927fde0743b775f9efe8ca5fef | |||
| 590ef89ec14ebd7cd0598c7c1e045838e7c484713265c10d923d314fa1ef433f | afbf2048890d54618d408a6001fc8fb276f6828c46f4fe1381e9775eec72ee47593df738 | |||
| encrypted_origin_name: b9d946ee16d9114b6d42524a1fe1e5fae9768c45d4b48ce13 | 95d18952440d33756d78caea4bd8218950d35afa6c46c535211eda39da277260cb8dab7c | |||
| 2e758d614911c31c3c1b73a90b63735a0c53f5b94cf176c1a00392618dac727bc287d18 | 00c6840a745e8150a6ee4893e72b6a51382f877f8c05a15e891a2bde07049760f0f09879 | |||
| 78d78b97e47ecaf90a44996d724dd3720e308abbbf04f672bc5a4db573291986be191b06 | ||||
| 03ff521accb6fa081c151c758f3092a89fc6ef591934ff4bc860896c57f83a31b237dd1b | ||||
| 803516c | ||||
| B.2. Anonymous Origin ID Test Vector | B.2. Anonymous Origin ID Test Vector | |||
| The test vector below for the procedure in Section 7 lists the | The test vector below for the procedure in Section 7 lists the | |||
| following values: | following values: | |||
| * sk_client: Client Secret, serialized and represented as a | * sk_client: Client Secret, serialized and represented as a | |||
| hexadecimal string. | hexadecimal string. | |||
| * pk_client: Client Key, serialized and represented as a hexadecimal | * pk_client: Client Key, serialized and represented as a hexadecimal | |||
| skipping to change at page 36, line 14 ¶ | skipping to change at page 40, line 20 ¶ | |||
| * request_blind: The request_blind value computed in Section 7.1.1, | * request_blind: The request_blind value computed in Section 7.1.1, | |||
| represented as a hexadecimal string. | represented as a hexadecimal string. | |||
| * index_key: The index_key value computed in Section 7.3, | * index_key: The index_key value computed in Section 7.3, | |||
| represented as a hexadecimal string. | represented as a hexadecimal string. | |||
| * anon_issuer_origin_id: The anon_issuer_origin_id value computed in | * anon_issuer_origin_id: The anon_issuer_origin_id value computed in | |||
| Section 7.4, represented as a hexadecimal string. | Section 7.4, represented as a hexadecimal string. | |||
| sk_sign: a04e2a1c1a58ed8ca09419fe937507964b640981de40c6c05c14bb547b17830 | sk_sign: f6e6a0c9de38663ca539ff2e6a04e4fca11dc569794dc405e2d17439d6ce4f6 | |||
| d5b18c1acf236482234f0e4ed8a6a36e5 | 7abb2b81a1852e0db993b6a0452eb60d6 | |||
| pk_sign: 03edf9e62abec27bd25f7171dcf24aeb163fbb026381cb634a8c41058b70b74 | pk_sign: 032db7483c710673e6999a5fb2a2c6eac1d891f89bbf58d985ff168d182ad51 | |||
| 083a704177d0aee92a9be2740fd627fa3a4 | 605c4369280efabb7692f661162e683f03c | |||
| sk_origin: abc3bbe04e44502bb3549551a87f4f843b7d6ba2fb789bd82ccebe7e304f0 | sk_origin: 85de5fbbd787da5093da0adb240eba0cc6ea90d72032fc4b6925dd7d0ab1d | |||
| 77f52125a4829747bd5c68a8942500d898c | a1e5ae0be27fe9f59e9ec7e1f1b15b28696 | |||
| request_blind: 0d19ff14f8d24f7cceb377a0f54b27295e270624814293493d264d967 | request_blind: 0698a149fb9d16bcb0a856062f74f9191e82b35e91224a57abce60f5b | |||
| c541b490d7e95ff1fbb40e4f89dfdd9e25ad997 | 79f03a669c6b5e093d57e647865f9fd4305b5a9 | |||
| request_key: 02031027dcbe5e5607749fc6e4e0967cca89d591545c0badc640febc682 | request_key: 0287b0ce6b957111263d8c6126af96d400bd5a9d0b0f62ade15ab789446 | |||
| 50199b87f3e60112a40425a03b9e8ac58d5bb77 | 06c209470ced7086d3c418dd32bf9245fd42678 | |||
| index_key: 03878a9ff9e8bc58c05f8b6e71374d14f81a113f2a6828c4fa482e2d3ea50 | index_key: 03188bec3dc02d2382b5251b6b4fd729d0472bbddf008c5e93b7c12270d9f | |||
| 04c0d790022a71d6437f905fb5d5daf036f52 | 57dde111c861c13be53822a1cebb604946066 | |||
| anon_issuer_origin_id: 3edd83377d6a22f5726dac066ee1864f2c0424cd4868f994b | anon_issuer_origin_id: 9b0f980e5c1142fddb4401e5cd2107a87d22b73753b0d5dc9 | |||
| 2e16ec270f4cc4e382a5b0730ca378797171fa2aec8222f | 3f9a8f5ed2ee7db78163c6a93cc41ae8158d562381c51ee | |||
| Authors' Addresses | Authors' Addresses | |||
| Scott Hendrickson | Scott Hendrickson | |||
| Google LLC | Google LLC | |||
| Email: scott@shendrickson.com | Email: scott@shendrickson.com | |||
| Jana Iyengar | Jana Iyengar | |||
| Fastly | Fastly | |||
| Email: jri@fastly.com | Email: jri@fastly.com | |||
| End of changes. 86 change blocks. | ||||
| 292 lines changed or deleted | 494 lines changed or added | |||
This html diff was produced by rfcdiff 1.48. The latest version is available from http://tools.ietf.org/tools/rfcdiff/ | ||||