parsec_tool/
util.rs

1// Copyright 2021 Contributors to the Parsec project.
2// SPDX-License-Identifier: Apache-2.0
3
4//! Utility code that is shared by multiple subcommands;
5
6use crate::error::{Result, ToolErrorKind};
7use log::{error, info};
8use parsec_client::core::interface::operations::psa_algorithm::{Algorithm, Hash, SignHash};
9use parsec_client::BasicClient;
10use picky_asn1::wrapper::IntegerAsn1;
11use serde::{Deserialize, Serialize};
12use sha2::digest::{Digest, DynDigest};
13
14#[derive(Serialize, Deserialize)]
15struct EccSignature {
16    r: IntegerAsn1,
17    s: IntegerAsn1,
18}
19
20/// Signs a given message using the hashing and signing policy that was associated with the given key when
21/// it was created.
22///
23/// If the signing key allows for the use of any hashing algorithm, then a default hash can optionally be passed
24/// by the caller, and this hash will be used (otherwise the function will fail).
25pub fn sign_message_with_policy(
26    basic_client: &BasicClient,
27    key_name: &str,
28    msg: &[u8],
29    default_hash: Option<Hash>,
30) -> Result<Vec<u8>> {
31    let alg = basic_client
32        .key_attributes(key_name)?
33        .policy
34        .permitted_algorithms;
35
36    let signature = match alg {
37        Algorithm::AsymmetricSignature(alg) => {
38            let hash = match alg.hash() {
39                Some(SignHash::Specific(hash)) => hash_data(msg, hash)?,
40                Some(SignHash::Any) => {
41                    if let Some(hash) = default_hash {
42                        hash_data(msg, hash)?
43                    } else {
44                        error!("Signing key allows any hashing algorithm, but no default was specified.");
45                        return Err(ToolErrorKind::NotSupported.into());
46                    }
47                }
48                _ => {
49                    error!("Asymmetric signing algorithm ({:?}) is not supported", alg);
50                    return Err(ToolErrorKind::NotSupported.into());
51                }
52            };
53            info!("Signing data with {:?}...", alg);
54            let mut sig = basic_client.psa_sign_hash(key_name, &hash, alg)?;
55            if alg.is_ecc_alg() {
56                let s = IntegerAsn1::from_bytes_be_unsigned(sig.split_off(sig.len() / 2));
57                sig = picky_asn1_der::to_vec(&EccSignature {
58                    r: IntegerAsn1::from_bytes_be_unsigned(sig),
59                    s,
60                })
61                .unwrap();
62            }
63
64            sig
65        }
66        other => {
67            error!(
68                "Key's algorithm is {:?} which can not be used for signing.",
69                other
70            );
71            return Err(ToolErrorKind::WrongKeyAlgorithm.into());
72        }
73    };
74
75    Ok(signature)
76}
77
78fn hash_data(data: &[u8], alg: Hash) -> Result<Vec<u8>> {
79    let mut hasher: Box<dyn DynDigest> = match alg {
80        Hash::Sha224 => Box::from(sha2::Sha224::new()),
81        Hash::Sha256 => Box::from(sha2::Sha256::new()),
82        Hash::Sha384 => Box::from(sha2::Sha384::new()),
83        Hash::Sha512 => Box::from(sha2::Sha512::new()),
84        _ => {
85            error!("Hashing algorithm ({:?}) not supported", alg);
86            return Err(ToolErrorKind::NotSupported.into());
87        }
88    };
89    info!("Hashing data with {:?}...", alg);
90    hasher.update(data);
91    Ok(hasher.finalize().to_vec())
92}