Snap for 10453563 from 924690d88ac076d17921eb8ba110233b7ea12e03 to mainline-ipsec-release

Change-Id: I048ee900b2324f8cebd4638e5f1750d250a03611
diff --git a/.cargo_vcs_info.json b/.cargo_vcs_info.json
index 2fcab48..79d70ec 100644
--- a/.cargo_vcs_info.json
+++ b/.cargo_vcs_info.json
@@ -1,6 +1,6 @@
 {
   "git": {
-    "sha1": "818bd1e3b3ff44e9b179532d2b4d942ea34bd1a1"
+    "sha1": "8a8552a8b57f004d08d081230659b47578c86b66"
   },
   "path_in_vcs": ""
 }
\ No newline at end of file
diff --git a/.github/workflows/ci.yml b/.github/workflows/ci.yml
index 3430ad1..14bc1f5 100644
--- a/.github/workflows/ci.yml
+++ b/.github/workflows/ci.yml
@@ -28,6 +28,7 @@
           components: rustfmt
           override: true
       - run: cargo build --release --workspace
+      - run: cargo build --release --workspace --features=std
 
   test:
     runs-on: ubuntu-latest
@@ -48,6 +49,28 @@
           components: rustfmt
           override: true
       - run: cargo test --workspace -- --nocapture
+      - run: cargo test --workspace --features=std -- --nocapture
+
+  examples:
+    runs-on: ubuntu-latest
+    strategy:
+      matrix:
+        rust:
+          - stable
+          - beta
+          - nightly-2022-01-01
+    steps:
+      - uses: actions/checkout@629c2de402a417ea7690ca6ce3f33229e27606a5 # v2
+        with:
+          submodules: true
+      - uses: actions-rs/toolchain@63eb9591781c46a70274cb3ebdf190fce92702e8 # v1
+        with:
+          profile: minimal
+          toolchain: ${{ matrix.rust }}
+          components: rustfmt
+          override: true
+      - run: cargo test --examples
+      - run: cargo test --features=std --examples
 
   no_std:
     name: Build for a no_std target
@@ -87,6 +110,7 @@
           components: rustfmt
           override: true
       - run: rustc --version
+      - run: cargo build --release --workspace
       - run: cargo build --release --workspace --all-features
 
   formatting:
@@ -94,7 +118,7 @@
     steps:
       - uses: actions/checkout@629c2de402a417ea7690ca6ce3f33229e27606a5 # v2
       - uses: actions/setup-go@424fc82d43fa5a37540bae62709ddcc23d9520d4 # v2
-      - run: go get github.com/campoy/embedmd
+      - run: go install github.com/campoy/embedmd@97c13d6
       - uses: actions/setup-ruby@b007fae6f1ffbe3a51c00a6df6f5ff01184d5340 # v1
       - run: gem install mdl
       - uses: actions-rs/toolchain@63eb9591781c46a70274cb3ebdf190fce92702e8 # v1
@@ -175,6 +199,7 @@
       - uses: actions-rs/install@69ec87709ffb5b19a7b5ddbf610cb221498bb1eb # v0.1.2
         with:
           crate: cargo-tarpaulin
+          version: 0.20.1
           use-tool-cache: true
       - run: cargo tarpaulin --verbose --ignore-tests --all-features --timeout=600 --out Xml
       - name: Upload to codecov.io
diff --git a/Android.bp b/Android.bp
index 4c0f293..7dcf870 100644
--- a/Android.bp
+++ b/Android.bp
@@ -1,4 +1,4 @@
-// This file is generated by cargo2android.py --run --device --dependencies.
+// This file is generated by cargo2android.py --config cargo2android.json.
 // Do not modify this file as changes will be overridden on upgrade.
 
 package {
@@ -18,16 +18,44 @@
     ],
 }
 
+rust_test {
+    name: "coset_test_src_lib",
+    host_supported: true,
+    crate_name: "coset",
+    cargo_env_compat: true,
+    cargo_pkg_version: "0.3.4",
+    srcs: ["src/lib.rs"],
+    test_suites: ["general-tests"],
+    auto_gen_config: true,
+    test_options: {
+        unit_test: true,
+    },
+    edition: "2018",
+    features: ["default"],
+    rustlibs: [
+        "libciborium",
+        "libciborium_io",
+        "libhex",
+    ],
+}
+
 rust_library {
     name: "libcoset",
     host_supported: true,
     crate_name: "coset",
     cargo_env_compat: true,
-    cargo_pkg_version: "0.3.1",
+    cargo_pkg_version: "0.3.4",
     srcs: ["src/lib.rs"],
     edition: "2018",
+    features: ["default"],
     rustlibs: [
         "libciborium",
         "libciborium_io",
     ],
+    apex_available: [
+        "//apex_available:platform",
+        "//apex_available:anyapex",
+    ],
+    product_available: true,
+    vendor_available: true,
 }
diff --git a/CHANGELOG.md b/CHANGELOG.md
index f8d4915..df2b2cf 100644
--- a/CHANGELOG.md
+++ b/CHANGELOG.md
@@ -1,5 +1,19 @@
 # Change Log
 
