Skip to content

Commit

Permalink
Update integrity block explainer to reflect v2
Browse files Browse the repository at this point in the history
  • Loading branch information
GrapeGreen committed Jul 29, 2024
1 parent 6ebe7e7 commit 663edb7
Showing 1 changed file with 106 additions and 52 deletions.
158 changes: 106 additions & 52 deletions explainers/integrity-signature.md
Original file line number Diff line number Diff line change
Expand Up @@ -15,9 +15,10 @@ Currently web bundles support signatures of individual responses (not of the who
The integrity signature must guarantee the integrity of the web bundle with one signature, whose public key is trusted.

Please note that the user agent should know which public key to trust as an attacker can modify the app and resign it with another private key. The source of trust can be:
* the trusted public keys can be included in enterprise policy;
* the public key can be obtained from a trusted app distributor;
* any other trusted source.

- the trusted public keys can be included in enterprise policy;
- the public key can be obtained from a trusted app distributor;
- any other trusted source.

In future it makes sense to add the ability to have more than one signature (one signature would be set by the developer, another by the app distributor) and key rotation (similar to what APK Signature V3 has). We should take this into account while proposing the solution in order to simplify the future development.

Expand All @@ -26,101 +27,154 @@ In future it makes sense to add the ability to have more than one signature (one
### Integrity block

We will introduce a data structure called integrity block that will contain:
* integrity signature
* additional relevant to signature information (e.g. public key, certificate, expiry date, etc).

- integrity signature
- additional relevant to signature information (e.g. public key, certificate, expiry date, etc).

The exact structure of it will be discussed later.

The integrity block will be located before the unsigned web bundle. Basically a web bundle with integrity signature will be a sequence of 2 CBOR objects:
* integrity-block (defined below), which contains a signature and related data;
* webbundle (defined [here](https://www.ietf.org/archive/id/draft-yasskin-wpack-bundled-exchanges-04.html)) which is the content of the signed web bundle.

In the binary form this sequence will be stored as a concatenation of the serialized integrity-block and webbundle CBOR objects. Unfortunately there is [no CDDL notation for CBOR sequences](https://datatracker.ietf.org/doc/html/rfc8742#section-4.1) in the top level.
- integrity-block (defined below), which contains a signature and related data;
- web-bundle (defined [here](https://www.ietf.org/archive/id/draft-yasskin-wpack-bundled-exchanges-04.html)) which is the content of the signed web bundle.

In the binary form this sequence will be stored as a concatenation of the serialized integrity-block and web-bundle CBOR objects. Unfortunately there is [no CDDL notation for CBOR sequences](https://datatracker.ietf.org/doc/html/rfc8742#section-4.1) in the top level.

### Structure of the integrity block

The integrity block structure will contain the following information:
Attributes in the form of a CBOR map. The attributes must contain a 32-bytes Ed25519 public key (the attributes map key is `ed25519PublicKey`). In future there can be more attributes, e.g. expiry date, key rotation attribute, etc. The keys in the map will always be strings.
Attributes in the form of a CBOR map. The attributes must always contain exactly one of the following two entries:

- A 32-byte Ed25519 public key (the attributes map key is `ed25519PublicKey`);
- A 33-byte Ecdsa P-256 public key for the Ecdsa P-256 SHA-256 signing scheme (the attributes map key is `ecdsaP256SHA256PublicKey`).

In the future there can be more attributes, e.g. expiry date, key rotation attribute, etc. The keys in the map will always be strings.

### Signature bytes.

CDDL of the signature is:

```
integrity-signature = [
integrity-signature-ed25519 = [
attributes: {
"ed25519PublicKey" => bstr .size 32, ; 32-bytes long Ed25519 public key
},
},
signature: bstr .size 64, ; 64 bytes long Ed25519 signature
]
integrity-signature-ecdsa-p256-sha256 = [
attributes: {
"ecdsaP256SHA256PublicKey" => bstr .size 33, ; 33-bytes long Ecdsa P-256 public key
},
signature: bstr, ; Ecdsa P-256 SHA-256 signature
]
integrity-signature = integrity-signature-ed25519 / integrity-signature-ecdsa-p256-sha256
```

Because in the future we will support more than one signature, we should put an `integrity-signature` object in a signature stack: we will "push" a new integrity-signature after signing a web bundle and "pop" an `integrity-signature` after we verify it. As a container for the signature stack we will use a CBOR array. For now after successful signing the signature stack will have just one `integrity-signature`.
Since web bundles might be signed by more than one signature, `integrity-signature` objects are wrapped in a signature list which is represented as a CBOR array.

The CDDL notation of the integrity block in its final state with the signature stack is following:
The CDDL notation of the integrity block in its final state with the signature list is as follows:

```
integrity-block = [
magic: h'F0 9F 96 8B F0 9F 93 A6',
; Version value is '1b\0\0' for beta and '1\0\0\0' for release.
version: bstr .size 4,
; In v1, each bundle must have exactly one signature.
signature-stack: [ 1*1 integrity-signature],
version: bstr .size 4, ; Version value is '2\0\0\0' for release.
attributes: {
"webBundleId" => tstr
}
signature-list: [ +integrity-signature ],
]
```

Please note that while signature validation or signing processes the signature stack may be empty, but in the final state (when the web bundle is signed and is ready for validation) there should be one integrity signature there.
Please note that during the signing processes the signature list may be empty, but in the final state (when the web bundle is signed and is ready for validation) there should be at least one integrity signature there.

To be able to recognize the signed bundle by first several bytes of the file we will add a magic number as the first element of the integrity block. [Similar to Web Bundles](https://wpack-wg.github.io/bundled-responses/draft-ietf-wpack-bundled-responses.html#section-4.1) let’s make integrity-block magic equals to the following hex numbers: 0xF0 0x9F 0x96 0x8B 0xF0 0x9F 0x93 0xA6 which are UTF-8 encoded “🖋📦” (U+1F58B, U+1F4E6)

The integrity block will also contain a version field. Also [similar to Web Bundles](https://wpack-wg.github.io/bundled-responses/draft-ietf-wpack-bundled-responses.html#section-4.1) we will make it a bytestring that must be 31 00 00 00 in base 16 (an ASCII "1" followed by 3 0s) for this version of integrity signature (for development period we will use `31 62 00 00` in base 16, which is ASCII "1b" followed by 2 0s). If the recipient doesn't support the version in this field, it must reject the validation of the integrity signature and return an error.
The integrity block will also contain a version field. Also [similar to Web Bundles](https://wpack-wg.github.io/bundled-responses/draft-ietf-wpack-bundled-responses.html#section-4.1) we will make it a bytestring that must be 32 00 00 00 in base 16 (an ASCII "2" followed by 3 0s) for this version of integrity signature. If the recipient doesn't support the version in this field, it must reject the validation of the integrity signature and return an error.

### Signing process

The input for signing is an unsigned web bundle file that doesn have an `integrity-block`. We assume that there is a private key and the corresponding public key. The process of signing is following:

1. Generate a minimal valid integrity-block. At this point the `integrity-block` must be deterministically encoded CBOR (see below) and consists of:

* Magic.
* Version.
* Signature stack that contains 0 signatures.
- Magic.
- Version.
- Attributes.
- Empty signature list.

2. Calculate a SHA-512 hash of the serialized content of the `web-bundle`.
3. Create a separate empty signature list (`temp-signature-list`) to store intermediate signing results.

4. For each signing key:

- Generate signature `attributes` CBOR map with respect to the `integrity-signature-ed25519.attributes` or `integrity-signature-ecdsa-p256-sha256.attributes` specification accordingly.
- Set `data-to-be-signed` as a concatenation of 6 elements listed below (the order must persist):
- 64 bit big-endian integer length of the web bundle hash from step 2;
- web bundle hash from step 2;
- 64 bit big-endian integer length of the serialized `integrity-block` from step 1;
- serialized `integrity-block` from step 1;
- 64 bit big-endian integer length of signature `attributes`;
- serialized signature `attributes`.
- Compute the binary signature of `data-to-be-signed`.
- Build an `integrity-signature` object that will be an array of 2 elements:
- attributes from step 2;
- the binary signature from the previous step.
- Add the `integrity-signature` to `temp-signature-list`.

2. Generate signature `attributes` CBOR map and put there a key-value pair where the key is "ed25519PublicKey", and the value is 32-byte long Ed25519 public key in the form of a byte string.
3. Calculate SHA-512 of the serialized content of the `webbundle`.
4. Set data-to-be-signed as a concatenation of 6 elements listed below (the order must persist):
* 64 bit big-endian integer length of the web bundle hash from step 3;
* web bundle hash from step 3;
* 64 bit big-endian integer length of the serialized `integrity-block`;
* serialized `integrity-block` from step 1;
* 64 bit big-endian integer length of the attributes;
* serialized attributes from step 2.
5. Compute signature of the data-to-be-signed. For Ed25519 the signature will be a binary string according to [rfc8410](https://datatracker.ietf.org/doc/html/rfc8410).
6. Build a `integrity-signature` object that will be an array of 2 elements:
* attributes from step 2;
* the value of integrity signature from step 5.
7. Add the `integrity-signature` object to the `signature-stack` of the `integrity-block`.
8. Combine serialized `integrity-block` with the web-bundle file in a [CBOR sequence](https://datatracker.ietf.org/doc/html/rfc8742) (basically store `integrity-block` and after it append the contents of the web bundle file).
6. Assign `integrity-block.signature-list` from `temp-signature-list`.
7. Combine the serialized `integrity-block` with the web bundle file in a [CBOR sequence](https://datatracker.ietf.org/doc/html/rfc8742) (basically store `integrity-block` and after it append the contents of the web bundle file).

### Signature validation
### Integrity Block Validation

The main assumption is that we can place the whole integrity-block in RAM.
The main assumption is that we can place the whole integrity-block into RAM.

Validation of the signature consist of the following steps:
In order for an integrity block to be deemed valid, it should pass both the signature validation requirements as well as identity validation requirements.

#### Signature validation

Validation of the signature consists of the following steps:

1. Read the `integrity-block` from the web bundle with the integrity signature file.
2. Check that version and magic have expected values.
3. Pop the `integrity-signature` from the `signature-stack` and store it separately. The result of this operation should be a deterministically encoded `integrity-block` with empty `signature-stack` and separately stored `integrity-signature`.
4. Verify that the attributes map of the integrity-signature has only one item with the "ed25519PublicKey" key. For future when we support more attributes: check that we can understand all the attributes. Consider signature as not valid if we don’t know how to process any attribute or the conditions that are set by the attribute are not held (e.g. a “timestamp” attribute is in future).
5. Read the value of the "ed25519PublicKey" attribute.
6. Calculate SHA-512 of the serialized content of the `webbundle`.
7. Validate the `signed-data` with the public key and signature obtained from `integrity-signature`. The `signed-data` should be concatenation of:
* 64 bit big-endian integer length of the web bundle hash from step 6;
* web bundle hash from step 6;
* 64 bit big-endian integer length of the `integrity-block`;
* serialized `integrity-block` from step 3;
* 64 bit big-endian integer length of the attributes;
* serialized attributes of the signature.
3. Calculate a SHA-512 hash of the serialized content of the `web-bundle`.
4. Create a minimal valid integrity block (`integrity-block-min`) by clearing `signature-list`.
5. For each signature in the original integrity block's `signature-list`:

- Verify that the signature conforms to the definition of `integrity-signature`. If not, this signature is considered unknown and silently ignored.
- If the `attributes` map contains any entries unknown to the current client, these are also silently discarded and do not affect the validity check.
- Read the public key from `attributes`.
- Validate the `signed-data` with the public key and signature obtained from `integrity-signature`. The `signed-data` is a concatenation of:
- 64 bit big-endian integer length of the web bundle hash from step 3;
- web bundle hash from step 3;
- 64 bit big-endian integer length of the `integrity-block` from step 4;
- serialized `integrity-block` from step 4;
- 64 bit big-endian integer length of the attributes;
- serialized attributes of the signature.

6. If there are no invalid signatures and at least one known signature, the signature validation process succeeds.

#### Identity Validation

Signature validation establishes the _trust_ of an signed web bundle; however, its _identity_ is defined via `integrity-block.attributes.webBundleId` and has to be validated separately to allow the embedder to perform key rotations.

The standard validation process goes as follows:

1. Query the embedder regarding the expected public key `K` for the given `web-bundle-id`.
2. If `K` is defined, ensure that `K` is present in `integrity-block.signature-list`.

- If yes, the identity vaidation succeeds; if not, fails.

3. If there's no expected public key for `web-bundle-id`, check whether this id can be obtained from any public key in `integrity-block.signature-list` according to the conversion process outlined in the Signed Web Bundle IDs [explainer](https://github.com/WICG/isolated-web-apps/blob/main/Scheme.md#signed-web-bundle-ids).

- If yes, the identity validation succeeds; if not, fails.

### Signing algorithm

So far we will support only Ed25519 signing algorithm OID ​​1.3.101.112 (RFC 8032).
The supported signing algorithms are

- Ed25519 ([RFC8032](https://datatracker.ietf.org/doc/html/rfc8032));
- Ecdsa P-256 SHA-256 ([RFC4754](https://datatracker.ietf.org/doc/html/rfc4754)).

Note that the signatures are not required to be homogeneous -- `signature-list` might mix in signatures of different kinds.

0 comments on commit 663edb7

Please sign in to comment.