aboutsummaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorDavid Drysdale <drysdale@google.com>2023-09-29 14:33:06 +0000
committerAutomerger Merge Worker <android-build-automerger-merge-worker@system.gserviceaccount.com>2023-09-29 14:33:06 +0000
commit207ea8ab7057e00d1da4ff43efc8c5524d66991a (patch)
treef3415fd34619226be7a6906f226e928fef2b287b
parentd524947244507d9517036a7922fc1c2ac134a9a9 (diff)
parent948fe0b03bff28483751f62b675e61174eab96dc (diff)
downloadcoset-207ea8ab7057e00d1da4ff43efc8c5524d66991a.tar.gz
Upgrade coset to 0.3.5 am: 16f92bd677 am: 948fe0b03b
Original change: https://android-review.googlesource.com/c/platform/external/rust/crates/coset/+/2769198 Change-Id: I4db5a2530853f21d4cd5de6184533e16dd46d29c Signed-off-by: Automerger Merge Worker <android-build-automerger-merge-worker@system.gserviceaccount.com>
-rw-r--r--.cargo_vcs_info.json2
-rw-r--r--.github/workflows/ci.yml127
-rw-r--r--Android.bp9
-rw-r--r--CHANGELOG.md10
-rw-r--r--Cargo.lock14
-rw-r--r--Cargo.toml4
-rw-r--r--Cargo.toml.orig4
-rw-r--r--METADATA10
-rw-r--r--README.md8
-rw-r--r--src/common/mod.rs62
-rw-r--r--src/common/tests.rs14
-rw-r--r--src/encrypt/tests.rs12
-rw-r--r--src/header/tests.rs8
-rw-r--r--src/key/tests.rs4
-rw-r--r--src/mac/tests.rs6
-rw-r--r--src/sign/mod.rs155
-rw-r--r--src/sign/tests.rs334
17 files changed, 621 insertions, 162 deletions
diff --git a/.cargo_vcs_info.json b/.cargo_vcs_info.json
index 79d70ec..1b3ac45 100644
--- a/.cargo_vcs_info.json
+++ b/.cargo_vcs_info.json
@@ -1,6 +1,6 @@
{
"git": {
- "sha1": "8a8552a8b57f004d08d081230659b47578c86b66"
+ "sha1": "90f5513741844bd5c5e0a0a751360c7edd0e8992"
},
"path_in_vcs": ""
} \ No newline at end of file
diff --git a/.github/workflows/ci.yml b/.github/workflows/ci.yml
index 14bc1f5..d5ff212 100644
--- a/.github/workflows/ci.yml
+++ b/.github/workflows/ci.yml
@@ -18,17 +18,15 @@ jobs:
rust:
- stable
- beta
- - nightly-2022-01-01
+ - nightly-2023-04-01
steps:
- - uses: actions/checkout@629c2de402a417ea7690ca6ce3f33229e27606a5 # v2
- - uses: actions-rs/toolchain@63eb9591781c46a70274cb3ebdf190fce92702e8 # v1
+ - uses: actions/checkout@8e5e7e5ab8b370d6c329ec480221332ada57f0ab # v3.5.2
+ - uses: dtolnay/rust-toolchain@a3ac054b2e7d62f514aa1bd57e3508c522fe772d # 1.68.2
with:
- profile: minimal
toolchain: ${{ matrix.rust }}
components: rustfmt
- override: true
- - run: cargo build --release --workspace
- - run: cargo build --release --workspace --features=std
+ - run: cargo +${{ matrix.rust }} build --release --workspace
+ - run: cargo +${{ matrix.rust }} build --release --workspace --features=std
test:
runs-on: ubuntu-latest
@@ -37,19 +35,17 @@ jobs:
rust:
- stable
- beta
- - nightly-2022-01-01
+ - nightly-2023-04-01
steps:
- - uses: actions/checkout@629c2de402a417ea7690ca6ce3f33229e27606a5 # v2
+ - uses: actions/checkout@8e5e7e5ab8b370d6c329ec480221332ada57f0ab # v3.5.2
with:
submodules: true
- - uses: actions-rs/toolchain@63eb9591781c46a70274cb3ebdf190fce92702e8 # v1
+ - uses: dtolnay/rust-toolchain@a3ac054b2e7d62f514aa1bd57e3508c522fe772d # 1.68.2
with:
- profile: minimal
toolchain: ${{ matrix.rust }}
components: rustfmt
- override: true
- - run: cargo test --workspace -- --nocapture
- - run: cargo test --workspace --features=std -- --nocapture
+ - run: cargo +${{ matrix.rust }} test --workspace -- --nocapture
+ - run: cargo +${{ matrix.rust }} test --workspace --features=std -- --nocapture
examples:
runs-on: ubuntu-latest
@@ -58,19 +54,17 @@ jobs:
rust:
- stable
- beta
- - nightly-2022-01-01
+ - nightly-2023-04-01
steps:
- - uses: actions/checkout@629c2de402a417ea7690ca6ce3f33229e27606a5 # v2
+ - uses: actions/checkout@8e5e7e5ab8b370d6c329ec480221332ada57f0ab # v3.5.2
with:
submodules: true
- - uses: actions-rs/toolchain@63eb9591781c46a70274cb3ebdf190fce92702e8 # v1
+ - uses: dtolnay/rust-toolchain@a3ac054b2e7d62f514aa1bd57e3508c522fe772d # 1.68.2
with:
- profile: minimal
toolchain: ${{ matrix.rust }}
components: rustfmt
- override: true
- - run: cargo test --examples
- - run: cargo test --features=std --examples
+ - run: cargo +${{ matrix.rust }} test --examples
+ - run: cargo +${{ matrix.rust }} test --features=std --examples
no_std:
name: Build for a no_std target
@@ -80,17 +74,15 @@ jobs:
rust:
- stable
- beta
- - nightly-2022-01-01
+ - nightly-2023-04-01
steps:
- - uses: actions/checkout@629c2de402a417ea7690ca6ce3f33229e27606a5 # v2
- - uses: actions-rs/toolchain@63eb9591781c46a70274cb3ebdf190fce92702e8 # v1
+ - uses: actions/checkout@8e5e7e5ab8b370d6c329ec480221332ada57f0ab # v3.5.2
+ - uses: dtolnay/rust-toolchain@a3ac054b2e7d62f514aa1bd57e3508c522fe772d # 1.68.2
with:
- profile: minimal
toolchain: ${{ matrix.rust }}
components: rustfmt
- target: thumbv6m-none-eabi
- override: true
- - run: cargo build --release --workspace --target thumbv6m-none-eabi
+ targets: thumbv6m-none-eabi
+ - run: cargo +${{ matrix.rust }} build --release --workspace --target thumbv6m-none-eabi
msrv:
name: Rust ${{matrix.rust}} MSRV
@@ -100,108 +92,93 @@ jobs:
matrix:
rust: [1.56.0, 1.57.0]
steps:
- - uses: actions/checkout@629c2de402a417ea7690ca6ce3f33229e27606a5 # v2
+ - uses: actions/checkout@8e5e7e5ab8b370d6c329ec480221332ada57f0ab # v3.5.2
with:
submodules: true
- - uses: actions-rs/toolchain@63eb9591781c46a70274cb3ebdf190fce92702e8 # v1
+ - uses: dtolnay/rust-toolchain@a3ac054b2e7d62f514aa1bd57e3508c522fe772d # 1.68.2
with:
- profile: minimal
toolchain: ${{ matrix.rust }}
components: rustfmt
- override: true
- run: rustc --version
- - run: cargo build --release --workspace
- - run: cargo build --release --workspace --all-features
+ - run: cargo +${{ matrix.rust }} build --release --workspace
+ - run: cargo +${{ matrix.rust }} build --release --workspace --all-features
formatting:
runs-on: ubuntu-latest
steps:
- - uses: actions/checkout@629c2de402a417ea7690ca6ce3f33229e27606a5 # v2
- - uses: actions/setup-go@424fc82d43fa5a37540bae62709ddcc23d9520d4 # v2
+ - uses: actions/checkout@8e5e7e5ab8b370d6c329ec480221332ada57f0ab # v3.5.2
+ - uses: actions/setup-go@6edd4406fa81c3da01a34fa6f6343087c207a568 # v3.5.0
- run: go install github.com/campoy/embedmd@97c13d6
- - uses: actions/setup-ruby@b007fae6f1ffbe3a51c00a6df6f5ff01184d5340 # v1
+ - uses: ruby/setup-ruby@d2b39ad0b52eca07d23f3aa14fdf2a3fcc1f411c # v1.148.0
+ with:
+ ruby-version: '2.7'
- run: gem install mdl
- - uses: actions-rs/toolchain@63eb9591781c46a70274cb3ebdf190fce92702e8 # v1
+ - uses: dtolnay/rust-toolchain@a3ac054b2e7d62f514aa1bd57e3508c522fe772d # 1.68.2
with:
- profile: minimal
- toolchain: nightly-2022-01-01
- override: true
+ toolchain: nightly-2023-04-01
components: rustfmt
- - run: cargo fmt --all -- --check
+ - run: cargo +nightly-2023-04-01 fmt --all -- --check
- run: scripts/check-format.sh
clippy:
runs-on: ubuntu-latest
steps:
- - uses: actions/checkout@629c2de402a417ea7690ca6ce3f33229e27606a5 # v2
- - uses: actions-rs/toolchain@63eb9591781c46a70274cb3ebdf190fce92702e8 # v1
+ - uses: actions/checkout@8e5e7e5ab8b370d6c329ec480221332ada57f0ab # v3.5.2
+ - uses: dtolnay/rust-toolchain@a3ac054b2e7d62f514aa1bd57e3508c522fe772d # 1.68.2
with:
- profile: minimal
- toolchain: nightly-2022-01-01
- override: true
+ toolchain: stable
components: rustfmt, clippy
- - run: cargo clippy --all-features --all-targets
+ - run: cargo +stable clippy --all-features --all-targets
- run: git diff --exit-code
doc:
runs-on: ubuntu-latest
steps:
- - uses: actions/checkout@629c2de402a417ea7690ca6ce3f33229e27606a5 # v2
- - uses: actions-rs/toolchain@63eb9591781c46a70274cb3ebdf190fce92702e8 # v1
+ - uses: actions/checkout@8e5e7e5ab8b370d6c329ec480221332ada57f0ab # v3.5.2
+ - uses: dtolnay/rust-toolchain@a3ac054b2e7d62f514aa1bd57e3508c522fe772d # 1.68.2
with:
- profile: minimal
toolchain: stable
- run: RUSTDOCFLAGS="-Dwarnings" cargo doc --no-deps --document-private-items
udeps:
runs-on: ubuntu-latest
steps:
- - uses: actions/checkout@629c2de402a417ea7690ca6ce3f33229e27606a5 # v2
- - uses: actions-rs/toolchain@63eb9591781c46a70274cb3ebdf190fce92702e8 # v1
+ - uses: actions/checkout@8e5e7e5ab8b370d6c329ec480221332ada57f0ab # v3.5.2
+ - uses: dtolnay/rust-toolchain@a3ac054b2e7d62f514aa1bd57e3508c522fe772d # 1.68.2
with:
- profile: minimal
- toolchain: nightly-2022-01-01
- override: true
+ toolchain: nightly-2023-04-01
components: rustfmt
- - uses: actions-rs/install@69ec87709ffb5b19a7b5ddbf610cb221498bb1eb # v0.1.2
- with:
- crate: cargo-udeps
- use-tool-cache: true
- version: 0.1.25
- - run: cargo udeps
+ - run: cargo +nightly-2023-04-01 install --locked --version 0.1.39 cargo-udeps
+ - run: cargo +nightly-2023-04-01 udeps
deny:
runs-on: ubuntu-latest
steps:
- - uses: actions/checkout@629c2de402a417ea7690ca6ce3f33229e27606a5 # v2
- - uses: actions-rs/toolchain@63eb9591781c46a70274cb3ebdf190fce92702e8 # v1
+ - uses: actions/checkout@8e5e7e5ab8b370d6c329ec480221332ada57f0ab # v3.5.2
+ - uses: dtolnay/rust-toolchain@a3ac054b2e7d62f514aa1bd57e3508c522fe772d # 1.68.2
with:
- profile: minimal
- toolchain: nightly-2022-01-01
- override: true
+ toolchain: nightly-2023-04-01
components: rustfmt
- - run: cargo install --locked --version 0.8.5 cargo-deny
+ - run: cargo +nightly-2023-04-01 install --locked --version 0.13.9 cargo-deny
- run: cargo deny check
coverage:
runs-on: ubuntu-latest
steps:
- - uses: actions/checkout@629c2de402a417ea7690ca6ce3f33229e27606a5 # v2
+ - uses: actions/checkout@8e5e7e5ab8b370d6c329ec480221332ada57f0ab # v3.5.2
with:
submodules: true
fetch-depth: 0
- - uses: actions-rs/toolchain@63eb9591781c46a70274cb3ebdf190fce92702e8 # v1
+ - uses: dtolnay/rust-toolchain@a3ac054b2e7d62f514aa1bd57e3508c522fe772d # 1.68.2
with:
- profile: minimal
- toolchain: nightly-2022-01-01
- override: true
+ toolchain: nightly-2023-04-01
components: rustfmt
- uses: actions-rs/install@69ec87709ffb5b19a7b5ddbf610cb221498bb1eb # v0.1.2
with:
crate: cargo-tarpaulin
- version: 0.20.1
+ version: 0.25.2
use-tool-cache: true
- - run: cargo tarpaulin --verbose --ignore-tests --all-features --timeout=600 --out Xml
+ - run: cargo +nightly-2023-04-01 tarpaulin --verbose --ignore-tests --all-features --timeout=600 --out Xml
- name: Upload to codecov.io
run: |
bash <(curl -s https://codecov.io/bash)
diff --git a/Android.bp b/Android.bp
index a44aa1c..f2681a6 100644
--- a/Android.bp
+++ b/Android.bp
@@ -20,11 +20,10 @@ license {
rust_test {
name: "coset_test_src_lib",
- // has rustc warnings
host_supported: true,
crate_name: "coset",
cargo_env_compat: true,
- cargo_pkg_version: "0.3.4",
+ cargo_pkg_version: "0.3.5",
srcs: ["src/lib.rs"],
test_suites: ["general-tests"],
auto_gen_config: true,
@@ -42,11 +41,10 @@ rust_test {
rust_library {
name: "libcoset",
- // has rustc warnings
host_supported: true,
crate_name: "coset",
cargo_env_compat: true,
- cargo_pkg_version: "0.3.4",
+ cargo_pkg_version: "0.3.5",
srcs: ["src/lib.rs"],
edition: "2018",
features: ["default"],
@@ -64,10 +62,9 @@ rust_library {
rust_library_rlib {
name: "libcoset_nostd",
- // has rustc warnings
crate_name: "coset",
cargo_env_compat: true,
- cargo_pkg_version: "0.3.4",
+ cargo_pkg_version: "0.3.5",
srcs: ["src/lib.rs"],
edition: "2018",
rustlibs: [
diff --git a/CHANGELOG.md b/CHANGELOG.md
index df2b2cf..b01d470 100644
--- a/CHANGELOG.md
+++ b/CHANGELOG.md
@@ -1,5 +1,15 @@
# Change Log
+## 0.3.5 - 2023-09-29
+
+- Add helper methods to create and verify detached signatures:
+ - Add `CoseSignBuilder` methods `add_detached_signature` and `try_add_detached_signature`.
+ - Add `CoseSign` method `verify_detached_signature`.
+ - Add `CoseSign1Builder` methods `create_detached_signature` and `try_create_detached_signature`.
+ - Add `CoseSign1` method `verify_detached_signature`.
+- Implement CBOR conversion traits for `ciborium::value::Value`.
+- Update `ciborium` dependency.
+
## 0.3.4 - 2023-01-25
- Add non-default `std` feature that turns on `impl Error for CoseError`.
diff --git a/Cargo.lock b/Cargo.lock
index 328bb43..8063a90 100644
--- a/Cargo.lock
+++ b/Cargo.lock
@@ -4,9 +4,9 @@ version = 3
[[package]]
name = "ciborium"
-version = "0.2.0"
+version = "0.2.1"
source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "b0c137568cc60b904a7724001b35ce2630fd00d5d84805fbb608ab89509d788f"
+checksum = "effd91f6c78e5a4ace8a5d3c0b6bfaec9e2baaef55f3efc00e45fb2e477ee926"
dependencies = [
"ciborium-io",
"ciborium-ll",
@@ -15,15 +15,15 @@ dependencies = [
[[package]]
name = "ciborium-io"
-version = "0.2.0"
+version = "0.2.1"
source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "346de753af073cc87b52b2083a506b38ac176a44cfb05497b622e27be899b369"
+checksum = "cdf919175532b369853f5d5e20b26b43112613fd6fe7aee757e35f7a44642656"
[[package]]
name = "ciborium-ll"
-version = "0.2.0"
+version = "0.2.1"
source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "213030a2b5a4e0c0892b6652260cf6ccac84827b83a85a534e178e3906c4cf1b"
+checksum = "defaa24ecc093c77630e6c15e17c51f5e187bf35ee514f4e2d67baaa96dae22b"
dependencies = [
"ciborium-io",
"half",
@@ -31,7 +31,7 @@ dependencies = [
[[package]]
name = "coset"
-version = "0.3.4"
+version = "0.3.5"
dependencies = [
"ciborium",
"ciborium-io",
diff --git a/Cargo.toml b/Cargo.toml
index 5f31443..540a482 100644
--- a/Cargo.toml
+++ b/Cargo.toml
@@ -12,7 +12,7 @@
[package]
edition = "2018"
name = "coset"
-version = "0.3.4"
+version = "0.3.5"
authors = [
"David Drysdale <drysdale@google.com>",
"Paul Crowley <paulcrowley@google.com>",
@@ -28,7 +28,7 @@ license = "Apache-2.0"
repository = "https://github.com/google/coset"
[dependencies.ciborium]
-version = "^0.2.0"
+version = "^0.2.1"
default-features = false
[dependencies.ciborium-io]
diff --git a/Cargo.toml.orig b/Cargo.toml.orig
index e011996..1e35011 100644
--- a/Cargo.toml.orig
+++ b/Cargo.toml.orig
@@ -1,6 +1,6 @@
[package]
name = "coset"
-version = "0.3.4"
+version = "0.3.5"
authors = ["David Drysdale <drysdale@google.com>", "Paul Crowley <paulcrowley@google.com>"]
edition = "2018"
license = "Apache-2.0"
@@ -15,7 +15,7 @@ default = []
std = []
[dependencies]
-ciborium = { version = "^0.2.0", default-features = false }
+ciborium = { version = "^0.2.1", default-features = false }
ciborium-io = { version = "^0.2.0", features = ["alloc"] }
[dev-dependencies]
diff --git a/METADATA b/METADATA
index 16e9249..9e30903 100644
--- a/METADATA
+++ b/METADATA
@@ -1,6 +1,6 @@
# This project was upgraded with external_updater.
# Usage: tools/external_updater/updater.sh update rust/crates/coset
-# For more info, check https://cs.android.com/android/platform/superproject/+/master:tools/external_updater/README.md
+# For more info, check https://cs.android.com/android/platform/superproject/+/main:tools/external_updater/README.md
name: "coset"
description: "Set of types for supporting COSE"
@@ -11,13 +11,13 @@ third_party {
}
url {
type: ARCHIVE
- value: "https://static.crates.io/crates/coset/coset-0.3.4.crate"
+ value: "https://static.crates.io/crates/coset/coset-0.3.5.crate"
}
- version: "0.3.4"
+ version: "0.3.5"
license_type: NOTICE
last_upgrade_date {
year: 2023
- month: 2
- day: 15
+ month: 9
+ day: 29
}
}
diff --git a/README.md b/README.md
index 5b51f27..fb51cd9 100644
--- a/README.md
+++ b/README.md
@@ -1,7 +1,7 @@
# COSET
[![Docs](https://img.shields.io/badge/docs-rust-brightgreen?style=for-the-badge)](https://google.github.io/coset)
-[![CI Status](https://img.shields.io/github/workflow/status/google/coset/CI?color=blue&style=for-the-badge)](https://github.com/google/coset/actions?query=workflow%3ACI)
+[![CI Status](https://img.shields.io/github/actions/workflow/status/google/coset/ci.yml?branch=main&color=blue&style=for-the-badge)](https://github.com/google/coset/actions?query=workflow%3ACI)
[![codecov](https://img.shields.io/codecov/c/github/google/coset?style=for-the-badge)](https://codecov.io/gh/google/coset)
This crate holds a set of Rust types for working with CBOR Object Signing and Encryption (COSE) objects, as defined in
@@ -13,9 +13,13 @@ example](examples/signature.rs) for documentation on how to use the code.
**This repo is under construction** and so details of the API and the code may change without warning.
+## Features
+
+The `std` feature of the crate enables an implementation of `std::error::Error` for `CoseError`.
+
## `no_std` Support
-This crate supports `no_std`, but uses the `alloc` crate.
+This crate supports `no_std` (when the `std` feature is not set, which is the default), but uses the `alloc` crate.
## Minimum Supported Rust Version
diff --git a/src/common/mod.rs b/src/common/mod.rs
index 106a3a3..b94ec31 100644
--- a/src/common/mod.rs
+++ b/src/common/mod.rs
@@ -57,8 +57,16 @@ pub enum CoseError {
/// Crate-specific Result type
pub type Result<T, E = CoseError> = core::result::Result<T, E>;
-impl core::convert::From<cbor::de::Error<EndOfFile>> for CoseError {
- fn from(e: cbor::de::Error<EndOfFile>) -> Self {
+impl<T> core::convert::From<cbor::de::Error<T>> for CoseError {
+ fn from(e: cbor::de::Error<T>) -> Self {
+ // Make sure we use our [`EndOfFile`] marker.
+ use cbor::de::Error::{Io, RecursionLimitExceeded, Semantic, Syntax};
+ let e = match e {
+ Io(_) => Io(EndOfFile),
+ Syntax(x) => Syntax(x),
+ Semantic(a, b) => Semantic(a, b),
+ RecursionLimitExceeded => RecursionLimitExceeded,
+ };
CoseError::DecodeFailed(e)
}
}
@@ -107,40 +115,11 @@ impl CoseError {
}
}
-/// Newtype wrapper around a byte slice to allow left-over data to be detected.
-struct MeasuringReader<'a>(&'a [u8]);
-
-impl<'a> MeasuringReader<'a> {
- fn new(buf: &'a [u8]) -> MeasuringReader<'a> {
- MeasuringReader(buf)
- }
-
- fn is_empty(&self) -> bool {
- self.0.is_empty()
- }
-}
-
-impl<'a> ciborium_io::Read for &mut MeasuringReader<'a> {
- type Error = EndOfFile;
-
- fn read_exact(&mut self, data: &mut [u8]) -> Result<(), Self::Error> {
- if data.len() > self.0.len() {
- return Err(EndOfFile);
- }
-
- let (prefix, suffix) = self.0.split_at(data.len());
- data.copy_from_slice(prefix);
- self.0 = suffix;
- Ok(())
- }
-}
-
/// Read a CBOR [`Value`] from a byte slice, failing if any extra data remains after the `Value` has
/// been read.
-fn read_to_value(slice: &[u8]) -> Result<Value> {
- let mut mr = MeasuringReader::new(slice);
- let value = cbor::de::from_reader(&mut mr)?;
- if mr.is_empty() {
+fn read_to_value(mut slice: &[u8]) -> Result<Value> {
+ let value = cbor::de::from_reader(&mut slice)?;
+ if slice.is_empty() {
Ok(value)
} else {
Err(CoseError::ExtraneousData)
@@ -157,7 +136,8 @@ pub trait AsCborValue: Sized {
/// Extension trait that adds serialization/deserialization methods.
pub trait CborSerializable: AsCborValue {
- /// Create an object instance from serialized CBOR data in a slice.
+ /// Create an object instance from serialized CBOR data in a slice. This method will fail (with
+ /// `CoseError::ExtraneousData`) if there is additional CBOR data after the object.
fn from_slice(slice: &[u8]) -> Result<Self> {
Self::from_cbor_value(read_to_value(slice)?)
}
@@ -197,6 +177,18 @@ pub trait TaggedCborSerializable: AsCborValue {
}
}
+/// Trivial implementation of [`AsCborValue`] for [`Value`].
+impl AsCborValue for Value {
+ fn from_cbor_value(value: Value) -> Result<Self> {
+ Ok(value)
+ }
+ fn to_cbor_value(self) -> Result<Value> {
+ Ok(self)
+ }
+}
+
+impl CborSerializable for Value {}
+
/// Algorithm identifier.
pub type Algorithm = crate::RegisteredLabelWithPrivate<iana::Algorithm>;
diff --git a/src/common/tests.rs b/src/common/tests.rs
index ed6f82b..e7b59aa 100644
--- a/src/common/tests.rs
+++ b/src/common/tests.rs
@@ -101,7 +101,7 @@ fn test_label_sort() {
#[test]
fn test_label_decode_fail() {
- let tests = vec![
+ let tests = [
("43010203", "expected int/tstr"),
("", "decode CBOR failure: Io(EndOfFile"),
("1e", "decode CBOR failure: Syntax"),
@@ -182,7 +182,7 @@ fn test_registered_label_sort() {
#[test]
fn test_registered_label_decode_fail() {
- let tests = vec![
+ let tests = [
("43010203", "expected int/tstr"),
("", "decode CBOR failure: Io(EndOfFile"),
("09", "expected recognized IANA value"),
@@ -292,7 +292,7 @@ fn test_registered_label_with_private_sort() {
#[test]
fn test_registered_label_with_private_decode_fail() {
- let tests = vec![
+ let tests = [
("43010203", "expected int/tstr"),
("", "decode CBOR failure: Io(EndOfFile"),
("09", "expected value in IANA or private use range"),
@@ -325,7 +325,7 @@ const CBOR_INT_OUT_OF_RANGE_HEX: &str = "1b8000000000000000";
#[test]
fn test_large_label_decode() {
- let tests = vec![(CBOR_NINT_MIN_HEX, i64::MIN), (CBOR_INT_MAX_HEX, i64::MAX)];
+ let tests = [(CBOR_NINT_MIN_HEX, i64::MIN), (CBOR_INT_MAX_HEX, i64::MAX)];
for (label_data, want) in tests.iter() {
let data = hex::decode(label_data).unwrap();
let got = Label::from_slice(&data).unwrap();
@@ -335,7 +335,7 @@ fn test_large_label_decode() {
#[test]
fn test_large_label_decode_fail() {
- let tests = vec![
+ let tests = [
(CBOR_NINT_OUT_OF_RANGE_HEX, "out of range integer value"),
(CBOR_INT_OUT_OF_RANGE_HEX, "out of range integer value"),
];
@@ -348,7 +348,7 @@ fn test_large_label_decode_fail() {
#[test]
fn test_large_registered_label_decode_fail() {
- let tests = vec![
+ let tests = [
(CBOR_NINT_OUT_OF_RANGE_HEX, "out of range integer value"),
(CBOR_INT_OUT_OF_RANGE_HEX, "out of range integer value"),
];
@@ -361,7 +361,7 @@ fn test_large_registered_label_decode_fail() {
#[test]
fn test_large_registered_label_with_private_decode_fail() {
- let tests = vec![
+ let tests = [
(CBOR_NINT_OUT_OF_RANGE_HEX, "out of range integer value"),
(CBOR_INT_OUT_OF_RANGE_HEX, "out of range integer value"),
];
diff --git a/src/encrypt/tests.rs b/src/encrypt/tests.rs
index 56e24c4..997014b 100644
--- a/src/encrypt/tests.rs
+++ b/src/encrypt/tests.rs
@@ -71,7 +71,7 @@ fn test_cose_recipient_decode() {
let mut got = CoseRecipient::from_slice(&got).unwrap();
got.protected.original_data = None;
- for mut recip in &mut got.recipients {
+ for recip in &mut got.recipients {
recip.protected.original_data = None;
}
assert_eq!(*recipient, got);
@@ -80,7 +80,7 @@ fn test_cose_recipient_decode() {
#[test]
fn test_cose_recipient_decode_fail() {
- let tests = vec![
+ let tests = [
(
concat!(
"a2", // 2-map (should be tuple)
@@ -184,7 +184,7 @@ fn test_cose_encrypt_decode() {
#[test]
fn test_cose_encrypt_decode_fail() {
- let tests = vec![
+ let tests = [
(
concat!(
"a2", // 2-map (should be tuple)
@@ -473,10 +473,10 @@ fn test_rfc8152_cose_encrypt_decode() {
let mut got = CoseEncrypt::from_tagged_slice(&got).unwrap();
got.protected.original_data = None;
- for mut recip in &mut got.recipients {
+ for recip in &mut got.recipients {
recip.protected.original_data = None;
}
- for mut sig in &mut got.unprotected.counter_signatures {
+ for sig in &mut got.unprotected.counter_signatures {
sig.protected.original_data = None;
}
assert_eq!(*encrypt, got);
@@ -518,7 +518,7 @@ fn test_cose_encrypt0_decode() {
#[test]
fn test_cose_encrypt0_decode_fail() {
- let tests = vec![
+ let tests = [
(
concat!(
"a2", // 2-map (should be tuple)
diff --git a/src/header/tests.rs b/src/header/tests.rs
index 6ff901a..9dff115 100644
--- a/src/header/tests.rs
+++ b/src/header/tests.rs
@@ -153,7 +153,7 @@ fn test_header_encode() {
assert_eq!(*header_data, hex::encode(&got), "case {}", i);
let mut got = Header::from_slice(&got).unwrap();
- for mut sig in &mut got.counter_signatures {
+ for sig in &mut got.counter_signatures {
sig.protected.original_data = None;
}
assert_eq!(*header, got);
@@ -168,7 +168,7 @@ fn test_header_encode() {
assert_eq!(*header_data, hex::encode(&protected_data), "case {}", i);
let mut got = ProtectedHeader::from_slice(&protected_data).unwrap();
- for mut sig in &mut got.header.counter_signatures {
+ for sig in &mut got.header.counter_signatures {
sig.protected.original_data = None;
}
assert!(!got.is_empty());
@@ -177,7 +177,7 @@ fn test_header_encode() {
// Also try parsing as a protected header inside a `bstr`
let prot_bstr_val = protected.cbor_bstr().unwrap();
let mut got = ProtectedHeader::from_cbor_bstr(prot_bstr_val).unwrap();
- for mut sig in &mut got.header.counter_signatures {
+ for sig in &mut got.header.counter_signatures {
sig.protected.original_data = None;
}
assert!(!got.is_empty());
@@ -361,7 +361,7 @@ fn test_header_decode_fail() {
#[test]
fn test_header_decode_dup_fail() {
- let tests = vec![
+ let tests = [
(
concat!(
"a3", // 3-map
diff --git a/src/key/tests.rs b/src/key/tests.rs
index 45f8eef..ada3673 100644
--- a/src/key/tests.rs
+++ b/src/key/tests.rs
@@ -569,7 +569,7 @@ fn test_cose_key_decode_fail() {
#[test]
fn test_cose_keyset_decode_fail() {
- let tests = vec![(
+ let tests = [(
concat!(
"a1", // 1-map
"a1", // 1-map
@@ -587,7 +587,7 @@ fn test_cose_keyset_decode_fail() {
#[test]
fn test_cose_key_decode_dup_fail() {
- let tests = vec![
+ let tests = [
(
concat!(
"a3", // 3-map
diff --git a/src/mac/tests.rs b/src/mac/tests.rs
index 9a5fffc..066e155 100644
--- a/src/mac/tests.rs
+++ b/src/mac/tests.rs
@@ -351,10 +351,10 @@ fn test_rfc8152_cose_mac_decode() {
let mut got = CoseMac::from_tagged_slice(&got).unwrap();
got.protected.original_data = None;
- for mut recip in &mut got.recipients {
+ for recip in &mut got.recipients {
recip.protected.original_data = None;
}
- for mut sig in &mut got.unprotected.counter_signatures {
+ for sig in &mut got.unprotected.counter_signatures {
sig.protected.original_data = None;
}
assert_eq!(*mac, got);
@@ -396,7 +396,7 @@ fn test_cose_mac0_decode() {
}
#[test]
fn test_cose_mac0_decode_fail() {
- let tests = vec![
+ let tests = [
(
concat!(
"a2", // 2-map (should be tuple)
diff --git a/src/sign/mod.rs b/src/sign/mod.rs
index a6bb344..9abc6a4 100644
--- a/src/sign/mod.rs
+++ b/src/sign/mod.rs
@@ -142,7 +142,7 @@ impl AsCborValue for CoseSign {
}
impl CoseSign {
- /// Verify the indidated signature value, using `verifier` on the signature value and serialized
+ /// Verify the indicated signature value, using `verifier` on the signature value and serialized
/// data (in that order).
///
/// # Panics
@@ -157,6 +157,29 @@ impl CoseSign {
verifier(&sig.signature, &tbs_data)
}
+ /// Verify the indicated signature value for a detached payload, using `verifier` on the
+ /// signature value and serialized data (in that order).
+ ///
+ /// # Panics
+ ///
+ /// This method will panic if `which` is >= `self.signatures.len()`.
+ ///
+ /// This method will panic if `self.payload.is_some()`.
+ pub fn verify_detached_signature<F, E>(
+ &self,
+ which: usize,
+ payload: &[u8],
+ aad: &[u8],
+ verifier: F,
+ ) -> Result<(), E>
+ where
+ F: FnOnce(&[u8], &[u8]) -> Result<(), E>,
+ {
+ let sig = &self.signatures[which];
+ let tbs_data = self.tbs_detached_data(payload, aad, sig);
+ verifier(&sig.signature, &tbs_data)
+ }
+
/// Construct the to-be-signed data for this object.
fn tbs_data(&self, aad: &[u8], sig: &CoseSignature) -> Vec<u8> {
sig_structure_data(
@@ -167,6 +190,22 @@ impl CoseSign {
self.payload.as_ref().unwrap_or(&vec![]),
)
}
+
+ /// Construct the to-be-signed data for this object, using a detached payload.
+ ///
+ /// # Panics
+ ///
+ /// This method will panic if `self.payload.is_some()`.
+ fn tbs_detached_data(&self, payload: &[u8], aad: &[u8], sig: &CoseSignature) -> Vec<u8> {
+ assert!(self.payload.is_none());
+ sig_structure_data(
+ SignatureContext::CoseSignature,
+ self.protected.clone(),
+ Some(sig.protected.clone()),
+ aad,
+ payload,
+ )
+ }
}
/// Builder for [`CoseSign`] objects.
@@ -199,6 +238,29 @@ impl CoseSignBuilder {
self.add_signature(sig)
}
+ /// Calculate the signature value for a detached payload, using `signer` to generate the
+ /// signature bytes that will be used to complete `sig`. Any protected header values should
+ /// be set before using this method.
+ ///
+ /// # Panics
+ ///
+ /// This method will panic if `self.payload.is_some()`.
+ #[must_use]
+ pub fn add_detached_signature<F>(
+ self,
+ mut sig: CoseSignature,
+ payload: &[u8],
+ aad: &[u8],
+ signer: F,
+ ) -> Self
+ where
+ F: FnOnce(&[u8]) -> Vec<u8>,
+ {
+ let tbs_data = self.0.tbs_detached_data(payload, aad, &sig);
+ sig.signature = signer(&tbs_data);
+ self.add_signature(sig)
+ }
+
/// Calculate the signature value, using `signer` to generate the signature bytes that will be
/// used to complete `sig`. Any protected header values should be set before using this
/// method.
@@ -215,6 +277,28 @@ impl CoseSignBuilder {
sig.signature = signer(&tbs_data)?;
Ok(self.add_signature(sig))
}
+
+ /// Calculate the signature value for a detached payload, using `signer` to generate the
+ /// signature bytes that will be used to complete `sig`. Any protected header values should
+ /// be set before using this method.
+ ///
+ /// # Panics
+ ///
+ /// This method will panic if `self.payload.is_some()`.
+ pub fn try_add_detached_signature<F, E>(
+ self,
+ mut sig: CoseSignature,
+ payload: &[u8],
+ aad: &[u8],
+ signer: F,
+ ) -> Result<Self, E>
+ where
+ F: FnOnce(&[u8]) -> Result<Vec<u8>, E>,
+ {
+ let tbs_data = self.0.tbs_detached_data(payload, aad, &sig);
+ sig.signature = signer(&tbs_data)?;
+ Ok(self.add_signature(sig))
+ }
}
/// Signed payload with a single signature.
@@ -283,6 +367,25 @@ impl CoseSign1 {
verifier(&self.signature, &tbs_data)
}
+ /// Verify the indicated signature value for a detached payload, using `verifier` on the
+ /// signature value and serialized data (in that order).
+ ///
+ /// # Panics
+ ///
+ /// This method will panic if `self.payload.is_some()`.
+ pub fn verify_detached_signature<F, E>(
+ &self,
+ payload: &[u8],
+ aad: &[u8],
+ verifier: F,
+ ) -> Result<(), E>
+ where
+ F: FnOnce(&[u8], &[u8]) -> Result<(), E>,
+ {
+ let tbs_data = self.tbs_detached_data(payload, aad);
+ verifier(&self.signature, &tbs_data)
+ }
+
/// Construct the to-be-signed data for this object.
fn tbs_data(&self, aad: &[u8]) -> Vec<u8> {
sig_structure_data(
@@ -293,6 +396,22 @@ impl CoseSign1 {
self.payload.as_ref().unwrap_or(&vec![]),
)
}
+
+ /// Construct the to-be-signed data for this object, using a detached payload.
+ ///
+ /// # Panics
+ ///
+ /// This method will panic if `self.payload.is_some()`.
+ fn tbs_detached_data(&self, payload: &[u8], aad: &[u8]) -> Vec<u8> {
+ assert!(self.payload.is_none());
+ sig_structure_data(
+ SignatureContext::CoseSign1,
+ self.protected.clone(),
+ None,
+ aad,
+ payload,
+ )
+ }
}
/// Builder for [`CoseSign1`] objects.
@@ -317,6 +436,21 @@ impl CoseSign1Builder {
self.signature(sig_data)
}
+ /// Calculate the signature value for a detached payload, using `signer` to generate the
+ /// signature bytes. Any protected header values should be set before using this method.
+ ///
+ /// # Panics
+ ///
+ /// This method will panic if `self.payload.is_some()`.
+ #[must_use]
+ pub fn create_detached_signature<F>(self, payload: &[u8], aad: &[u8], signer: F) -> Self
+ where
+ F: FnOnce(&[u8]) -> Vec<u8>,
+ {
+ let sig_data = signer(&self.0.tbs_detached_data(payload, aad));
+ self.signature(sig_data)
+ }
+
/// Calculate the signature value, using `signer` to generate the signature bytes. Any
/// protected header values should be set before using this method.
pub fn try_create_signature<F, E>(self, aad: &[u8], signer: F) -> Result<Self, E>
@@ -326,6 +460,25 @@ impl CoseSign1Builder {
let sig_data = signer(&self.0.tbs_data(aad))?;
Ok(self.signature(sig_data))
}
+
+ /// Calculate the signature value for a detached payload, using `signer` to generate the
+ /// signature bytes. Any protected header values should be set before using this method.
+ ///
+ /// # Panics
+ ///
+ /// This method will panic if `self.payload.is_some()`.
+ pub fn try_create_detached_signature<F, E>(
+ self,
+ payload: &[u8],
+ aad: &[u8],
+ signer: F,
+ ) -> Result<Self, E>
+ where
+ F: FnOnce(&[u8]) -> Result<Vec<u8>, E>,
+ {
+ let sig_data = signer(&self.0.tbs_detached_data(payload, aad))?;
+ Ok(self.signature(sig_data))
+ }
}
/// Possible signature contexts.
diff --git a/src/sign/tests.rs b/src/sign/tests.rs
index d802b90..a35c14c 100644
--- a/src/sign/tests.rs
+++ b/src/sign/tests.rs
@@ -381,7 +381,7 @@ fn test_cose_sign_encode() {
let mut got = CoseSign::from_slice(&got).unwrap();
got.protected.original_data = None;
- for mut sig in &mut got.signatures {
+ for sig in &mut got.signatures {
sig.protected.original_data = None;
}
assert_eq!(*sign, got);
@@ -393,7 +393,7 @@ fn test_cose_sign_encode() {
let mut got = CoseSign::from_tagged_slice(&got).unwrap();
got.protected.original_data = None;
- for mut sig in &mut got.signatures {
+ for sig in &mut got.signatures {
sig.protected.original_data = None;
}
assert_eq!(*sign, got);
@@ -751,10 +751,10 @@ fn test_rfc8152_cose_sign_decode() {
let mut got = CoseSign::from_tagged_slice(&got).unwrap();
got.protected.original_data = None;
- for mut sig in &mut got.signatures {
+ for sig in &mut got.signatures {
sig.protected.original_data = None;
}
- for mut sig in &mut got.unprotected.counter_signatures {
+ for sig in &mut got.unprotected.counter_signatures {
sig.protected.original_data = None;
}
assert_eq!(*sign, got);
@@ -1178,6 +1178,65 @@ fn test_sign_roundtrip() {
}
#[test]
+fn test_sign_detached_roundtrip() {
+ let signer = FakeSigner {};
+ let verifier = signer;
+
+ let pt = b"This is the content";
+ let aad = b"this is additional data";
+
+ let protected = HeaderBuilder::new()
+ .algorithm(iana::Algorithm::ES256)
+ .key_id(b"11".to_vec())
+ .build();
+ let sign = CoseSignBuilder::new()
+ .protected(protected.clone())
+ .add_detached_signature(
+ CoseSignatureBuilder::new().protected(protected).build(),
+ pt,
+ aad,
+ |pt| signer.sign(pt),
+ )
+ .build();
+
+ let sign_data = sign.to_vec().unwrap();
+ let mut sign = CoseSign::from_slice(&sign_data).unwrap();
+
+ assert!(sign
+ .verify_detached_signature(0, pt, aad, |sig, data| verifier.verify(sig, data))
+ .is_ok());
+
+ // Changing an unprotected header leaves the signature valid.
+ sign.unprotected.content_type = Some(ContentType::Text("text/plain".to_owned()));
+ assert!(sign
+ .verify_detached_signature(0, pt, aad, |sig, data| verifier.verify(sig, data))
+ .is_ok());
+
+ // Providing a different `payload` means the signature won't validate.
+ assert!(sign
+ .verify_detached_signature(0, b"not payload", aad, |sig, data| verifier
+ .verify(sig, data))
+ .is_err());
+
+ // Providing a different `aad` means the signature won't validate.
+ assert!(sign
+ .verify_detached_signature(0, pt, b"not aad", |sig, data| verifier.verify(sig, data))
+ .is_err());
+
+ // Changing a protected header invalidates the signature.
+ let mut sign2 = sign.clone();
+ sign2.protected = ProtectedHeader::default();
+ assert!(sign2
+ .verify_detached_signature(0, pt, aad, |sig, data| verifier.verify(sig, data))
+ .is_err());
+ let mut sign3 = sign;
+ sign3.signatures[0].protected = ProtectedHeader::default();
+ assert!(sign2
+ .verify_detached_signature(0, pt, aad, |sig, data| verifier.verify(sig, data))
+ .is_err());
+}
+
+#[test]
fn test_sign_noncanonical() {
let signer = FakeSigner {};
let verifier = signer;
@@ -1289,6 +1348,41 @@ fn test_sign_create_result() {
}
#[test]
+fn test_sign_detached_create_result() {
+ let signer = FakeSigner {};
+
+ let pt = b"This is the content";
+ let aad = b"this is additional data";
+
+ let protected = HeaderBuilder::new()
+ .algorithm(iana::Algorithm::ES256)
+ .key_id(b"11".to_vec())
+ .build();
+ let _sign = CoseSignBuilder::new()
+ .protected(protected.clone())
+ .try_add_detached_signature(
+ CoseSignatureBuilder::new()
+ .protected(protected.clone())
+ .build(),
+ pt,
+ aad,
+ |pt| signer.try_sign(pt),
+ )
+ .unwrap()
+ .build();
+
+ let result = CoseSignBuilder::new()
+ .protected(protected.clone())
+ .try_add_detached_signature(
+ CoseSignatureBuilder::new().protected(protected).build(),
+ pt,
+ aad,
+ |pt| signer.fail_sign(pt),
+ );
+ expect_err(result, "failed");
+}
+
+#[test]
#[should_panic]
fn test_sign_sig_index_invalid() {
let signer = FakeSigner {};
@@ -1316,6 +1410,167 @@ fn test_sign_sig_index_invalid() {
}
#[test]
+#[should_panic]
+fn test_sign_detached_sig_index_invalid() {
+ let signer = FakeSigner {};
+ let verifier = signer;
+
+ let pt = b"This is the content";
+ let aad = b"this is additional data";
+
+ let protected = HeaderBuilder::new()
+ .algorithm(iana::Algorithm::ES256)
+ .key_id(b"11".to_vec())
+ .build();
+ let sign = CoseSignBuilder::new()
+ .protected(protected)
+ .add_detached_signature(CoseSignatureBuilder::new().build(), pt, aad, |pt| {
+ signer.sign(pt)
+ })
+ .build();
+
+ // Attempt to verify an out-of-range signature
+ let _ = sign.verify_detached_signature(sign.signatures.len(), pt, aad, |sig, data| {
+ verifier.verify(sig, data)
+ });
+}
+
+#[test]
+#[should_panic]
+fn test_sign1_create_detached_signature_embeddedpayload() {
+ let signer = FakeSigner {};
+
+ let payload = b"this is the content";
+ let aad = b"this is additional data";
+
+ // Attempt to create a detached signature for a message with an embedded payload
+ let _ = CoseSign1Builder::new()
+ .protected(
+ HeaderBuilder::new()
+ .algorithm(iana::Algorithm::ES256)
+ .build(),
+ )
+ .payload(payload.to_vec())
+ .create_detached_signature(payload, aad, |pt| signer.sign(pt))
+ .build();
+}
+
+#[test]
+#[should_panic]
+fn test_sign1_try_create_detached_signature_embeddedpayload() {
+ let signer = FakeSigner {};
+
+ let payload = b"this is the content";
+ let aad = b"this is additional data";
+
+ // Attempt to create a detached signature for a message with an embedded payload
+ let _ = CoseSign1Builder::new()
+ .protected(
+ HeaderBuilder::new()
+ .algorithm(iana::Algorithm::ES256)
+ .build(),
+ )
+ .payload(payload.to_vec())
+ .try_create_detached_signature(payload, aad, |pt| Ok::<Vec<u8>, String>(signer.sign(pt)))
+ .unwrap()
+ .build();
+}
+
+#[test]
+#[should_panic]
+fn test_sign1_verify_detached_signature_embeddedpayload() {
+ let signer = FakeSigner {};
+
+ let payload = b"this is the content";
+ let aad = b"this is additional data";
+
+ let mut sign1 = CoseSign1Builder::new()
+ .protected(
+ HeaderBuilder::new()
+ .algorithm(iana::Algorithm::ES256)
+ .build(),
+ )
+ .create_detached_signature(payload, aad, |pt| signer.sign(pt))
+ .build();
+
+ // Attempt to verify a detached signature for a message with an embedded payload
+ sign1.payload = Some(payload.to_vec());
+ sign1
+ .verify_detached_signature(payload, aad, |sig, data| signer.verify(sig, data))
+ .unwrap()
+}
+
+#[test]
+#[should_panic]
+fn test_sign_add_detached_signature_embeddedpayload() {
+ let signer = FakeSigner {};
+
+ let payload = b"this is the content";
+ let aad = b"this is additional data";
+
+ // Attempt to add a detached signature to a message with an embedded payload
+ let _ = CoseSignBuilder::new()
+ .protected(
+ HeaderBuilder::new()
+ .algorithm(iana::Algorithm::ES256)
+ .build(),
+ )
+ .payload(payload.to_vec())
+ .add_detached_signature(CoseSignatureBuilder::new().build(), payload, aad, |pt| {
+ signer.sign(pt)
+ })
+ .build();
+}
+
+#[test]
+#[should_panic]
+fn test_sign_try_add_detached_signature_embeddedpayload() {
+ let signer = FakeSigner {};
+
+ let payload = b"this is the content";
+ let aad = b"this is additional data";
+
+ // Attempt to create a detached signature for a message with an embedded payload
+ let _ = CoseSignBuilder::new()
+ .protected(
+ HeaderBuilder::new()
+ .algorithm(iana::Algorithm::ES256)
+ .build(),
+ )
+ .payload(payload.to_vec())
+ .try_add_detached_signature(CoseSignatureBuilder::new().build(), payload, aad, |pt| {
+ Ok::<Vec<u8>, String>(signer.sign(pt))
+ })
+ .unwrap()
+ .build();
+}
+
+#[test]
+#[should_panic]
+fn test_sign_verify_detached_signature_embeddedpayload() {
+ let signer = FakeSigner {};
+
+ let payload = b"this is the content";
+ let aad = b"this is additional data";
+
+ let mut sign = CoseSignBuilder::new()
+ .protected(
+ HeaderBuilder::new()
+ .algorithm(iana::Algorithm::ES256)
+ .build(),
+ )
+ .add_detached_signature(CoseSignatureBuilder::new().build(), payload, aad, |pt| {
+ signer.sign(pt)
+ })
+ .build();
+
+ // Attempt to verify a detached signature for a message with an embedded payload
+ sign.payload = Some(payload.to_vec());
+ sign.verify_detached_signature(0, payload, aad, |sig, data| signer.verify(sig, data))
+ .unwrap()
+}
+
+#[test]
fn test_sign1_roundtrip() {
let signer = FakeSigner {};
let verifier = signer;
@@ -1360,6 +1615,54 @@ fn test_sign1_roundtrip() {
}
#[test]
+fn test_sign1_detached_roundtrip() {
+ let signer = FakeSigner {};
+ let verifier = signer;
+
+ let pt = b"This is the content";
+ let aad = b"this is additional data";
+
+ let protected = HeaderBuilder::new()
+ .algorithm(iana::Algorithm::ES256)
+ .key_id(b"11".to_vec())
+ .build();
+ let sign1 = CoseSign1Builder::new()
+ .protected(protected)
+ .create_detached_signature(pt, aad, |pt| signer.sign(pt))
+ .build();
+
+ let sign1_data = sign1.to_vec().unwrap();
+ let mut sign1 = CoseSign1::from_slice(&sign1_data).unwrap();
+
+ assert!(sign1
+ .verify_detached_signature(pt, aad, |sig, data| verifier.verify(sig, data))
+ .is_ok());
+
+ // Changing an unprotected header leaves the signature valid.
+ sign1.unprotected.content_type = Some(ContentType::Text("text/plain".to_owned()));
+ assert!(sign1
+ .verify_detached_signature(pt, aad, |sig, data| verifier.verify(sig, data))
+ .is_ok());
+
+ // Providing a different 'payload' means the signature won't validate.
+ assert!(sign1
+ .verify_detached_signature(b"not payload", aad, |sig, data| verifier.verify(sig, data))
+ .is_err());
+
+ // Providing a different `aad` means the signature won't validate.
+ assert!(sign1
+ .verify_detached_signature(pt, b"not aad", |sig, data| verifier.verify(sig, data))
+ .is_err());
+
+ // Changing a protected header invalidates the signature.
+ sign1.protected.original_data = None;
+ sign1.protected.header.content_type = Some(ContentType::Text("text/plain".to_owned()));
+ assert!(sign1
+ .verify_detached_signature(pt, aad, |sig, data| verifier.verify(sig, data))
+ .is_err());
+}
+
+#[test]
fn test_sign1_create_result() {
let signer = FakeSigner {};
@@ -1385,6 +1688,29 @@ fn test_sign1_create_result() {
}
#[test]
+fn test_sign1_create_detached_result() {
+ let signer = FakeSigner {};
+
+ let pt = b"This is the content";
+ let aad = b"this is additional data";
+
+ let protected = HeaderBuilder::new()
+ .algorithm(iana::Algorithm::ES256)
+ .key_id(b"11".to_vec())
+ .build();
+ let _sign = CoseSign1Builder::new()
+ .protected(protected.clone())
+ .try_create_detached_signature(pt, aad, |pt| signer.try_sign(pt))
+ .unwrap()
+ .build();
+
+ let result = CoseSign1Builder::new()
+ .protected(protected)
+ .try_create_detached_signature(pt, aad, |pt| signer.fail_sign(pt));
+ expect_err(result, "failed");
+}
+
+#[test]
fn test_sign1_noncanonical() {
let signer = FakeSigner {};
let verifier = signer;