+## 0.3.4 - 2023-01-25
+
+- Add non-default `std` feature that turns on `impl Error for CoseError`.
+- Add `cwt::ClaimsSetBuilder::private_claim` method.
+- Update documentation for existing encryption methods to make it clear that they only support AEAD encryption.
+
+## 0.3.3 - 2022-09-30
+
+- Add `CoseKeyBuilder` methods `kty`, `key_type` and `new_okp_key`.
+
+## 0.3.2 - 2022-04-02
+
+- Add basic [CWT](https://datatracker.ietf.org/doc/html/rfc8392) support in `cwt` module, via the `ClaimsSet` type.
+
 ## 0.3.1 - 2022-02-23
 
 - Implement `Display` for `CoseError`.
diff --git a/Cargo.lock b/Cargo.lock
index d257ebb..328bb43 100644
--- a/Cargo.lock
+++ b/Cargo.lock
@@ -31,7 +31,7 @@
 
 [[package]]
 name = "coset"
-version = "0.3.1"
+version = "0.3.4"
 dependencies = [
  "ciborium",
  "ciborium-io",
diff --git a/Cargo.toml b/Cargo.toml
index eb4661b..5f31443 100644
--- a/Cargo.toml
+++ b/Cargo.toml
@@ -12,13 +12,21 @@
 [package]
 edition = "2018"
 name = "coset"
-version = "0.3.1"
-authors = ["David Drysdale <drysdale@google.com>", "Paul Crowley <paulcrowley@google.com>"]
+version = "0.3.4"
+authors = [
+    "David Drysdale <drysdale@google.com>",
+    "Paul Crowley <paulcrowley@google.com>",
+]
 description = "Set of types for supporting COSE"
-keywords = ["cryptography", "cose"]
+readme = "README.md"
+keywords = [
+    "cryptography",
+    "cose",
+]
 categories = ["cryptography"]
 license = "Apache-2.0"
 repository = "https://github.com/google/coset"
+
 [dependencies.ciborium]
 version = "^0.2.0"
 default-features = false
@@ -26,5 +34,10 @@
 [dependencies.ciborium-io]
 version = "^0.2.0"
 features = ["alloc"]
+
 [dev-dependencies.hex]
 version = "^0.4.2"
+
+[features]
+default = []
+std = []
diff --git a/Cargo.toml.orig b/Cargo.toml.orig
index 9555fdf..e011996 100644
--- a/Cargo.toml.orig
+++ b/Cargo.toml.orig
@@ -1,6 +1,6 @@
 [package]
 name = "coset"
-version = "0.3.1"
+version = "0.3.4"
 authors = ["David Drysdale <drysdale@google.com>", "Paul Crowley <paulcrowley@google.com>"]
 edition = "2018"
 license = "Apache-2.0"
@@ -9,6 +9,11 @@
 keywords = ["cryptography", "cose"]
 categories = ["cryptography"]
 
+[features]
+default = []
+# `std` feature enables an `Error` impl for `CoseError`
+std = []
+
 [dependencies]
 ciborium = { version = "^0.2.0", default-features = false }
 ciborium-io = { version = "^0.2.0", features = ["alloc"] }
diff --git a/METADATA b/METADATA
index c3bb0c2..16e9249 100644
--- a/METADATA
+++ b/METADATA
@@ -1,3 +1,7 @@
+# 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
+
 name: "coset"
 description: "Set of types for supporting COSE"
 third_party {
@@ -7,13 +11,13 @@
   }
   url {
     type: ARCHIVE
-    value: "https://static.crates.io/crates/coset/coset-0.3.1.crate"
+    value: "https://static.crates.io/crates/coset/coset-0.3.4.crate"
   }
-  version: "0.3.1"
+  version: "0.3.4"
   license_type: NOTICE
   last_upgrade_date {
-    year: 2022
-    month: 3
-    day: 1
+    year: 2023
+    month: 2
+    day: 15
   }
 }
diff --git a/TEST_MAPPING b/TEST_MAPPING
index 2f7de78..c1f672e 100644
--- a/TEST_MAPPING
+++ b/TEST_MAPPING
@@ -1,13 +1,21 @@
 // Generated by update_crate_tests.py for tests that depend on this crate.
 {
+  "imports": [
+    {
+      "path": "system/keymint/derive"
+    },
+    {
+      "path": "system/keymint/hal"
+    }
+  ],
   "presubmit": [
     {
-      "name": "libcert_request_validator_tests"
+      "name": "coset_test_src_lib"
     }
   ],
   "presubmit-rust": [
     {
-      "name": "libcert_request_validator_tests"
+      "name": "coset_test_src_lib"
     }
   ]
 }
diff --git a/cargo2android.json b/cargo2android.json
new file mode 100644
index 0000000..cf7ea4a
--- /dev/null
+++ b/cargo2android.json
@@ -0,0 +1,7 @@
+{
+  "device": true,
+  "run": true,
+  "dependencies": true,
+  "vendor-available": true,
+  "tests": true
+}
diff --git a/examples/cwt.rs b/examples/cwt.rs
new file mode 100644
index 0000000..5972cf9
--- /dev/null
+++ b/examples/cwt.rs
@@ -0,0 +1,96 @@
+// Copyright 2022 Google LLC
+//
+// 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.
+//
+////////////////////////////////////////////////////////////////////////////////
+
+//! Example program demonstrating signed CWT processing.
+use coset::{cbor::value::Value, cwt, iana, CborSerializable, CoseError};
+
+#[derive(Copy, Clone)]
+struct FakeSigner {}
+
+// Use a fake signer/verifier (to avoid pulling in lots of dependencies).
+impl FakeSigner {
+    fn sign(&self, data: &[u8]) -> Vec<u8> {
+        data.to_vec()
+    }
+
+    fn verify(&self, sig: &[u8], data: &[u8]) -> Result<(), String> {
+        if sig != self.sign(data) {
+            Err("failed to verify".to_owned())
+        } else {
+            Ok(())
+        }
+    }
+}
+
+fn main() -> Result<(), CoseError> {
+    // Build a fake signer/verifier (to avoid pulling in lots of dependencies).
+    let signer = FakeSigner {};
+    let verifier = signer;
+
+    // Build a CWT ClaimsSet (cf. RFC 8392 A.3).
+    let claims = cwt::ClaimsSetBuilder::new()
+        .issuer("coap://as.example.com".to_string())
+        .subject("erikw".to_string())
+        .audience("coap://light.example.com".to_string())
+        .expiration_time(cwt::Timestamp::WholeSeconds(1444064944))
+        .not_before(cwt::Timestamp::WholeSeconds(1443944944))
+        .issued_at(cwt::Timestamp::WholeSeconds(1443944944))
+        .cwt_id(vec![0x0b, 0x71])
+        // Add additional standard claim.
+        .claim(
+            iana::CwtClaimName::Scope,
+            Value::Text("email phone".to_string()),
+        )
+        // Add additional private-use claim.
+        .private_claim(-70_000, Value::Integer(42.into()))
+        .build();
+    let aad = b"";
+
+    // Build a `CoseSign1` object.
+    let protected = coset::HeaderBuilder::new()
+        .algorithm(iana::Algorithm::ES256)
+        .build();
+    let unprotected = coset::HeaderBuilder::new()
+        .key_id(b"AsymmetricECDSA256".to_vec())
+        .build();
+    let sign1 = coset::CoseSign1Builder::new()
+        .protected(protected)
+        .unprotected(unprotected)
+        .payload(claims.clone().to_vec()?)
+        .create_signature(aad, |pt| signer.sign(pt))
+        .build();
+
+    // Serialize to bytes.
+    let sign1_data = sign1.to_vec()?;
+
+    // At the receiving end, deserialize the bytes back to a `CoseSign1` object.
+    let sign1 = coset::CoseSign1::from_slice(&sign1_data)?;
+
+    // Real code would:
+    // - Use the key ID to identify the relevant local key.
+    // - Check that the key is of the same type as `sign1.protected.algorithm`.
+
+    // Check the signature.
+    let result = sign1.verify_signature(aad, |sig, data| verifier.verify(sig, data));
+    println!("Signature verified: {:?}.", result);
+    assert!(result.is_ok());
+
+    // Now it's safe to parse the payload.
+    let recovered_claims = cwt::ClaimsSet::from_slice(&sign1.payload.unwrap())?;
+
+    assert_eq!(recovered_claims, claims);
+    Ok(())
+}
diff --git a/examples/signature.rs b/examples/signature.rs
index 4512df7..cf8b91a 100644
--- a/examples/signature.rs
+++ b/examples/signature.rs
@@ -15,7 +15,7 @@
 ////////////////////////////////////////////////////////////////////////////////
 
 //! Example program demonstrating signature creation.
-use coset::{iana, CborSerializable};
+use coset::{iana, CborSerializable, CoseError};
 
 #[derive(Copy, Clone)]
 struct FakeSigner {}
@@ -35,7 +35,7 @@
     }
 }
 
