diff --git a/schemas/map.schema.json b/schemas/map.schema.json new file mode 100644 index 0000000..9ae7271 --- /dev/null +++ b/schemas/map.schema.json @@ -0,0 +1,55 @@ +{ + "$schema": "http://json-schema.org/draft-04/schema#", + "type": "object", + "required": ["repositories", "mapping"], + "additionalProperties": false, + "properties": { + "repositories": { + "type": "object", + "items": { + "type": "object", + "patternProperties": { + ".+": { + "type": "object", + "items": { + "type": "string", + "minimum": 1 + } + } + } + } + }, + "mapping": { + "type": "array", + "items": { + "type": "object", + "additionalProperties": false, + "required": ["paths", "repositories", "threshold", "terminating"], + "properties": { + "paths": { + "type": "array", + "items": { + "type": "string", + "minLength": 1 + } + }, + "repositories": { + "type": "array", + "items": { + "type": "string", + "minLength": 1 + } + }, + "threshold": { + "type": "integer", + "minimum": 1 + }, + "terminating": { + "type": "boolean" + } + } + } + } + } +} + diff --git a/schemas/root.schema.json b/schemas/root.schema.json new file mode 100644 index 0000000..99368a4 --- /dev/null +++ b/schemas/root.schema.json @@ -0,0 +1,190 @@ +{ + "$schema": "http://json-schema.org/draft-04/schema#", + "type": "object", + "required": ["signatures", "signed"], + "additionalProperties": false, + "properties": { + "signatures": { + "type": "array", + "items": { + "type": "object", + "required": ["keyid", "sig"], + "additionalProperties": false, + "properties": { + "keyid": { + "type": "string", + "minLength": 64, + "maxLength": 64, + "pattern": "^[a-f0-9]{64}$" + }, + "sig": { + "type": "string", + "minLength": 1 + } + } + } + }, + "signed": { + "type": "object", + "required": ["_type", "expires", "keys", "roles", "spec_version", "version"], + "additionalProperties": false, + "properties": { + "_type": { + "enum": ["root"] + }, + "consistent_snapshot": { + "type": "boolean" + }, + "expires": { + "type": "string", + "pattern": "^[0-9]{4}-[0-9]{2}-[0-9]{2}T[0-9]{2}:[0-9]{2}:[0-9]{2}Z$" + }, + "keys": { + "type": "object", + "additionalProperties": false, + "patternProperties": { + "^[0-9a-f]{64}$": { + "type": "object", + "required": ["keytype", "keyval", "scheme"], + "additionalProperties": true, + "properties": { + "keytype": { + "enum": ["rsa", "ed25519", "ecdsa-sha2-nistp256"] + }, + "keyval": { + "type": "object", + "required": ["public"], + "additionalProperties": false, + "properties": { + "public": { + "type": "string", + "minLength": 1 + } + } + }, + "scheme": { + "enum": ["rsassa-pss-sha256", "ed25519", "ecdsa-sha2-nistp256"] + } + } + } + } + }, + "roles": { + "type": "object", + "required": ["root", "snapshot", "targets", "timestamp"], + "additionalProperties": false, + "properties": { + "mirror": { + "type": "object", + "required": ["keyids", "threshold"], + "additionalProperties": false, + "properties": { + "keyids": { + "type": "array", + "items": { + "type": "string", + "minLength": 64, + "maxLength": 64, + "pattern": "^[a-f0-9]{64}$" + } + }, + "threshold": { + "type": "number", + "minimum": 1 + } + } + }, + "root": { + "type": "object", + "required": ["keyids", "threshold"], + "additionalProperties": false, + "properties": { + "keyids": { + "type": "array", + "items": { + "type": "string", + "minLength": 64, + "maxLength": 64, + "pattern": "^[a-f0-9]{64}$" + } + }, + "threshold": { + "type": "number", + "minimum": 1 + } + } + }, + "snapshot": { + "type": "object", + "required": ["keyids", "threshold"], + "additionalProperties": false, + "properties": { + "keyids": { + "type": "array", + "items": { + "type": "string", + "minLength": 64, + "maxLength": 64, + "pattern": "^[a-f0-9]{64}$" + } + }, + "threshold": { + "type": "number", + "minimum": 1 + } + } + }, + "targets": { + "type": "object", + "required": ["keyids", "threshold"], + "additionalProperties": false, + "properties": { + "keyids": { + "type": "array", + "items": { + "type": "string", + "minLength": 64, + "maxLength": 64, + "pattern": "^[a-f0-9]{64}$" + } + }, + "threshold": { + "type": "number", + "minimum": 1 + } + } + }, + "timestamp": { + "type": "object", + "required": ["keyids", "threshold"], + "additionalProperties": false, + "properties": { + "keyids": { + "type": "array", + "items": { + "type": "string", + "minLength": 64, + "maxLength": 64, + "pattern": "^[a-f0-9]{64}$" + } + }, + "threshold": { + "type": "number", + "minimum": 1 + } + } + } + } + }, + "spec_version": { + "type": "string", + "pattern": "^[0-9]+\\.[0-9]+(\\.[0-9]+)?$" + }, + "version": { + "type": "number", + "minimum": 1 + } + } + } + } +} diff --git a/schemas/snapshot.schema.json b/schemas/snapshot.schema.json new file mode 100644 index 0000000..ec7f0cd --- /dev/null +++ b/schemas/snapshot.schema.json @@ -0,0 +1,93 @@ +{ + "$schema": "http://json-schema.org/draft-04/schema#", + "type": "object", + "additionalProperties": false, + "properties": { + "signatures": { + "type": "array", + "items": [ + { + "type": "object", + "additionalProperties": false, + "properties": { + "keyid": { + "type": "string", + "minLength": 64, + "maxLength": 64, + "pattern": "^[a-f0-9]{64}$" + }, + "sig": { + "type": "string", + "minLength": 1 + } + }, + "required": [ + "keyid", + "sig" + ] + } + ] + }, + "signed": { + "type": "object", + "additionalProperties": false, + "properties": { + "_type": { + "enum": ["snapshot"] + }, + "expires": { + "type": "string", + "pattern": "^[0-9]{4}-[0-9]{2}-[0-9]{2}T[0-9]{2}:[0-9]{2}:[0-9]{2}Z$" + }, + "meta": { + "type": "object", + "additionalProperties": true, + "properties": { + "targets.json": { + "type": "object", + "additionalProperties": false, + "properties": { + "version": { + "type": "integer", + "minimum": 1 + }, + "length": { + "type": "integer", + "minimum": 1 + }, + "hashes": { + "type": "object" + } + }, + "required": [ + "version" + ] + } + }, + "required": [ + "targets.json" + ] + }, + "spec_version": { + "type": "string", + "pattern": "^[0-9]+\\.[0-9]+(\\.[0-9]+)?$" + }, + "version": { + "type": "integer", + "minimum": 1 + } + }, + "required": [ + "_type", + "expires", + "meta", + "spec_version", + "version" + ] + } + }, + "required": [ + "signatures", + "signed" + ] +} diff --git a/schemas/targets.schema.json b/schemas/targets.schema.json new file mode 100644 index 0000000..8828616 --- /dev/null +++ b/schemas/targets.schema.json @@ -0,0 +1,178 @@ +{ + "$schema": "http://json-schema.org/draft-04/schema#", + "type": "object", + "additionalProperties": false, + "properties": { + "signatures": { + "type": "array", + "items": [ + { + "type": "object", + "additionalProperties": false, + "properties": { + "keyid": { + "type": "string", + "minLength": 64, + "maxLength": 64, + "pattern": "^[a-f0-9]{64}$" + }, + "sig": { + "type": "string", + "minLength": 1 + } + }, + "required": [ + "keyid", + "sig" + ] + } + ] + }, + "signed": { + "type": "object", + "additionalProperties": false, + "properties": { + "_type": { + "enum": ["targets"] + }, + "delegations": { + "type": "object", + "additionalProperties": false, + "properties": { + "keys": { + "type": "object", + "patternProperties": { + "^[0-9a-f]{64}$": { + "type": "object", + "required": ["keytype", "keyval", "scheme"], + "additionalProperties": true, + "properties": { + "keytype": { + "enum": ["rsa", "ed25519", "ecdsa-sha2-nistp256"] + }, + "keyval": { + "type": "object", + "required": ["public"], + "additionalProperties": false, + "properties": { + "public": { + "type": "string", + "minLength": 1 + } + } + }, + "scheme": { + "enum": ["rsassa-pss-sha256", "ed25519", "ecdsa-sha2-nistp256"] + } + } + } + } + }, + "roles": { + "type": "array", + "items": { + "type": "object", + "additionalProperties": false, + "properties": { + "name": { + "type": "string", + "minLength": 1 + }, + "keyids": { + "type": "array", + "items": { + "type": "string", + "minLength": 64, + "maxLength": 64, + "pattern": "^[a-f0-9]{64}$" + } + }, + "threshold": { + "type": "integer", + "minimum": 1 + }, + "path_hash_prefixes": { + "type": "string", + "minLength": 1 + }, + "paths": { + "type": "array", + "minLength": 1 + }, + "terminating": { + "type": "boolean" + } + }, + "required": ["name", "keyids", "threshold", "paths", "terminating"] + } + } + } + }, + "expires": { + "type": "string", + "pattern": "^[0-9]{4}-[0-9]{2}-[0-9]{2}T[0-9]{2}:[0-9]{2}:[0-9]{2}Z$" + }, + "spec_version": { + "type": "string", + "pattern": "^[0-9]+\\.[0-9]+(\\.[0-9]+)?$" + }, + "targets": { + "type": "object", + "additionalProperties": false, + "patternProperties": { + ".+": { + "type": "object", + "additionalProperties": false, + "properties": { + "custom": { + "type": "object" + }, + "hashes": { + "type": "object", + "additionalProperties": false, + "properties": { + "sha256": { + "type": "string", + "minLength": 64, + "maxLength": 64, + "pattern": "^[a-f0-9]{64}$" + }, + "sha512": { + "type": "string", + "minLength": 128, + "maxLength": 128, + "pattern": "^[a-f0-9]{128}$" + } + } + }, + "length": { + "type": "integer", + "minimum": 1 + } + }, + "required": [ + "hashes", + "length" + ] + } + } + }, + "version": { + "type": "integer", + "minimum": 1 + } + }, + "required": [ + "_type", + "expires", + "spec_version", + "targets", + "version" + ] + } + }, + "required": [ + "signatures", + "signed" + ] +} diff --git a/schemas/timestamp.schema.json b/schemas/timestamp.schema.json new file mode 100644 index 0000000..7282c9a --- /dev/null +++ b/schemas/timestamp.schema.json @@ -0,0 +1,110 @@ +{ + "$schema": "http://json-schema.org/draft-04/schema#", + "type": "object", + "additionalProperties": false, + "properties": { + "signatures": { + "type": "array", + "items": [ + { + "type": "object", + "additionalProperties": false, + "properties": { + "keyid": { + "type": "string", + "minLength": 64, + "maxLength": 64, + "pattern": "^[a-f0-9]{64}$" + }, + "sig": { + "type": "string", + "minLength": 1 + } + }, + "required": [ + "keyid", + "sig" + ] + } + ] + }, + "signed": { + "type": "object", + "additionalProperties": false, + "properties": { + "_type": { + "enum": ["timestamp"] + }, + "expires": { + "type": "string", + "pattern": "^[0-9]{4}-[0-9]{2}-[0-9]{2}T[0-9]{2}:[0-9]{2}:[0-9]{2}Z$" + }, + "meta": { + "type": "object", + "additionalProperties": false, + "properties": { + "snapshot.json": { + "type": "object", + "additionalProperties": false, + "properties": { + "hashes": { + "type": "object", + "additionalProperties": false, + "properties": { + "sha256": { + "type": "string", + "minLength": 64, + "maxLength": 64, + "pattern": "^[a-f0-9]{64}$" + }, + "sha512": { + "type": "string", + "minLength": 128, + "maxLength": 128, + "pattern": "^[a-f0-9]{128}$" + } + } + }, + "length": { + "type": "integer", + "minimum": 1 + }, + "version": { + "type": "integer", + "minimum": 1 + } + }, + "required": [ + "hashes", + "length", + "version" + ] + } + }, + "required": [ + "snapshot.json" + ] + }, + "spec_version": { + "type": "string", + "pattern": "^[0-9]+\\.[0-9]+(\\.[0-9]+)?$" + }, + "version": { + "type": "integer", + "minimum": 1 + } + }, + "required": [ + "_type", + "expires", + "meta", + "spec_version", + "version" + ] + } + }, + "required": [ + "signatures", + "signed" + ] +} diff --git a/tuf-spec.md b/tuf-spec.md index 9616036..ce85497 100644 --- a/tuf-spec.md +++ b/tuf-spec.md @@ -3,7 +3,7 @@ Title: The Update Framework Specification Shortname: TUF Status: LS Abstract: A framework for securing software update systems. -Date: 2022-04-28 +Date: 2022-09-15 Editor: Justin Cappos, NYU Editor: Trishank Karthik Kuppusamy, Datadog Editor: Joshua Lock, VMware @@ -16,7 +16,7 @@ Boilerplate: copyright no, conformance no Local Boilerplate: header yes Markup Shorthands: css no, markdown yes Metadata Include: This version off, Abstract off -Text Macro: VERSION 1.0.30 +Text Macro: VERSION 1.0.31 Note: We strive to make the specification easy to implement, so if you come @@ -516,6 +516,10 @@ floating-point numbers omitted. When calculating the digest of an object, we use the "canonical JSON" subdialect as described at [Canonical JSON]( http://wiki.laptop.org/go/Canonical_JSON). +Users of TUF can use JSON schema files available in [theupdateframework/specification +repository](https://github.com/theupdateframework/specification/tree/master/schemas) +to validate TUF JSON files. + ## File formats: general principles ## {#file-formats-general-principles} All signed metadata objects have the format: