Merge "Upgrade const-oid to 0.9.1" am: c4d298b98e am: 89c822acf7

Original change: https://android-review.googlesource.com/c/platform/external/rust/crates/const-oid/+/2335343

Change-Id: I002484bc4299e950d1cd99eceaca6ad4d4e896ae
Signed-off-by: Automerger Merge Worker <[email protected]>
diff --git a/.cargo_vcs_info.json b/.cargo_vcs_info.json
index 65a697a..80de78a 100644
--- a/.cargo_vcs_info.json
+++ b/.cargo_vcs_info.json
@@ -1,6 +1,6 @@
 {
   "git": {
-    "sha1": "87223cd4203097729be3fad21d23ef183405991b"
+    "sha1": "252671f5828a6df3c1a31222519cd278c8704e4b"
   },
   "path_in_vcs": "const-oid"
 }
\ No newline at end of file
diff --git a/Android.bp b/Android.bp
index c6af18a..334199c 100644
--- a/Android.bp
+++ b/Android.bp
@@ -1,8 +1,6 @@
 // This file is generated by cargo2android.py --config cargo2android.json.
 // Do not modify this file as changes will be overridden on upgrade.
 
-
-
 package {
     default_applicable_licenses: ["external_rust_crates_const-oid_license"],
 }
@@ -37,7 +35,7 @@
     name: "libconst_oid",
     crate_name: "const_oid",
     cargo_env_compat: true,
-    cargo_pkg_version: "0.9.0",
+    cargo_pkg_version: "0.9.1",
     srcs: ["src/lib.rs"],
     edition: "2021",
     features: ["db"],
diff --git a/CHANGELOG.md b/CHANGELOG.md
index 4dcb4fb..f6ea7d3 100644
--- a/CHANGELOG.md
+++ b/CHANGELOG.md
@@ -4,6 +4,14 @@
 The format is based on [Keep a Changelog](https://keepachangelog.com/en/1.0.0/),
 and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0.html).
 
+## 0.9.1 (2022-11-12)
+### Added
+- clippy lints for checked arithmetic and panics ([#561])
+- `DynAssociatedOid` trait ([#758])
+
+[#561]: https://github.com/RustCrypto/formats/pull/561
+[#758]: https://github.com/RustCrypto/formats/pull/758
+
 ## 0.9.0 (2022-03-11)
 ### Added
 - Fallible `const fn` parser + `::new_unwrap` ([#458], [#459])
diff --git a/Cargo.toml b/Cargo.toml
index d45e11e..0080b6f 100644
--- a/Cargo.toml
+++ b/Cargo.toml
@@ -13,19 +13,38 @@
 edition = "2021"
 rust-version = "1.57"
 name = "const-oid"
-version = "0.9.0"
+version = "0.9.1"
 authors = ["RustCrypto Developers"]
-description = "Const-friendly implementation of the ISO/IEC Object Identifier (OID) standard\nas defined in ITU X.660, with support for BER/DER encoding/decoding as well as\nheapless no_std (i.e. embedded) support\n"
+description = """
+Const-friendly implementation of the ISO/IEC Object Identifier (OID) standard
+as defined in ITU X.660, with support for BER/DER encoding/decoding as well as
+heapless no_std (i.e. embedded) support
+"""
 documentation = "https://docs.rs/const-oid"
 readme = "README.md"
-keywords = ["iso", "iec", "itu", "oid"]
-categories = ["cryptography", "data-structures", "encoding", "no-std", "parser-implementations"]
+keywords = [
+    "iso",
+    "iec",
+    "itu",
+    "oid",
+]
+categories = [
+    "cryptography",
+    "data-structures",
+    "encoding",
+    "no-std",
+    "parser-implementations",
+]
 license = "Apache-2.0 OR MIT"
 repository = "https://github.com/RustCrypto/formats/tree/master/const-oid"
-resolver = "2"
+
 [package.metadata.docs.rs]
 all-features = true
-rustdoc-args = ["--cfg", "docsrs"]
+rustdoc-args = [
+    "--cfg",
+    "docsrs",
+]
+
 [dev-dependencies.hex-literal]
 version = "0.3"
 
diff --git a/Cargo.toml.orig b/Cargo.toml.orig
index 5978809..ffc05e6 100644
--- a/Cargo.toml.orig
+++ b/Cargo.toml.orig
@@ -1,6 +1,6 @@
 [package]
 name = "const-oid"
-version = "0.9.0"
+version = "0.9.1"
 authors = ["RustCrypto Developers"]
 license = "Apache-2.0 OR MIT"
 description = """
diff --git a/LICENSE-MIT b/LICENSE-MIT
new file mode 100644
index 0000000..1b78809
--- /dev/null
+++ b/LICENSE-MIT
@@ -0,0 +1,25 @@
+Copyright (c) 2020-2022 The RustCrypto Project Developers
+
+Permission is hereby granted, free of charge, to any
+person obtaining a copy of this software and associated
+documentation files (the "Software"), to deal in the
+Software without restriction, including without
+limitation the rights to use, copy, modify, merge,
+publish, distribute, sublicense, and/or sell copies of
+the Software, and to permit persons to whom the Software
+is furnished to do so, subject to the following
+conditions:
+
+The above copyright notice and this permission notice
+shall be included in all copies or substantial portions
+of the Software.
+
+THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF
+ANY KIND, EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED
+TO THE WARRANTIES OF MERCHANTABILITY, FITNESS FOR A
+PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT
+SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY
+CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION
+OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR
+IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER
+DEALINGS IN THE SOFTWARE.
diff --git a/METADATA b/METADATA
index 6afb80d..c994f5c 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/const-oid
+# For more info, check https://cs.android.com/android/platform/superproject/+/master:tools/external_updater/README.md
+
 name: "const-oid"
 description: "Const-friendly implementation of the ISO/IEC Object Identifier (OID) standard as defined in ITU X.660, with support for BER/DER encoding/decoding as well as heapless no_std (i.e. embedded) support"
 third_party {
@@ -7,14 +11,13 @@
   }
   url {
     type: ARCHIVE
-    value: "https://static.crates.io/crates/const-oid/const-oid-0.9.0.crate"
+    value: "https://static.crates.io/crates/const-oid/const-oid-0.9.1.crate"
   }
-  version: "0.9.0"
-  # Dual-licensed, using the least restrictive per go/thirdpartylicenses#same.
+  version: "0.9.1"
   license_type: NOTICE
   last_upgrade_date {
     year: 2022
-    month: 8
-    day: 19
+    month: 12
+    day: 8
   }
 }
diff --git a/README.md b/README.md
index bd6dbad..fae3cfc 100644
--- a/README.md
+++ b/README.md
@@ -79,7 +79,7 @@
 
 [//]: # (badges)
 
-[crate-image]: https://img.shields.io/crates/v/const-oid.svg
+[crate-image]: https://buildstats.info/crate/const-oid
 [crate-link]: https://crates.io/crates/const-oid
 [docs-image]: https://docs.rs/const-oid/badge.svg
 [docs-link]: https://docs.rs/const-oid/
diff --git a/src/arcs.rs b/src/arcs.rs
index b3f9f24..7bf7a9a 100644
--- a/src/arcs.rs
+++ b/src/arcs.rs
@@ -66,9 +66,15 @@
                 let mut arc_bytes = 0;
 
                 loop {
-                    match self.oid.as_bytes().get(offset + arc_bytes).cloned() {
+                    let len = checked_add!(offset, arc_bytes);
+
+                    match self.oid.as_bytes().get(len).cloned() {
+                        // The arithmetic below includes advance checks
+                        // against `ARC_MAX_BYTES` and `ARC_MAX_LAST_OCTET`
+                        // which ensure the operations will not overflow.
+                        #[allow(clippy::integer_arithmetic)]
                         Some(byte) => {
-                            arc_bytes += 1;
+                            arc_bytes = checked_add!(arc_bytes, 1);
 
                             if (arc_bytes > ARC_MAX_BYTES) && (byte & ARC_MAX_LAST_OCTET != 0) {
                                 return Err(Error::ArcTooBig);
@@ -77,7 +83,7 @@
                             result = result << 7 | (byte & 0b1111111) as Arc;
 
                             if byte & 0b10000000 == 0 {
-                                self.cursor = Some(offset + arc_bytes);
+                                self.cursor = Some(checked_add!(offset, arc_bytes));
                                 return Ok(Some(result));
                             }
                         }
@@ -123,16 +129,21 @@
             return Err(Error::ArcInvalid { arc: second_arc });
         }
 
+        // The checks above ensure this operation will not overflow
+        #[allow(clippy::integer_arithmetic)]
         let byte = (first_arc * (ARC_MAX_SECOND + 1)) as u8 + second_arc as u8;
+
         Ok(Self(byte))
     }
 
     /// Get the value of the first arc
+    #[allow(clippy::integer_arithmetic)]
     pub(crate) const fn first_arc(self) -> Arc {
         self.0 as Arc / (ARC_MAX_SECOND + 1)
     }
 
     /// Get the value of the second arc
+    #[allow(clippy::integer_arithmetic)]
     pub(crate) const fn second_arc(self) -> Arc {
         self.0 as Arc % (ARC_MAX_SECOND + 1)
     }
@@ -141,6 +152,8 @@
 impl TryFrom<u8> for RootArcs {
     type Error = Error;
 
+    // Ensured not to overflow by constructor invariants
+    #[allow(clippy::integer_arithmetic)]
     fn try_from(octet: u8) -> Result<Self> {
         let first = octet as Arc / (ARC_MAX_SECOND + 1);
         let second = octet as Arc % (ARC_MAX_SECOND + 1);
diff --git a/src/checked.rs b/src/checked.rs
new file mode 100644
index 0000000..7ff16a2
--- /dev/null
+++ b/src/checked.rs
@@ -0,0 +1,11 @@
+//! Checked arithmetic helpers.
+
+/// `const fn`-friendly checked addition helper.
+macro_rules! checked_add {
+    ($a:expr, $b:expr) => {
+        match $a.checked_add($b) {
+            Some(n) => n,
+            None => return Err(Error::Length),
+        }
+    };
+}
diff --git a/src/db.rs b/src/db.rs
index 9491d2f..971990d 100644
--- a/src/db.rs
+++ b/src/db.rs
@@ -9,7 +9,7 @@
 //! [RFC 5280]: https://datatracker.ietf.org/doc/html/rfc5280
 //! [Object Identifier Descriptors]: https://www.iana.org/assignments/ldap-parameters/ldap-parameters.xhtml#ldap-parameters-3
 
-#![allow(missing_docs)]
+#![allow(clippy::integer_arithmetic, missing_docs)]
 
 mod gen;
 
diff --git a/src/encoder.rs b/src/encoder.rs
index 877b6ed..4df3aab 100644
--- a/src/encoder.rs
+++ b/src/encoder.rs
@@ -61,6 +61,8 @@
                 self.state = State::FirstArc(arc);
                 Ok(self)
             }
+            // Ensured not to overflow by `ARC_MAX_SECOND` check
+            #[allow(clippy::integer_arithmetic)]
             State::FirstArc(first_arc) => {
                 if arc > ARC_MAX_SECOND {
                     return Err(Error::ArcInvalid { arc });
@@ -71,10 +73,13 @@
                 self.cursor = 1;
                 Ok(self)
             }
+            // TODO(tarcieri): finer-grained overflow safety / checked arithmetic
+            #[allow(clippy::integer_arithmetic)]
             State::Body => {
                 // Total number of bytes in encoded arc - 1
                 let nbytes = base128_len(arc);
 
+                // Shouldn't overflow on any 16-bit+ architectures
                 if self.cursor + nbytes + 1 >= ObjectIdentifier::MAX_SIZE {
                     return Err(Error::Length);
                 }
@@ -109,8 +114,10 @@
     const fn encode_base128_byte(mut self, mut n: u32, i: usize, continued: bool) -> Result<Self> {
         let mask = if continued { 0b10000000 } else { 0 };
 
+        // Underflow checked by branch
+        #[allow(clippy::integer_arithmetic)]
         if n > 0x80 {
-            self.bytes[self.cursor + i] = (n & 0b1111111) as u8 | mask;
+            self.bytes[checked_add!(self.cursor, i)] = (n & 0b1111111) as u8 | mask;
             n >>= 7;
 
             if i > 0 {
diff --git a/src/error.rs b/src/error.rs
index 72658f2..528ce78 100644
--- a/src/error.rs
+++ b/src/error.rs
@@ -44,6 +44,24 @@
     TrailingDot,
 }
 
+impl Error {
+    /// Escalate this error into a panic.
+    ///
+    /// This is a workaround until `Result::unwrap` is allowed in `const fn`.
+    #[allow(clippy::panic)]
+    pub(crate) const fn panic(self) -> ! {
+        match self {
+            Error::ArcInvalid { .. } | Error::ArcTooBig => panic!("OID contains invalid arc"),
+            Error::Base128 => panic!("OID contains arc with invalid base 128 encoding"),
+            Error::DigitExpected { .. } => panic!("OID expected to start with digit"),
+            Error::Empty => panic!("OID value is empty"),
+            Error::Length => panic!("OID length invalid"),
+            Error::NotEnoughArcs => panic!("OID requires minimum of 3 arcs"),
+            Error::TrailingDot => panic!("OID ends with invalid trailing '.'"),
+        }
+    }
+}
+
 impl fmt::Display for Error {
     fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
         match *self {
diff --git a/src/lib.rs b/src/lib.rs
index 01337e5..d847065 100644
--- a/src/lib.rs
+++ b/src/lib.rs
@@ -2,12 +2,20 @@
 #![cfg_attr(docsrs, feature(doc_cfg))]
 #![doc = include_str!("../README.md")]
 #![doc(
-    html_logo_url = "https://raw.githubusercontent.com/RustCrypto/meta/master/logo.svg",
-    html_favicon_url = "https://raw.githubusercontent.com/RustCrypto/meta/master/logo.svg",
-    html_root_url = "https://docs.rs/const-oid/0.9.0"
+    html_logo_url = "https://raw.githubusercontent.com/RustCrypto/media/6ee8e381/logo.svg",
+    html_favicon_url = "https://raw.githubusercontent.com/RustCrypto/media/6ee8e381/logo.svg"
 )]
-#![forbid(unsafe_code, clippy::unwrap_used)]
-#![warn(missing_docs, rust_2018_idioms)]
+#![forbid(unsafe_code)]
+#![warn(
+    clippy::integer_arithmetic,
+    clippy::panic,
+    clippy::panic_in_result_fn,
+    clippy::unwrap_used,
+    missing_docs,
+    rust_2018_idioms,
+    unused_lifetimes,
+    unused_qualifications
+)]
 
 /// Local Android change: Use std to allow building as a dylib.
 #[cfg(android_dylib)]
@@ -16,6 +24,9 @@
 #[cfg(feature = "std")]
 extern crate std;
 
+#[macro_use]
+mod checked;
+
 mod arcs;
 mod encoder;
 mod error;
@@ -39,6 +50,22 @@
     const OID: ObjectIdentifier;
 }
 
+/// A trait which associates a dynamic, `&self`-dependent OID with a type,
+/// which may change depending on the type's value.
+///
+/// This trait is object safe and auto-impl'd for any types which impl
+/// [`AssociatedOid`].
+pub trait DynAssociatedOid {
+    /// Get the OID associated with this value.
+    fn oid(&self) -> ObjectIdentifier;
+}
+
+impl<T: AssociatedOid> DynAssociatedOid for T {
+    fn oid(&self) -> ObjectIdentifier {
+        T::OID
+    }
+}
+
 /// Object identifier (OID).
 ///
 /// OIDs are hierarchical structures consisting of "arcs", i.e. integer
@@ -89,13 +116,7 @@
     pub const fn new_unwrap(s: &str) -> Self {
         match Self::new(s) {
             Ok(oid) => oid,
-            Err(Error::ArcInvalid { .. } | Error::ArcTooBig) => panic!("OID contains invalid arc"),
-            Err(Error::Base128) => panic!("OID contains arc with invalid base 128 encoding"),
-            Err(Error::DigitExpected { .. }) => panic!("OID expected to start with digit"),
-            Err(Error::Empty) => panic!("OID value is empty"),
-            Err(Error::Length) => panic!("OID length invalid"),
-            Err(Error::NotEnoughArcs) => panic!("OID requires minimum of 3 arcs"),
-            Err(Error::TrailingDot) => panic!("OID ends with invalid trailing '.'"),
+            Err(err) => err.panic(),
         }
     }
 
@@ -225,8 +246,10 @@
         for (i, arc) in self.arcs().enumerate() {
             write!(f, "{}", arc)?;
 
-            if i < len - 1 {
-                write!(f, ".")?;
+            if let Some(j) = i.checked_add(1) {
+                if j < len {
+                    write!(f, ".")?;
+                }
             }
         }
 
diff --git a/src/parser.rs b/src/parser.rs
index 3554ec6..6f875fa 100644
--- a/src/parser.rs
+++ b/src/parser.rs
@@ -49,6 +49,8 @@
                 }
                 Err(err) => Err(err),
             },
+            // TODO(tarcieri): checked arithmetic
+            #[allow(clippy::integer_arithmetic)]
             [byte @ b'0'..=b'9', remaining @ ..] => {
                 let digit = byte.saturating_sub(b'0');
                 self.current_arc = self.current_arc * 10 + digit as Arc;