-fn main() {
+fn main() -> Result<(), CoseError> {
     // Build a fake signer/verifier (to avoid pulling in lots of dependencies).
     let signer = FakeSigner {};
     let verifier = signer;
@@ -56,7 +56,7 @@
         .build();
 
     // Serialize to bytes.
-    let sign1_data = sign1.to_vec().unwrap();
+    let sign1_data = sign1.to_vec()?;
     println!(
         "'{}' + '{}' => {}",
         String::from_utf8_lossy(pt),
@@ -65,7 +65,7 @@
     );
 
     // At the receiving end, deserialize the bytes back to a `CoseSign1` object.
-    let mut sign1 = coset::CoseSign1::from_slice(&sign1_data).unwrap();
+    let mut sign1 = coset::CoseSign1::from_slice(&sign1_data)?;
 
     // Check the signature, which needs to have the same `aad` provided.
     let result = sign1.verify_signature(aad, |sig, data| verifier.verify(sig, data));
@@ -85,7 +85,9 @@
 
     // Changing a protected header invalidates the signature.
     sign1.protected.header.content_type = Some(coset::ContentType::Text("text/plain".to_owned()));
+    sign1.protected.original_data = None;
     assert!(sign1
         .verify_signature(aad, |sig, data| verifier.verify(sig, data))
         .is_err());
+    Ok(())
 }
diff --git a/patches/std.diff b/patches/std.diff
index dd24015..244b78c 100644
--- a/patches/std.diff
+++ b/patches/std.diff
@@ -1,15 +1,15 @@
 diff --git a/src/lib.rs b/src/lib.rs
-index 2a8ceb3..3c46fcf 100644
+index 4ce9c93..a800c89 100644
 --- a/src/lib.rs
 +++ b/src/lib.rs
-@@ -100,6 +100,9 @@
+@@ -100,6 +100,10 @@
  #![deny(rustdoc::broken_intra_doc_links)]
  extern crate alloc;
  
 +/// Use std to allow building as a dylib.
++#[cfg(android_dylib)]
 +extern crate std;
 +
  /// Re-export of the `ciborium` crate used for underlying CBOR encoding.
  pub use ciborium as cbor;
- 
-
+ 
\ No newline at end of file
diff --git a/scripts/check-format.sh b/scripts/check-format.sh
index 445a564..220022c 100755
--- a/scripts/check-format.sh
+++ b/scripts/check-format.sh
@@ -90,7 +90,7 @@
 
 EMBEDMD="$(go env GOPATH)/bin/embedmd"
 if [[ ! -x "$EMBEDMD" ]]; then
-  go get github.com/campoy/embedmd
+  go install github.com/campoy/embedmd@97c13d6
 fi
 for f in "${MD_FILES[@]}"; do
   "$EMBEDMD" -d "$f"
diff --git a/src/common/mod.rs b/src/common/mod.rs
index c1f2973..106a3a3 100644
--- a/src/common/mod.rs
+++ b/src/common/mod.rs
@@ -87,6 +87,9 @@
     }
 }
 
