Merge "Shift host-only to visibility restriction" into main
diff --git a/.cargo_vcs_info.json b/.cargo_vcs_info.json
index eac35cf..246b3ff 100644
--- a/.cargo_vcs_info.json
+++ b/.cargo_vcs_info.json
@@ -1,6 +1,6 @@
 {
   "git": {
-    "sha1": "2e3a6ab6d9b3d541604711aadb9bc1d5f53abc02"
+    "sha1": "c07eecfb9565ae5268ae484f9d6f5e913959c301"
   },
   "path_in_vcs": "sec1"
 }
\ No newline at end of file
diff --git a/Android.bp b/Android.bp
index 64a10d7..aa0a126 100644
--- a/Android.bp
+++ b/Android.bp
@@ -36,7 +36,7 @@
     host_supported: true,
     crate_name: "sec1",
     cargo_env_compat: true,
-    cargo_pkg_version: "0.3.0",
+    cargo_pkg_version: "0.7.3",
     srcs: ["src/lib.rs"],
     edition: "2021",
     features: [
@@ -67,7 +67,7 @@
     name: "libsec1_nostd",
     crate_name: "sec1",
     cargo_env_compat: true,
-    cargo_pkg_version: "0.3.0",
+    cargo_pkg_version: "0.7.3",
     srcs: ["src/lib.rs"],
     edition: "2021",
     features: [
diff --git a/CHANGELOG.md b/CHANGELOG.md
index eb96da0..44a0fc9 100644
--- a/CHANGELOG.md
+++ b/CHANGELOG.md
@@ -4,6 +4,47 @@
 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.7.3 (2023-07-16)
+### Added
+- Impl `Hash` for `EncodedPoint` ([#1102])
+
+[#1102]: https://github.com/RustCrypto/formats/pull/1102
+
+## 0.7.2 (2023-04-09)
+### Added
+- Impl `ModulusSize` for `U24` ([#995])
+
+[#995]: https://github.com/RustCrypto/formats/pull/995
+
+## 0.7.1 (2023-02-27)
+### Fixed
+- Encode `ECPrivateKey` version ([#908])
+
+[#908]: https://github.com/RustCrypto/formats/pull/908
+
+## 0.7.0 (2023-02-26) [YANKED]
+### Changed
+- MSRV 1.65 ([#805])
+- Bump `serdect` to v0.2 ([#893])
+- Bump `der` dependency to v0.7 ([#899])
+- Bump `spki` dependency to v0.7 ([#900])
+- Bump `pkcs8` to v0.10 ([#902])
+
+[#805]: https://github.com/RustCrypto/formats/pull/805
+[#893]: https://github.com/RustCrypto/formats/pull/893
+[#899]: https://github.com/RustCrypto/formats/pull/899
+[#900]: https://github.com/RustCrypto/formats/pull/900
+[#902]: https://github.com/RustCrypto/formats/pull/902
+
+## 0.6.0 (Skipped)
+- Skipped to synchronize version number with `der` and `spki`
+
+## 0.5.0 (Skipped)
+- Skipped to synchronize version number with `der` and `spki`
+
+## 0.4.0 (Skipped)
+- Skipped to synchronize version number with `der` and `spki`
+
 ## 0.3.0 (2022-05-08)
 ### Added
 - Make `der` feature optional but on-by-default ([#497])
diff --git a/Cargo.toml b/Cargo.toml
index 40cd053..c640dea 100644
--- a/Cargo.toml
+++ b/Cargo.toml
@@ -11,9 +11,9 @@
 
 [package]
 edition = "2021"
-rust-version = "1.57"
+rust-version = "1.65"
 name = "sec1"
-version = "0.3.0"
+version = "0.7.3"
 authors = ["RustCrypto Developers"]
 description = """
 Pure Rust implementation of SEC1: Elliptic Curve Cryptography encoding formats
@@ -36,7 +36,6 @@
 ]
 license = "Apache-2.0 OR MIT"
 repository = "https://github.com/RustCrypto/formats/tree/master/sec1"
-resolver = "2"
 
 [package.metadata.docs.rs]
 all-features = true
@@ -46,27 +45,27 @@
 ]
 
 [dependencies.base16ct]
-version = "0.1.1"
+version = "0.2"
 optional = true
 default-features = false
 
 [dependencies.der]
-version = "0.6"
+version = "0.7"
 features = ["oid"]
 optional = true
 
 [dependencies.generic-array]
-version = "0.14.4"
+version = "0.14.7"
 optional = true
 default-features = false
 
 [dependencies.pkcs8]
-version = "0.9"
+version = "0.10"
 optional = true
 default-features = false
 
 [dependencies.serdect]
-version = "0.1"
+version = "0.2"
 features = ["alloc"]
 optional = true
 default-features = false
@@ -82,32 +81,40 @@
 default-features = false
 
 [dev-dependencies.hex-literal]
-version = "0.3"
+version = "0.4"
 
 [dev-dependencies.tempfile]
 version = "3"
 
 [features]
 alloc = [
-    "der/alloc",
-    "pkcs8/alloc",
-    "zeroize/alloc",
+    "der?/alloc",
+    "pkcs8?/alloc",
+    "zeroize?/alloc",
 ]
 default = [
     "der",
     "point",
 ]
+der = [
+    "dep:der",
+    "zeroize",
+]
 pem = [
     "alloc",
     "der/pem",
     "pkcs8/pem",
 ]
 point = [
-    "base16ct",
-    "generic-array",
+    "dep:base16ct",
+    "dep:generic-array",
 ]
-serde = ["serdect"]
+serde = ["dep:serdect"]
 std = [
-    "der/std",
     "alloc",
+    "der?/std",
+]
+zeroize = [
+    "dep:zeroize",
+    "der?/zeroize",
 ]
diff --git a/Cargo.toml.orig b/Cargo.toml.orig
index f1ee649..eaa20b9 100644
--- a/Cargo.toml.orig
+++ b/Cargo.toml.orig
@@ -1,6 +1,6 @@
 [package]
 name = "sec1"
-version = "0.3.0"
+version = "0.7.3"
 description = """
 Pure Rust implementation of SEC1: Elliptic Curve Cryptography encoding formats
 including ASN.1 DER-serialized private keys as well as the
@@ -13,28 +13,31 @@
 keywords = ["crypto", "key", "elliptic-curve", "secg"]
 readme = "README.md"
 edition = "2021"
-rust-version = "1.57"
+rust-version = "1.65"
 
 [dependencies]
-base16ct = { version = "0.1.1", optional = true, default-features = false, path = "../base16ct" }
-der = { version = "0.6", optional = true, features = ["oid"], path = "../der" }
-generic-array = { version = "0.14.4", optional = true, default-features = false }
-pkcs8 = { version = "0.9", optional = true, default-features = false, path = "../pkcs8" }
-serdect = { version = "0.1", optional = true, default-features = false, features = ["alloc"], path = "../serdect" }
+base16ct = { version = "0.2", optional = true, default-features = false }
+der = { version = "0.7", optional = true, features = ["oid"] }
+generic-array = { version = "0.14.7", optional = true, default-features = false }
+pkcs8 = { version = "0.10", optional = true, default-features = false }
+serdect = { version = "0.2", optional = true, default-features = false, features = ["alloc"] }
 subtle = { version = "2", optional = true, default-features = false }
 zeroize = { version = "1", optional = true, default-features = false }
 
 [dev-dependencies]
-hex-literal = "0.3"
+hex-literal = "0.4"
 tempfile = "3"
 
 [features]
 default = ["der", "point"]
-alloc = ["der/alloc", "pkcs8/alloc", "zeroize/alloc"]
+alloc = ["der?/alloc", "pkcs8?/alloc", "zeroize?/alloc"]
+std = ["alloc", "der?/std"]
+
+der = ["dep:der", "zeroize"]
 pem = ["alloc", "der/pem", "pkcs8/pem"]
-point = ["base16ct", "generic-array"]
-serde = ["serdect"]
-std = ["der/std", "alloc"]
+point = ["dep:base16ct", "dep:generic-array"]
+serde = ["dep:serdect"]
+zeroize = ["dep:zeroize", "der?/zeroize"]
 
 [package.metadata.docs.rs]
 all-features = true
diff --git a/LICENSE-MIT b/LICENSE-MIT
new file mode 100644
index 0000000..68ddaa3
--- /dev/null
+++ b/LICENSE-MIT
@@ -0,0 +1,25 @@
+Copyright (c) 2021-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 4bc13a3..309de68 100644
--- a/METADATA
+++ b/METADATA
@@ -1,20 +1,20 @@
+# This project was upgraded with external_updater.
+# Usage: tools/external_updater/updater.sh update rust/crates/sec1
+# For more info, check https://cs.android.com/android/platform/superproject/+/main:tools/external_updater/README.md
+
 name: "sec1"
 description: "Pure Rust implementation of SEC1: Elliptic Curve Cryptography encoding formats including ASN.1 DER-serialized private keys (also described in RFC5915) as well as the Elliptic-Curve-Point-to-Octet-String and Octet-String-to-Elliptic Curve-Point encoding algorithms."
 third_party {
-  url {
-    type: HOMEPAGE
-    value: "https://crates.io/crates/sec1"
-  }
-  url {
-    type: ARCHIVE
-    value: "https://static.crates.io/crates/sec1/sec1-0.3.0.crate"
-  }
-  version: "0.3.0"
-  # Dual-licensed, using the least restrictive per go/thirdpartylicenses#same.
   license_type: NOTICE
   last_upgrade_date {
-    year: 2022
-    month: 9
-    day: 6
+    year: 2023
+    month: 12
+    day: 15
+  }
+  homepage: "https://crates.io/crates/sec1"
+  identifier {
+    type: "Archive"
+    value: "https://static.crates.io/crates/sec1/sec1-0.7.3.crate"
+    version: "0.7.3"
   }
 }
diff --git a/README.md b/README.md
index fe1d9b6..5678de6 100644
--- a/README.md
+++ b/README.md
@@ -18,7 +18,7 @@
 
 ## Minimum Supported Rust Version
 
-This crate requires **Rust 1.57** at a minimum.
+This crate requires **Rust 1.65** at a minimum.
 
 We may change the MSRV in the future, but it will be accompanied by a minor
 version bump.
@@ -45,7 +45,7 @@
 [docs-image]: https://docs.rs/sec1/badge.svg
 [docs-link]: https://docs.rs/sec1/
 [license-image]: https://img.shields.io/badge/license-Apache2.0/MIT-blue.svg
-[rustc-image]: https://img.shields.io/badge/rustc-1.57+-blue.svg
+[rustc-image]: https://img.shields.io/badge/rustc-1.65+-blue.svg
 [chat-image]: https://img.shields.io/badge/zulip-join_chat-blue.svg
 [chat-link]: https://rustcrypto.zulipchat.com/#narrow/stream/300570-formats
 [build-image]: https://github.com/RustCrypto/formats/workflows/sec1/badge.svg?branch=master&event=push
diff --git a/src/error.rs b/src/error.rs
index 3700ac5..0d8bc8b 100644
--- a/src/error.rs
+++ b/src/error.rs
@@ -14,7 +14,6 @@
 pub enum Error {
     /// ASN.1 DER-related errors.
     #[cfg(feature = "der")]
-    #[cfg_attr(docsrs, doc(cfg(feature = "der")))]
     Asn1(der::Error),
 
     /// Cryptographic errors.
@@ -52,7 +51,6 @@
 }
 
 #[cfg(feature = "der")]
-#[cfg_attr(docsrs, doc(cfg(feature = "der")))]
 impl From<der::Error> for Error {
     fn from(err: der::Error) -> Error {
         Error::Asn1(err)
diff --git a/src/lib.rs b/src/lib.rs
index 6b658b6..6b44d3d 100644
--- a/src/lib.rs
+++ b/src/lib.rs
@@ -1,12 +1,18 @@
 #![no_std]
-#![cfg_attr(docsrs, feature(doc_cfg))]
+#![cfg_attr(docsrs, feature(doc_auto_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_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, unused_qualifications)]
+#![forbid(unsafe_code)]
+#![warn(
+    clippy::mod_module_files,
+    clippy::unwrap_used,
+    missing_docs,
+    rust_2018_idioms,
+    unused_qualifications
+)]
 
 //! ## `serde` support
 //!
@@ -52,15 +58,13 @@
 #[cfg(feature = "der")]
 pub use crate::{parameters::EcParameters, private_key::EcPrivateKey, traits::DecodeEcPrivateKey};
 
-#[cfg(feature = "alloc")]
+#[cfg(all(feature = "alloc", feature = "der"))]
 pub use crate::traits::EncodeEcPrivateKey;
 
 #[cfg(feature = "pem")]
-#[cfg_attr(docsrs, doc(cfg(feature = "pem")))]
 pub use der::pem::{self, LineEnding};
 
 #[cfg(feature = "pkcs8")]
-#[cfg_attr(docsrs, doc(cfg(feature = "pkcs8")))]
 pub use pkcs8;
 
 #[cfg(feature = "pkcs8")]
@@ -74,5 +78,4 @@
 ///
 /// <http://oid-info.com/get/1.2.840.10045.2.1>
 #[cfg(feature = "pkcs8")]
-#[cfg_attr(docsrs, doc(cfg(feature = "pkcs8")))]
 pub const ALGORITHM_OID: ObjectIdentifier = ObjectIdentifier::new_unwrap("1.2.840.10045.2.1");
diff --git a/src/parameters.rs b/src/parameters.rs
index ed9d152..20458e6 100644
--- a/src/parameters.rs
+++ b/src/parameters.rs
@@ -18,7 +18,6 @@
 ///   -- with ANSI X9.
 /// ```
 #[derive(Copy, Clone, Debug, Eq, PartialEq)]
-#[cfg_attr(docsrs, doc(cfg(feature = "der")))]
 pub enum EcParameters {
     /// Elliptic curve named by a particular OID.
     ///
@@ -41,7 +40,7 @@
         }
     }
 
-    fn encode_value(&self, writer: &mut dyn Writer) -> der::Result<()> {
+    fn encode_value(&self, writer: &mut impl Writer) -> der::Result<()> {
         match self {
             Self::NamedCurve(oid) => oid.encode_value(writer),
         }
diff --git a/src/point.rs b/src/point.rs
index eb0d2ca..818f5bd 100644
--- a/src/point.rs
+++ b/src/point.rs
@@ -10,11 +10,12 @@
 use core::{
     cmp::Ordering,
     fmt::{self, Debug},
+    hash::{Hash, Hasher},
     ops::Add,
     str,
 };
 use generic_array::{
-    typenum::{U1, U28, U32, U48, U66},
+    typenum::{U1, U24, U28, U32, U48, U66},
     ArrayLength, GenericArray,
 };
 
@@ -59,7 +60,7 @@
     }
 }
 
-impl_modulus_size!(U28, U32, U48, U66);
+impl_modulus_size!(U24, U28, U32, U48, U66);
 
 /// SEC1 encoded curve point.
 ///
@@ -157,7 +158,6 @@
 
     /// Get boxed byte slice containing the serialized [`EncodedPoint`]
     #[cfg(feature = "alloc")]
-    #[cfg_attr(docsrs, doc(cfg(feature = "alloc")))]
     pub fn to_bytes(&self) -> Box<[u8]> {
         self.as_bytes().to_vec().into_boxed_slice()
     }
@@ -295,6 +295,15 @@
     }
 }
 
+impl<Size> Hash for EncodedPoint<Size>
+where
+    Size: ModulusSize,
+{
+    fn hash<H: Hasher>(&self, state: &mut H) {
+        self.as_bytes().hash(state)
+    }
+}
+
 impl<Size: ModulusSize> PartialOrd for EncodedPoint<Size>
 where
     Size: ModulusSize,
@@ -381,7 +390,6 @@
 }
 
 #[cfg(feature = "serde")]
-#[cfg_attr(docsrs, doc(cfg(feature = "serde")))]
 impl<Size> Serialize for EncodedPoint<Size>
 where
     Size: ModulusSize,
@@ -395,7 +403,6 @@
 }
 
 #[cfg(feature = "serde")]
-#[cfg_attr(docsrs, doc(cfg(feature = "serde")))]
 impl<'de, Size> Deserialize<'de> for EncodedPoint<Size>
 where
     Size: ModulusSize,
@@ -405,7 +412,7 @@
         D: de::Deserializer<'de>,
     {
         let bytes = serdect::slice::deserialize_hex_or_bin_vec(deserializer)?;
-        Self::from_bytes(&bytes).map_err(de::Error::custom)
+        Self::from_bytes(bytes).map_err(de::Error::custom)
     }
 }
 
@@ -663,8 +670,8 @@
 
     #[test]
     fn decode_invalid_tag() {
-        let mut compressed_bytes = COMPRESSED_BYTES.clone();
-        let mut uncompressed_bytes = UNCOMPRESSED_BYTES.clone();
+        let mut compressed_bytes = COMPRESSED_BYTES;
+        let mut uncompressed_bytes = UNCOMPRESSED_BYTES;
 
         for bytes in &mut [&mut compressed_bytes[..], &mut uncompressed_bytes[..]] {
             for tag in 0..=0xFF {
diff --git a/src/private_key.rs b/src/private_key.rs
index 2362432..5315799 100644
--- a/src/private_key.rs
+++ b/src/private_key.rs
@@ -8,11 +8,12 @@
 use crate::{EcParameters, Error, Result};
 use core::fmt;
 use der::{
-    asn1::{BitStringRef, ContextSpecific, OctetStringRef},
-    Decode, DecodeValue, Encode, Header, Reader, Sequence, Tag, TagMode, TagNumber,
+    asn1::{BitStringRef, ContextSpecific, ContextSpecificRef, OctetStringRef},
+    Decode, DecodeValue, Encode, EncodeValue, Header, Length, Reader, Sequence, Tag, TagMode,
+    TagNumber, Writer,
 };
 
-#[cfg(feature = "alloc")]
+#[cfg(all(feature = "alloc", feature = "zeroize"))]
 use der::SecretDocument;
 
 #[cfg(feature = "pem")]
@@ -58,7 +59,6 @@
 /// [SEC1: Elliptic Curve Cryptography (Version 2.0)]: https://www.secg.org/sec1-v2.pdf
 /// [RFC5915 Section 3]: https://datatracker.ietf.org/doc/html/rfc5915#section-3
 #[derive(Clone)]
-#[cfg_attr(docsrs, doc(cfg(feature = "der")))]
 pub struct EcPrivateKey<'a> {
     /// Private key data.
     pub private_key: &'a [u8],
@@ -70,6 +70,30 @@
     pub public_key: Option<&'a [u8]>,
 }
 
+impl<'a> EcPrivateKey<'a> {
+    fn context_specific_parameters(&self) -> Option<ContextSpecificRef<'_, EcParameters>> {
+        self.parameters.as_ref().map(|params| ContextSpecificRef {
+            tag_number: EC_PARAMETERS_TAG,
+            tag_mode: TagMode::Explicit,
+            value: params,
+        })
+    }
+
+    fn context_specific_public_key(
+        &self,
+    ) -> der::Result<Option<ContextSpecific<BitStringRef<'a>>>> {
+        self.public_key
+            .map(|pk| {
+                BitStringRef::from_bytes(pk).map(|value| ContextSpecific {
+                    tag_number: PUBLIC_KEY_TAG,
+                    tag_mode: TagMode::Explicit,
+                    value,
+                })
+            })
+            .transpose()
+    }
+}
+
 impl<'a> DecodeValue<'a> for EcPrivateKey<'a> {
     fn decode_value<R: Reader<'a>>(reader: &mut R, header: Header) -> der::Result<Self> {
         reader.read_nested(header.length, |reader| {
@@ -93,33 +117,25 @@
     }
 }
 
-impl<'a> Sequence<'a> for EcPrivateKey<'a> {
-    fn fields<F, T>(&self, f: F) -> der::Result<T>
-    where
-        F: FnOnce(&[&dyn Encode]) -> der::Result<T>,
-    {
-        f(&[
-            &VERSION,
-            &OctetStringRef::new(self.private_key)?,
-            &self.parameters.as_ref().map(|params| ContextSpecific {
-                tag_number: EC_PARAMETERS_TAG,
-                tag_mode: TagMode::Explicit,
-                value: *params,
-            }),
-            &self
-                .public_key
-                .map(|pk| {
-                    BitStringRef::from_bytes(pk).map(|value| ContextSpecific {
-                        tag_number: PUBLIC_KEY_TAG,
-                        tag_mode: TagMode::Explicit,
-                        value,
-                    })
-                })
-                .transpose()?,
-        ])
+impl EncodeValue for EcPrivateKey<'_> {
+    fn value_len(&self) -> der::Result<Length> {
+        VERSION.encoded_len()?
+            + OctetStringRef::new(self.private_key)?.encoded_len()?
+            + self.context_specific_parameters().encoded_len()?
+            + self.context_specific_public_key()?.encoded_len()?
+    }
+
+    fn encode_value(&self, writer: &mut impl Writer) -> der::Result<()> {
+        VERSION.encode(writer)?;
+        OctetStringRef::new(self.private_key)?.encode(writer)?;
+        self.context_specific_parameters().encode(writer)?;
+        self.context_specific_public_key()?.encode(writer)?;
+        Ok(())
     }
 }
 
+impl<'a> Sequence<'a> for EcPrivateKey<'a> {}
+
 impl<'a> TryFrom<&'a [u8]> for EcPrivateKey<'a> {
     type Error = Error;
 
@@ -138,7 +154,6 @@
 }
 
 #[cfg(feature = "alloc")]
-#[cfg_attr(docsrs, doc(cfg(feature = "alloc")))]
 impl TryFrom<EcPrivateKey<'_>> for SecretDocument {
     type Error = Error;
 
@@ -148,7 +163,6 @@
 }
 
 #[cfg(feature = "alloc")]
-#[cfg_attr(docsrs, doc(cfg(feature = "alloc")))]
 impl TryFrom<&EcPrivateKey<'_>> for SecretDocument {
     type Error = Error;
 
@@ -158,7 +172,6 @@
 }
 
 #[cfg(feature = "pem")]
-#[cfg_attr(docsrs, doc(cfg(feature = "pem")))]
 impl PemLabel for EcPrivateKey<'_> {
     const PEM_LABEL: &'static str = "EC PRIVATE KEY";
 }
diff --git a/src/traits.rs b/src/traits.rs
index cf0d971..304019e 100644
--- a/src/traits.rs
+++ b/src/traits.rs
@@ -21,7 +21,6 @@
 use zeroize::Zeroizing;
 
 /// Parse an [`EcPrivateKey`] from a SEC1-encoded document.
-#[cfg_attr(docsrs, doc(cfg(feature = "der")))]
 pub trait DecodeEcPrivateKey: Sized {
     /// Deserialize SEC1 private key from ASN.1 DER-encoded data
     /// (binary format).
@@ -35,7 +34,6 @@
     /// -----BEGIN EC PRIVATE KEY-----
     /// ```
     #[cfg(feature = "pem")]
-    #[cfg_attr(docsrs, doc(cfg(feature = "pem")))]
     fn from_sec1_pem(s: &str) -> Result<Self> {
         let (label, doc) = SecretDocument::from_pem(s)?;
         EcPrivateKey::validate_pem_label(label)?;
@@ -45,15 +43,12 @@
     /// Load SEC1 private key from an ASN.1 DER-encoded file on the local
     /// filesystem (binary format).
     #[cfg(feature = "std")]
-    #[cfg_attr(docsrs, doc(cfg(feature = "std")))]
     fn read_sec1_der_file(path: impl AsRef<Path>) -> Result<Self> {
         Self::from_sec1_der(SecretDocument::read_der_file(path)?.as_bytes())
     }
 
     /// Load SEC1 private key from a PEM-encoded file on the local filesystem.
     #[cfg(all(feature = "pem", feature = "std"))]
-    #[cfg_attr(docsrs, doc(cfg(feature = "pem")))]
-    #[cfg_attr(docsrs, doc(cfg(feature = "std")))]
     fn read_sec1_pem_file(path: impl AsRef<Path>) -> Result<Self> {
         let (label, doc) = SecretDocument::read_pem_file(path)?;
         EcPrivateKey::validate_pem_label(&label)?;
@@ -63,7 +58,6 @@
 
 /// Serialize a [`EcPrivateKey`] to a SEC1 encoded document.
 #[cfg(feature = "alloc")]
-#[cfg_attr(docsrs, doc(cfg(all(feature = "alloc", feature = "der"))))]
 pub trait EncodeEcPrivateKey {
     /// Serialize a [`SecretDocument`] containing a SEC1-encoded private key.
     fn to_sec1_der(&self) -> Result<SecretDocument>;
@@ -72,7 +66,6 @@
     ///
     /// To use the OS's native line endings, pass `Default::default()`.
     #[cfg(feature = "pem")]
-    #[cfg_attr(docsrs, doc(cfg(feature = "pem")))]
     fn to_sec1_pem(&self, line_ending: LineEnding) -> Result<Zeroizing<String>> {
         let doc = self.to_sec1_der()?;
         Ok(doc.to_pem(EcPrivateKey::PEM_LABEL, line_ending)?)
@@ -80,15 +73,12 @@
 
     /// Write ASN.1 DER-encoded SEC1 private key to the given path.
     #[cfg(feature = "std")]
-    #[cfg_attr(docsrs, doc(cfg(feature = "std")))]
     fn write_sec1_der_file(&self, path: impl AsRef<Path>) -> Result<()> {
         Ok(self.to_sec1_der()?.write_der_file(path)?)
     }
 
     /// Write ASN.1 DER-encoded SEC1 private key to the given path.
     #[cfg(all(feature = "pem", feature = "std"))]
-    #[cfg_attr(docsrs, doc(cfg(feature = "pem")))]
-    #[cfg_attr(docsrs, doc(cfg(feature = "std")))]
     fn write_sec1_pem_file(&self, path: impl AsRef<Path>, line_ending: LineEnding) -> Result<()> {
         let doc = self.to_sec1_der()?;
         Ok(doc.write_pem_file(path, EcPrivateKey::PEM_LABEL, line_ending)?)
@@ -96,14 +86,16 @@
 }
 
 #[cfg(feature = "pkcs8")]
-#[cfg_attr(docsrs, doc(cfg(feature = "pkcs8")))]
-impl<T: pkcs8::DecodePrivateKey> DecodeEcPrivateKey for T {
+impl<T> DecodeEcPrivateKey for T
+where
+    T: for<'a> TryFrom<pkcs8::PrivateKeyInfo<'a>, Error = pkcs8::Error>,
+{
     fn from_sec1_der(private_key: &[u8]) -> Result<Self> {
         let params_oid = EcPrivateKey::from_der(private_key)?
             .parameters
             .and_then(|params| params.named_curve());
 
-        let algorithm = pkcs8::AlgorithmIdentifier {
+        let algorithm = pkcs8::AlgorithmIdentifierRef {
             oid: ALGORITHM_OID,
             parameters: params_oid.as_ref().map(Into::into),
         };
@@ -117,7 +109,6 @@
 }
 
 #[cfg(all(feature = "alloc", feature = "pkcs8"))]
-#[cfg_attr(docsrs, doc(cfg(all(feature = "alloc", feature = "pkcs8"))))]
 impl<T: pkcs8::EncodePrivateKey> EncodeEcPrivateKey for T {
     fn to_sec1_der(&self) -> Result<SecretDocument> {
         let doc = self.to_pkcs8_der()?;
diff --git a/tests/private_key.rs b/tests/private_key.rs
index 5b985da..224a947 100644
--- a/tests/private_key.rs
+++ b/tests/private_key.rs
@@ -6,6 +6,9 @@
 use hex_literal::hex;
 use sec1::{EcParameters, EcPrivateKey};
 
+#[cfg(feature = "alloc")]
+use der::Encode;
+
 /// NIST P-256 SEC1 private key encoded as ASN.1 DER.
 ///
 /// Note: this key is extracted from the corresponding `p256-priv.der`
@@ -30,3 +33,11 @@
     );
     assert_eq!(key.public_key, Some(hex!("041CACFFB55F2F2CEFD89D89EB374B2681152452802DEEA09916068137D839CF7FC481A44492304D7EF66AC117BEFE83A8D08F155F2B52F9F618DD447029048E0F").as_ref()));
 }
+
+#[cfg(feature = "alloc")]
+#[test]
+fn encode_p256_der() {
+    let key = EcPrivateKey::try_from(P256_DER_EXAMPLE).unwrap();
+    let key_encoded = key.to_der().unwrap();
+    assert_eq!(P256_DER_EXAMPLE, key_encoded);
+}
diff --git a/tests/traits.rs b/tests/traits.rs
index 4bcd679..ab6e09a 100644
--- a/tests/traits.rs
+++ b/tests/traits.rs
@@ -1,6 +1,6 @@
 //! Tests for SEC1 encoding/decoding traits.
 
-#![cfg(any(feature = "pem", feature = "std"))]
+#![cfg(any(feature = "pem", all(feature = "der", feature = "std")))]
 
 use der::SecretDocument;
 use sec1::{DecodeEcPrivateKey, EncodeEcPrivateKey, Result};