idnits 2.17.1 draft-ietf-acme-authority-token-04.txt: Checking boilerplate required by RFC 5378 and the IETF Trust (see https://trustee.ietf.org/license-info): ---------------------------------------------------------------------------- No issues found here. Checking nits according to https://www.ietf.org/id-info/1id-guidelines.txt: ---------------------------------------------------------------------------- No issues found here. Checking nits according to https://www.ietf.org/id-info/checklist : ---------------------------------------------------------------------------- ** There are 4 instances of too long lines in the document, the longest one being 13 characters in excess of 72. Miscellaneous warnings: ---------------------------------------------------------------------------- == The copyright year in the IETF Trust and authors Copyright Line does not match the current year == The document seems to lack the recommended RFC 2119 boilerplate, even if it appears to use RFC 2119 keywords. (The document does seem to have the reference to RFC 2119 which the ID-Checklist requires). -- The document date (November 4, 2019) is 1635 days in the past. Is this intentional? Checking references for intended status: Informational ---------------------------------------------------------------------------- == Missing Reference: 'RFCThis' is mentioned on line 424, but not defined == Unused Reference: 'I-D.ietf-acme-service-provider' is defined on line 458, but no explicit reference was found in the text == Unused Reference: 'I-D.ietf-acme-star' is defined on line 463, but no explicit reference was found in the text == Unused Reference: 'RFC7340' is defined on line 480, but no explicit reference was found in the text == Unused Reference: 'RFC8224' is defined on line 493, but no explicit reference was found in the text == Unused Reference: 'RFC8225' is defined on line 499, but no explicit reference was found in the text == Outdated reference: A later version (-13) exists of draft-ietf-acme-authority-token-tnauthlist-04 Summary: 1 error (**), 0 flaws (~~), 9 warnings (==), 1 comment (--). Run idnits with the --verbose option for more detailed information about the items above. -------------------------------------------------------------------------------- 2 Network Working Group J. Peterson 3 Internet-Draft Neustar 4 Intended status: Informational M. Barnes 5 Expires: May 7, 2020 Independent 6 D. Hancock 7 C. Wendt 8 Comcast 9 November 4, 2019 11 ACME Challenges Using an Authority Token 12 draft-ietf-acme-authority-token-04.txt 14 Abstract 16 Some proposed extensions to the Automated Certificate Management 17 Environment (ACME) rely on proving eligibility for certificates 18 through consulting an external authority that issues a token 19 according to a particular policy. This document specifies a generic 20 Authority Token challenge for ACME which supports subtype claims for 21 different identifiers or namespaces that can be defined separately 22 for specific applications. 24 Status of This Memo 26 This Internet-Draft is submitted in full conformance with the 27 provisions of BCP 78 and BCP 79. 29 Internet-Drafts are working documents of the Internet Engineering 30 Task Force (IETF). Note that other groups may also distribute 31 working documents as Internet-Drafts. The list of current Internet- 32 Drafts is at https://datatracker.ietf.org/drafts/current/. 34 Internet-Drafts are draft documents valid for a maximum of six months 35 and may be updated, replaced, or obsoleted by other documents at any 36 time. It is inappropriate to use Internet-Drafts as reference 37 material or to cite them other than as "work in progress." 39 This Internet-Draft will expire on May 7, 2020. 41 Copyright Notice 43 Copyright (c) 2019 IETF Trust and the persons identified as the 44 document authors. All rights reserved. 46 This document is subject to BCP 78 and the IETF Trust's Legal 47 Provisions Relating to IETF Documents 48 (https://trustee.ietf.org/license-info) in effect on the date of 49 publication of this document. Please review these documents 50 carefully, as they describe your rights and restrictions with respect 51 to this document. Code Components extracted from this document must 52 include Simplified BSD License text as described in Section 4.e of 53 the Trust Legal Provisions and are provided without warranty as 54 described in the Simplified BSD License. 56 Table of Contents 58 1. Introduction . . . . . . . . . . . . . . . . . . . . . . . . 2 59 2. Terminology . . . . . . . . . . . . . . . . . . . . . . . . . 3 60 3. Challenges for an Authority Token . . . . . . . . . . . . . . 3 61 3.1. Token Type Requirements . . . . . . . . . . . . . . . . . 4 62 3.2. Authority Token Scope . . . . . . . . . . . . . . . . . . 4 63 3.3. Binding Challenges . . . . . . . . . . . . . . . . . . . 5 64 4. ATC tkauth-type Registration . . . . . . . . . . . . . . . . 6 65 5. Acquiring a Token . . . . . . . . . . . . . . . . . . . . . . 7 66 5.1. Basic REST Interface . . . . . . . . . . . . . . . . . . 7 67 6. Using an Authority Token in a Challenge . . . . . . . . . . . 8 68 7. Acknowledgements . . . . . . . . . . . . . . . . . . . . . . 10 69 8. IANA Considerations . . . . . . . . . . . . . . . . . . . . . 10 70 9. Security Considerations . . . . . . . . . . . . . . . . . . . 10 71 10. Normative References . . . . . . . . . . . . . . . . . . . . 10 72 Authors' Addresses . . . . . . . . . . . . . . . . . . . . . . . 12 74 1. Introduction 76 ACME [I-D.ietf-acme-acme] is a mechanism for automating certificate 77 management on the Internet. It enables administrative entities to 78 prove effective control over resources like domain names, and 79 automates the process of generating and issuing certificates. 81 In some cases, proving effective control over an identifier requires 82 an attestation from a third party who has authority over the 83 resource, for example, an external policy administrator for a 84 namespace other than the DNS application ACME was originally designed 85 to support. In order to automate the process of issuing certificates 86 for those resources, this specification defines a generic Authority 87 Token challenge that ACME servers can issue in order to require 88 clients to return such a token. The challenge contains a type 89 indication that tells the client what sort of token it needs to 90 acquire. It is expected that the Authority Token challenge will be 91 usable for a variety of identifier types. 93 For example, the system of [I-D.ietf-acme-authority-token-tnauthlist] 94 provides a mechanism that allows service providers to acquire 95 certificates corresponding to a Service Provider Code (SPC) as 96 defined in [RFC8226] by consulting an external authority responsible 97 for those codes. Furthermore, Communications Service Providers 98 (CSPs) can delegate authority over numbers to their customers, and 99 those CSPs who support ACME can then help customers to acquire 100 certificates for those numbering resources with ACME. This can 101 permit number acquisition flows compatible with those shown in 102 [RFC8396]. Another, similar example would a mechanism that permits 103 CSPs to delegate authority for particular telephone numbers to 104 customers, as described in [I-D.ietf-acme-telephone]. 106 2. Terminology 108 In this document, the key words "MUST", "MUST NOT", "REQUIRED", 109 "SHALL", "SHALL NOT", "SHOULD", "SHOULD NOT", "RECOMMENDED", "NOT 110 RECOMMENDED", "MAY", and "OPTIONAL" are to be interpreted as 111 described in [RFC2119]. 113 3. Challenges for an Authority Token 115 Proving that a device on the Internet has effective control over a 116 non-Internet resource is not as straightforward as proving control 117 over an Internet resources like a DNS zone or a web page. Provided 118 that the issuer of identifiers in a namespace, or someone acting on 119 the issuer's behalf, can implement a service that grants Authority 120 Tokens to the people to whom it has issued identifiers, a generic 121 token could be used as a response to an ACME challenge. This 122 specification, therefore, defines an Authority Token issued by an 123 authority over a namespace to an ACME client for delivery to a CA in 124 response to a challenge. Authority over a hierarchical namespace can 125 also be delegated, so that delegates of a root authority can 126 themselves act as Token Authorities for certain types of names. 128 This architecture assumes a trust relationship between CAs and Token 129 Authorities: that CAs are willing to accept the attestation of Token 130 Authorities for particular types of identifiers as sufficient proof 131 to issue a credential. It furthermore assumes that ACME clients have 132 a relationship with Token Authorities which permits them to 133 authenticate and authorize the issuance of Authority Tokens to the 134 proper entities. This ACME challenge has no applicability to 135 identifiers or authorities where those pre-associations cannot be 136 assumed. 138 ACME challenges that support Authority Tokens therefore need to 139 specify the type of token they require; CAs can even provide a hint 140 in their challenges to ACME clients that tells them how to find a 141 Token Authority who can issue tokens for a given namespace. This 142 challenge type thus requires a new "tkauth-type" element, and may 143 optionally supply a "token-authority" designating a location where 144 tokens can be acquired. The set of "tkauth-type" values and the 145 semantic requirements for those tokens are tracked by an IANA 146 registry. 148 3.1. Token Type Requirements 150 The IANA will maintain a registry of tkauth-types under a policy of 151 Specification Required. In order to register a new tkauth-type, 152 specifications must address the following requirements. 154 While Authority Token types do not need to be specific to a 155 namespace, every token must carry enough information for a CA to 156 determine the name that it will issue a certificate for. Some types 157 of Authority Token types might be reusable for a number of different 158 namespaces; other might be specific to a particular type of name. 159 Therefore, in defining tkauth-types, future specifications must 160 indicate how a token conveys to the CA the name(s) that the Token 161 Authority is attesting that the ACME client controls. 163 While nothing precludes use cases where an ACME client is itself a 164 Token Authority, an ACME client will typically need a protocol to 165 request and retrieve an Authority Token. The Token Authority will 166 require certain information from an ACME client in order to ascertain 167 that it is the right entity to request a certificate for a particular 168 name. The protocols used to request an Authority Token MUST convey 169 to the Token Authority the identifier type and value from the ACME 170 challenge, as well as the binding (see Section 3.3), and those MUST 171 be reflected in the Authority Token. A baseline mechanism for how 172 the Token Authority authenticates and authorizes ACME clients to 173 receive Authority Tokens is given in Section 5. 175 Because the assignment of resources can change over time, 176 demonstrations of authority must be regularly refreshed. Definitions 177 of a tkauth-type MUST specify how they manage the freshness of 178 authority assignments. Typically, a CA will expect a regular 179 refreshing of the token. 181 3.2. Authority Token Scope 183 An Authority Token is used to answer a challenge from an ACME server, 184 upon a request for the issuance of a certificate. It could be that 185 the AT is requested from the Token Authority after a challenge has 186 been received, or it could be that the AT was acquired prior to the 187 initial ACME client request. A Token Authority could grant to a 188 client a Token that has the exact same scope as the requested 189 certificate; alternatively, an Authority Token could attest to all of 190 the resources that the client is eligible to receive certificates 191 for, which could be a superset of the scope of the requested 192 certificate. 194 For example, imagine a case where an Authority for DNS names knows 195 that a client is eligible to receive certificates for "example.com" 196 and "example.net". The client asks an ACME server for a certificate 197 for "example.com", the server directs the client to acquire an 198 Authority Token from the Authority. When the client sends an 199 acquisition request (see Section 5) to the Authority, the Authority 200 could issue a token scoped just to "example.com", or a token that 201 attests the client is eligible to receive certificates for both 202 "example.com" or "example.net". The advantage of the latter is that 203 if, at a later time (but one within the expiry of the JWT), the 204 client wanted to acquire a certificate for "example.net", it would 205 not have to return to the Authority, as the Token effectively pre- 206 authorized the issuance of that certificate. 208 Applications of the Authority Token to different identifier types 209 might require different scopes, so registrations of tkauth-types 210 should be clear if and how a scope greater than that of the requested 211 certificate would be conveyed in a token. 213 3.3. Binding Challenges 215 Applications that use the Authority Token need a way to correlate 216 tokens issued by an Authority with the proper ACME client, to prevent 217 replay or cut-and-paste attacks using a token issued for a different 218 purpose. To mitigate this, Authority Tokens contain a binding signed 219 by an Authority; an ACME server can use the binding to determine that 220 a Token presented by a client was in fact granted by the Authority 221 based on a request from the client, and not from some other entity. 223 Binding an Authority Token to a particular ACME account entails that 224 the Token could be reused up until its expiry for multiple challenges 225 issued by an ACME server. This might be a desirable property when 226 using short-lived certificates, for example, or in any cases where 227 the ACME server issues challenges more frequently that an Authority 228 Token can or should issue tokens, or in cases where the Authority 229 Token scope (see Section 3.2) is broad, so certificates with a more 230 narrow scope may periodically be issued. 232 For some identifier types, it may be more appropriate to bind the 233 Authority Token to a nonce specific to the challenge rather than to 234 an ACME account fingerprint. Any specification of the use of the 235 nonce for this purpose is left to the identifier type profile for the 236 Authority Token. 238 4. ATC tkauth-type Registration 240 This draft registers a tkauth-type of "atc", for the Authority Token 241 Challenge. Here the "atc" tkauth-type signifies a standard JWT token 242 [RFC7519] using a JWS-defined signature string [RFC7515]. This may 243 be used for any number of different identifier types given in ACME 244 challenges. The "atc" element (defined below) lists the identifier 245 type used by tokens based on ATC. The use of "atc" is restricted to 246 JWTs, if non-JWT tokens were desired for ACME challenges, a different 247 tkauth-type should be defined for them. 249 For this ACME Authority Token usage of JWT, the payload of the JWT 250 OPTIONALLY contain an "iss" indicating the Token Authority that 251 generated the token, if the "x5u" element in the header does not 252 already convey that information; typically, this will be the same 253 location that appeared in the "token-authority" field of the ACME 254 challenge. In order to satisfy the requirement for replay prevention 255 the JWT MUST contain a "jti" element, and an "exp" claim. In 256 addition to helping to manage replay, the "jti" provides a convenient 257 way to reliably track with the same "atc" Authority Token is being 258 used for multiple challenges over time within its set expiry. 260 The JWT payload must also contain a new JWT claim, "atc", for 261 Authority Token Challenge, which contains three mandatory elements in 262 an array: the identifier type ("tktype"), the identifier value 263 ("tkvalue"), and the binding ("fingerprint"). The identifier type 264 and value are those given in the ACME challenge and conveyed to the 265 Token Authority by the ACME client. Following the example of 266 [I-D.ietf-acme-authority-token-tnauthlist], the "tkvalue" identifier 267 type could be the TNAuthList, with a "tkvalue" as defined in 268 [RFC8226] that the Token Authority is attesting. Practically 269 speaking, that scope may comprise a list of Service Provider Code 270 elements, telephone number range elements, and/or individual 271 telephone numbers. For the purposes of the "atc" tkauth-type, the 272 binding "fingerprint" is assumed to be a fingerprint of the ACME 273 credential for the account used to request the certificate, but the 274 specification of how the binding is generated is left to the 275 identifier type profile for the Authority Token. 277 So for example: 279 { "typ":"JWT", 280 "alg":"ES256", 281 "x5u":"https://authority.example.org/cert"} 282 { 283 "iss":"https://authority.example.org/authz", 284 "exp":1300819380, 285 "jti":"id6098364921", 286 "atc":{"tktype":"TnAuthList","tkvalue":"F83n2a...avn27DN3==","fingerprint": 287 "SHA256 56:3E:CF:AE:83:CA:4D:15:B0:29:FF:1B:71:D3:BA:B9:19:81:F8:50: 288 9B:DF:4A:D4:39:72:E2:B1:F0:B9:38:E3"} } 290 Optionally, the "atc" element may contain a fourth element, "ca". If 291 set to "true", the "ca" element indicates that the Token Authority is 292 granting permission to issue a certification authority certificate 293 rather than an end-entity certificate for the names in question. 294 This permits subordinate delegations from the issued certificate. If 295 the "ca" element is absent, the Token Authority is explicitly 296 withholding permission. The "atc" object in the example above would 297 then look like: 299 "atc":{"tktype":"TnAuthList","tkvalue":"F83n2a...avn27DN3==","ca":true, 300 "fingerprint":"SHA256 56:3E:CF:AE:83:CA:4D:15:B0:29:FF:1B:71:D3:BA:B9:19:81:F8:50: 301 9B:DF:4A:D4:39:72:E2:B1:F0:B9:38:E3"} } 303 Specifications of "tktype" identifier type may define additional 304 optional "atc" elements. 306 5. Acquiring a Token 308 The acquisition of a Authority Token requires a network interface, 309 apart from potential use cases where the entity that acts as an ACME 310 client itself also acts as a Token Authority trusted by the ACME 311 server. Implementations compliant with this specification MUST 312 support an HTTPS REST interface for Authority Token acquisition as 313 described below, though other interfaces MAY be supported as well. 315 5.1. Basic REST Interface 317 In order to request an Authority Token from a Token Authority, a 318 client sends an HTTPS POST request. Different services may organize 319 their web resources in domain-specific ways, but the resource locator 320 should specify the account of the client, an identifier for the 321 service provider, and finally a locator for the token. 323 POST /at/account/:id/token HTTP/1.1 324 Host: authority.example.com 325 Content-Type: application/json 327 The body of the POST request will contain the ATC element that the 328 client is requesting the Token Authority generate. 330 { 331 "atc":{"tktype":"TnAuthList","tkvalue":"F83n2a...avn27DN3==", 332 "fingerprint":"SHA256 56:3E:CF:AE:83:CA:4D:15:B0:29:FF:1B:71:D3:BA:B9:19:81:F8:50: 333 9B:DF:4A:D4:39:72:E2:B1:F0:B9:38:E3"} } 334 } 336 In common use cases, the "tkvalue" in this request is asking that the 337 Token Authority issue a token that attests the entire scope of 338 authority to which the client is entitled. The client may also 339 request an AT with some subset of its own authority via the "tkvalue" 340 element in the ATC object. The way that "tkvalue" is defined will 341 necessarily be specific to the identifier type. For the TNAuthlist 342 identifier type, for example, an object requesting an AT could 343 request authority for only a single telephone number in a way that is 344 defined in the TNAuthList specification. 346 Finally, the JSON object may also contain a optional boolean element 347 "ca" which signifies that the client is requesting that the Token 348 Authority issue an AT with the "ca" flag set, as described in 349 Section 4. 351 After an HTTPS-level challenge to verify the identity of the client 352 and subsequently making an authorization decision, in the success 353 case the Token Authority returns a 200 OK with a body of type 354 "application/json" containing the Authority Token. 356 6. Using an Authority Token in a Challenge 358 Taking the identifier example of TNAuthList from 359 [I-D.ietf-acme-authority-token-tnauthlist], an ACME for this tkauth- 360 type challenge might for example look as follows: 362 HTTP/1.1 200 OK 363 Content-Type: application/json 364 Link: ;rel="directory" 366 { 367 "status": "pending", 369 "identifier": { 370 "type": "TNAuthList", 371 "value": "F83n2a...avn27DN3==" 372 }, 373 "challenges": [ 374 { 375 "type": "tkauth-01", 376 "tkauth-type": "atc", 377 "token-authority": "https://authority.example.org/authz", 378 "url": "https://boulder.example.com/authz/asdf/0" 379 "token": "IlirfxKKXAsHtmzK29Pj8A" } 380 ], 381 } 383 Entities receiving this challenge know that they can, as a proof, 384 acquire an ATC token from the designated Token Authority (specified 385 in the "token-authority" field), and that this authority can provide 386 tokens corresponding to the identifier type of "TNAuthList". 388 Once the ATC has been acquired by the ACME Client, it can be posted 389 back to the URL given by the ACME challenge. 391 POST /acme/authz/asdf/0 HTTP/1.1 392 Host: boulder.example.com 393 Content-Type: application/jose+json 395 { 396 "protected": base64url({ 397 "alg": "ES256", 398 "kid": "https://boulder.example.com/acme/reg/asdf", 399 "nonce": "Q_s3MWoqT05TrdkM2MTDcw", 400 "url": "https://boulder.example.com/acme/authz/asdf/0" 401 }), 402 "payload": base64url({ 403 "atc": "evaGxfADs...62jcerQ" 404 }), 405 "signature": "5wUrDI3eAaV4wl2Rfj3aC0Pp--XB3t4YYuNgacv_D3U" 406 } 408 The "atc" field in this response contains the Authority Token. 410 7. Acknowledgements 412 We would like to thank you for your contributions to this problem 413 statement and framework. 415 8. IANA Considerations 417 This document requests that the IANA registers a new ACME identifier 418 type (per [I-D.ietf-acme-acme]) for the label "atc", for which the 419 reference is [RFCThis]. 421 This document further requests that the IANA create a registry for 422 "token types" as used in these challenges, following the requirements 423 in Section 3.1, pre-populated with the label of "atc" per Section 4 424 with a value of [RFCThis]. 426 9. Security Considerations 428 Per the guidance in [I-D.ietf-acme-acme], ACME transactions MUST use 429 TLS, and similarly the HTTPS REST transactions used to request and 430 acquire authority tokens MUST use TLS. These measures are intended 431 to prevent the capture of Authority Tokens by eavesdroppers. 433 The capture of Authority Tokens by an adversary could enable an 434 attacker to acquire a certificate from a CA. Therefore, all 435 Authority Tokens MUST contain a field that identifies to the CA which 436 ACME client requested the token from the authority; here that is the 437 fingerprint specified in Section 4). All Authority Tokens must 438 specify an expiry (of the token itself as proof for a CA, as opposed 439 to the expiry of the name), and for some application, it may make 440 sense of that expiry to be quite short. Any protocol used to 441 retrieve Authority Tokens from an authority MUST use confidentiality 442 to prevent eavesdroppers from acquiring an Authority Token. 444 10. Normative References 446 [I-D.ietf-acme-acme] 447 Barnes, R., Hoffman-Andrews, J., McCarney, D., and J. 448 Kasten, "Automatic Certificate Management Environment 449 (ACME)", draft-ietf-acme-acme-18 (work in progress), 450 December 2018. 452 [I-D.ietf-acme-authority-token-tnauthlist] 453 Wendt, C., Hancock, D., Barnes, M., and J. Peterson, 454 "TNAuthList profile of ACME Authority Token", draft-ietf- 455 acme-authority-token-tnauthlist-04 (work in progress), 456 September 2019. 458 [I-D.ietf-acme-service-provider] 459 Barnes, M. and C. Wendt, "ACME Identifiers and Challenges 460 for VoIP Service Providers", draft-ietf-acme-service- 461 provider-02 (work in progress), October 2017. 463 [I-D.ietf-acme-star] 464 Sheffer, Y., Lopez, D., Dios, O., Pastor, A., and T. 465 Fossati, "Support for Short-Term, Automatically-Renewed 466 (STAR) Certificates in Automated Certificate Management 467 Environment (ACME)", draft-ietf-acme-star-11 (work in 468 progress), October 2019. 470 [I-D.ietf-acme-telephone] 471 Peterson, J. and R. Barnes, "ACME Identifiers and 472 Challenges for Telephone Numbers", draft-ietf-acme- 473 telephone-01 (work in progress), October 2017. 475 [RFC2119] Bradner, S., "Key words for use in RFCs to Indicate 476 Requirement Levels", BCP 14, RFC 2119, 477 DOI 10.17487/RFC2119, March 1997, 478 . 480 [RFC7340] Peterson, J., Schulzrinne, H., and H. Tschofenig, "Secure 481 Telephone Identity Problem Statement and Requirements", 482 RFC 7340, DOI 10.17487/RFC7340, September 2014, 483 . 485 [RFC7515] Jones, M., Bradley, J., and N. Sakimura, "JSON Web 486 Signature (JWS)", RFC 7515, DOI 10.17487/RFC7515, May 487 2015, . 489 [RFC7519] Jones, M., Bradley, J., and N. Sakimura, "JSON Web Token 490 (JWT)", RFC 7519, DOI 10.17487/RFC7519, May 2015, 491 . 493 [RFC8224] Peterson, J., Jennings, C., Rescorla, E., and C. Wendt, 494 "Authenticated Identity Management in the Session 495 Initiation Protocol (SIP)", RFC 8224, 496 DOI 10.17487/RFC8224, February 2018, 497 . 499 [RFC8225] Wendt, C. and J. Peterson, "PASSporT: Personal Assertion 500 Token", RFC 8225, DOI 10.17487/RFC8225, February 2018, 501 . 503 [RFC8226] Peterson, J. and S. Turner, "Secure Telephone Identity 504 Credentials: Certificates", RFC 8226, 505 DOI 10.17487/RFC8226, February 2018, 506 . 508 [RFC8396] Peterson, J. and T. McGarry, "Managing, Ordering, 509 Distributing, Exposing, and Registering Telephone Numbers 510 (MODERN): Problem Statement, Use Cases, and Framework", 511 RFC 8396, DOI 10.17487/RFC8396, July 2018, 512 . 514 Authors' Addresses 516 Jon Peterson 517 Neustar, Inc. 518 1800 Sutter St Suite 570 519 Concord, CA 94520 520 US 522 Email: jon.peterson@team.neustar 524 Mary Barnes 525 Independent 527 Email: mary.ietf.barnes@gmail.com 529 David Hancock 530 Comcast 532 Email: davidhancock.ietf@gmail.com 534 Chris Wendt 535 Comcast 536 One Comcast Center 537 Philadelphia, PA 19103 538 USA 540 Email: chris-ietf@chriswendt.net