+#[cfg(feature = "std")]
+impl std::error::Error for CoseError {}
+
 impl CoseError {
     fn fmt_msg(&self, f: &mut core::fmt::Formatter<'_>) -> core::fmt::Result {
         match self {
diff --git a/src/cwt/mod.rs b/src/cwt/mod.rs
new file mode 100644
index 0000000..d12531a
--- /dev/null
+++ b/src/cwt/mod.rs
@@ -0,0 +1,199 @@
+// Copyright 2021 Google LLC
+//
+// 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.
+//
+////////////////////////////////////////////////////////////////////////////////
+
+//! CBOR Web Token functionality.
+
+use crate::{
+    cbor::value::Value,
+    common::AsCborValue,
+    iana,
+    iana::{EnumI64, WithPrivateRange},
+    util::{cbor_type_error, ValueTryAs},
+    CoseError,
+};
+use alloc::{collections::BTreeSet, string::String, vec::Vec};
+use core::convert::TryInto;
+
+#[cfg(test)]
+mod tests;
+
+/// Number of seconds since UNIX epoch.
+#[derive(Clone, Debug, PartialEq)]
+pub enum Timestamp {
+    WholeSeconds(i64),
+    FractionalSeconds(f64),
+}
+
+impl AsCborValue for Timestamp {
+    fn from_cbor_value(value: Value) -> Result<Self, CoseError> {
+        match value {
+            Value::Integer(i) => Ok(Timestamp::WholeSeconds(i.try_into()?)),
+            Value::Float(f) => Ok(Timestamp::FractionalSeconds(f)),
+            _ => cbor_type_error(&value, "int/float"),
+        }
+    }
+    fn to_cbor_value(self) -> Result<Value, CoseError> {
+        Ok(match self {
+            Timestamp::WholeSeconds(t) => Value::Integer(t.into()),
+            Timestamp::FractionalSeconds(f) => Value::Float(f),
+        })
+    }
+}
+
+/// Claim name.
+pub type ClaimName = crate::RegisteredLabelWithPrivate<iana::CwtClaimName>;
+
+/// Structure representing a CWT Claims Set.
+#[derive(Clone, Debug, Default, PartialEq)]
+pub struct ClaimsSet {
+    /// Issuer
+    pub issuer: Option<String>,
+    /// Subject
+    pub subject: Option<String>,
+    /// Audience
+    pub audience: Option<String>,
+    /// Expiration Time
+    pub expiration_time: Option<Timestamp>,
+    /// Not Before
+    pub not_before: Option<Timestamp>,
+    /// Issued At
+    pub issued_at: Option<Timestamp>,
+    /// CWT ID
+    pub cwt_id: Option<Vec<u8>>,
+    /// Any additional claims.
+    pub rest: Vec<(ClaimName, Value)>,
+}
+
+impl crate::CborSerializable for ClaimsSet {}
+
+const ISS: ClaimName = ClaimName::Assigned(iana::CwtClaimName::Iss);
+const SUB: ClaimName = ClaimName::Assigned(iana::CwtClaimName::Sub);
+const AUD: ClaimName = ClaimName::Assigned(iana::CwtClaimName::Aud);
+const EXP: ClaimName = ClaimName::Assigned(iana::CwtClaimName::Exp);
+const NBF: ClaimName = ClaimName::Assigned(iana::CwtClaimName::Nbf);
+const IAT: ClaimName = ClaimName::Assigned(iana::CwtClaimName::Iat);
+const CTI: ClaimName = ClaimName::Assigned(iana::CwtClaimName::Cti);
+
+impl AsCborValue for ClaimsSet {
+    fn from_cbor_value(value: Value) -> Result<Self, CoseError> {
+        let m = match value {
+            Value::Map(m) => m,
+            v => return cbor_type_error(&v, "map"),
+        };
+
+        let mut claims = Self::default();
+        let mut seen = BTreeSet::new();
+        for (n, value) in m.into_iter() {
+            // The `ciborium` CBOR library does not police duplicate map keys, so do it here.
+            let name = ClaimName::from_cbor_value(n)?;
+            if seen.contains(&name) {
+                return Err(CoseError::DuplicateMapKey);
+            }
+            seen.insert(name.clone());
+            match name {
+                x if x == ISS => claims.issuer = Some(value.try_as_string()?),
+                x if x == SUB => claims.subject = Some(value.try_as_string()?),
+                x if x == AUD => claims.audience = Some(value.try_as_string()?),
+                x if x == EXP => claims.expiration_time = Some(Timestamp::from_cbor_value(value)?),
+                x if x == NBF => claims.not_before = Some(Timestamp::from_cbor_value(value)?),
+                x if x == IAT => claims.issued_at = Some(Timestamp::from_cbor_value(value)?),
+                x if x == CTI => claims.cwt_id = Some(value.try_as_bytes()?),
+                name => claims.rest.push((name, value)),
+            }
+        }
+        Ok(claims)
+    }
+
+    fn to_cbor_value(self) -> Result<Value, CoseError> {
+        let mut map = Vec::new();
+        if let Some(iss) = self.issuer {
+            map.push((ISS.to_cbor_value()?, Value::Text(iss)));
+        }
+        if let Some(sub) = self.subject {
+            map.push((SUB.to_cbor_value()?, Value::Text(sub)));
+        }
+        if let Some(aud) = self.audience {
+            map.push((AUD.to_cbor_value()?, Value::Text(aud)));
+        }
+        if let Some(exp) = self.expiration_time {
+            map.push((EXP.to_cbor_value()?, exp.to_cbor_value()?));
+        }
+        if let Some(nbf) = self.not_before {
+            map.push((NBF.to_cbor_value()?, nbf.to_cbor_value()?));
+        }
+        if let Some(iat) = self.issued_at {
+            map.push((IAT.to_cbor_value()?, iat.to_cbor_value()?));
+        }
+        if let Some(cti) = self.cwt_id {
+            map.push((CTI.to_cbor_value()?, Value::Bytes(cti)));
+        }
+        for (label, value) in self.rest {
+            map.push((label.to_cbor_value()?, value));
+        }
+        Ok(Value::Map(map))
+    }
+}
+
+/// Builder for [`ClaimsSet`] objects.
+#[derive(Default)]
+pub struct ClaimsSetBuilder(ClaimsSet);
+
+impl ClaimsSetBuilder {
+    builder! {ClaimsSet}
+    builder_set_optional! {issuer: String}
+    builder_set_optional! {subject: String}
+    builder_set_optional! {audience: String}
+    builder_set_optional! {expiration_time: Timestamp}
+    builder_set_optional! {not_before: Timestamp}
+    builder_set_optional! {issued_at: Timestamp}
+    builder_set_optional! {cwt_id: Vec<u8>}
+
+    /// Set a claim name:value pair.
+    ///
+    /// # Panics
+    ///
+    /// This function will panic if it used to set a claim with name from the range [1, 7].
+    #[must_use]
+    pub fn claim(mut self, name: iana::CwtClaimName, value: Value) -> Self {
+        if name.to_i64() >= iana::CwtClaimName::Iss.to_i64()
+            && name.to_i64() <= iana::CwtClaimName::Cti.to_i64()
+        {
+            panic!("claim() method used to set core claim"); // safe: invalid input
+        }
+        self.0.rest.push((ClaimName::Assigned(name), value));
+        self
+    }
+
+    /// Set a claim name:value pair where the `name` is text.
+    #[must_use]
+    pub fn text_claim(mut self, name: String, value: Value) -> Self {
+        self.0.rest.push((ClaimName::Text(name), value));
+        self
+    }
+
+    /// Set a claim  where the claim key is a numeric value from the private use range.
+    ///
+    /// # Panics
+    ///
+    /// This function will panic if it is used to set a claim with a key value outside of the
+    /// private use range.
+    #[must_use]
+    pub fn private_claim(mut self, id: i64, value: Value) -> Self {
+        assert!(iana::CwtClaimName::is_private(id));
+        self.0.rest.push((ClaimName::PrivateUse(id), value));
+        self
+    }
+}
diff --git a/src/cwt/tests.rs b/src/cwt/tests.rs
new file mode 100644
index 0000000..ff59dd6
--- /dev/null
+++ b/src/cwt/tests.rs
@@ -0,0 +1,235 @@
+// Copyright 2021 Google LLC
+//
+// 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.
+//
+////////////////////////////////////////////////////////////////////////////////
+
+use super::*;
+use crate::{cbor::value::Value, iana, iana::WithPrivateRange, util::expect_err, CborSerializable};
+use alloc::{borrow::ToOwned, vec};
+
+#[test]
+fn test_cwt_encode() {
+    let tests = vec![
+        (
+            ClaimsSet {
+                issuer: Some("abc".to_owned()),
+                ..Default::default()
+            },
+            concat!(
+                "a1", // 1-map
+                "01", "63", "616263" // 1 (iss) => 3-tstr
+            ),
+        ),
+        (ClaimsSetBuilder::new().build(), concat!("a0")),
+        (
+            ClaimsSetBuilder::new()
+                .issuer("aaa".to_owned())
+                .subject("bb".to_owned())
+                .audience("c".to_owned())
+                .expiration_time(Timestamp::WholeSeconds(0x100))
+                .not_before(Timestamp::WholeSeconds(0x200))
+                .issued_at(Timestamp::WholeSeconds(0x10))
+                .cwt_id(vec![1, 2, 3, 4])
+                .private_claim(-70_000, Value::Integer(0.into()))
+                .build(),
+            concat!(
+                "a8", // 8-map
+                "01",
+                "63",
+                "616161", // 1 (iss) => 3-tstr
+                "02",
+                "62",
+                "6262", // 2 (sub) => 2-tstr
+                "03",
+                "61",
+                "63", // 3 (aud) => 1-tstr
+                "04",
+                "19",
+                "0100", // 4 (exp) => uint
+                "05",
+                "19",
+                "0200", // 5 (nbf) => uint
+                "06",
+                "10", // 6 (iat) => uint
+                "07",
+                "44",
+                "01020304", // 7 => bstr
+                "3a0001116f",
+                "00" // -70000 => uint
+            ),
+        ),
+        (
+            ClaimsSetBuilder::new()
+                .claim(
+                    iana::CwtClaimName::Cnf,
+                    Value::Map(vec![(Value::Integer(0.into()), Value::Integer(0.into()))]),
+                )
+                .build(),
+            concat!(
+                "a1", // 1-map
+                "08", "a1", "00", "00"
+            ),
+        ),
+        (
+            ClaimsSetBuilder::new()
+                .text_claim("aa".to_owned(), Value::Integer(0.into()))
+                .build(),
+            concat!(
+                "a1", // 1-map
+                "62", "6161", "00",
+            ),
+        ),
+        (
+            ClaimsSetBuilder::new()
+                .expiration_time(Timestamp::FractionalSeconds(1.5))
+                .build(),
+            concat!(
+                "a1", // 1-map
+                "04", // 4 (exp) =>
+                // Note: ciborium serializes floats as the smallest float type that
+                // will parse back to the original f64!  As a result, 1.5 is encoded
+                // as an f16.
+                "f9", "3e00",
+            ),
+        ),
+    ];
+    for (i, (claims, claims_data)) in tests.iter().enumerate() {
+        let got = claims.clone().to_vec().unwrap();
+        assert_eq!(*claims_data, hex::encode(&got), "case {}", i);
+
+        let got = ClaimsSet::from_slice(&got).unwrap();
+        assert_eq!(*claims, got);
+    }
+}
+
+#[test]
+fn test_cwt_decode_fail() {
+    let tests = vec![
+        (
+            concat!(
+                "81", // 1-arr
+                "01",
+            ),
+            "expected map",
+        ),
+        (
+            concat!(
+                "a1", // 1-map
+                "01", "08", // 1 (iss) => int (invalid value type)
+            ),
+            "expected tstr",
+        ),
+        (
+            concat!(
+                "a1", // 1-map
+                "02", "08", // 2 (sub) => int (invalid value type)
+            ),
+            "expected tstr",
+        ),
+        (
+            concat!(
+                "a1", // 1-map
+                "03", "08", // 3 (aud) => int (invalid value type)
+            ),
+            "expected tstr",
+        ),
+        (
+            concat!(
+                "a1", // 1-map
+                "04", "40", // 4 (exp) => bstr (invalid value type)
+            ),
+            "expected int/float",
+        ),
+        (
+            concat!(
+                "a1", // 1-map
+                "05", "40", // 5 (nbf) => bstr (invalid value type)
+            ),
+            "expected int/float",
+        ),
+        (
+            concat!(
+                "a1", // 1-map
+                "06", "40", // 6 (iat) => bstr (invalid value type)
+            ),
+            "expected int/float",
+        ),
+        (
+            concat!(
+                "a1", // 1-map
+                "07", "01", // 5 (cti) => uint (invalid value type)
+            ),
+            "expected bstr",
+        ),
+        (
+            concat!(
+                "a1", // 1-map
+                "07", "40", // 5 (cti) => 0-bstr
+                "06", "01", // 6 (iat) => 1
+            ),
+            "extraneous data",
+        ),
+        (
+            concat!(
+                "a2", // 1-map
+                "07", "40", // 5 (cti) => 0-bstr
+                "07", "40", // 5 (cti) => 0-bstr
+            ),
+            "duplicate map key",
+        ),
+    ];
+    for (claims_data, err_msg) in tests.iter() {
+        let data = hex::decode(claims_data).unwrap();
+        let result = ClaimsSet::from_slice(&data);
+        expect_err(result, err_msg);
+    }
+}
+
+#[test]
+fn test_cwt_is_private() {
+    assert!(!iana::CwtClaimName::is_private(1));
+    assert!(iana::CwtClaimName::is_private(-500_000));
+}
+
+#[test]
+#[should_panic]
+fn test_cwt_claims_builder_core_param_panic() {
+    // Attempting to set a core claim (in range [1,7]) via `.claim()` panics.
+    let _claims = ClaimsSetBuilder::new()
+        .claim(iana::CwtClaimName::Iss, Value::Null)
+        .build();
+}
+
+#[test]
+#[should_panic]
+fn test_cwt_claims_builder_non_private_panic() {
+    // Attempting to set a claim outside of private range via `.private_claim()` panics.
+    let _claims = ClaimsSetBuilder::new()
+        .private_claim(100, Value::Null)
+        .build();
+}
+
+#[test]
+fn test_cwt_dup_claim() {
+    // Set a duplicate map key.
+    let claims = ClaimsSetBuilder::new()
+        .claim(iana::CwtClaimName::AceProfile, Value::Integer(1.into()))
+        .claim(iana::CwtClaimName::AceProfile, Value::Integer(2.into()))
+        .build();
+    // Encoding succeeds.
+    let data = claims.to_vec().unwrap();
+    // But an attempt to parse the encoded data fails.
+    let result = ClaimsSet::from_slice(&data);
+    expect_err(result, "duplicate map key");
+}
diff --git a/src/encrypt/mod.rs b/src/encrypt/mod.rs
index 22f9777..db38de5 100644
--- a/src/encrypt/mod.rs
+++ b/src/encrypt/mod.rs
@@ -95,8 +95,8 @@
 }
 
 impl CoseRecipient {
-    /// Decrypt the `ciphertext` value, using `cipher` to decrypt the cipher text and
-    /// combined AAD.
+    /// Decrypt the `ciphertext` value with an AEAD, using `cipher` to decrypt the cipher text and
+    /// combined AAD as per RFC 8152 section 5.3.
     ///
     /// # Panics
     ///
@@ -140,9 +140,9 @@
         self
     }
 
-    /// Calculate the ciphertext value, using `cipher` to generate the encrypted bytes from the
-    /// plaintext and combined AAD (in that order).  Any protected header values should be set
-    /// before using this method.
+    /// Calculate the ciphertext value with an AEAD, using `cipher` to generate the encrypted bytes
+    /// from the plaintext and combined AAD (in that order) as per RFC 8152 section 5.3.  Any
+    /// protected header values should be set before using this method.
     ///
     /// # Panics
     ///
@@ -162,9 +162,9 @@
         self.ciphertext(cipher(plaintext, &aad))
     }
 
-    /// Calculate the ciphertext value, using `cipher` to generate the encrypted bytes from the
-    /// plaintext and combined AAD (in that order).  Any protected header values should be set
-    /// before using this method.
+    /// Calculate the ciphertext value with an AEAD, using `cipher` to generate the encrypted bytes
+    /// from the plaintext and combined AAD (in that order) as per RFC 8152 section 5.3.  Any
+    /// protected header values should be set before using this method.
     ///
     /// # Panics
     ///
@@ -183,8 +183,8 @@
         Ok(self.ciphertext(cipher(plaintext, &aad)?))
     }
 
