mirror of
https://github.com/storytold/pdfcraft.git
synced 2026-10-31 21:48:09 -06:00
418 lines
19 KiB
Rust
418 lines
19 KiB
Rust
//! Digests, public keys (verification everywhere) and private keys (signing).
|
|
//!
|
|
//! Verification uses RustCrypto. RSA private-key operations use `aws-lc-rs` on native targets
|
|
//! (constant-time; ADR-0009: the `rsa` crate is never used for private keys there). ECDSA
|
|
//! signing uses RustCrypto with deterministic nonces (RFC 6979).
|
|
|
|
use crate::SignError;
|
|
use crate::der::{self, Tlv, tag};
|
|
|
|
pub mod oid {
|
|
pub const SHA1: &str = "1.3.14.3.2.26";
|
|
pub const SHA256: &str = "2.16.840.1.101.3.4.2.1";
|
|
pub const SHA384: &str = "2.16.840.1.101.3.4.2.2";
|
|
pub const SHA512: &str = "2.16.840.1.101.3.4.2.3";
|
|
pub const RSA: &str = "1.2.840.113549.1.1.1";
|
|
pub const RSA_SHA1: &str = "1.2.840.113549.1.1.5";
|
|
pub const RSA_PSS: &str = "1.2.840.113549.1.1.10";
|
|
pub const RSA_SHA256: &str = "1.2.840.113549.1.1.11";
|
|
pub const RSA_SHA384: &str = "1.2.840.113549.1.1.12";
|
|
pub const RSA_SHA512: &str = "1.2.840.113549.1.1.13";
|
|
pub const MGF1: &str = "1.2.840.113549.1.1.8";
|
|
pub const EC: &str = "1.2.840.10045.2.1";
|
|
pub const P256: &str = "1.2.840.10045.3.1.7";
|
|
pub const P384: &str = "1.3.132.0.34";
|
|
pub const ECDSA_SHA1: &str = "1.2.840.10045.4.1";
|
|
pub const ECDSA_SHA256: &str = "1.2.840.10045.4.3.2";
|
|
pub const ECDSA_SHA384: &str = "1.2.840.10045.4.3.3";
|
|
pub const ECDSA_SHA512: &str = "1.2.840.10045.4.3.4";
|
|
}
|
|
|
|
/// Message digests.
|
|
#[derive(Clone, Copy, Debug, PartialEq, Eq, Hash)]
|
|
pub enum DigestAlg {
|
|
Sha1,
|
|
Sha256,
|
|
Sha384,
|
|
Sha512,
|
|
}
|
|
|
|
impl DigestAlg {
|
|
pub fn from_oid(o: &str) -> Option<DigestAlg> {
|
|
Some(match o {
|
|
oid::SHA1 => DigestAlg::Sha1,
|
|
oid::SHA256 => DigestAlg::Sha256,
|
|
oid::SHA384 => DigestAlg::Sha384,
|
|
oid::SHA512 => DigestAlg::Sha512,
|
|
_ => return None,
|
|
})
|
|
}
|
|
|
|
pub fn oid(self) -> &'static str {
|
|
match self {
|
|
DigestAlg::Sha1 => oid::SHA1,
|
|
DigestAlg::Sha256 => oid::SHA256,
|
|
DigestAlg::Sha384 => oid::SHA384,
|
|
DigestAlg::Sha512 => oid::SHA512,
|
|
}
|
|
}
|
|
|
|
pub fn name(self) -> &'static str {
|
|
match self {
|
|
DigestAlg::Sha1 => "SHA-1",
|
|
DigestAlg::Sha256 => "SHA-256",
|
|
DigestAlg::Sha384 => "SHA-384",
|
|
DigestAlg::Sha512 => "SHA-512",
|
|
}
|
|
}
|
|
|
|
/// The digest of the concatenation of `parts`.
|
|
pub fn digest(self, parts: &[&[u8]]) -> Vec<u8> {
|
|
fn run<D: sha2::Digest>(parts: &[&[u8]]) -> Vec<u8> {
|
|
let mut h = D::new();
|
|
for p in parts {
|
|
h.update(p);
|
|
}
|
|
h.finalize().to_vec()
|
|
}
|
|
match self {
|
|
DigestAlg::Sha1 => run::<sha1::Sha1>(parts),
|
|
DigestAlg::Sha256 => run::<sha2::Sha256>(parts),
|
|
DigestAlg::Sha384 => run::<sha2::Sha384>(parts),
|
|
DigestAlg::Sha512 => run::<sha2::Sha512>(parts),
|
|
}
|
|
}
|
|
|
|
/// The `AlgorithmIdentifier` (no parameters, as RFC 5754 recommends).
|
|
pub fn algorithm(self) -> Vec<u8> {
|
|
der::algorithm(self.oid(), None)
|
|
}
|
|
}
|
|
|
|
/// How a signature value is computed.
|
|
#[derive(Clone, Copy, Debug, PartialEq, Eq)]
|
|
pub enum Scheme {
|
|
RsaPkcs1,
|
|
RsaPss,
|
|
Ecdsa,
|
|
}
|
|
|
|
/// A signature `AlgorithmIdentifier`: the scheme and, when the identifier names one, the digest.
|
|
pub fn signature_algorithm(alg: &Tlv<'_>) -> Result<(Scheme, Option<DigestAlg>), SignError> {
|
|
let parts = alg.children()?;
|
|
let o = parts.first().ok_or_else(|| SignError::Malformed("empty algorithm".into()))?.oid()?;
|
|
Ok(match o.as_str() {
|
|
oid::RSA => (Scheme::RsaPkcs1, None),
|
|
oid::RSA_SHA1 => (Scheme::RsaPkcs1, Some(DigestAlg::Sha1)),
|
|
oid::RSA_SHA256 => (Scheme::RsaPkcs1, Some(DigestAlg::Sha256)),
|
|
oid::RSA_SHA384 => (Scheme::RsaPkcs1, Some(DigestAlg::Sha384)),
|
|
oid::RSA_SHA512 => (Scheme::RsaPkcs1, Some(DigestAlg::Sha512)),
|
|
oid::RSA_PSS => {
|
|
// RSASSA-PSS-params: [0] hashAlgorithm (default SHA-1).
|
|
let hash = parts
|
|
.get(1)
|
|
.and_then(|p| p.children().ok())
|
|
.and_then(|c| c.into_iter().find(|t| t.tag == tag::ctx(0)))
|
|
.and_then(|h| h.inner().ok())
|
|
.and_then(|a| a.children().ok())
|
|
.and_then(|a| a.first().and_then(|o| o.oid().ok()))
|
|
.and_then(|o| DigestAlg::from_oid(&o))
|
|
.unwrap_or(DigestAlg::Sha1);
|
|
(Scheme::RsaPss, Some(hash))
|
|
}
|
|
oid::ECDSA_SHA1 => (Scheme::Ecdsa, Some(DigestAlg::Sha1)),
|
|
oid::ECDSA_SHA256 => (Scheme::Ecdsa, Some(DigestAlg::Sha256)),
|
|
oid::ECDSA_SHA384 => (Scheme::Ecdsa, Some(DigestAlg::Sha384)),
|
|
oid::ECDSA_SHA512 => (Scheme::Ecdsa, Some(DigestAlg::Sha512)),
|
|
oid::EC => (Scheme::Ecdsa, None),
|
|
other => return Err(SignError::Unsupported(format!("signature algorithm {other}"))),
|
|
})
|
|
}
|
|
|
|
/// A public key from a `SubjectPublicKeyInfo`.
|
|
#[derive(Clone, Debug, PartialEq, Eq)]
|
|
pub enum PublicKey {
|
|
Rsa { n: Vec<u8>, e: Vec<u8> },
|
|
P256(Vec<u8>),
|
|
P384(Vec<u8>),
|
|
}
|
|
|
|
impl PublicKey {
|
|
pub fn from_spki(spki: &Tlv<'_>) -> Result<PublicKey, SignError> {
|
|
let parts = spki.children()?;
|
|
let [alg, key] = parts.as_slice() else { return Err(SignError::Malformed("SubjectPublicKeyInfo".into())) };
|
|
let alg = alg.children()?;
|
|
let o = alg.first().ok_or_else(|| SignError::Malformed("key algorithm".into()))?.oid()?;
|
|
let bits = key.bits()?;
|
|
match o.as_str() {
|
|
oid::RSA => {
|
|
let k = Tlv::parse_all(bits)?.children()?;
|
|
let [n, e] = k.as_slice() else { return Err(SignError::Malformed("RSAPublicKey".into())) };
|
|
Ok(PublicKey::Rsa { n: n.uint_bytes().to_vec(), e: e.uint_bytes().to_vec() })
|
|
}
|
|
oid::EC => match alg.get(1).map(|c| c.oid()).transpose()?.as_deref() {
|
|
Some(oid::P256) => Ok(PublicKey::P256(bits.to_vec())),
|
|
Some(oid::P384) => Ok(PublicKey::P384(bits.to_vec())),
|
|
other => Err(SignError::Unsupported(format!("elliptic curve {}", other.unwrap_or("?")))),
|
|
},
|
|
other => Err(SignError::Unsupported(format!("public key algorithm {other}"))),
|
|
}
|
|
}
|
|
|
|
/// The `SubjectPublicKeyInfo` encoding.
|
|
pub fn spki(&self) -> Vec<u8> {
|
|
match self {
|
|
PublicKey::Rsa { n, e } => {
|
|
let rsa = der::seq(&[&der::uint(n), &der::uint(e)]);
|
|
der::seq(&[&der::algorithm(oid::RSA, Some(&der::null())), &der::bit_string(&rsa)])
|
|
}
|
|
PublicKey::P256(p) => der::seq(&[&der::seq(&[&der::oid(oid::EC), &der::oid(oid::P256)]), &der::bit_string(p)]),
|
|
PublicKey::P384(p) => der::seq(&[&der::seq(&[&der::oid(oid::EC), &der::oid(oid::P384)]), &der::bit_string(p)]),
|
|
}
|
|
}
|
|
|
|
/// "RSA 2048-bit", "ECDSA P-256"…
|
|
pub fn describe(&self) -> String {
|
|
match self {
|
|
PublicKey::Rsa { n, .. } => format!("RSA {}-bit", n.len() * 8),
|
|
PublicKey::P256(_) => "ECDSA P-256".into(),
|
|
PublicKey::P384(_) => "ECDSA P-384".into(),
|
|
}
|
|
}
|
|
|
|
/// Check `sig` over a message whose digest (with `alg`) is `digest`.
|
|
pub fn verify(&self, scheme: Scheme, alg: DigestAlg, digest: &[u8], sig: &[u8]) -> Result<bool, SignError> {
|
|
match (self, scheme) {
|
|
(PublicKey::Rsa { n, e }, Scheme::RsaPkcs1 | Scheme::RsaPss) => {
|
|
use rsa::{BoxedUint, Pkcs1v15Sign, RsaPublicKey, pss::Pss};
|
|
let key = RsaPublicKey::new(BoxedUint::from_be_slice_vartime(n), BoxedUint::from_be_slice_vartime(e))
|
|
.map_err(|e| SignError::Malformed(format!("RSA key: {e}")))?;
|
|
let ok = match (scheme, alg) {
|
|
(Scheme::RsaPkcs1, DigestAlg::Sha1) => key.verify(Pkcs1v15Sign::new::<sha1::Sha1>(), digest, sig),
|
|
(Scheme::RsaPkcs1, DigestAlg::Sha256) => key.verify(Pkcs1v15Sign::new::<sha2::Sha256>(), digest, sig),
|
|
(Scheme::RsaPkcs1, DigestAlg::Sha384) => key.verify(Pkcs1v15Sign::new::<sha2::Sha384>(), digest, sig),
|
|
(Scheme::RsaPkcs1, DigestAlg::Sha512) => key.verify(Pkcs1v15Sign::new::<sha2::Sha512>(), digest, sig),
|
|
(_, DigestAlg::Sha1) => key.verify(Pss::<sha1::Sha1>::new(), digest, sig),
|
|
(_, DigestAlg::Sha256) => key.verify(Pss::<sha2::Sha256>::new(), digest, sig),
|
|
(_, DigestAlg::Sha384) => key.verify(Pss::<sha2::Sha384>::new(), digest, sig),
|
|
(_, DigestAlg::Sha512) => key.verify(Pss::<sha2::Sha512>::new(), digest, sig),
|
|
};
|
|
Ok(ok.is_ok())
|
|
}
|
|
(PublicKey::P256(p), Scheme::Ecdsa) => {
|
|
use p256::ecdsa::signature::hazmat::PrehashVerifier;
|
|
let key = p256::ecdsa::VerifyingKey::from_sec1_bytes(p).map_err(|_| SignError::Malformed("P-256 key".into()))?;
|
|
let Ok(s) = p256::ecdsa::Signature::from_der(sig) else { return Ok(false) };
|
|
Ok(key.verify_prehash(digest, &s).is_ok())
|
|
}
|
|
(PublicKey::P384(p), Scheme::Ecdsa) => {
|
|
use p384::ecdsa::signature::hazmat::PrehashVerifier;
|
|
let key = p384::ecdsa::VerifyingKey::from_sec1_bytes(p).map_err(|_| SignError::Malformed("P-384 key".into()))?;
|
|
let Ok(s) = p384::ecdsa::Signature::from_der(sig) else { return Ok(false) };
|
|
Ok(key.verify_prehash(digest, &s).is_ok())
|
|
}
|
|
_ => Ok(false),
|
|
}
|
|
}
|
|
}
|
|
|
|
enum Inner {
|
|
#[cfg(not(target_arch = "wasm32"))]
|
|
Rsa(aws_lc_rs::signature::RsaKeyPair),
|
|
#[cfg(target_arch = "wasm32")]
|
|
Rsa,
|
|
P256(p256::ecdsa::SigningKey),
|
|
P384(p384::ecdsa::SigningKey),
|
|
/// A key held elsewhere (the macOS Keychain, a token) that signs on request.
|
|
External(std::sync::Arc<dyn ExternalKey>),
|
|
}
|
|
|
|
/// A private key PrintCraft can't read, only ask to sign (OS key stores, tokens).
|
|
pub trait ExternalKey: Send + Sync {
|
|
/// Sign `msg`, hashing it with `alg` (PKCS #1 v1.5 for RSA, DER-encoded ECDSA).
|
|
fn sign(&self, alg: DigestAlg, msg: &[u8]) -> Result<Vec<u8>, SignError>;
|
|
}
|
|
|
|
/// A private key that can sign.
|
|
pub struct PrivateKey {
|
|
inner: Inner,
|
|
public: PublicKey,
|
|
/// The PKCS#8 `PrivateKeyInfo` it came from (to write it back into a PKCS#12 file).
|
|
pkcs8: Vec<u8>,
|
|
}
|
|
|
|
impl std::fmt::Debug for PrivateKey {
|
|
fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
|
|
write!(f, "PrivateKey({})", self.public.describe())
|
|
}
|
|
}
|
|
|
|
impl PrivateKey {
|
|
/// A key held outside PrintCraft's memory (its public half is `public`). It can't be
|
|
/// exported to a PKCS #12 file.
|
|
pub fn external(public: PublicKey, key: std::sync::Arc<dyn ExternalKey>) -> PrivateKey {
|
|
PrivateKey { inner: Inner::External(key), public, pkcs8: Vec::new() }
|
|
}
|
|
|
|
/// Whether the key lives outside the app (and so can't be exported).
|
|
pub fn is_external(&self) -> bool {
|
|
matches!(self.inner, Inner::External(_))
|
|
}
|
|
|
|
/// From a PKCS#8 `PrivateKeyInfo`.
|
|
pub fn from_pkcs8(info: &[u8]) -> Result<PrivateKey, SignError> {
|
|
let t = Tlv::parse_all(info)?.children()?;
|
|
let [_version, alg, key, ..] = t.as_slice() else { return Err(SignError::Malformed("PrivateKeyInfo".into())) };
|
|
let alg = alg.children()?;
|
|
let o = alg.first().ok_or_else(|| SignError::Malformed("key algorithm".into()))?.oid()?;
|
|
let key = key.expect(tag::OCTET_STRING, "private key")?;
|
|
match o.as_str() {
|
|
oid::RSA => {
|
|
let k = Tlv::parse_all(key.value)?.children()?;
|
|
let (n, e) = match k.as_slice() {
|
|
[_, n, e, ..] => (n.uint_bytes().to_vec(), e.uint_bytes().to_vec()),
|
|
_ => return Err(SignError::Malformed("RSAPrivateKey".into())),
|
|
};
|
|
#[cfg(not(target_arch = "wasm32"))]
|
|
let inner = Inner::Rsa(
|
|
aws_lc_rs::signature::RsaKeyPair::from_pkcs8(info)
|
|
.or_else(|_| aws_lc_rs::signature::RsaKeyPair::from_der(key.value))
|
|
.map_err(|e| SignError::Malformed(format!("RSA private key: {e}")))?,
|
|
);
|
|
#[cfg(target_arch = "wasm32")]
|
|
let inner = Inner::Rsa;
|
|
Ok(PrivateKey { inner, public: PublicKey::Rsa { n, e }, pkcs8: info.to_vec() })
|
|
}
|
|
oid::EC => {
|
|
// ECPrivateKey: version, privateKey OCTET STRING, [0] parameters, [1] publicKey.
|
|
let ec = Tlv::parse_all(key.value)?.children()?;
|
|
let d = ec.get(1).ok_or_else(|| SignError::Malformed("ECPrivateKey".into()))?.expect(tag::OCTET_STRING, "EC private key")?.value;
|
|
let curve = match alg.get(1).map(|c| c.oid()).transpose()? {
|
|
Some(c) => c,
|
|
None => ec
|
|
.iter()
|
|
.find(|t| t.tag == tag::ctx(0))
|
|
.and_then(|p| p.inner().ok())
|
|
.and_then(|p| p.oid().ok())
|
|
.ok_or_else(|| SignError::Malformed("EC curve".into()))?,
|
|
};
|
|
match curve.as_str() {
|
|
oid::P256 => {
|
|
let k = p256::ecdsa::SigningKey::from_slice(d).map_err(|_| SignError::Malformed("P-256 key".into()))?;
|
|
let public = PublicKey::P256(k.verifying_key().to_sec1_point(false).as_bytes().to_vec());
|
|
Ok(PrivateKey { inner: Inner::P256(k), public, pkcs8: info.to_vec() })
|
|
}
|
|
oid::P384 => {
|
|
let k = p384::ecdsa::SigningKey::from_slice(d).map_err(|_| SignError::Malformed("P-384 key".into()))?;
|
|
let public = PublicKey::P384(k.verifying_key().to_sec1_point(false).as_bytes().to_vec());
|
|
Ok(PrivateKey { inner: Inner::P384(k), public, pkcs8: info.to_vec() })
|
|
}
|
|
other => Err(SignError::Unsupported(format!("elliptic curve {other}"))),
|
|
}
|
|
}
|
|
other => Err(SignError::Unsupported(format!("private key algorithm {other}"))),
|
|
}
|
|
}
|
|
|
|
/// A new RSA key (Acrobat's default for a self-signed digital ID is 2048-bit RSA).
|
|
#[cfg(not(target_arch = "wasm32"))]
|
|
pub fn generate_rsa(bits: usize) -> Result<PrivateKey, SignError> {
|
|
use aws_lc_rs::encoding::AsDer;
|
|
use aws_lc_rs::rsa::KeySize;
|
|
let size = match bits {
|
|
2048 => KeySize::Rsa2048,
|
|
3072 => KeySize::Rsa3072,
|
|
4096 => KeySize::Rsa4096,
|
|
_ => return Err(SignError::Unsupported(format!("{bits}-bit RSA keys"))),
|
|
};
|
|
let kp = aws_lc_rs::signature::RsaKeyPair::generate(size).map_err(|_| SignError::Crypto("RSA key generation failed".into()))?;
|
|
let der = kp.as_der().map_err(|_| SignError::Crypto("RSA key encoding failed".into()))?;
|
|
PrivateKey::from_pkcs8(der.as_ref())
|
|
}
|
|
|
|
/// RSA keys are generated only on native targets (ADR-0009).
|
|
#[cfg(target_arch = "wasm32")]
|
|
pub fn generate_rsa(_bits: usize) -> Result<PrivateKey, SignError> {
|
|
Err(SignError::Unsupported("creating RSA keys in the browser (use a P-256 key)".into()))
|
|
}
|
|
|
|
/// A new P-256 key.
|
|
pub fn generate_p256() -> Result<PrivateKey, SignError> {
|
|
loop {
|
|
let mut d = [0u8; 32];
|
|
getrandom::fill(&mut d).map_err(|e| SignError::Crypto(format!("no randomness: {e}")))?;
|
|
if let Ok(k) = p256::ecdsa::SigningKey::from_slice(&d) {
|
|
let point = k.verifying_key().to_sec1_point(false).as_bytes().to_vec();
|
|
// PKCS#8 with an ECPrivateKey (RFC 5915) carrying the public key.
|
|
let ec = der::seq(&[&der::int(1), &der::octets(&d), &der::explicit(1, &der::bit_string(&point))]);
|
|
let info = der::seq(&[&der::int(0), &der::seq(&[&der::oid(oid::EC), &der::oid(oid::P256)]), &der::octets(&ec)]);
|
|
return PrivateKey::from_pkcs8(&info);
|
|
}
|
|
}
|
|
}
|
|
|
|
pub fn public_key(&self) -> &PublicKey {
|
|
&self.public
|
|
}
|
|
|
|
pub fn pkcs8(&self) -> &[u8] {
|
|
&self.pkcs8
|
|
}
|
|
|
|
/// The signature `AlgorithmIdentifier` this key writes with `alg`.
|
|
pub fn signature_algorithm(&self, alg: DigestAlg) -> Vec<u8> {
|
|
match (&self.public, alg) {
|
|
(PublicKey::Rsa { .. }, DigestAlg::Sha1) => der::algorithm(oid::RSA_SHA1, Some(&der::null())),
|
|
(PublicKey::Rsa { .. }, DigestAlg::Sha256) => der::algorithm(oid::RSA_SHA256, Some(&der::null())),
|
|
(PublicKey::Rsa { .. }, DigestAlg::Sha384) => der::algorithm(oid::RSA_SHA384, Some(&der::null())),
|
|
(PublicKey::Rsa { .. }, DigestAlg::Sha512) => der::algorithm(oid::RSA_SHA512, Some(&der::null())),
|
|
(_, DigestAlg::Sha1) => der::algorithm(oid::ECDSA_SHA1, None),
|
|
(_, DigestAlg::Sha256) => der::algorithm(oid::ECDSA_SHA256, None),
|
|
(_, DigestAlg::Sha384) => der::algorithm(oid::ECDSA_SHA384, None),
|
|
(_, DigestAlg::Sha512) => der::algorithm(oid::ECDSA_SHA512, None),
|
|
}
|
|
}
|
|
|
|
/// The digest a signature with this key should use: SHA-256, or SHA-384 for P-384.
|
|
pub fn preferred_digest(&self) -> DigestAlg {
|
|
match self.public {
|
|
PublicKey::P384(_) => DigestAlg::Sha384,
|
|
_ => DigestAlg::Sha256,
|
|
}
|
|
}
|
|
|
|
/// Sign `msg` (hashed with `alg`). New signatures never use SHA-1.
|
|
pub fn sign(&self, alg: DigestAlg, msg: &[u8]) -> Result<Vec<u8>, SignError> {
|
|
if alg == DigestAlg::Sha1 {
|
|
return Err(SignError::Unsupported("SHA-1 for new signatures".into()));
|
|
}
|
|
match &self.inner {
|
|
#[cfg(not(target_arch = "wasm32"))]
|
|
Inner::Rsa(kp) => {
|
|
use aws_lc_rs::signature::{RSA_PKCS1_SHA256, RSA_PKCS1_SHA384, RSA_PKCS1_SHA512};
|
|
let enc: &'static dyn aws_lc_rs::signature::RsaEncoding = match alg {
|
|
DigestAlg::Sha384 => &RSA_PKCS1_SHA384,
|
|
DigestAlg::Sha512 => &RSA_PKCS1_SHA512,
|
|
_ => &RSA_PKCS1_SHA256,
|
|
};
|
|
let mut sig = vec![0u8; kp.public_modulus_len()];
|
|
kp.sign(enc, &aws_lc_rs::rand::SystemRandom::new(), msg, &mut sig).map_err(|_| SignError::Crypto("RSA signing failed".into()))?;
|
|
Ok(sig)
|
|
}
|
|
#[cfg(target_arch = "wasm32")]
|
|
Inner::Rsa => Err(SignError::Unsupported("RSA signing in the browser".into())),
|
|
Inner::P256(k) => {
|
|
use p256::ecdsa::signature::hazmat::PrehashSigner;
|
|
let s: p256::ecdsa::Signature = k.sign_prehash(&alg.digest(&[msg])).map_err(|e| SignError::Crypto(e.to_string()))?;
|
|
Ok(s.to_der().as_bytes().to_vec())
|
|
}
|
|
Inner::External(k) => k.sign(alg, msg),
|
|
Inner::P384(k) => {
|
|
use p384::ecdsa::signature::hazmat::PrehashSigner;
|
|
let s: p384::ecdsa::Signature = k.sign_prehash(&alg.digest(&[msg])).map_err(|e| SignError::Crypto(e.to_string()))?;
|
|
Ok(s.to_der().as_bytes().to_vec())
|
|
}
|
|
}
|
|
}
|
|
}
|