diff --git a/letsencrypt/client/acme.py b/letsencrypt/client/acme.py index 169fdc090..3a419c8f1 100755 --- a/letsencrypt/client/acme.py +++ b/letsencrypt/client/acme.py @@ -5,266 +5,19 @@ import json, jsonschema -schemas = { -"authorization": """{ - "id": "https://letsencrypt.org/schema/01/authorization#", - "$schema": "http://json-schema.org/draft-04/schema#", - "description": "Schema for an authorization message", - "type": "object", - "required": ["type"], - "properties": { - "type" : { - "enum" : [ "authorization" ] - }, - "recoveryToken" : { - "type": "string" - }, - "identifier" : { - "type": "string" - }, - "jwk": { - "type": "object" - } - } -}""", - -"authorizationRequest": """{ - "id": "https://letsencrypt.org/schema/01/authorizationRequest#", - "$schema": "http://json-schema.org/draft-04/schema#", - "description": "Schema for an authorizationRequest message", - "type": "object", - "required": ["type", "sessionID", "nonce", "signature", "responses"], - "properties": { - "type" : { - "enum" : [ "authorizationRequest" ] - }, - "sessionID" : { - "type" : "string" - }, - "nonce" : { - "type": "string" - }, - "signature" : { - "type": "object" - }, - "responses": { - "type": "array", - "minItems": 1, - "items": { - "anyOf": [ - { "type": "object" }, - { "type": "null" } - ] - } - }, - "contact": { - "type": "array", - "minItems": 1, - "items": { - "type": "string" - } - } - } -}""", - -"certificate": """{ - "id": "https://letsencrypt.org/schema/01/certificate#", - "$schema": "http://json-schema.org/draft-04/schema#", - "description": "Schema for a certificate message", - "type": "object", - "required": ["type", "certificate"], - "properties": { - "type" : { - "enum" : [ "certificate" ] - }, - "certificate" : { - "type" : "string" - }, - "chain" : { - "type": "array", - "minItems": 1, - "items": { - "type": "string" - } - }, - "refresh" : { - "type": "string" - } - } -}""", - -"certificateRequest": """{ - "id": "https://letsencrypt.org/schema/01/certificateRequest#", - "$schema": "http://json-schema.org/draft-04/schema#", - "description": "Schema for a certificateRequest message", - "type": "object", - "required": ["type", "csr", "signature"], - "properties": { - "type" : { - "enum" : [ "certificateRequest" ] - }, - "csr" : { - "type" : "string" - }, - "signature" : { - "type": "object" - } - } -}""", - -"challenge": """{ - "id": "https://letsencrypt.org/schema/01/challenge#", - "$schema": "http://json-schema.org/draft-04/schema#", - "description": "Schema for a challenge message", - "type": "object", - "required": ["type", "sessionID", "nonce", "challenges"], - "properties": { - "type" : { - "enum" : [ "challenge" ] - }, - "sessionID" : { - "type" : "string" - }, - "nonce" : { - "type": "string" - }, - "challenges": { - "type": "array", - "minItems": 1, - "items": { - "type": "object" - } - }, - "combinations": { - "type": "array", - "minItems": 1, - "items": { - "type": "array", - "minItems": 1, - "items": { - "type": "integer" - } - } - } - } -}""", - -"challengeRequest": """{ - "id": "https://letsencrypt.org/schema/01/challengeRequest#", - "$schema": "http://json-schema.org/draft-04/schema#", - "description": "Schema for a challengeRequest message", - "type": "object", - "required": ["type", "identifier"], - "properties": { - "type" : { - "enum" : [ "challengeRequest" ] - }, - "identifier" : { - "type": "string" - } - } -}""", - -"defer": """{ - "id": "https://letsencrypt.org/schema/01/defer#", - "$schema": "http://json-schema.org/draft-04/schema#", - "description": "Schema for a defer message", - "type": "object", - "required": ["type", "token"], - "properties": { - "type" : { - "enum" : [ "defer" ] - }, - "token" : { - "type": "string" - }, - "interval" : { - "type": "integer" - }, - "message": { - "type": "string" - } - } -}""", - -"error": """{ - "id": "https://letsencrypt.org/schema/01/error#", - "$schema": "http://json-schema.org/draft-04/schema#", - "description": "Schema for an error message", - "type": "object", - "required": ["type", "error"], - "properties": { - "type" : { - "enum" : [ "error" ] - }, - "error" : { - "enum" : [ "malformed", "unauthorized", "serverInternal", "nonSupported", "unknown", "badCSR" ] - }, - "message" : { - "type": "string" - }, - "moreInfo": { - "type": "string" - } - } -}""", - -"revocation": """{ - "id": "https://letsencrypt.org/schema/01/revocation#", - "$schema": "http://json-schema.org/draft-04/schema#", - "description": "Schema for a revocation message", - "type": "object", - "required": ["type"], - "properties": { - "type" : { - "enum" : [ "revocation" ] - } - } -}""", - -"revocationRequest": """{ - "id": "https://letsencrypt.org/schema/01/revocationRequest#", - "$schema": "http://json-schema.org/draft-04/schema#", - "description": "Schema for a revocationRequest message", - "type": "object", - "required": ["type", "certificate", "signature"], - "properties": { - "type" : { - "enum" : [ "revocationRequest" ] - }, - "certificate" : { - "type" : "string" - }, - "signature" : { - "type": "object" - } - } -}""", - -"statusRequest": """{ - "id": "https://letsencrypt.org/schema/01/statusRequest#", - "$schema": "http://json-schema.org/draft-04/schema#", - "description": "Schema for a statusRequest message", - "type": "object", - "required": ["type", "token"], - "properties": { - "type" : { - "enum" : [ "statusRequest" ] - }, - "token" : { - "type": "string" - } - } -}""" +schemata = {schema: json.load(open("letsencrypt/client/schemata/%s.json" % schema)) for schema in [ + "authorization", "authorizationRequest", "certificate", "certificateRequest", + "challenge", "challengeRequest", "defer", "error", "revocation", + "revocationRequest", "statusRequest"] } -schemas = {name: json.loads(schema) for name, schema in schemas.iteritems()} - def acme_object_validate(j): j = json.loads(j) if not isinstance(j, dict): raise jsonschema.ValidationError("this is not a dictionary object") if not j.has_key("type"): raise jsonschema.ValidationError("missing type field") - jsonschema.validate(j, schemas[j["type"]]) + if not schemata.has_key(j["type"]): + raise jsonschema.ValidationError("unknown type %s" % j["type"]) + jsonschema.validate(j, schemata[j["type"]]) diff --git a/letsencrypt/client/schemata/authorization.json b/letsencrypt/client/schemata/authorization.json new file mode 100644 index 000000000..59877b648 --- /dev/null +++ b/letsencrypt/client/schemata/authorization.json @@ -0,0 +1,21 @@ +{ + "id": "https://letsencrypt.org/schema/01/authorization#", + "$schema": "http://json-schema.org/draft-04/schema#", + "description": "Schema for an authorization message", + "type": "object", + "required": ["type"], + "properties": { + "type" : { + "enum" : [ "authorization" ] + }, + "recoveryToken" : { + "type": "string" + }, + "identifier" : { + "type": "string" + }, + "jwk": { + "$ref": "file:letsencrypt/client/schemata/jwk.json" + } + } +} diff --git a/letsencrypt/client/schemata/authorizationRequest.json b/letsencrypt/client/schemata/authorizationRequest.json new file mode 100644 index 000000000..a0d198333 --- /dev/null +++ b/letsencrypt/client/schemata/authorizationRequest.json @@ -0,0 +1,38 @@ +{ + "id": "https://letsencrypt.org/schema/01/authorizationRequest#", + "$schema": "http://json-schema.org/draft-04/schema#", + "description": "Schema for an authorizationRequest message", + "type": "object", + "required": ["type", "sessionID", "nonce", "signature", "responses"], + "properties": { + "type" : { + "enum" : [ "authorizationRequest" ] + }, + "sessionID" : { + "type" : "string" + }, + "nonce" : { + "type": "string" + }, + "signature" : { + "$ref": "file:letsencrypt/client/schemata/signature.json" + }, + "responses": { + "type": "array", + "minItems": 1, + "items": { + "anyOf": [ + { "$ref": "file:letsencrypt/client/schemata/responseobject.json" }, + { "type": "null" } + ] + } + }, + "contact": { + "type": "array", + "minItems": 1, + "items": { + "type": "string" + } + } + } +} diff --git a/letsencrypt/client/schemata/certificate.json b/letsencrypt/client/schemata/certificate.json new file mode 100644 index 000000000..1d4e98947 --- /dev/null +++ b/letsencrypt/client/schemata/certificate.json @@ -0,0 +1,25 @@ +{ + "id": "https://letsencrypt.org/schema/01/certificate#", + "$schema": "http://json-schema.org/draft-04/schema#", + "description": "Schema for a certificate message", + "type": "object", + "required": ["type", "certificate"], + "properties": { + "type" : { + "enum" : [ "certificate" ] + }, + "certificate" : { + "type" : "string" + }, + "chain" : { + "type": "array", + "minItems": 1, + "items": { + "type": "string" + } + }, + "refresh" : { + "type": "string" + } + } +} diff --git a/letsencrypt/client/schemata/certificateRequest.json b/letsencrypt/client/schemata/certificateRequest.json new file mode 100644 index 000000000..0ea5b83d7 --- /dev/null +++ b/letsencrypt/client/schemata/certificateRequest.json @@ -0,0 +1,19 @@ +{ + "id": "https://letsencrypt.org/schema/01/certificateRequest#", + "$schema": "http://json-schema.org/draft-04/schema#", + "description": "Schema for a certificateRequest message", + "type": "object", + "required": ["type", "csr", "signature"], + "properties": { + "type" : { + "enum" : [ "certificateRequest" ] + }, + "csr" : { + "type" : "string" , + "pattern": "^[-_=0-9A-Za-z]+$" + }, + "signature" : { + "$ref": "file:letsencrypt/client/schemata/signature.json" + } + } +} diff --git a/letsencrypt/client/schemata/challenge.json b/letsencrypt/client/schemata/challenge.json new file mode 100644 index 000000000..92e22424b --- /dev/null +++ b/letsencrypt/client/schemata/challenge.json @@ -0,0 +1,36 @@ +{ + "id": "https://letsencrypt.org/schema/01/challenge#", + "$schema": "http://json-schema.org/draft-04/schema#", + "description": "Schema for a challenge message", + "type": "object", + "required": ["type", "sessionID", "nonce", "challenges"], + "properties": { + "type" : { + "enum" : [ "challenge" ] + }, + "sessionID" : { + "type" : "string" + }, + "nonce" : { + "type": "string" + }, + "challenges": { + "type": "array", + "minItems": 1, + "items": { + "$ref": "file:letsencrypt/client/schemata/challengeobject.json" + } + }, + "combinations": { + "type": "array", + "minItems": 1, + "items": { + "type": "array", + "minItems": 1, + "items": { + "type": "integer" + } + } + } + } +} diff --git a/letsencrypt/client/schemata/challengeRequest.json b/letsencrypt/client/schemata/challengeRequest.json new file mode 100644 index 000000000..0762fa9c8 --- /dev/null +++ b/letsencrypt/client/schemata/challengeRequest.json @@ -0,0 +1,15 @@ +{ + "id": "https://letsencrypt.org/schema/01/challengeRequest#", + "$schema": "http://json-schema.org/draft-04/schema#", + "description": "Schema for a challengeRequest message", + "type": "object", + "required": ["type", "identifier"], + "properties": { + "type" : { + "enum" : [ "challengeRequest" ] + }, + "identifier" : { + "type": "string" + } + } +} diff --git a/letsencrypt/client/schemata/challengeobject.json b/letsencrypt/client/schemata/challengeobject.json new file mode 100644 index 000000000..b760e246e --- /dev/null +++ b/letsencrypt/client/schemata/challengeobject.json @@ -0,0 +1,130 @@ +{ + "id": "https://lets-encrypt.org/schema/01/challengeobject#", + "$schema": "http://json-schema.org/draft-04/schema#", + "description": "Subschema for an individual challenge (within challenge)", + "anyOf": [ + { "type": "object", + "required": ["type", "token"], + "properties": { + "type": { + "enum" : [ "simpleHttps" ] + }, + "token": { + "type": "string" + } + } + }, + { "type": "object", + "required": ["type", "r", "nonce"], + "properties": { + "type": { + "enum" : [ "dvsni" ] + }, + "r": { + "type" : [ "string" ], + "pattern": "^[-_=0-9A-Za-z]+$" + }, + "nonce": { + "type": "string", + "pattern": "^[0-9a-f]+$" + } + } + }, + { "type": "object", + "required": ["type"], + "properties": { + "type": { + "enum" : [ "recoveryContact" ] + }, + "activationURL": { + "type" : "string" + }, + "successURL": { + "type": "string" + }, + "contact": { + "type": "string" + } + } + }, + { "type": "object", + "required": ["type"], + "properties": { + "type": { + "enum" : [ "recoveryToken" ] + } + } + }, + { "type": "object", + "required": ["type", "alg", "nonce", "hints"], + "properties": { + "type": { + "enum" : [ "proofOfPossession" ] + }, + "alg": { + "type": "string" + }, + "nonce": { + "type": "string", + "pattern": "^[-_=0-9A-Za-z]+$" + }, + "hints": { + "type": "object", + "properties": { + "jwk": { + "type": "object" + }, + "certFingerprints": { + "type": "array", + "minItems": 1, + "items": { + "type": "string", + "pattern": "^[0-9a-f]+$" + } + }, + "subjectKeyIdentifiers": { + "type": "array", + "minItems": 1, + "items": { + "type": "string", + "pattern": "^[0-9a-f]+$" + } + }, + "serialNumbers": { + "type": "array", + "minItems": 1, + "items": { + "type": "integer" + } + }, + "issuers": { + "type": "array", + "minItems": 1, + "items": { + "type": "string" + } + }, + "authorizedFor": { + "type": "array", + "minItems": 1, + "items": { + "type": "string" + } + } + } + } + } + }, + { "type": "object", + "required": ["type", "token"], + "properties": { + "type": { + "enum" : [ "dns" ] + }, + "token": { + "type": "string" + } + } + } + ] +} diff --git a/letsencrypt/client/schemata/defer.json b/letsencrypt/client/schemata/defer.json new file mode 100644 index 000000000..21edd614b --- /dev/null +++ b/letsencrypt/client/schemata/defer.json @@ -0,0 +1,21 @@ +{ + "id": "https://letsencrypt.org/schema/01/defer#", + "$schema": "http://json-schema.org/draft-04/schema#", + "description": "Schema for a defer message", + "type": "object", + "required": ["type", "token"], + "properties": { + "type" : { + "enum" : [ "defer" ] + }, + "token" : { + "type": "string" + }, + "interval" : { + "type": "integer" + }, + "message": { + "type": "string" + } + } +} diff --git a/letsencrypt/client/schemata/error.json b/letsencrypt/client/schemata/error.json new file mode 100644 index 000000000..359506b52 --- /dev/null +++ b/letsencrypt/client/schemata/error.json @@ -0,0 +1,21 @@ +{ + "id": "https://letsencrypt.org/schema/01/error#", + "$schema": "http://json-schema.org/draft-04/schema#", + "description": "Schema for an error message", + "type": "object", + "required": ["type", "error"], + "properties": { + "type" : { + "enum" : [ "error" ] + }, + "error" : { + "enum" : [ "malformed", "unauthorized", "serverInternal", "nonSupported", "unknown", "badCSR" ] + }, + "message" : { + "type": "string" + }, + "moreInfo": { + "type": "string" + } + } +} diff --git a/letsencrypt/client/schemata/jwk.json b/letsencrypt/client/schemata/jwk.json new file mode 100644 index 000000000..536585e79 --- /dev/null +++ b/letsencrypt/client/schemata/jwk.json @@ -0,0 +1,19 @@ +{ + "id": "https://lets-encrypt.org/schema/01/jwk#", + "$schema": "http://json-schema.org/draft-04/schema#", + "description": "Schema for a jwk (**kty RSA/e=65537 ONLY**)", + "type": "object", + "required": ["kty", "e", "n"], + "properties": { + "kty": { + "enum" : [ "RSA" ] + }, + "e": { + "enum" : [ "AQAB" ] + }, + "n": { + "type": "string", + "pattern": "^[-_=0-9A-Za-z]+$" + } + } +} diff --git a/letsencrypt/client/schemata/responseobject.json b/letsencrypt/client/schemata/responseobject.json new file mode 100644 index 000000000..7e45853e6 --- /dev/null +++ b/letsencrypt/client/schemata/responseobject.json @@ -0,0 +1,75 @@ +{ + "id": "https://lets-encrypt.org/schema/01/responseobject#", + "$schema": "http://json-schema.org/draft-04/schema#", + "description": "Subschema for an individual challenge response (within authorizationRequest)", + "anyOf": [ + { "type": "object", + "required": ["type", "path"], + "properties": { + "type": { + "enum" : [ "simpleHttps" ] + }, + "path": { + "type": "string" + } + } + }, + { "type": "object", + "required": ["type", "s"], + "properties": { + "type": { + "enum" : [ "dvsni" ] + }, + "s": { + "type" : [ "string" ], + "pattern": "^[-_=0-9A-Za-z]+$" + } + } + }, + { "type": "object", + "required": ["type"], + "properties": { + "type": { + "enum" : [ "recoveryContact" ] + }, + "token": { + "type" : "string" + } + } + }, + { "type": "object", + "required": ["type"], + "properties": { + "type": { + "enum" : [ "recoveryToken" ] + }, + "token": { + "type" : "string" + } + } + }, + { "type": "object", + "required": ["type", "nonce", "signature"], + "properties": { + "type": { + "enum" : [ "proofOfPossession" ] + }, + "nonce": { + "type": "string", + "pattern": "^[-_=0-9A-Za-z]+$" + }, + "signature": { + "$ref": "file:letsencrypt/client/schemata/signature.json" + } + } + }, + { "type": "object", + "required": ["type"], + "properties": { + "type": { + "enum" : [ "dns" ] + } + } + } + ] +} diff --git a/letsencrypt/client/schemata/revocation.json b/letsencrypt/client/schemata/revocation.json new file mode 100644 index 000000000..53455d506 --- /dev/null +++ b/letsencrypt/client/schemata/revocation.json @@ -0,0 +1,12 @@ +{ + "id": "https://letsencrypt.org/schema/01/revocation#", + "$schema": "http://json-schema.org/draft-04/schema#", + "description": "Schema for a revocation message", + "type": "object", + "required": ["type"], + "properties": { + "type" : { + "enum" : [ "revocation" ] + } + } +} diff --git a/letsencrypt/client/schemata/revocationRequest.json b/letsencrypt/client/schemata/revocationRequest.json new file mode 100644 index 000000000..38cbe85b8 --- /dev/null +++ b/letsencrypt/client/schemata/revocationRequest.json @@ -0,0 +1,18 @@ +{ + "id": "https://letsencrypt.org/schema/01/revocationRequest#", + "$schema": "http://json-schema.org/draft-04/schema#", + "description": "Schema for a revocationRequest message", + "type": "object", + "required": ["type", "certificate", "signature"], + "properties": { + "type" : { + "enum" : [ "revocationRequest" ] + }, + "certificate" : { + "type" : "string" + }, + "signature" : { + "$ref": "file:letsencrypt/client/schemata/signature.json" + } + } +} diff --git a/letsencrypt/client/schemata/signature.json b/letsencrypt/client/schemata/signature.json new file mode 100644 index 000000000..54828d332 --- /dev/null +++ b/letsencrypt/client/schemata/signature.json @@ -0,0 +1,71 @@ +{ + "id": "https://lets-encrypt.org/schema/01/signature#", + "$schema": "http://json-schema.org/draft-04/schema#", + "description": "Schema for a signature (alg RS256/e=65537 or P-256 ONLY)", + "type": "object", + "required": ["alg", "nonce", "sig", "jwk"], + "properties": { + "anyOf": [ + { + "alg" : { + "enum" : [ "RS256" ] + }, + "nonce" : { + "type" : "string" + }, + "sig" : { + "type": "string", + "pattern": "^[-_=0-9A-Za-z]+$" + }, + "jwk": { + "type": "object", + "required": ["kty", "e", "n"], + "properties": { + "kty": { + "enum" : [ "RSA" ] + }, + "e": { + "enum" : [ "AQAB" ] + }, + "n": { + "type": "string", + "pattern": "^[-_=0-9A-Za-z]+$" + } + } + } + }, + { + "alg" : { + "enum" : [ "ES256" ] + }, + "nonce" : { + "type" : "string" + }, + "sig" : { + "type": "string", + "pattern": "^[-_=0-9A-Za-z]+$" + }, + "jwk": { + "type": "object", + "required": ["kty", "crv", "x", "y"], + "properties": { + "kty": { + "enum" : [ "EC" ] + }, + "crv": { + "enum" : [ "P-256" ] + }, + "x": { + "type": "string", + "pattern": "^[-_=0-9A-Za-z]+$" + }, + "y": { + "type": "string", + "pattern": "^[-_=0-9A-Za-z]+$" + } + } + } + } + ] + } +} diff --git a/letsencrypt/client/schemata/statusRequest.json b/letsencrypt/client/schemata/statusRequest.json new file mode 100644 index 000000000..8e4221cbe --- /dev/null +++ b/letsencrypt/client/schemata/statusRequest.json @@ -0,0 +1,15 @@ +{ + "id": "https://letsencrypt.org/schema/01/statusRequest#", + "$schema": "http://json-schema.org/draft-04/schema#", + "description": "Schema for a statusRequest message", + "type": "object", + "required": ["type", "token"], + "properties": { + "type" : { + "enum" : [ "statusRequest" ] + }, + "token" : { + "type": "string" + } + } +}