-    /// Construct the combined AAD data needed for encryption. Any protected header values should be
-    /// set before using this method.
+    /// Construct the combined AAD data needed for encryption with an AEAD. Any protected header
+    /// values should be set before using this method.
     ///
     /// # Panics
     ///
@@ -261,7 +261,7 @@
 }
 
 impl CoseEncrypt {
-    /// Decrypt the `ciphertext` value, using `cipher` to decrypt the cipher text and
+    /// Decrypt the `ciphertext` value with an AEAD, using `cipher` to decrypt the cipher text and
     /// combined AAD.
     ///
     /// # Panics
@@ -291,9 +291,9 @@
     builder_set! {unprotected: Header}
     builder_set_optional! {ciphertext: Vec<u8>}
 
-    /// Calculate the ciphertext value, using `cipher` to generate the encrypted bytes from the
-    /// plaintext and combined AAD (in that order).  Any protected header values should be set
-    /// before using this method.
+    /// Calculate the ciphertext value with an AEAD, using `cipher` to generate the encrypted bytes
+    /// from the plaintext and combined AAD (in that order) as per RFC 8152 section 5.3.  Any
+    /// protected header values should be set before using this method.
     #[must_use]
     pub fn create_ciphertext<F>(self, plaintext: &[u8], external_aad: &[u8], cipher: F) -> Self
     where
@@ -307,9 +307,9 @@
         self.ciphertext(cipher(plaintext, &aad))
     }
 
