use JSON Schema schemata for ACME protocol validation

This commit is contained in:
Seth Schoen
2014-11-17 14:50:01 -08:00
parent ac28e9f880
commit 16af948b36
16 changed files with 543 additions and 254 deletions
+7 -254
View File
@@ -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"]])
@@ -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"
}
}
}
@@ -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"
}
}
}
}
@@ -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"
}
}
}
@@ -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"
}
}
}
@@ -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"
}
}
}
}
}
@@ -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"
}
}
}
@@ -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"
}
}
}
]
}
+21
View File
@@ -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"
}
}
}
+21
View File
@@ -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"
}
}
}
+19
View File
@@ -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]+$"
}
}
}
@@ -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" ]
}
}
}
]
}
@@ -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" ]
}
}
}
@@ -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"
}
}
}
@@ -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]+$"
}
}
}
}
]
}
}
@@ -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"
}
}
}