openapi: 3.0.3 info: title: Open Multi-Perspective Issuance Corroboration APIv2 Spec description: |- This is the spec for the Open MPIC Protocol. This API implements a multiple-perspective vantage point system for Domain Control Validation (DCV) and CAA checking. termsOfService: https://tbd contact: email: open-mpic@princeton.edu license: name: MIT License version: 3.9.0 externalDocs: description: Find out more about the project at open-mpic.org url: https://open-mpic.org/ servers: - url: https://your-api.example.com/ paths: /mpic: post: summary: Perform MPIC. requestBody: description: Parameters for MPIC content: application/json: schema: oneOf: - $ref: '#/components/schemas/DCVParams' - $ref: '#/components/schemas/CAAParams' example: check_type: caa domain_or_ip_target: example.com trace_identifier: ac2d0392-8699-4037-a0e0-0a6db1ff8b89 orchestration_parameters: perspective_count: 6 quorum_count: 4 max_attempts: 3 caa_check_parameters: certificate_type: tls-server caa_domains: - letsencrypt.org required: true responses: '200': description: Successfully processed request. Validation may-or-may-not be successful. Check response body. content: application/json: schema: oneOf: - $ref: '#/components/schemas/CAAResponse' - $ref: '#/components/schemas/DCVResponse' example: request_orchestration_parameters: perspective_count: 6 quorum_count: 4 max_attempts: 3 actual_orchestration_parameters: perspective_count: 6 quorum_count: 4 attempt_count: 1 is_valid: true domain_or_ip_target: example.com mpic_completed: true check_type: caa perspectives: - perspective_code: us-east-2 check_passed: true check_completed: true errors: null timestamp_ns: 1725822089205545500 check_type: caa details: caa_record_present: false found_at: null response: null - perspective_code: eu-west-2 check_passed: true check_completed: true errors: null timestamp_ns: 1725822089670721500 check_type: caa details: caa_record_present: false found_at: null response: null - perspective_code: ap-northeast-2 check_passed: true check_completed: true errors: null timestamp_ns: 1725822089672918800 check_type: caa details: caa_record_present: false found_at: null response: null - perspective_code: ap-southeast-1 check_passed: true check_completed: true errors: null timestamp_ns: 1725822089693880600 check_type: caa details: caa_record_present: false found_at: null response: null - perspective_code: eu-central-1 check_passed: true check_completed: true errors: null timestamp_ns: 1725822089754537000 check_type: caa details: caa_record_present: false found_at: null response: null - perspective_code: us-west-2 check_passed: true check_completed: true errors: null timestamp_ns: 1725822089912938200 check_type: caa details: caa_record_present: false found_at: null response: null caa_check_parameters: null trace_identifier: ac2d0392-8699-4037-a0e0-0a6db1ff8b89 '400': description: Bad Request '500': description: Internal Server Error components: schemas: CheckType: type: string enum: ['dcv', 'caa'] CheckTypeDCV: type: string enum: ['dcv'] CheckTypeCAA: type: string enum: ['caa'] DomainOrIPTarget: type: string example: "example.com" description: "The FQDN (no trailing .) or public IP for which control is being validated. Interpreted as a wildcard domain if path begins with '*.'" DcvCheckParameters: oneOf: - $ref: '#/components/schemas/AcmeHTTP01ValidationParameters' - $ref: '#/components/schemas/AcmeDNS01ValidationParameters' - $ref: '#/components/schemas/AcmeTLSALPN01ValidationParameters' - $ref: '#/components/schemas/WebsiteChangeValidationParameters' - $ref: '#/components/schemas/DNSChangeValidationParameters' - $ref: '#/components/schemas/DNSPersistentValidationParameters' - $ref: '#/components/schemas/ContactPhoneValidationParameters' - $ref: '#/components/schemas/ContactEmailValidationParameters' - $ref: '#/components/schemas/IpAddressValidationParameters' discriminator: propertyName: validation_method mapping: acme-http-01: '#/components/schemas/AcmeHTTP01ValidationParameters' acme-dns-01: '#/components/schemas/AcmeDNS01ValidationParameters' acme-tls-alpn-01: '#/components/schemas/AcmeTLSALPN01ValidationParameters' website-change: '#/components/schemas/WebsiteChangeValidationParameters' dns-change: '#/components/schemas/DNSChangeValidationParameters' dns-persistent: '#/components/schemas/DNSPersistentValidationParameters' contact-phone-txt: '#/components/schemas/ContactPhoneValidationParameters' contact-phone-caa: '#/components/schemas/ContactPhoneValidationParameters' contact-email-txt: '#/components/schemas/ContactEmailValidationParameters' contact-email-caa: '#/components/schemas/ContactEmailValidationParameters' ip-address: '#/components/schemas/IpAddressValidationParameters' reverse-address-lookup: '#/components/schemas/BaseDNSChangeValidationParameters' CaaCheckParameters: type: object properties: certificate_type: type: string enum: ['tls-server', 's-mime'] # doc: https://swagger.io/docs/specification/data-models/enums/ description: "Specifies the type of certificate the CA will be signing for the applicant as it pertains to parsing CAA records." caa_domains: type: array example: - "letsencrypt.org" description: "Valid CAA domain names in issue or issuewild (e.g., CAA 0 issue \"letsencrypt.org\") tags that permit issuance by the calling CA. If left empty, the default configured CAA domain name(s) are used." items: type: string allow_lookup_failure: type: boolean example: false default: false description: "If true, a failure to complete the CAA lookup (DNS resolution failure, specifically a DNS resolution timeout or SERVFAIL) will be treated as if no CAA records were found (that is, as permission to issue). If false, a failure to complete the CAA lookup will result in a failed check. Defaults to False. Use with great caution! This is intended to accommodate a carve-out in the CA/Browser Forum Baseline Requirements for CAA checking that allows CAA lookup failures to be treated as permission to issue in certain, narrow circumstances. Because this option weakens a security check, it must be used with great caution, and the Issuing Certificate Authority (CA), which utilizes the MPIC client, is entirely responsible for both enabling this feature and validating that the specific circumstances of the DNS failure justify overriding the standard security requirement." ValidationMethod: description: "The type of domain control validation the network perspectives should use." type: string enum: ['acme-dns-01', 'acme-http-01', 'acme-tls-alpn-01', 'contact-phone-txt', 'contact-phone-caa', 'contact-email-txt', 'contact-email-caa', 'dns-change', 'ip-address', 'reverse-address-lookup', 'website-change'] # doc: https://swagger.io/docs/specification/data-models/enums/ TraceIdentifier: type: string example: 'ac2d0392-8699-4037-a0e0-0a6db1ff8b89' description: "A optional unique identifier for the request. This identifier is used to track the request through the system and can be used to correlate the request with other systems." HTTPHeaders: type: object example: {"User-Agent": "myCA.example.com"} description: "An optional dictionary of HTTP headers to be sent with the request." additionalProperties: type: string nullable: true BaseValidationParameters: type: object required: - validation_method properties: validation_method: $ref: '#/components/schemas/ValidationMethod' WebsiteChangeValidationParameters: allOf: - $ref: '#/components/schemas/BaseValidationParameters' - type: object required: - http_token_path - challenge_value properties: http_token_path: type: string example: 'challenge.html' description: "The path to check for the challenge token. The full URL used to retrieve the expected value is constructed via the syntax (url scheme) + \"://\" + identifier + \"/.well-known/pki-validation/\" + path." challenge_value: type: string example: 'challenge_token' description: "The expected value to be observed at this URL. The API ensures the challenge value is contained in the response webpage obtained from the domain." match_regex: type: string example: '^\w*TOKENVAL\w*$' description: "An optional regex string which must have a match in the returned webpage contents for the challenge to be successful. This is checked in addition to ensuring the token is contained in the webpage." nullable: true url_scheme: description: "HTTP (port 80) or HTTPS (port 443). If not specified, the protocol is assumed to be HTTP." type: string enum: ['http', 'https'] nullable: true require_exact_case: type: boolean example: true default: true description: "Controls how the challenge_value is compared against the retrieved webpage contents when checking that it is contained as a substring. If true, the comparison is case-sensitive: the challenge_value MUST appear with exactly the same characters and case. If false, the containment check is case-insensitive using ASCII case-folding for letters A–Z/a–z, while still requiring the same character sequence ignoring case. This flag does not affect how match_regex is evaluated. Defaults to true." http_headers: $ref: '#/components/schemas/HTTPHeaders' AcmeHTTP01ValidationParameters: allOf: - $ref: '#/components/schemas/BaseValidationParameters' - type: object required: - token - key_authorization properties: token: type: string example: 'ACME_TOKEN' description: "The token for the ACME http-01 challenge as described in RFC 8555 Section 8.3." key_authorization: type: string example: 'key_authorization' description: "The key authorization to be sent in the HTTP GET body as described in RFC 8555 Section 8.1. The value will be checked by stripping trailing whitespace from the response and then performing an equality check on the two strings." http_headers: $ref: '#/components/schemas/HTTPHeaders' BaseDNSChangeValidationParameters: allOf: - $ref: '#/components/schemas/BaseValidationParameters' - type: object required: - challenge_value properties: challenge_value: type: string example: 'challenge_token' description: "The expected value to be observed as a response to this DNS challenge. The challenge will be completed successfully if the challenge_value is observed within one of the RDATA fields associated with this DNS record type at the FQDN." DNSChangeValidationParameters: allOf: - $ref: '#/components/schemas/BaseDNSChangeValidationParameters' - type: object required: - dns_record_type properties: dns_record_type: type: string enum: ['CNAME', 'TXT', 'CAA'] description: "The DNS record type to be queried." dns_name_prefix: type: string example: '_dcv' description: "The domain label prefix where to look for the challenge_value. If label is not an empty string, the FQDN which will be queried for the challenge_value is constructed via the syntax label + \".\" + domain_or_ip_target + \".\". If label is an empty string the query is sent directly to domain_or_ip_target + \".\". domain_or_ip_target MUST be a domain to use this method." nullable: true require_exact_match: type: boolean example: true default: false description: "Whether the observed DNS record value MUST be exactly equal to challenge_value, as opposed to only containing it as a substring. If true, the comparison is performed on the entire record value. If false, the challenge_value MAY appear anywhere as a substring of the record value. Case-sensitivity for both equality and substring comparisons is controlled exclusively by require_exact_case. Defaults to false." require_exact_case: type: boolean example: true default: true description: "Whether string comparison between challenge_value and the observed DNS record value is case-sensitive. If true, ASCII letters MUST match exactly in case. If false, comparison is case-insensitive: both strings are normalized to the same case (e.g., converted to lowercase) before performing either equality or substring comparison. This flag applies both when require_exact_match is true (full-string comparison) and when it is false (substring comparison). Defaults to true." DNSPersistentValidationParameters: allOf: - $ref: '#/components/schemas/BaseValidationParameters' - type: object required: - issuer_domain_names - expected_account_uri properties: issuer_domain_names: type: array example: - "ca.example.com" - "ca.example.org" description: "The list of accepted issuer domains for the subscriber. If the observed issuer domain in the record contains any of these domains, that portion of the challenge is considered passed." expected_account_uri: type: string example: "https://ca.example.com/acct/12345" description: "The expected account URI for the subscriber. The challenge will be completed successfully if the expected account URI is observed as the value for the accounturi parameter in the TXT record at the target challenge domain." AcmeDNS01ValidationParameters: allOf: - $ref: '#/components/schemas/BaseValidationParameters' - type: object required: - key_authorization_hash properties: key_authorization_hash: type: string description: "The base64url encoding of the SHA-256 digest [FIPS180-4] of the key authorization as defined in RFC 8555 Section 8.1. The challenge will be completed successfully if the expected-challenge is observed byte-for-byte identical in one of the RDATA fields found with the query for _acme-challenge.{domain_or_ip_target} IN TXT. To use this method, domain_or_ip_target MUST be a domain." AcmeTLSALPN01ValidationParameters: allOf: - $ref: '#/components/schemas/BaseValidationParameters' - type: object required: - key_authorization_hash properties: key_authorization_hash: type: string description: "The SHA-256 digest [FIPS180-4] of the key authorization as defined in RFC 8555 Section 8.1." ContactEmailValidationParameters: allOf: - $ref: '#/components/schemas/BaseDNSChangeValidationParameters' ContactPhoneValidationParameters: allOf: - $ref: '#/components/schemas/BaseDNSChangeValidationParameters' IpAddressValidationParameters: allOf: - $ref: '#/components/schemas/BaseDNSChangeValidationParameters' - type: object required: - dns_record_type properties: dns_record_type: type: string enum: ['A', 'AAAA'] description: "The DNS record type to be queried." HTTPMethodCheckResponseDetails: type: object description: "Details relevant to an HTTP-based DCV check result from a perspective." required: - response_history - response_url - response_status_code - response_page properties: response_history: nullable: true description: "An array of all redirects that were given before the final response was found. It can be empty if there are no redirects. Otherwise it contains a list of objects with status codes and the URLs that returned those status codes in order starting with the original challenge URL." type: array items: type: object required: - response_status_code - response_url properties: response_status_code: type: integer description: "The status code retrieved from this URL." response_url: type: string description: "The URL that returned this status code." response_url: nullable: true description: "The URL that returned the final response. This is equal to the original challenge URL if there are no redirects." type: string response_status_code: nullable: true type: integer description: "The status code returned by the final URL." response_page: nullable: true type: string description: "The first 100 bytes of the page returned at the final URL. If the page is less than 100 bytes, the entire page content is returned." resolved_ip: nullable: true type: string description: "The IP address used to communicate with the domain_or_ip_target." validation_method: $ref: '#/components/schemas/ValidationMethod' DNSMethodCheckResponseDetails: description: "Details relevant to a DNS-based DCV check result from a perspective." type: object required: - records_seen - response_code - ad_flag - found_at properties: records_seen: type: array description: "A list of the records in the RRset retrieved for the DNS query. Empty list is acceptable if there are no RRsets" items: type: string nullable: true response_code: type: integer description: "The DNS response code (RCODE) resulting from the query." nullable: true ad_flag: type: boolean description: "Whether or not the recursive resolver set the Authenticated Data (AD) flag in its DNS response." nullable: true found_at: type: string description: "The domain where the DNS records were resolved when performing the DNS lookup for the identifier. (DNS records may be hosted at parent zone of identifier.)" nullable: true cname_chain: type: array items: type: string description: "Any CNAMEs that were followed to obtain the final DNS lookup. May be an empty list if no CNAMEs were found. The last domain in the list is the domain that contained the final record. Not applicable if the challenge record type is CNAME." nullable: true TLSALPNMethodCheckResponseDetails: description: "Details relevant to a TLS-ALPN check." type: object required: - common_name properties: common_name: type: string description: "The common name of the cert observed at the server." BaseOrchestrationParameters: type: object properties: perspective_count: type: integer example: 6 description: "The number of vantage points to use for this request. Allows the implementing API endpoint flexibility in picking vantage points so long as vantage points are picked from at least two different Regional Internet Registry service regions if possible. Vantage point selection by the implementing server MUST be deterministic for a particular \"domain\" value such that requests with the same domain value to the same API endpoint always result in the same vantage point usage (presuming the API endpoint was not reconfigured between requests). Implementations SHOULD also make vantage point selection non-predictable (e.g., using a secret keyed cryptographic hash) so that an outside entity without access to the internal configuration of the API endpoint cannot predict which vantage points will be used for a specified domain." quorum_count: type: integer example: 5 description: "The number of network perspectives that must successfully complete the validation methods specified. As long as this value is greater than 2, the API will enforce that successful network perspectives must be spread across two Regional Internet Registry service regions (so long as there is a perspective used in at least two different Regional Internet Registry service regions). A quorum count of 0 instructs the API to operate in a monitoring only mode and always return success = true even if no vantage points succeed. In general 0 <= quorum count <= perspectives-count." RequestOrchestrationParameters: allOf: - $ref: '#/components/schemas/BaseOrchestrationParameters' - type: object properties: max_attempts: type: integer example: 3 description: "The number of times MPIC should be attempted before returning a failure. If the number of attempts is exceeded, the API will return a failure response. An implementation may choose to retry with the same or different perspectives each time. It is recommended implementations cap the number of distinct sets of perspectives that will ever validate a particular identifier to avoid adversaries retrying many times in the interest of getting favorable perspective sets." nullable: true cohort_for_single_attempt: type: integer example: 2 description: "The specific cohort (indicating ordinal number; that is, 1 for first, 2 for second, and so forth) of perspectives to use for a single-attempt corroboration. Only valid in MPIC implementations that group available perspectives into disjoint (non-overlapping) sets or \"cohorts,\" with each cohort being a valid set of perspectives to use for an MPIC check, based on parameters such as perspective_count. Must be within the number of valid perspective \"cohorts\" that would be created from available perspectives as part of MPIC coordination. Intended for MPIC client implementations that wish to perform custom orchestration of MPIC checks against multiple disjoint sets of perspectives. If set, max_attempts will automatically be set to 1 for the MPIC operation. Defaults to None." nullable: true EffectiveOrchestrationParameters: allOf: - $ref: '#/components/schemas/BaseOrchestrationParameters' - type: object properties: attempt_count: type: integer example: 2 description: "The number of times MPIC has been retried before either completing successfully or reaching the maximum number of allowed attempts." BaseCheckResponse: type: object required: - check_completed - check_passed - timestamp_ns properties: check_passed: type: boolean example: True description: "Indicator for whether the check was successful at this perspective." timestamp_ns: type: integer format: int64 description: "The epoch timestamp in nanoseconds of the check." check_completed: type: boolean description: "A boolean indicating if the check was completed without errors. If false, the check_passed field may need to be interpreted as not valid." errors: type: array items: type: object properties: error_type: type: string example: "validation:acme-dns-01" description: "The type of the error in the format \"type:subtype\". If associated with a validation method the error type should read \"validation:affected-method\"." error_message: type: string example: "Observed token 1234 instead of expected token 4567 at _acme-challenge.example.com in record type TXT." description: "An error message explaining the observed error." CheckResponseDCV: allOf: - $ref: '#/components/schemas/BaseCheckResponse' - type: object required: - check_type - details properties: check_type: $ref: '#/components/schemas/CheckTypeDCV' details: oneOf: - $ref: '#/components/schemas/HTTPMethodCheckResponseDetails' - $ref: '#/components/schemas/DNSMethodCheckResponseDetails' - $ref: '#/components/schemas/TLSALPNMethodCheckResponseDetails' PerspectiveResponsesDCV: type: array description: "Detailed responses from the result of the validation attempt at each perspective." items: type: object required: - perspective_code - check_response properties: perspective_code: type: string example: "us-east-2" description: "A unique identifier for the perspective used." check_response: $ref: '#/components/schemas/CheckResponseDCV' PreviousAttemptResultsDCV: type: array description: "An array of previous attempts to validate the domain. This array is empty if this is the first attempt." items: $ref: '#/components/schemas/PerspectiveResponsesDCV' nullable: true CheckResponseCAA: allOf: - $ref: '#/components/schemas/BaseCheckResponse' - type: object required: - check_type - details properties: check_type: $ref: '#/components/schemas/CheckTypeCAA' details: type: object required: - caa_record_present - found_at - records_seen description: "Details relating to the CAA lookup at this perspective. Implementations may choose to expose additional details or omit details depending on the result." properties: caa_record_present: description: "Indicates if a record was found when resolving CAA records for an identifier." type: boolean found_at: description: "The domain where CAA records were resolved when performing CAA lookup for identifier. (CAA records may be hosted at parent zone of identifier.)" type: string nullable: true records_seen: description: "A list of the records in the RRset retrieved for the CAA lookup. Empty list is acceptable if there are no RRsets" type: string nullable: true PerspectiveResponsesCAA: type: array description: "Detailed responses from the result of the CAA check at each perspective." items: type: object required: - perspective_code - check_response properties: perspective_code: type: string example: "us-east-2" description: "A unique identifier for the perspective used." check_response: $ref: '#/components/schemas/CheckResponseCAA' PreviousAttemptResultsCAA: type: array description: "An array of previous attempts to check CAA. This array is empty if this is the first attempt." items: $ref: '#/components/schemas/PerspectiveResponsesCAA' nullable: true BaseParams: type: object required: - domain_or_ip_target properties: domain_or_ip_target: $ref: '#/components/schemas/DomainOrIPTarget' orchestration_parameters: $ref: '#/components/schemas/RequestOrchestrationParameters' trace_identifier: $ref: '#/components/schemas/TraceIdentifier' DCVParams: allOf: - $ref: '#/components/schemas/BaseParams' - type: object required: - check_type - dcv_check_parameters properties: check_type: $ref: '#/components/schemas/CheckTypeDCV' dcv_check_parameters: $ref: '#/components/schemas/DcvCheckParameters' CAAParams: allOf: - $ref: '#/components/schemas/BaseParams' - type: object required: - check_type properties: check_type: $ref: '#/components/schemas/CheckTypeCAA' caa_check_parameters: $ref: '#/components/schemas/CaaCheckParameters' BaseMPICResponse: type: object required: - request_orchestration_parameters - actual_orchestration_parameters - domain_or_ip_target - is_valid - mpic_completed properties: domain_or_ip_target: $ref: '#/components/schemas/DomainOrIPTarget' is_valid: type: boolean description: "A boolean indicating if corroboration was successful." request_orchestration_parameters: allOf: - $ref: '#/components/schemas/RequestOrchestrationParameters' nullable: true actual_orchestration_parameters: $ref: '#/components/schemas/EffectiveOrchestrationParameters' trace_identifier: $ref: '#/components/schemas/TraceIdentifier' mpic_completed: type: boolean description: "A boolean indicating if the MPIC process completed without errors." DCVResponse: allOf: - $ref: '#/components/schemas/BaseMPICResponse' - type: object required: - check_type - dcv_check_parameters - perspectives properties: check_type: $ref: '#/components/schemas/CheckTypeDCV' dcv_check_parameters: $ref: '#/components/schemas/DcvCheckParameters' perspectives: $ref: '#/components/schemas/PerspectiveResponsesDCV' previous_attempt_results: $ref: '#/components/schemas/PreviousAttemptResultsDCV' CAAResponse: allOf: - $ref: '#/components/schemas/BaseMPICResponse' - type: object required: - check_type - caa_check_parameters - perspectives properties: check_type: $ref: '#/components/schemas/CheckTypeCAA' caa_check_parameters: allOf: - $ref: '#/components/schemas/CaaCheckParameters' nullable: true perspectives: $ref: '#/components/schemas/PerspectiveResponsesCAA' previous_attempt_results: $ref: '#/components/schemas/PreviousAttemptResultsCAA'