-    /// Calculate the ciphertext value, using `cipher` to generate the encrypted bytes from the
-    /// plaintext and combined AAD (in that order).  Any protected header values should be set
-    /// before using this method.
+    /// Calculate the ciphertext value with an AEAD, using `cipher` to generate the encrypted bytes
+    /// from the plaintext and combined AAD (in that order) as per RFC 8152 section 5.3.  Any
+    /// protected header values should be set before using this method.
     pub fn try_create_ciphertext<F, E>(
         self,
         plaintext: &[u8],
@@ -389,7 +389,7 @@
 }
 
 impl CoseEncrypt0 {
-    /// Decrypt the `ciphertext` value, using `cipher` to decrypt the cipher text and
+    /// Decrypt the `ciphertext` value with an AEAD, using `cipher` to decrypt the cipher text and
     /// combined AAD.
     ///
     /// # Panics
@@ -419,9 +419,9 @@
     builder_set! {unprotected: Header}
     builder_set_optional! {ciphertext: Vec<u8>}
 
-    /// Calculate the ciphertext value, using `cipher` to generate the encrypted bytes from the
-    /// plaintext and combined AAD (in that order).  Any protected header values should be set
-    /// before using this method.
+    /// Calculate the ciphertext value with an AEAD, using `cipher` to generate the encrypted bytes
+    /// from the plaintext and combined AAD (in that order) as per RFC 8152 section 5.3.  Any
+    /// protected header values should be set before using this method.
     #[must_use]
     pub fn create_ciphertext<F>(self, plaintext: &[u8], external_aad: &[u8], cipher: F) -> Self
     where
@@ -435,9 +435,9 @@
         self.ciphertext(cipher(plaintext, &aad))
     }
 
