rsa/src/pkcs1v15.rs

1191 lines
35 KiB
Rust
Raw Normal View History

//! PKCS#1 v1.5 support as described in [RFC8017 § 8.2].
//!
//! # Usage
//!
//! See [code example in the toplevel rustdoc](../index.html#pkcs1-v15-signatures).
//!
//! [RFC8017 § 8.2]: https://datatracker.ietf.org/doc/html/rfc8017#section-8.2
use alloc::{boxed::Box, string::ToString, vec::Vec};
use core::fmt::{Debug, Display, Formatter, LowerHex, UpperHex};
use core::marker::PhantomData;
use digest::Digest;
use num_bigint::BigUint;
use pkcs8::{
spki::{
der::AnyRef, AlgorithmIdentifierRef, AssociatedAlgorithmIdentifier,
SignatureAlgorithmIdentifier,
},
AssociatedOid, Document, EncodePrivateKey, EncodePublicKey, SecretDocument,
};
use rand_core::CryptoRngCore;
use signature::{
hazmat::{PrehashSigner, PrehashVerifier},
DigestSigner, DigestVerifier, Keypair, RandomizedDigestSigner, RandomizedSigner,
SignatureEncoding, Signer, Verifier,
};
use zeroize::Zeroizing;
Internals refactoring (#304) * feat: decouple key generation and random generation Make generate_multi_prime_key_with_exp() generic enough to generate abstract key structure. Rewrite RsaPrivateKey constructors to use RsaPrivateKey::from_components(). * feat: move key-related traits to separate module Move PublicKeyParts to the separate module. * feat: stop using RsaPrivateKey in internals.rs Make internals.rs generic enough to be moved to the algorithms module. * feat: move soft RSA implementation to crate::algorithms::rsa.rs Separate software RSA implementation to separate module under crate::algorithms. * key: drop raw_int_*_primitive wrappers Now as raw_int_encryption_primitive() and raw_int_decryption_primitive() became simple wrappers around properly defined functions we can inline them and always use software RSA algorithm from src::algorithms::rsa.rs. * feat: move internals.rs to src/algortihms/pad.rs internals.rs now contains only small functions related to BigUint to Vec<u8> conversion. Move them to src/algorithms/pad.rs and get rid of internals.rs * algorithms: protect all functions with pub(crate) While it is expected that the functions inside algorithms crates might be useful (and used) by other parties, they are low level functions and as such impose a high risk of being misused. Protect all of them with pub(crate) to prevent them from being exposed by mistake. Also add big fat warnings to raw RSA functions, which should never be used unless authors knows exactly what they are using. Signed-off-by: Dmitry Baryshkov <dmitry.baryshkov@linaro.org>
2023-04-24 01:53:21 +03:00
use crate::algorithms::pad::{uint_to_be_pad, uint_to_zeroizing_be_pad};
use crate::algorithms::pkcs1v15::*;
Internals refactoring (#304) * feat: decouple key generation and random generation Make generate_multi_prime_key_with_exp() generic enough to generate abstract key structure. Rewrite RsaPrivateKey constructors to use RsaPrivateKey::from_components(). * feat: move key-related traits to separate module Move PublicKeyParts to the separate module. * feat: stop using RsaPrivateKey in internals.rs Make internals.rs generic enough to be moved to the algorithms module. * feat: move soft RSA implementation to crate::algorithms::rsa.rs Separate software RSA implementation to separate module under crate::algorithms. * key: drop raw_int_*_primitive wrappers Now as raw_int_encryption_primitive() and raw_int_decryption_primitive() became simple wrappers around properly defined functions we can inline them and always use software RSA algorithm from src::algorithms::rsa.rs. * feat: move internals.rs to src/algortihms/pad.rs internals.rs now contains only small functions related to BigUint to Vec<u8> conversion. Move them to src/algorithms/pad.rs and get rid of internals.rs * algorithms: protect all functions with pub(crate) While it is expected that the functions inside algorithms crates might be useful (and used) by other parties, they are low level functions and as such impose a high risk of being misused. Protect all of them with pub(crate) to prevent them from being exposed by mistake. Also add big fat warnings to raw RSA functions, which should never be used unless authors knows exactly what they are using. Signed-off-by: Dmitry Baryshkov <dmitry.baryshkov@linaro.org>
2023-04-24 01:53:21 +03:00
use crate::algorithms::rsa::{rsa_decrypt_and_check, rsa_encrypt};
use crate::dummy_rng::DummyRng;
use crate::errors::{Error, Result};
Internals refactoring (#304) * feat: decouple key generation and random generation Make generate_multi_prime_key_with_exp() generic enough to generate abstract key structure. Rewrite RsaPrivateKey constructors to use RsaPrivateKey::from_components(). * feat: move key-related traits to separate module Move PublicKeyParts to the separate module. * feat: stop using RsaPrivateKey in internals.rs Make internals.rs generic enough to be moved to the algorithms module. * feat: move soft RSA implementation to crate::algorithms::rsa.rs Separate software RSA implementation to separate module under crate::algorithms. * key: drop raw_int_*_primitive wrappers Now as raw_int_encryption_primitive() and raw_int_decryption_primitive() became simple wrappers around properly defined functions we can inline them and always use software RSA algorithm from src::algorithms::rsa.rs. * feat: move internals.rs to src/algortihms/pad.rs internals.rs now contains only small functions related to BigUint to Vec<u8> conversion. Move them to src/algorithms/pad.rs and get rid of internals.rs * algorithms: protect all functions with pub(crate) While it is expected that the functions inside algorithms crates might be useful (and used) by other parties, they are low level functions and as such impose a high risk of being misused. Protect all of them with pub(crate) to prevent them from being exposed by mistake. Also add big fat warnings to raw RSA functions, which should never be used unless authors knows exactly what they are using. Signed-off-by: Dmitry Baryshkov <dmitry.baryshkov@linaro.org>
2023-04-24 01:53:21 +03:00
use crate::key;
use crate::keytraits::PublicKeyParts;
use crate::padding::{PaddingScheme, SignatureScheme};
use crate::traits::{Decryptor, EncryptingKeypair, RandomizedDecryptor, RandomizedEncryptor};
use crate::{RsaPrivateKey, RsaPublicKey};
/// Encryption using PKCS#1 v1.5 padding.
#[derive(Clone, Copy, Debug, Default, Eq, PartialEq)]
pub struct Pkcs1v15Encrypt;
impl PaddingScheme for Pkcs1v15Encrypt {
fn decrypt<Rng: CryptoRngCore>(
self,
rng: Option<&mut Rng>,
priv_key: &RsaPrivateKey,
ciphertext: &[u8],
) -> Result<Vec<u8>> {
decrypt(rng, priv_key, ciphertext)
}
fn encrypt<Rng: CryptoRngCore>(
self,
rng: &mut Rng,
pub_key: &RsaPublicKey,
msg: &[u8],
) -> Result<Vec<u8>> {
encrypt(rng, pub_key, msg)
}
}
/// Digital signatures using PKCS#1 v1.5 padding.
#[derive(Clone, Debug, Eq, PartialEq)]
pub struct Pkcs1v15Sign {
/// Length of hash to use.
pub hash_len: Option<usize>,
/// Prefix.
pub prefix: Box<[u8]>,
}
impl Pkcs1v15Sign {
/// Create new PKCS#1 v1.5 padding for the given digest.
///
/// The digest must have an [`AssociatedOid`]. Make sure to enable the `oid`
/// feature of the relevant digest crate.
pub fn new<D>() -> Self
where
D: Digest + AssociatedOid,
{
Self {
hash_len: Some(<D as Digest>::output_size()),
prefix: pkcs1v15_generate_prefix::<D>().into_boxed_slice(),
}
}
/// Create new PKCS#1 v1.5 padding for computing an unprefixed signature.
///
/// This sets `hash_len` to `None` and uses an empty `prefix`.
pub fn new_unprefixed() -> Self {
Self {
hash_len: None,
prefix: Box::new([]),
}
}
/// Create new PKCS#1 v1.5 padding for computing an unprefixed signature.
///
/// This sets `hash_len` to `None` and uses an empty `prefix`.
#[deprecated(since = "0.9.0", note = "use Pkcs1v15Sign::new_unprefixed instead")]
pub fn new_raw() -> Self {
Self::new_unprefixed()
}
}
impl SignatureScheme for Pkcs1v15Sign {
fn sign<Rng: CryptoRngCore>(
self,
rng: Option<&mut Rng>,
priv_key: &RsaPrivateKey,
hashed: &[u8],
) -> Result<Vec<u8>> {
if let Some(hash_len) = self.hash_len {
if hashed.len() != hash_len {
return Err(Error::InputNotHashed);
}
}
sign(rng, priv_key, &self.prefix, hashed)
}
fn verify(self, pub_key: &RsaPublicKey, hashed: &[u8], sig: &[u8]) -> Result<()> {
if let Some(hash_len) = self.hash_len {
if hashed.len() != hash_len {
return Err(Error::InputNotHashed);
}
}
verify(
pub_key,
self.prefix.as_ref(),
hashed,
&BigUint::from_bytes_be(sig),
)
}
}
/// PKCS#1 v1.5 signatures as described in [RFC8017 § 8.2].
///
/// [RFC8017 § 8.2]: https://datatracker.ietf.org/doc/html/rfc8017#section-8.2
#[derive(Clone, PartialEq, Eq)]
pub struct Signature {
inner: BigUint,
}
impl SignatureEncoding for Signature {
type Repr = Box<[u8]>;
}
impl TryFrom<&[u8]> for Signature {
type Error = signature::Error;
fn try_from(bytes: &[u8]) -> signature::Result<Self> {
Ok(Self {
inner: BigUint::from_bytes_be(bytes),
})
}
}
impl From<Signature> for Box<[u8]> {
fn from(signature: Signature) -> Box<[u8]> {
signature.inner.to_bytes_be().into_boxed_slice()
}
}
impl Debug for Signature {
fn fmt(&self, fmt: &mut Formatter<'_>) -> core::result::Result<(), core::fmt::Error> {
fmt.debug_tuple("Signature")
.field(&self.to_string())
.finish()
}
}
impl LowerHex for Signature {
fn fmt(&self, f: &mut Formatter<'_>) -> core::fmt::Result {
write!(f, "{:x}", &self.inner)
}
}
impl UpperHex for Signature {
fn fmt(&self, f: &mut Formatter<'_>) -> core::fmt::Result {
write!(f, "{:X}", &self.inner)
}
}
impl Display for Signature {
fn fmt(&self, f: &mut Formatter<'_>) -> core::fmt::Result {
write!(f, "{:X}", self)
}
}
/// Encrypts the given message with RSA and the padding
/// scheme from PKCS#1 v1.5. The message must be no longer than the
/// length of the public modulus minus 11 bytes.
2018-07-24 14:31:06 +02:00
#[inline]
fn encrypt<R: CryptoRngCore + ?Sized>(
rng: &mut R,
pub_key: &RsaPublicKey,
msg: &[u8],
) -> Result<Vec<u8>> {
key::check_public(pub_key)?;
let em = pkcs1v15_encrypt_pad(rng, msg, pub_key.size())?;
let int = Zeroizing::new(BigUint::from_bytes_be(&em));
Internals refactoring (#304) * feat: decouple key generation and random generation Make generate_multi_prime_key_with_exp() generic enough to generate abstract key structure. Rewrite RsaPrivateKey constructors to use RsaPrivateKey::from_components(). * feat: move key-related traits to separate module Move PublicKeyParts to the separate module. * feat: stop using RsaPrivateKey in internals.rs Make internals.rs generic enough to be moved to the algorithms module. * feat: move soft RSA implementation to crate::algorithms::rsa.rs Separate software RSA implementation to separate module under crate::algorithms. * key: drop raw_int_*_primitive wrappers Now as raw_int_encryption_primitive() and raw_int_decryption_primitive() became simple wrappers around properly defined functions we can inline them and always use software RSA algorithm from src::algorithms::rsa.rs. * feat: move internals.rs to src/algortihms/pad.rs internals.rs now contains only small functions related to BigUint to Vec<u8> conversion. Move them to src/algorithms/pad.rs and get rid of internals.rs * algorithms: protect all functions with pub(crate) While it is expected that the functions inside algorithms crates might be useful (and used) by other parties, they are low level functions and as such impose a high risk of being misused. Protect all of them with pub(crate) to prevent them from being exposed by mistake. Also add big fat warnings to raw RSA functions, which should never be used unless authors knows exactly what they are using. Signed-off-by: Dmitry Baryshkov <dmitry.baryshkov@linaro.org>
2023-04-24 01:53:21 +03:00
uint_to_be_pad(rsa_encrypt(pub_key, &int)?, pub_key.size())
}
/// Decrypts a plaintext using RSA and the padding scheme from PKCS#1 v1.5.
///
/// If an `rng` is passed, it uses RSA blinding to avoid timing side-channel attacks.
///
/// Note that whether this function returns an error or not discloses secret
/// information. If an attacker can cause this function to run repeatedly and
/// learn whether each instance returned an error then they can decrypt and
/// forge signatures as if they had the private key. See
/// `decrypt_session_key` for a way of solving this problem.
2018-07-24 14:31:06 +02:00
#[inline]
fn decrypt<R: CryptoRngCore + ?Sized>(
rng: Option<&mut R>,
priv_key: &RsaPrivateKey,
ciphertext: &[u8],
) -> Result<Vec<u8>> {
key::check_public(priv_key)?;
Internals refactoring (#304) * feat: decouple key generation and random generation Make generate_multi_prime_key_with_exp() generic enough to generate abstract key structure. Rewrite RsaPrivateKey constructors to use RsaPrivateKey::from_components(). * feat: move key-related traits to separate module Move PublicKeyParts to the separate module. * feat: stop using RsaPrivateKey in internals.rs Make internals.rs generic enough to be moved to the algorithms module. * feat: move soft RSA implementation to crate::algorithms::rsa.rs Separate software RSA implementation to separate module under crate::algorithms. * key: drop raw_int_*_primitive wrappers Now as raw_int_encryption_primitive() and raw_int_decryption_primitive() became simple wrappers around properly defined functions we can inline them and always use software RSA algorithm from src::algorithms::rsa.rs. * feat: move internals.rs to src/algortihms/pad.rs internals.rs now contains only small functions related to BigUint to Vec<u8> conversion. Move them to src/algorithms/pad.rs and get rid of internals.rs * algorithms: protect all functions with pub(crate) While it is expected that the functions inside algorithms crates might be useful (and used) by other parties, they are low level functions and as such impose a high risk of being misused. Protect all of them with pub(crate) to prevent them from being exposed by mistake. Also add big fat warnings to raw RSA functions, which should never be used unless authors knows exactly what they are using. Signed-off-by: Dmitry Baryshkov <dmitry.baryshkov@linaro.org>
2023-04-24 01:53:21 +03:00
let em = rsa_decrypt_and_check(priv_key, rng, &BigUint::from_bytes_be(ciphertext))?;
let em = uint_to_zeroizing_be_pad(em, priv_key.size())?;
pkcs1v15_encrypt_unpad(em, priv_key.size())
}
/// Calculates the signature of hashed using
/// RSASSA-PKCS1-V1_5-SIGN from RSA PKCS#1 v1.5. Note that `hashed` must
/// be the result of hashing the input message using the given hash
/// function. If hash is `None`, hashed is signed directly. This isn't
/// advisable except for interoperability.
///
/// If `rng` is not `None` then RSA blinding will be used to avoid timing
/// side-channel attacks.
///
/// This function is deterministic. Thus, if the set of possible
/// messages is small, an attacker may be able to build a map from
/// messages to signatures and identify the signed messages. As ever,
/// signatures provide authenticity, not confidentiality.
2018-07-24 14:31:06 +02:00
#[inline]
fn sign<R: CryptoRngCore + ?Sized>(
2018-07-24 14:31:06 +02:00
rng: Option<&mut R>,
priv_key: &RsaPrivateKey,
prefix: &[u8],
2018-07-24 14:31:06 +02:00
hashed: &[u8],
) -> Result<Vec<u8>> {
let em = pkcs1v15_sign_pad(prefix, hashed, priv_key.size())?;
2018-07-24 14:31:06 +02:00
uint_to_zeroizing_be_pad(
Internals refactoring (#304) * feat: decouple key generation and random generation Make generate_multi_prime_key_with_exp() generic enough to generate abstract key structure. Rewrite RsaPrivateKey constructors to use RsaPrivateKey::from_components(). * feat: move key-related traits to separate module Move PublicKeyParts to the separate module. * feat: stop using RsaPrivateKey in internals.rs Make internals.rs generic enough to be moved to the algorithms module. * feat: move soft RSA implementation to crate::algorithms::rsa.rs Separate software RSA implementation to separate module under crate::algorithms. * key: drop raw_int_*_primitive wrappers Now as raw_int_encryption_primitive() and raw_int_decryption_primitive() became simple wrappers around properly defined functions we can inline them and always use software RSA algorithm from src::algorithms::rsa.rs. * feat: move internals.rs to src/algortihms/pad.rs internals.rs now contains only small functions related to BigUint to Vec<u8> conversion. Move them to src/algorithms/pad.rs and get rid of internals.rs * algorithms: protect all functions with pub(crate) While it is expected that the functions inside algorithms crates might be useful (and used) by other parties, they are low level functions and as such impose a high risk of being misused. Protect all of them with pub(crate) to prevent them from being exposed by mistake. Also add big fat warnings to raw RSA functions, which should never be used unless authors knows exactly what they are using. Signed-off-by: Dmitry Baryshkov <dmitry.baryshkov@linaro.org>
2023-04-24 01:53:21 +03:00
rsa_decrypt_and_check(priv_key, rng, &BigUint::from_bytes_be(&em))?,
priv_key.size(),
)
2018-07-24 14:31:06 +02:00
}
/// Verifies an RSA PKCS#1 v1.5 signature.
#[inline]
fn verify(pub_key: &RsaPublicKey, prefix: &[u8], hashed: &[u8], sig: &BigUint) -> Result<()> {
Internals refactoring (#304) * feat: decouple key generation and random generation Make generate_multi_prime_key_with_exp() generic enough to generate abstract key structure. Rewrite RsaPrivateKey constructors to use RsaPrivateKey::from_components(). * feat: move key-related traits to separate module Move PublicKeyParts to the separate module. * feat: stop using RsaPrivateKey in internals.rs Make internals.rs generic enough to be moved to the algorithms module. * feat: move soft RSA implementation to crate::algorithms::rsa.rs Separate software RSA implementation to separate module under crate::algorithms. * key: drop raw_int_*_primitive wrappers Now as raw_int_encryption_primitive() and raw_int_decryption_primitive() became simple wrappers around properly defined functions we can inline them and always use software RSA algorithm from src::algorithms::rsa.rs. * feat: move internals.rs to src/algortihms/pad.rs internals.rs now contains only small functions related to BigUint to Vec<u8> conversion. Move them to src/algorithms/pad.rs and get rid of internals.rs * algorithms: protect all functions with pub(crate) While it is expected that the functions inside algorithms crates might be useful (and used) by other parties, they are low level functions and as such impose a high risk of being misused. Protect all of them with pub(crate) to prevent them from being exposed by mistake. Also add big fat warnings to raw RSA functions, which should never be used unless authors knows exactly what they are using. Signed-off-by: Dmitry Baryshkov <dmitry.baryshkov@linaro.org>
2023-04-24 01:53:21 +03:00
let em = uint_to_be_pad(rsa_encrypt(pub_key, sig)?, pub_key.size())?;
pkcs1v15_sign_unpad(prefix, hashed, &em, pub_key.size())
}
/// Signing key for PKCS#1 v1.5 signatures as described in [RFC8017 § 8.2].
///
/// [RFC8017 § 8.2]: https://datatracker.ietf.org/doc/html/rfc8017#section-8.2
#[derive(Debug, Clone)]
pub struct SigningKey<D>
where
D: Digest,
{
inner: RsaPrivateKey,
prefix: Vec<u8>,
phantom: PhantomData<D>,
}
impl<D> SigningKey<D>
where
D: Digest,
{
/// Create a new signing key from the give RSA private key with an empty prefix.
///
/// ## Note: unprefixed signatures are uncommon
///
/// In most cases you'll want to use [`SigningKey::new`].
pub fn new_unprefixed(key: RsaPrivateKey) -> Self {
Self {
inner: key,
prefix: Vec::new(),
phantom: Default::default(),
}
}
/// Generate a new signing key with an empty prefix.
pub fn random_unprefixed<R: CryptoRngCore + ?Sized>(
rng: &mut R,
bit_size: usize,
) -> Result<Self> {
Ok(Self {
inner: RsaPrivateKey::new(rng, bit_size)?,
prefix: Vec::new(),
phantom: Default::default(),
})
}
}
impl<D> AssociatedAlgorithmIdentifier for SigningKey<D>
where
D: Digest,
{
type Params = AnyRef<'static>;
const ALGORITHM_IDENTIFIER: AlgorithmIdentifierRef<'static> = pkcs1::ALGORITHM_ID;
}
impl<D> SignatureAlgorithmIdentifier for SigningKey<D>
where
D: Digest + oid::RsaSignatureAssociatedOid,
{
type Params = AnyRef<'static>;
const SIGNATURE_ALGORITHM_IDENTIFIER: AlgorithmIdentifierRef<'static> =
AlgorithmIdentifierRef {
oid: D::OID,
parameters: Some(AnyRef::NULL),
};
}
impl<D> From<RsaPrivateKey> for SigningKey<D>
where
D: Digest,
{
fn from(key: RsaPrivateKey) -> Self {
Self::new_unprefixed(key)
}
}
impl<D> From<SigningKey<D>> for RsaPrivateKey
where
D: Digest,
{
fn from(key: SigningKey<D>) -> Self {
key.inner
}
}
impl<D> SigningKey<D>
where
D: Digest + AssociatedOid,
{
/// Create a new signing key with a prefix for the digest `D`.
pub fn new(key: RsaPrivateKey) -> Self {
Self {
inner: key,
prefix: pkcs1v15_generate_prefix::<D>(),
phantom: Default::default(),
}
}
/// Generate a new signing key with a prefix for the digest `D`.
pub fn random<R: CryptoRngCore + ?Sized>(rng: &mut R, bit_size: usize) -> Result<Self> {
Ok(Self {
inner: RsaPrivateKey::new(rng, bit_size)?,
prefix: pkcs1v15_generate_prefix::<D>(),
phantom: Default::default(),
})
}
/// Create a new signing key with a prefix for the digest `D`.
#[deprecated(since = "0.9.0", note = "use SigningKey::new instead")]
pub fn new_with_prefix(key: RsaPrivateKey) -> Self {
Self::new(key)
}
/// Generate a new signing key with a prefix for the digest `D`.
#[deprecated(since = "0.9.0", note = "use SigningKey::random instead")]
pub fn random_with_prefix<R: CryptoRngCore + ?Sized>(
rng: &mut R,
bit_size: usize,
) -> Result<Self> {
Self::random(rng, bit_size)
}
}
impl<D> AsRef<RsaPrivateKey> for SigningKey<D>
where
D: Digest,
{
fn as_ref(&self) -> &RsaPrivateKey {
&self.inner
}
}
impl<D> EncodePrivateKey for SigningKey<D>
where
D: Digest,
{
fn to_pkcs8_der(&self) -> pkcs8::Result<SecretDocument> {
self.inner.to_pkcs8_der()
}
}
impl<D> Signer<Signature> for SigningKey<D>
where
D: Digest,
{
fn try_sign(&self, msg: &[u8]) -> signature::Result<Signature> {
sign::<DummyRng>(None, &self.inner, &self.prefix, &D::digest(msg))?
.as_slice()
.try_into()
}
}
impl<D> RandomizedSigner<Signature> for SigningKey<D>
where
D: Digest,
{
2023-01-07 21:26:52 -07:00
fn try_sign_with_rng(
&self,
2023-01-07 21:26:52 -07:00
rng: &mut impl CryptoRngCore,
msg: &[u8],
) -> signature::Result<Signature> {
sign(Some(rng), &self.inner, &self.prefix, &D::digest(msg))?
.as_slice()
.try_into()
}
}
impl<D> DigestSigner<D, Signature> for SigningKey<D>
where
D: Digest,
{
fn try_sign_digest(&self, digest: D) -> signature::Result<Signature> {
sign::<DummyRng>(None, &self.inner, &self.prefix, &digest.finalize())?
.as_slice()
.try_into()
}
}
impl<D> RandomizedDigestSigner<D, Signature> for SigningKey<D>
where
D: Digest,
{
2023-01-07 21:26:52 -07:00
fn try_sign_digest_with_rng(
&self,
2023-01-07 21:26:52 -07:00
rng: &mut impl CryptoRngCore,
digest: D,
) -> signature::Result<Signature> {
sign(Some(rng), &self.inner, &self.prefix, &digest.finalize())?
.as_slice()
.try_into()
}
}
impl<D> PrehashSigner<Signature> for SigningKey<D>
where
D: Digest,
{
fn sign_prehash(&self, prehash: &[u8]) -> signature::Result<Signature> {
sign::<DummyRng>(None, &self.inner, &self.prefix, prehash)?
.as_slice()
.try_into()
}
}
/// Verifying key for PKCS#1 v1.5 signatures as described in [RFC8017 § 8.2].
///
/// [RFC8017 § 8.2]: https://datatracker.ietf.org/doc/html/rfc8017#section-8.2
#[derive(Debug)]
pub struct VerifyingKey<D>
where
D: Digest,
{
inner: RsaPublicKey,
prefix: Vec<u8>,
phantom: PhantomData<D>,
}
/* Implemented manually so we don't have to bind D with Clone */
impl<D> Clone for VerifyingKey<D>
where
D: Digest,
{
fn clone(&self) -> Self {
Self {
inner: self.inner.clone(),
prefix: self.prefix.clone(),
phantom: Default::default(),
}
}
}
impl<D> VerifyingKey<D>
where
D: Digest,
{
/// Create a new verifying key from an RSA public key with an empty prefix.
///
/// ## Note: unprefixed signatures are uncommon
///
/// In most cases you'll want to use [`VerifyingKey::new`] instead.
pub fn new_unprefixed(key: RsaPublicKey) -> Self {
Self {
inner: key,
prefix: Vec::new(),
phantom: Default::default(),
}
}
}
impl<D> AssociatedAlgorithmIdentifier for VerifyingKey<D>
where
D: Digest,
{
type Params = AnyRef<'static>;
const ALGORITHM_IDENTIFIER: AlgorithmIdentifierRef<'static> = pkcs1::ALGORITHM_ID;
}
impl<D> SignatureAlgorithmIdentifier for VerifyingKey<D>
where
D: Digest + oid::RsaSignatureAssociatedOid,
{
type Params = AnyRef<'static>;
const SIGNATURE_ALGORITHM_IDENTIFIER: AlgorithmIdentifierRef<'static> =
AlgorithmIdentifierRef {
oid: D::OID,
parameters: Some(AnyRef::NULL),
};
}
impl<D> From<RsaPublicKey> for VerifyingKey<D>
where
D: Digest,
{
fn from(key: RsaPublicKey) -> Self {
Self::new_unprefixed(key)
}
}
impl<D> From<VerifyingKey<D>> for RsaPublicKey
where
D: Digest,
{
fn from(key: VerifyingKey<D>) -> Self {
key.inner
}
}
impl<D> VerifyingKey<D>
where
D: Digest + AssociatedOid,
{
/// Create a new verifying key with a prefix for the digest `D`.
pub fn new(key: RsaPublicKey) -> Self {
Self {
inner: key,
prefix: pkcs1v15_generate_prefix::<D>(),
phantom: Default::default(),
}
}
/// Create a new verifying key with a prefix for the digest `D`.
#[deprecated(since = "0.9.0", note = "use VerifyingKey::new instead")]
pub fn new_with_prefix(key: RsaPublicKey) -> Self {
Self::new(key)
}
}
impl<D> AsRef<RsaPublicKey> for VerifyingKey<D>
where
D: Digest,
{
fn as_ref(&self) -> &RsaPublicKey {
&self.inner
}
}
impl<D> Keypair for SigningKey<D>
where
D: Digest,
{
type VerifyingKey = VerifyingKey<D>;
fn verifying_key(&self) -> Self::VerifyingKey {
VerifyingKey {
inner: self.inner.to_public_key(),
prefix: self.prefix.clone(),
phantom: Default::default(),
}
}
}
impl<D> Verifier<Signature> for VerifyingKey<D>
where
D: Digest,
{
fn verify(&self, msg: &[u8], signature: &Signature) -> signature::Result<()> {
verify(
&self.inner,
&self.prefix.clone(),
&D::digest(msg),
&signature.inner,
)
.map_err(|e| e.into())
}
}
impl<D> DigestVerifier<D, Signature> for VerifyingKey<D>
where
D: Digest,
{
fn verify_digest(&self, digest: D, signature: &Signature) -> signature::Result<()> {
verify(
&self.inner,
&self.prefix,
&digest.finalize(),
&signature.inner,
)
.map_err(|e| e.into())
}
}
impl<D> PrehashVerifier<Signature> for VerifyingKey<D>
where
D: Digest,
{
fn verify_prehash(&self, prehash: &[u8], signature: &Signature) -> signature::Result<()> {
verify(&self.inner, &self.prefix, prehash, &signature.inner).map_err(|e| e.into())
}
}
impl<D> EncodePublicKey for VerifyingKey<D>
where
D: Digest,
{
fn to_public_key_der(&self) -> pkcs8::spki::Result<Document> {
self.inner.to_public_key_der()
}
}
/// Encryption key for PKCS#1 v1.5 encryption as described in [RFC8017 § 7.2].
///
/// [RFC8017 § 7.2]: https://datatracker.ietf.org/doc/html/rfc8017#section-7.2
#[derive(Debug, Clone)]
pub struct EncryptingKey {
inner: RsaPublicKey,
}
impl EncryptingKey {
/// Create a new verifying key from an RSA public key.
pub fn new(key: RsaPublicKey) -> Self {
Self { inner: key }
}
}
impl RandomizedEncryptor for EncryptingKey {
fn encrypt_with_rng<R: CryptoRngCore + ?Sized>(
&self,
rng: &mut R,
msg: &[u8],
) -> Result<Vec<u8>> {
encrypt(rng, &self.inner, msg)
}
}
/// Decryption key for PKCS#1 v1.5 decryption as described in [RFC8017 § 7.2].
///
/// [RFC8017 § 7.2]: https://datatracker.ietf.org/doc/html/rfc8017#section-7.2
#[derive(Debug, Clone)]
pub struct DecryptingKey {
inner: RsaPrivateKey,
}
impl DecryptingKey {
/// Create a new verifying key from an RSA public key.
pub fn new(key: RsaPrivateKey) -> Self {
Self { inner: key }
}
}
impl Decryptor for DecryptingKey {
fn decrypt(&self, ciphertext: &[u8]) -> Result<Vec<u8>> {
decrypt::<DummyRng>(None, &self.inner, ciphertext)
}
}
impl RandomizedDecryptor for DecryptingKey {
fn decrypt_with_rng<R: CryptoRngCore + ?Sized>(
&self,
rng: &mut R,
ciphertext: &[u8],
) -> Result<Vec<u8>> {
decrypt(Some(rng), &self.inner, ciphertext)
}
}
impl EncryptingKeypair for DecryptingKey {
type EncryptingKey = EncryptingKey;
fn encrypting_key(&self) -> EncryptingKey {
EncryptingKey {
inner: self.inner.clone().into(),
}
}
}
mod oid {
use const_oid::ObjectIdentifier;
/// A trait which associates an RSA-specific OID with a type.
pub(crate) trait RsaSignatureAssociatedOid {
/// The OID associated with this type.
const OID: ObjectIdentifier;
}
#[cfg(feature = "sha1")]
impl RsaSignatureAssociatedOid for sha1::Sha1 {
const OID: ObjectIdentifier =
const_oid::ObjectIdentifier::new_unwrap("1.2.840.113549.1.1.5");
}
#[cfg(feature = "sha2")]
impl RsaSignatureAssociatedOid for sha2::Sha224 {
const OID: ObjectIdentifier =
const_oid::ObjectIdentifier::new_unwrap("1.2.840.113549.1.1.14");
}
#[cfg(feature = "sha2")]
impl RsaSignatureAssociatedOid for sha2::Sha256 {
const OID: ObjectIdentifier =
const_oid::ObjectIdentifier::new_unwrap("1.2.840.113549.1.1.11");
}
#[cfg(feature = "sha2")]
impl RsaSignatureAssociatedOid for sha2::Sha384 {
const OID: ObjectIdentifier =
const_oid::ObjectIdentifier::new_unwrap("1.2.840.113549.1.1.12");
}
#[cfg(feature = "sha2")]
impl RsaSignatureAssociatedOid for sha2::Sha512 {
const OID: ObjectIdentifier =
const_oid::ObjectIdentifier::new_unwrap("1.2.840.113549.1.1.13");
}
}
#[cfg(test)]
mod tests {
use super::*;
use base64ct::{Base64, Encoding};
use hex_literal::hex;
use num_bigint::BigUint;
use num_traits::FromPrimitive;
use num_traits::Num;
use rand_chacha::{
rand_core::{RngCore, SeedableRng},
ChaCha8Rng,
};
2018-07-24 14:31:06 +02:00
use sha1::{Digest, Sha1};
use sha2::Sha256;
use sha3::Sha3_256;
use signature::{RandomizedSigner, Signer, Verifier};
2018-07-24 14:31:06 +02:00
use crate::{PublicKeyParts, RsaPrivateKey, RsaPublicKey};
fn get_private_key() -> RsaPrivateKey {
// In order to generate new test vectors you'll need the PEM form of this key:
// -----BEGIN RSA PRIVATE KEY-----
// MIIBOgIBAAJBALKZD0nEffqM1ACuak0bijtqE2QrI/KLADv7l3kK3ppMyCuLKoF0
// fd7Ai2KW5ToIwzFofvJcS/STa6HA5gQenRUCAwEAAQJBAIq9amn00aS0h/CrjXqu
// /ThglAXJmZhOMPVn4eiu7/ROixi9sex436MaVeMqSNf7Ex9a8fRNfWss7Sqd9eWu
// RTUCIQDasvGASLqmjeffBNLTXV2A5g4t+kLVCpsEIZAycV5GswIhANEPLmax0ME/
// EO+ZJ79TJKN5yiGBRsv5yvx5UiHxajEXAiAhAol5N4EUyq6I9w1rYdhPMGpLfk7A
// IU2snfRJ6Nq2CQIgFrPsWRCkV+gOYcajD17rEqmuLrdIRexpg8N1DOSXoJ8CIGlS
// tAboUGBxTDq3ZroNism3DaMIbKPyYrAqhKov1h5V
// -----END RSA PRIVATE KEY-----
RsaPrivateKey::from_components(
BigUint::from_str_radix("9353930466774385905609975137998169297361893554149986716853295022578535724979677252958524466350471210367835187480748268864277464700638583474144061408845077", 10).unwrap(),
BigUint::from_u64(65537).unwrap(),
BigUint::from_str_radix("7266398431328116344057699379749222532279343923819063639497049039389899328538543087657733766554155839834519529439851673014800261285757759040931985506583861", 10).unwrap(),
vec![
BigUint::from_str_radix("98920366548084643601728869055592650835572950932266967461790948584315647051443",10).unwrap(),
BigUint::from_str_radix("94560208308847015747498523884063394671606671904944666360068158221458669711639", 10).unwrap()
],
).unwrap()
}
#[test]
fn test_decrypt_pkcs1v15() {
let priv_key = get_private_key();
let tests = [[
"gIcUIoVkD6ATMBk/u/nlCZCCWRKdkfjCgFdo35VpRXLduiKXhNz1XupLLzTXAybEq15juc+EgY5o0DHv/nt3yg==",
"x",
], [
"Y7TOCSqofGhkRb+jaVRLzK8xw2cSo1IVES19utzv6hwvx+M8kFsoWQm5DzBeJCZTCVDPkTpavUuEbgp8hnUGDw==",
"testing.",
], [
"arReP9DJtEVyV2Dg3dDp4c/PSk1O6lxkoJ8HcFupoRorBZG+7+1fDAwT1olNddFnQMjmkb8vxwmNMoTAT/BFjQ==",
"testing.\n",
], [
"WtaBXIoGC54+vH0NH0CHHE+dRDOsMc/6BrfFu2lEqcKL9+uDuWaf+Xj9mrbQCjjZcpQuX733zyok/jsnqe/Ftw==",
"01234567890123456789012345678901234567890123456789012",
]];
for test in &tests {
let out = priv_key
.decrypt(Pkcs1v15Encrypt, &Base64::decode_vec(test[0]).unwrap())
.unwrap();
assert_eq!(out, test[1].as_bytes());
}
}
#[test]
fn test_encrypt_decrypt_pkcs1v15() {
2022-03-14 14:22:48 +00:00
let mut rng = ChaCha8Rng::from_seed([42; 32]);
let priv_key = get_private_key();
let k = priv_key.size();
for i in 1..100 {
let mut input = vec![0u8; i * 8];
rng.fill_bytes(&mut input);
if input.len() > k - 11 {
input = input[0..k - 11].to_vec();
}
let pub_key: RsaPublicKey = priv_key.clone().into();
let ciphertext = encrypt(&mut rng, &pub_key, &input).unwrap();
assert_ne!(input, ciphertext);
let blind: bool = rng.next_u32() < (1u32 << 31);
let blinder = if blind { Some(&mut rng) } else { None };
let plaintext = decrypt(blinder, &priv_key, &ciphertext).unwrap();
assert_eq!(input, plaintext);
}
}
2018-07-24 14:31:06 +02:00
#[test]
fn test_decrypt_pkcs1v15_traits() {
let priv_key = get_private_key();
let decrypting_key = DecryptingKey::new(priv_key);
let tests = [[
"gIcUIoVkD6ATMBk/u/nlCZCCWRKdkfjCgFdo35VpRXLduiKXhNz1XupLLzTXAybEq15juc+EgY5o0DHv/nt3yg==",
"x",
], [
"Y7TOCSqofGhkRb+jaVRLzK8xw2cSo1IVES19utzv6hwvx+M8kFsoWQm5DzBeJCZTCVDPkTpavUuEbgp8hnUGDw==",
"testing.",
], [
"arReP9DJtEVyV2Dg3dDp4c/PSk1O6lxkoJ8HcFupoRorBZG+7+1fDAwT1olNddFnQMjmkb8vxwmNMoTAT/BFjQ==",
"testing.\n",
], [
"WtaBXIoGC54+vH0NH0CHHE+dRDOsMc/6BrfFu2lEqcKL9+uDuWaf+Xj9mrbQCjjZcpQuX733zyok/jsnqe/Ftw==",
"01234567890123456789012345678901234567890123456789012",
]];
for test in &tests {
let out = decrypting_key
.decrypt(&Base64::decode_vec(test[0]).unwrap())
.unwrap();
assert_eq!(out, test[1].as_bytes());
}
}
#[test]
fn test_encrypt_decrypt_pkcs1v15_traits() {
let mut rng = ChaCha8Rng::from_seed([42; 32]);
let priv_key = get_private_key();
let k = priv_key.size();
let decrypting_key = DecryptingKey::new(priv_key);
for i in 1..100 {
let mut input = vec![0u8; i * 8];
rng.fill_bytes(&mut input);
if input.len() > k - 11 {
input = input[0..k - 11].to_vec();
}
let encrypting_key = decrypting_key.encrypting_key();
let ciphertext = encrypting_key.encrypt_with_rng(&mut rng, &input).unwrap();
assert_ne!(input, ciphertext);
let blind: bool = rng.next_u32() < (1u32 << 31);
let plaintext = if blind {
decrypting_key
.decrypt_with_rng(&mut rng, &ciphertext)
.unwrap()
} else {
decrypting_key.decrypt(&ciphertext).unwrap()
};
assert_eq!(input, plaintext);
}
}
2018-07-24 14:31:06 +02:00
#[test]
fn test_sign_pkcs1v15() {
let priv_key = get_private_key();
let tests = [(
"Test.\n",
hex!(
"a4f3fa6ea93bcdd0c57be020c1193ecbfd6f200a3d95c409769b029578fa0e33"
"6ad9a347600e40d3ae823b8c7e6bad88cc07c1d54c3a1523cbbb6d58efc362ae"
),
)];
2018-07-24 14:31:06 +02:00
for (text, expected) in &tests {
let digest = Sha1::digest(text.as_bytes()).to_vec();
2018-07-24 14:31:06 +02:00
let out = priv_key.sign(Pkcs1v15Sign::new::<Sha1>(), &digest).unwrap();
2018-07-24 14:31:06 +02:00
assert_ne!(out, digest);
assert_eq!(out, expected);
2022-03-14 14:22:48 +00:00
let mut rng = ChaCha8Rng::from_seed([42; 32]);
2018-07-24 14:31:06 +02:00
let out2 = priv_key
.sign_with_rng(&mut rng, Pkcs1v15Sign::new::<Sha1>(), &digest)
2018-07-24 14:31:06 +02:00
.unwrap();
assert_eq!(out2, expected);
}
}
#[test]
fn test_sign_pkcs1v15_signer() {
let priv_key = get_private_key();
let tests = [(
"Test.\n",
hex!(
"a4f3fa6ea93bcdd0c57be020c1193ecbfd6f200a3d95c409769b029578fa0e33"
"6ad9a347600e40d3ae823b8c7e6bad88cc07c1d54c3a1523cbbb6d58efc362ae"
),
)];
let signing_key = SigningKey::<Sha1>::new(priv_key);
for (text, expected) in &tests {
let out = signing_key.sign(text.as_bytes()).to_bytes();
assert_ne!(out.as_ref(), text.as_bytes());
assert_ne!(out.as_ref(), &Sha1::digest(text.as_bytes()).to_vec());
assert_eq!(out.as_ref(), expected);
let mut rng = ChaCha8Rng::from_seed([42; 32]);
let out2 = signing_key
.sign_with_rng(&mut rng, text.as_bytes())
.to_bytes();
assert_eq!(out2.as_ref(), expected);
}
}
#[test]
fn test_sign_pkcs1v15_signer_sha2_256() {
let priv_key = get_private_key();
let tests = [(
"Test.\n",
hex!(
"2ffae3f3e130287b3a1dcb320e46f52e8f3f7969b646932273a7e3a6f2a182ea"
"02d42875a7ffa4a148aa311f9e4b562e4e13a2223fb15f4e5bf5f2b206d9451b"
),
)];
let signing_key = SigningKey::<Sha256>::new(priv_key);
for (text, expected) in &tests {
let out = signing_key.sign(text.as_bytes()).to_bytes();
assert_ne!(out.as_ref(), text.as_bytes());
assert_eq!(out.as_ref(), expected);
let mut rng = ChaCha8Rng::from_seed([42; 32]);
let out2 = signing_key
.sign_with_rng(&mut rng, text.as_bytes())
.to_bytes();
assert_eq!(out2.as_ref(), expected);
}
}
#[test]
fn test_sign_pkcs1v15_signer_sha3_256() {
let priv_key = get_private_key();
let tests = [(
"Test.\n",
hex!(
"55e9fba3354dfb51d2c8111794ea552c86afc2cab154652c03324df8c2c51ba7"
"2ff7c14de59a6f9ba50d90c13a7537cc3011948369f1f0ec4a49d21eb7e723f9"
),
)];
let signing_key = SigningKey::<Sha3_256>::new(priv_key);
for (text, expected) in &tests {
let out = signing_key.sign(text.as_bytes()).to_bytes();
assert_ne!(out.as_ref(), text.as_bytes());
assert_eq!(out.as_ref(), expected);
let mut rng = ChaCha8Rng::from_seed([42; 32]);
let out2 = signing_key
.sign_with_rng(&mut rng, text.as_bytes())
.to_bytes();
assert_eq!(out2.as_ref(), expected);
}
}
#[test]
fn test_sign_pkcs1v15_digest_signer() {
let priv_key = get_private_key();
let tests = [(
"Test.\n",
hex!(
"a4f3fa6ea93bcdd0c57be020c1193ecbfd6f200a3d95c409769b029578fa0e33"
"6ad9a347600e40d3ae823b8c7e6bad88cc07c1d54c3a1523cbbb6d58efc362ae"
),
)];
let signing_key = SigningKey::new(priv_key);
for (text, expected) in &tests {
let mut digest = Sha1::new();
digest.update(text.as_bytes());
let out = signing_key.sign_digest(digest).to_bytes();
assert_ne!(out.as_ref(), text.as_bytes());
assert_ne!(out.as_ref(), &Sha1::digest(text.as_bytes()).to_vec());
assert_eq!(out.as_ref(), expected);
let mut rng = ChaCha8Rng::from_seed([42; 32]);
let mut digest = Sha1::new();
digest.update(text.as_bytes());
let out2 = signing_key
.sign_digest_with_rng(&mut rng, digest)
.to_bytes();
assert_eq!(out2.as_ref(), expected);
}
}
#[test]
fn test_verify_pkcs1v15() {
let priv_key = get_private_key();
let tests = [
(
"Test.\n",
hex!(
"a4f3fa6ea93bcdd0c57be020c1193ecbfd6f200a3d95c409769b029578fa0e33"
"6ad9a347600e40d3ae823b8c7e6bad88cc07c1d54c3a1523cbbb6d58efc362ae"
),
true,
),
(
"Test.\n",
hex!(
"a4f3fa6ea93bcdd0c57be020c1193ecbfd6f200a3d95c409769b029578fa0e33"
"6ad9a347600e40d3ae823b8c7e6bad88cc07c1d54c3a1523cbbb6d58efc362af"
),
false,
),
];
let pub_key: RsaPublicKey = priv_key.into();
for (text, sig, expected) in &tests {
let digest = Sha1::digest(text.as_bytes()).to_vec();
let result = pub_key.verify(Pkcs1v15Sign::new::<Sha1>(), &digest, sig);
match expected {
true => result.expect("failed to verify"),
false => {
result.expect_err("expected verifying error");
}
}
}
}
#[test]
fn test_verify_pkcs1v15_signer() {
let priv_key = get_private_key();
let tests = [
(
"Test.\n",
hex!(
"a4f3fa6ea93bcdd0c57be020c1193ecbfd6f200a3d95c409769b029578fa0e33"
"6ad9a347600e40d3ae823b8c7e6bad88cc07c1d54c3a1523cbbb6d58efc362ae"
),
true,
),
(
"Test.\n",
hex!(
"a4f3fa6ea93bcdd0c57be020c1193ecbfd6f200a3d95c409769b029578fa0e33"
"6ad9a347600e40d3ae823b8c7e6bad88cc07c1d54c3a1523cbbb6d58efc362af"
),
false,
),
];
let pub_key: RsaPublicKey = priv_key.into();
let verifying_key = VerifyingKey::<Sha1>::new(pub_key);
for (text, sig, expected) in &tests {
let result = verifying_key.verify(
text.as_bytes(),
&Signature::try_from(sig.as_slice()).unwrap(),
);
match expected {
true => result.expect("failed to verify"),
false => {
result.expect_err("expected verifying error");
}
}
}
}
#[test]
fn test_verify_pkcs1v15_digest_signer() {
let priv_key = get_private_key();
let tests = [
(
"Test.\n",
hex!(
"a4f3fa6ea93bcdd0c57be020c1193ecbfd6f200a3d95c409769b029578fa0e33"
"6ad9a347600e40d3ae823b8c7e6bad88cc07c1d54c3a1523cbbb6d58efc362ae"
),
true,
),
(
"Test.\n",
hex!(
"a4f3fa6ea93bcdd0c57be020c1193ecbfd6f200a3d95c409769b029578fa0e33"
"6ad9a347600e40d3ae823b8c7e6bad88cc07c1d54c3a1523cbbb6d58efc362af"
),
false,
),
];
let pub_key: RsaPublicKey = priv_key.into();
let verifying_key = VerifyingKey::new(pub_key);
for (text, sig, expected) in &tests {
let mut digest = Sha1::new();
digest.update(text.as_bytes());
let result =
verifying_key.verify_digest(digest, &Signature::try_from(sig.as_slice()).unwrap());
match expected {
true => result.expect("failed to verify"),
false => {
result.expect_err("expected verifying error");
}
}
}
}
2018-07-24 14:31:06 +02:00
#[test]
fn test_unpadded_signature() {
let msg = b"Thu Dec 19 18:06:16 EST 2013\n";
let expected_sig = Base64::decode_vec("pX4DR8azytjdQ1rtUiC040FjkepuQut5q2ZFX1pTjBrOVKNjgsCDyiJDGZTCNoh9qpXYbhl7iEym30BWWwuiZg==").unwrap();
2018-07-24 14:31:06 +02:00
let priv_key = get_private_key();
let sig = priv_key.sign(Pkcs1v15Sign::new_unprefixed(), msg).unwrap();
2018-07-24 14:31:06 +02:00
assert_eq!(expected_sig, sig);
let pub_key: RsaPublicKey = priv_key.into();
pub_key
.verify(Pkcs1v15Sign::new_unprefixed(), msg, &sig)
.expect("failed to verify");
2018-07-24 14:31:06 +02:00
}
#[test]
fn test_unpadded_signature_hazmat() {
let msg = b"Thu Dec 19 18:06:16 EST 2013\n";
let expected_sig = Base64::decode_vec("pX4DR8azytjdQ1rtUiC040FjkepuQut5q2ZFX1pTjBrOVKNjgsCDyiJDGZTCNoh9qpXYbhl7iEym30BWWwuiZg==").unwrap();
let priv_key = get_private_key();
let signing_key = SigningKey::<Sha1>::new_unprefixed(priv_key);
let sig = signing_key
.sign_prehash(msg)
.expect("Failure during sign")
.to_bytes();
assert_eq!(sig.as_ref(), expected_sig);
let verifying_key = signing_key.verifying_key();
verifying_key
.verify_prehash(msg, &Signature::try_from(expected_sig.as_slice()).unwrap())
.expect("failed to verify");
}
2020-03-06 23:10:55 +01:00
}