From 3e36607f1c3cc4216b9e325d7e5c3eeb1c63b6d3 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Magnus=20B=C3=A4ck?= Date: Tue, 27 Sep 2022 21:11:25 +0200 Subject: [PATCH 1/2] ArtC: Add data.fileInformation.integrityProtection This new member allows publishers to include the checksum of the artifact's file(s) when the artifact is announced, allowing consumers to verify that the file they eventually download is the original file. --- .../EiffelArtifactCreatedEvent/3.3.0.yml | 248 ++++++++++++++++ .../EiffelArtifactCreatedEvent.md | 22 +- .../EiffelArtifactCreatedEvent/checksum.json | 31 ++ schemas/EiffelArtifactCreatedEvent/3.3.0.json | 267 ++++++++++++++++++ 4 files changed, 567 insertions(+), 1 deletion(-) create mode 100644 definitions/EiffelArtifactCreatedEvent/3.3.0.yml create mode 100644 examples/events/EiffelArtifactCreatedEvent/checksum.json create mode 100644 schemas/EiffelArtifactCreatedEvent/3.3.0.json diff --git a/definitions/EiffelArtifactCreatedEvent/3.3.0.yml b/definitions/EiffelArtifactCreatedEvent/3.3.0.yml new file mode 100644 index 00000000..95f8df91 --- /dev/null +++ b/definitions/EiffelArtifactCreatedEvent/3.3.0.yml @@ -0,0 +1,248 @@ +# Copyright 2017-2022 Ericsson AB and others. +# For a full list of individual contributors, please see the commit history. +# +# Licensed under the Apache License, Version 2.0 (the "License"); +# you may not use this file except in compliance with the License. +# You may obtain a copy of the License at +# +# http://www.apache.org/licenses/LICENSE-2.0 +# +# Unless required by applicable law or agreed to in writing, software +# distributed under the License is distributed on an "AS IS" BASIS, +# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +# See the License for the specific language governing permissions and +# limitations under the License. +--- +$schema: http://json-schema.org/draft-04/schema# +_abbrev: ArtC +_description: The EiffelArtifactCreatedEvent declares that a software + artifact has been created, what its coordinates are, what it contains + and how it was created. +type: object +properties: + meta: + $ref: ../EiffelMetaProperty/3.1.0.yml + data: + type: object + properties: + identity: + _description: The identity of the created artifact, in [purl + format](https://github.com/package-url/purl-spec). + _format: '[purl specification](https://github.com/package-url/purl-spec)' + type: string + pattern: '^pkg:' + fileInformation: + _description: A list of the artifact file contents. This information + is optional and, when included, MAY include a complete or + incomplete list of contents. In other words, it may be used + to highlight only particular files of interest, such as launcher + binaries or other entry-points. + type: array + items: + type: object + properties: + name: + _description: The name (including relative path from + the root of the artifact) on syntax appropriate for + the artifact packaging type. + type: string + tags: + _description: Any tags associated with the file, to support + navigation and identification of items of interest. + type: array + items: + type: string + integrityProtection: + _description: An optional object containing a digest of + the file's contents, i.e. a checksum, computed using + the specified algorithm. + type: object + properties: + alg: + _description: The cryptographic algorithm used to compute + the digest of the file's contents. + _format: One of the hash algorithms listed in section 1 of + [NIST FIPS 180-4](https://nvlpubs.nist.gov/nistpubs/FIPS/NIST.FIPS.180-4.pdf), + excluding "SHA-1". + type: string + enum: + - SHA-224 + - SHA-256 + - SHA-384 + - SHA-512 + - SHA-512/224 + - SHA-512/256 + digest: + _description: The digest of the file contents. + _format: A lowercase string of hexadecimal digits. + type: string + pattern: ^[0-9a-f]+$ + required: + - alg + - digest + additionalProperties: false + required: + - name + additionalProperties: false + buildCommand: + _description: The command used to build the artifact within + the identified environment. Used for reproducability purposes. + type: string + requiresImplementation: + _description: |- + Defines whether this artifact requires an implementing artifact. This is typically used for interfaces requiring some backend implementation, although the interface does not presume to define _which_ implementation. Implicitly interpreted as "ANY" if undefined. + NONE signifies that there SHALL no implementations of this artifact. In other words, a composition containing another artifact identifying it in __data.implements__ would be illegal. + ANY signifies that there may or may not be implementations of this artifact. + EXACTLY_ONE signifies that a legal composition must contain one and only one implementation of this artifact. + AT_LEAST_ONE signifies that a legal composition must contain one or more implementations of this artifact. + type: string + enum: + - NONE + - ANY + - EXACTLY_ONE + - AT_LEAST_ONE + dependsOn: + _description: An array of [purl identified](https://github.com/package-url/purl-spec) + entities this artifact depends on. While not included in + the purl specification itself, the Eiffel protocol allows + version range notation according to [Maven syntax](https://docs.oracle.com/middleware/1212/core/MAVEN/maven_version.htm#MAVEN402) + to be used for the version component of the package identity. + Note that the purl specification always requires the version + component to be percent-encoded. + _format: '[purl specification](https://github.com/package-url/purl-spec)' + type: array + items: + type: string + pattern: '^pkg:' + implements: + _description: An array of [purl identified](https://github.com/package-url/purl-spec) + entities this artifact implements. The typical use case of + this is to identify interfaces implemented by this artifact. + While not included in the purl specification itself, the + Eiffel protocol allows version range notation according to + [Maven syntax](https://docs.oracle.com/middleware/1212/core/MAVEN/maven_version.htm#MAVEN402) + to be used for the version component of the package identity. + Note that the purl specification always requires the version + component to be percent-encoded. + _format: '[purl specification](https://github.com/package-url/purl-spec)' + type: array + items: + type: string + pattern: '^pkg:' + name: + _description: Any (colloquial) name of the artifact. Unlike + __data.identity__, this is not intended as an unambiguous + identifier of the artifact, but as a descriptive and human + readable name. + type: string + customData: + type: array + items: + $ref: ../EiffelCustomDataProperty/1.0.0.yml + required: + - identity + additionalProperties: false + links: + type: array + items: + $ref: ../EiffelEventLink/1.1.1.yml +required: + - meta + - data + - links +additionalProperties: false +_links: + CAUSE: + description: 'Identifies a cause of the event occurring. SHOULD + not be used in conjunction with __CONTEXT__: individual events + providing __CAUSE__ within a larger context gives rise to ambiguity. + It is instead recommended to let the root event of the context + declare __CAUSE__.' + required: false + multiple: true + targets: + any_type: true + types: [] + COMPOSITION: + description: Identifies the composition from which this artifact + was built. + required: false + multiple: false + targets: + any_type: false + types: + - EiffelCompositionDefinedEvent + CONTEXT: + description: Identifies the activity or test suite of which this + event constitutes a part. + required: false + multiple: false + targets: + any_type: false + types: + - EiffelActivityTriggeredEvent + - EiffelTestSuiteStartedEvent + ENVIRONMENT: + description: Identifies the environment in which this artifact + was built. + required: false + multiple: false + targets: + any_type: false + types: + - EiffelEnvironmentDefinedEvent + FLOW_CONTEXT: + description: 'Identifies the flow context of the event: which is + the continuous integration and delivery flow in which this occurred + – e.g. which product, project, track or version this is applicable + to.' + required: false + multiple: true + targets: + any_type: false + types: + - EiffelFlowContextDefinedEvent + PREVIOUS_VERSION: + description: Identifies a latest previous version (there may be + more than one in case of merges) of the artifact the event represents. + required: false + multiple: true + targets: + any_type: false + types: + - EiffelArtifactCreatedEvent +_history: + - version: 3.3.0 + introduced_in: No edition set + changes: Added data.fileInformation.integrityProtection member (see [Issue 290](https://github.com/eiffel-community/eiffel/issues/290)). + - version: 3.2.0 + introduced_in: 'No edition set' + changes: Add schema URL to the meta object (see [Issue 280](https://github.com/eiffel-community/eiffel/issues/280)). + - version: 3.1.0 + introduced_in: '[edition-lyon](../../../tree/edition-lyon)' + changes: Add links.domainId member (see [Issue 233](https://github.com/eiffel-community/eiffel/issues/233)). + - version: 3.0.0 + introduced_in: '[edition-agen](../../../tree/edition-agen)' + changes: Improved information integrity protection (see [Issue + 185](https://github.com/eiffel-community/eiffel/issues/185)). + - version: 2.0.0 + introduced_in: '[dc5ec6f](../../../blob/dc5ec6fb87e293eeffe88fdafe698eec0f5a2c89/eiffel-vocabulary/EiffelArtifactCreatedEvent.md)' + changes: Introduced purl identifiers instead of GAVs (see [Issue + 182](https://github.com/eiffel-community/eiffel/issues/182)) + - version: 1.1.0 + introduced_in: '[edition-toulouse](../../../tree/edition-toulouse)' + changes: Multiple links of type FLOW_CONTEXT allowed. + - version: 1.0.0 + introduced_in: '[edition-bordeaux](../../../tree/edition-bordeaux)' + changes: Initial version. +_examples: + - title: Simple example + url: ../examples/events/EiffelArtifactCreatedEvent/simple.json + - title: Interface example + url: ../examples/events/EiffelArtifactCreatedEvent/interface.json + - title: Backend example + url: ../examples/events/EiffelArtifactCreatedEvent/backend.json + - title: Dependent example + url: ../examples/events/EiffelArtifactCreatedEvent/dependent.json + - title: Checksum example + url: ../examples/events/EiffelArtifactCreatedEvent/checksum.json diff --git a/eiffel-vocabulary/EiffelArtifactCreatedEvent.md b/eiffel-vocabulary/EiffelArtifactCreatedEvent.md index 25cab669..3ac7dfc9 100644 --- a/eiffel-vocabulary/EiffelArtifactCreatedEvent.md +++ b/eiffel-vocabulary/EiffelArtifactCreatedEvent.md @@ -1,5 +1,5 @@ @@ -29,6 +29,24 @@ __Type:__ String[] __Required:__ No __Description:__ Any tags associated with the file, to support navigation and identification of items of interest. +#### data.fileInformation.integrityProtection +__Type:__ Object +__Required:__ No +__Description:__ An optional object containing a digest of the file's contents, i.e. a checksum, computed using the specified algorithm. + +##### data.fileInformation.integrityProtection.alg +__Type:__ String +__Format:__ One of the hash algorithms listed in section 1 of [NIST FIPS 180-4](https://nvlpubs.nist.gov/nistpubs/FIPS/NIST.FIPS.180-4.pdf), excluding "SHA-1". +__Required:__ Yes +__Legal values:__ SHA-224, SHA-256, SHA-384, SHA-512, SHA-512/224, SHA-512/256 +__Description:__ The cryptographic algorithm used to compute the digest of the file's contents. + +##### data.fileInformation.integrityProtection.digest +__Type:__ String +__Format:__ A lowercase string of hexadecimal digits. +__Required:__ Yes +__Description:__ The digest of the file contents. + ### data.buildCommand __Type:__ String __Required:__ No @@ -235,6 +253,7 @@ __Description:__ A URI pointing at a location from where the schema used when cr | Version | Introduced in | Changes | | ------- | ------------- | ------- | +| 3.3.0 | No edition set | Added data.fileInformation.integrityProtection member (see [Issue 290](https://github.com/eiffel-community/eiffel/issues/290)). | | 3.2.0 | No edition set | Add schema URL to the meta object (see [Issue 280](https://github.com/eiffel-community/eiffel/issues/280)). | | 3.1.0 | [edition-lyon](../../../tree/edition-lyon) | Add links.domainId member (see [Issue 233](https://github.com/eiffel-community/eiffel/issues/233)). | | 3.0.0 | [edition-agen](../../../tree/edition-agen) | Improved information integrity protection (see [Issue 185](https://github.com/eiffel-community/eiffel/issues/185)). | @@ -249,3 +268,4 @@ __Description:__ A URI pointing at a location from where the schema used when cr * [Interface example](../examples/events/EiffelArtifactCreatedEvent/interface.json) * [Backend example](../examples/events/EiffelArtifactCreatedEvent/backend.json) * [Dependent example](../examples/events/EiffelArtifactCreatedEvent/dependent.json) +* [Checksum example](../examples/events/EiffelArtifactCreatedEvent/checksum.json) diff --git a/examples/events/EiffelArtifactCreatedEvent/checksum.json b/examples/events/EiffelArtifactCreatedEvent/checksum.json new file mode 100644 index 00000000..0fcb04a5 --- /dev/null +++ b/examples/events/EiffelArtifactCreatedEvent/checksum.json @@ -0,0 +1,31 @@ +{ + "meta": { + "type": "EiffelArtifactCreatedEvent", + "version": "3.3.0", + "time": 1234567890, + "id": "aaaaaaaa-bbbb-5ccc-8ddd-eeeeeeeeeee0" + }, + "data": { + "identity": "pkg:generic/empty-file", + "fileInformation": [ + { + "name": "empty-file.txt", + "integrityProtection": { + "alg": "SHA-256", + "digest": "e3b0c44298fc1c149afbf4c8996fb92427ae41e4649b934ca495991b7852b855" + } + } + ], + "name": "This artifact is just an empty file" + }, + "links": [ + { + "type": "CAUSE", + "target": "aaaaaaaa-bbbb-5ccc-8ddd-eeeeeeeeeee1" + }, + { + "type": "COMPOSITION", + "target": "aaaaaaaa-bbbb-5ccc-8ddd-eeeeeeeeeee1" + } + ] +} diff --git a/schemas/EiffelArtifactCreatedEvent/3.3.0.json b/schemas/EiffelArtifactCreatedEvent/3.3.0.json new file mode 100644 index 00000000..5b8adba3 --- /dev/null +++ b/schemas/EiffelArtifactCreatedEvent/3.3.0.json @@ -0,0 +1,267 @@ +{ + "$schema": "http://json-schema.org/draft-04/schema#", + "type": "object", + "properties": { + "meta": { + "type": "object", + "properties": { + "id": { + "type": "string", + "pattern": "^[0-9a-f]{8}-[0-9a-f]{4}-[1-5][0-9a-f]{3}-[89ab][0-9a-f]{3}-[0-9a-f]{12}$" + }, + "type": { + "type": "string", + "enum": [ + "EiffelArtifactCreatedEvent" + ] + }, + "version": { + "type": "string", + "enum": [ + "3.3.0" + ], + "default": "3.3.0" + }, + "time": { + "type": "integer" + }, + "tags": { + "type": "array", + "items": { + "type": "string" + } + }, + "source": { + "type": "object", + "properties": { + "domainId": { + "type": "string" + }, + "host": { + "type": "string" + }, + "name": { + "type": "string" + }, + "serializer": { + "type": "string", + "pattern": "^pkg:" + }, + "uri": { + "type": "string" + } + }, + "additionalProperties": false + }, + "security": { + "type": "object", + "properties": { + "authorIdentity": { + "type": "string" + }, + "integrityProtection": { + "type": "object", + "properties": { + "signature": { + "type": "string" + }, + "alg": { + "type": "string", + "enum": [ + "HS256", + "HS384", + "HS512", + "RS256", + "RS384", + "RS512", + "ES256", + "ES384", + "ES512", + "PS256", + "PS384", + "PS512" + ] + }, + "publicKey": { + "type": "string" + } + }, + "required": [ + "signature", + "alg" + ], + "additionalProperties": false + }, + "sequenceProtection": { + "type": "array", + "items": { + "type": "object", + "properties": { + "sequenceName": { + "type": "string" + }, + "position": { + "type": "integer" + } + }, + "additionalProperties": false, + "required": [ + "sequenceName", + "position" + ] + } + } + }, + "additionalProperties": false, + "required": [ + "authorIdentity" + ] + }, + "schemaUri": { + "type": "string" + } + }, + "required": [ + "id", + "type", + "version", + "time" + ], + "additionalProperties": false + }, + "data": { + "type": "object", + "properties": { + "identity": { + "type": "string", + "pattern": "^pkg:" + }, + "fileInformation": { + "type": "array", + "items": { + "type": "object", + "properties": { + "name": { + "type": "string" + }, + "tags": { + "type": "array", + "items": { + "type": "string" + } + }, + "integrityProtection": { + "type": "object", + "properties": { + "alg": { + "type": "string", + "enum": [ + "SHA-224", + "SHA-256", + "SHA-384", + "SHA-512", + "SHA-512/224", + "SHA-512/256" + ] + }, + "digest": { + "type": "string", + "pattern": "^[0-9a-f]+$" + } + }, + "required": [ + "alg", + "digest" + ], + "additionalProperties": false + } + }, + "required": [ + "name" + ], + "additionalProperties": false + } + }, + "buildCommand": { + "type": "string" + }, + "requiresImplementation": { + "type": "string", + "enum": [ + "NONE", + "ANY", + "EXACTLY_ONE", + "AT_LEAST_ONE" + ] + }, + "dependsOn": { + "type": "array", + "items": { + "type": "string", + "pattern": "^pkg:" + } + }, + "implements": { + "type": "array", + "items": { + "type": "string", + "pattern": "^pkg:" + } + }, + "name": { + "type": "string" + }, + "customData": { + "type": "array", + "items": { + "type": "object", + "properties": { + "key": { + "type": "string" + }, + "value": {} + }, + "required": [ + "key", + "value" + ], + "additionalProperties": false + } + } + }, + "required": [ + "identity" + ], + "additionalProperties": false + }, + "links": { + "type": "array", + "items": { + "type": "object", + "properties": { + "type": { + "type": "string" + }, + "target": { + "type": "string", + "pattern": "^[0-9a-f]{8}-[0-9a-f]{4}-[1-5][0-9a-f]{3}-[89ab][0-9a-f]{3}-[0-9a-f]{12}$" + }, + "domainId": { + "type": "string" + } + }, + "required": [ + "type", + "target" + ], + "additionalProperties": false + } + } + }, + "required": [ + "meta", + "data", + "links" + ], + "additionalProperties": false +} From 7a2d76ff6ff0f5e157ee01e1449a17704d8a8768 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Magnus=20B=C3=A4ck?= Date: Thu, 27 Oct 2022 10:39:20 +0200 Subject: [PATCH 2/2] security.md: Mention integrity of referenced items --- eiffel-syntax-and-usage/security.md | 11 +++++++++-- 1 file changed, 9 insertions(+), 2 deletions(-) diff --git a/eiffel-syntax-and-usage/security.md b/eiffel-syntax-and-usage/security.md index 869ab4bd..e9ec4328 100644 --- a/eiffel-syntax-and-usage/security.md +++ b/eiffel-syntax-and-usage/security.md @@ -22,13 +22,20 @@ Let us begin by establishing that the Eiffel protocol by itself can be considere In the words of ISO27000, confidentiality means that "information is not made available or disclosed to unauthorized individuals, entities, or processes". This can be achieved through multiple (and often complementary) means, such as encryption and access control. The Eiffel protocol itself cannot support confidentiality; instead it is a property of the systems used to transport, process and store the Eiffel events. In other words, if the confidentiality of information communicated as Eiffel events is a concern for you, then you are recommended to take appropriate action to ensure the confidentiality of your data, both at rest and in transit, e.g. through encryption. ## Integrity -In information security, integrity refers to the accuracy and completeness of data. In other words, safeguarding integrity requires protection against both malicious and unintentional tampering with or corruption of data in an unauthorized or undetected manner. This cannot be _solved_ by a communication protocol, as it relies on adequate infrastructure and processes for managing the data, but it can be _supported_. Eiffel supports data integrity through digital signatures. -A digital signature is a combination of hashing technology and encryption. Hash functions are commonly used to ensure data integrity and are familiar to most software professionals. By using checksums or hash values (particularly hash values, or _digests_, produced by cryptographic hash functions such as the SHA series), any piece of data of arbitrary length is computed into a fixed length digest, with any alteration of the input data resulting in a different digest. Consequently, as long as the digest of the received data matches the digest of the data sent, its integrity can be verified. Unfortunately, the digest must be securely communicated: any malicious attacker able to not only manipulate the data but also the digest can make a corrupted message appear authentic. This is why digital signing also employs encryption. +In information security, integrity refers to the accuracy and completeness of data. In other words, safeguarding integrity requires protection against both malicious and unintentional tampering with or corruption of data in an unauthorized or undetected manner. This cannot be _solved_ by a communication protocol, as it relies on adequate infrastructure and processes for managing the data, but it can be _supported_. Eiffel supports two aspects of data integrity; integrity of event payloads and integrity of referenced items. + +### Integrity of event payloads + +The integrity of the contents of an Eiffel event, i.e. its payload, can be protected via a digital signature. A digital signature is a combination of hashing technology and encryption. Hash functions are commonly used to ensure data integrity and are familiar to most software professionals. By using checksums or hash values (particularly hash values, or _digests_, produced by cryptographic hash functions such as the SHA series), any piece of data of arbitrary length is computed into a fixed length digest, with any alteration of the input data resulting in a different digest. Consequently, as long as the digest of the received data matches the digest of the data sent, its integrity can be verified. Unfortunately, the digest must be securely communicated: any malicious attacker able to not only manipulate the data but also the digest can make a corrupted message appear authentic. This is why digital signing also employs encryption. The Eiffel protocol's support is influenced by [JSON Web Signatures (JWS)](https://tools.ietf.org/html/rfc7515), with slight modifications to allow inclusion of the signature within the event message itself, rather than as part of a header. This serves to keep every Eiffel event self-contained, with information integrity protection optional for the producer to include and optional for the consumer to consider. Note that this optionality does not in any way lessen the strength of the security provided: it is always up to the recipient of an unprotected Eiffel event to decide whether to trust it or not. In this sense, Eiffel support of data integrity is very similar to that employed by other document formats, such as the [Portable Document Format](http://www.adobe.com/devnet/pdf/pdf_reference.html). Apart from digital signing, the Eiffel protocol also supports author identification using [Distinguished Names](https://tools.ietf.org/html/rfc2253) and event sequence integrity protection. Please see the documentation of each event type for further information and detailed instructions on correct usage. +### Integrity of referenced items + +Some Eiffel events reference external data whose integrity is important to track, e.g. the files that make up an artifact. In this case a cryptographic digest of the data is stored in the Eiffel event, allowing event consumers to verify that the data hasn't been modified by either an honest mistake, corruption, or malicious intent. The use of such a digest may be combined with a digital signature of the event to secure the integrity of the digest. + ## Availability Availability of information communicated over the Eiffel protocol is a property of the communication channels and storage solutions used. In other words, similarly to confidentiality, it is an infrastructural concern and external to the protocol itself.