-    /// Calculate the ciphertext value, using `cipher` to generate the encrypted bytes from the
-    /// plaintext and combined AAD (in that order).  Any protected header values should be set
-    /// before using this method.
+    /// Calculate the ciphertext value with an AEAD, using `cipher` to generate the encrypted bytes
+    /// from the plaintext and combined AAD (in that order) as per RFC 8152 section 5.3.  Any
+    /// protected header values should be set before using this method.
     pub fn try_create_ciphertext<F, E>(
         self,
         plaintext: &[u8],
diff --git a/src/iana/mod.rs b/src/iana/mod.rs
index 41c2ef8..3702014 100644
--- a/src/iana/mod.rs
+++ b/src/iana/mod.rs
@@ -20,6 +20,7 @@
 //! - <https://www.iana.org/assignments/cose/cose.xhtml>
 //! - <https://www.iana.org/assignments/cbor-tags/cbor-tags.xhtml>
 //! - <https://www.iana.org/assignments/core-parameters/core-parameters.xhtml#content-formats>
+//! - <https://www.iana.org/assignments/cwt/cwt.xhtml>
 
 #[cfg(test)]
 mod tests;
@@ -743,3 +744,54 @@
         VndOmaLwm2mCbor: 11544,
     }
 }
+
+iana_registry! {
+    /// CBOR Web Token (CWT) Claims
+    /// From IANA registry <https://www.iana.org/assignments/cwt/cwt.xhtml>
+    /// as of 2021-10-21.
+    CwtClaimName {
+        /// Health certificate ("hcert": map).
+        Hcert: -260,
+        /// Challenge nonce ("EUPHNonce": bstr).
+        EuphNonce: -259,
+        /// Signing prefix for multi-app restricted operating environment ("EATMAROEPrefix": bstr).
+        EatMaroePrefix: -258,
+        /// FIDO Device Onboarding EAT ("EAT-FDO": array).
+        EatFido: -257,
+        /// Reserved value.
+        Reserved: 0,
+        /// Issuer ("iss": tstr).
+        Iss: 1,
+        /// Subject ("sub": tstr)
+        Sub: 2,
+        /// Audience ("aud": tstr)
+        Aud: 3,
+        /// Expiration Time, as seconds since UNIX epoch ("exp": int/float)
+        Exp: 4,
+        /// Not Before, as seconds since UNIX epoch ("nbf": int/float)
+        Nbf: 5,
+        /// Issued at, as seconds since UNIX epoch ("iat": int/float)
+        Iat: 6,
+        /// CWT ID ("cti": bstr)
+        Cti: 7,
+        /// Confirmation ("cnf": map)
+        Cnf: 8,
+        /// Scope of an access token ("scope": bstr/tstr)
+        Scope: 9,
+        /// The ACE profile a token is supposed to be used with ("ace_profile": int)
+        AceProfile: 38,
+        /// The client-nonce sent to the AS by the RS via the client ("cnonce": bstr)
+        CNonce: 39,
+        /// The expiration time of a token measured from when it was received at the RS in seconds ("exi": int)
+        Exi: 40,
+    }
+}
+
+/// Integer values for CWT claims below this value are reserved for private use.
+pub const CWT_CLAIM_PRIVATE_USE_MAX: i64 = -65536;
+
+impl WithPrivateRange for CwtClaimName {
+    fn is_private(i: i64) -> bool {
+        i < CWT_CLAIM_PRIVATE_USE_MAX
+    }
+}
diff --git a/src/key/mod.rs b/src/key/mod.rs
index b81ea8c..07ee7a7 100644
--- a/src/key/mod.rs
+++ b/src/key/mod.rs
@@ -183,6 +183,7 @@
 
 impl CoseKeyBuilder {
     builder! {CoseKey}
+    builder_set! {kty: KeyType}
     builder_set! {key_id: Vec<u8>}
     builder_set! {base_iv: Vec<u8>}
 
@@ -250,6 +251,21 @@
         })
     }
 
+    /// Constructor for a octet keypair key.
+    pub fn new_okp_key() -> Self {
+        Self(CoseKey {
+            kty: KeyType::Assigned(iana::KeyType::OKP),
+            ..Default::default()
+        })
+    }
+
+    /// Set the key type.
+    #[must_use]
+    pub fn key_type(mut self, key_type: iana::KeyType) -> Self {
+        self.0.kty = KeyType::Assigned(key_type);
+        self
+    }
+
     /// Set the algorithm.
     #[must_use]
     pub fn algorithm(mut self, alg: iana::Algorithm) -> Self {
diff --git a/src/key/tests.rs b/src/key/tests.rs
index 713fe1a..45f8eef 100644
--- a/src/key/tests.rs
+++ b/src/key/tests.rs
@@ -16,7 +16,7 @@
 
 use super::*;
 use crate::{cbor::value::Value, iana, util::expect_err, CborSerializable};
-use alloc::{borrow::ToOwned, vec};
+use alloc::{borrow::ToOwned, string::ToString, vec};
 
 #[test]
 fn test_cose_key_encode() {
@@ -702,6 +702,31 @@
                 ..Default::default()
             },
         ),
+        (
+            CoseKeyBuilder::new_okp_key().build(),
+            CoseKey {
+                kty: KeyType::Assigned(iana::KeyType::OKP),
+                ..Default::default()
+            },
+        ),
+        (
+            CoseKeyBuilder::new()
+                .key_type(iana::KeyType::WalnutDSA)
+                .build(),
+            CoseKey {
+                kty: KeyType::Assigned(iana::KeyType::WalnutDSA),
+                ..Default::default()
+            },
+        ),
+        (
+            CoseKeyBuilder::new()
+                .kty(KeyType::Text("test".to_string()))
+                .build(),
+            CoseKey {
+                kty: KeyType::Text("test".to_string()),
+                ..Default::default()
+            },
+        ),
     ];
     for (got, want) in tests {
         assert_eq!(got, want);
diff --git a/src/lib.rs b/src/lib.rs
index 3c46fcf..5ee10f3 100644
--- a/src/lib.rs
+++ b/src/lib.rs
@@ -96,11 +96,12 @@
 //! [COSE]: https://tools.ietf.org/html/rfc8152
 //! [CBOR]: https://tools.ietf.org/html/rfc7049
 
-#![no_std]
+#![cfg_attr(not(feature = "std"), no_std)]
 #![deny(rustdoc::broken_intra_doc_links)]
 extern crate alloc;
 
 /// Use std to allow building as a dylib.
+#[cfg(android_dylib)]
 extern crate std;
 
 /// Re-export of the `ciborium` crate used for underlying CBOR encoding.
@@ -109,6 +110,7 @@
 #[macro_use]
 pub(crate) mod util;
 
+pub mod cwt;
 #[macro_use]
 pub mod iana;
 
diff --git a/src/util/mod.rs b/src/util/mod.rs
index d12337c..5dde295 100644
--- a/src/util/mod.rs
+++ b/src/util/mod.rs
@@ -21,7 +21,7 @@
     common::AsCborValue,
     CoseError, Result,
 };
-use alloc::{boxed::Box, vec::Vec};
+use alloc::{boxed::Box, string::String, vec::Vec};
 
 #[cfg(test)]
 mod tests;
@@ -71,6 +71,9 @@
 
     /// Extractor for [`Value::Tag`]
     fn try_as_tag(self) -> Result<(u64, Box<Value>)>;
+
+    /// Extractor for [`Value::Text`]
+    fn try_as_string(self) -> Result<String>;
 }
 
 impl ValueTryAs for Value {
@@ -131,6 +134,14 @@
             cbor_type_error(&self, "tag")
         }
     }
+
+    fn try_as_string(self) -> Result<String> {
+        if let Value::Text(s) = self {
+            Ok(s)
+        } else {
+            cbor_type_error(&self, "tstr")
+        }
+    }
 }
 
 /// Convert each item of an iterator to CBOR, and wrap the lot in
@@ -153,6 +164,7 @@
     result: Result<T, E>,
     err_msg: &str,
 ) {
+    #[cfg(not(feature = "std"))]
     use alloc::format;
     match result {
         Ok(_) => {