Skip to content

Commit 057a4c5

Browse files
committed
Implement and test SigningKey::public_key()
1 parent 124c326 commit 057a4c5

File tree

7 files changed

+108
-3
lines changed

7 files changed

+108
-3
lines changed

Cargo.lock

+2-2
Some generated files are not rendered by default. Learn more about customizing how changed files appear on GitHub.

graviola/src/high/ecdsa.rs

+25
Original file line numberDiff line numberDiff line change
@@ -122,6 +122,31 @@ impl<C: Curve> SigningKey<C> {
122122
output.get(..used).ok_or(Error::WrongLength)
123123
}
124124

125+
/// Encode this private key's corresponding public key in SubjectPublicKeyInfo DER format.
126+
///
127+
/// The encoding is written to the start of `output`, and the used span is
128+
/// returned. [`Error::WrongLength`] is returned if `output` is not sufficient
129+
/// to contain the full encoding.
130+
pub fn to_spki_der<'a>(&self, output: &'a mut [u8]) -> Result<&'a [u8], Error> {
131+
let mut pub_key_buffer = [0u8; MAX_UNCOMPRESSED_PUBLIC_KEY_LEN];
132+
let pub_key_buffer = self
133+
.private_key
134+
.public_key_encode_uncompressed(&mut pub_key_buffer)?;
135+
136+
let spki = asn1::pkix::SubjectPublicKeyInfo {
137+
algorithm: asn1::pkix::AlgorithmIdentifier {
138+
algorithm: asn1::oid::id_ecPublicKey.clone(),
139+
parameters: Some(asn1::Any::ObjectId(C::oid())),
140+
},
141+
subjectPublicKey: asn1::BitString::new(pub_key_buffer),
142+
};
143+
144+
let len = spki
145+
.encode(&mut asn1::Encoder::new(output))
146+
.map_err(Error::Asn1Error)?;
147+
Ok(&output[..len])
148+
}
149+
125150
/// ECDSA signing, returning a fixed-length signature.
126151
///
127152
/// The `message` is hashed using `H`. The message is a sequence of byte

graviola/src/high/rsa.rs

+39
Original file line numberDiff line numberDiff line change
@@ -44,6 +44,45 @@ impl VerifyingKey {
4444
Ok(Self(pub_key))
4545
}
4646

47+
/// Encodes this RSA verification key in SubjectPublicKeyInfo DER format.
48+
///
49+
/// The `SubjectPublicKeyInfo.algorithm` identifier is `rsaEncryption`.
50+
///
51+
/// `output` is the output buffer, and the encoding is written to the start
52+
/// of this buffer. An error is returned if the encoding is larger than
53+
/// the supplied buffer. Otherwise, on success, the range containing the
54+
/// encoding is returned.
55+
pub fn to_spki_der<'a>(&self, output: &'a mut [u8]) -> Result<&'a [u8], Error> {
56+
let _entry = Entry::new_public();
57+
58+
let mut buffer = [0u8; rsa_pub::MAX_PUBLIC_MODULUS_BYTES + 1];
59+
let public_modulus = self.0.n.to_bytes_asn1(&mut buffer)?;
60+
let public_exponent = self.0.e.to_be_bytes();
61+
62+
let pub_key = pkix::RSAPublicKey {
63+
modulus: asn1::Integer::new(public_modulus),
64+
publicExponent: asn1::Integer::new(&public_exponent),
65+
};
66+
let mut pub_key_buffer = [0u8; rsa_pub::MAX_PUBLIC_MODULUS_BYTES + 64];
67+
let used = pub_key
68+
.encode(&mut asn1::Encoder::new(&mut pub_key_buffer))
69+
.map_err(Error::Asn1Error)?;
70+
let pub_key_buffer = &pub_key_buffer[..used];
71+
72+
let spki = pkix::SubjectPublicKeyInfo {
73+
algorithm: pkix::AlgorithmIdentifier {
74+
algorithm: asn1::oid::rsaEncryption.clone(),
75+
parameters: Some(asn1::Any::Null(asn1::Null)),
76+
},
77+
subjectPublicKey: asn1::BitString::new(pub_key_buffer),
78+
};
79+
80+
let len = spki
81+
.encode(&mut asn1::Encoder::new(output))
82+
.map_err(Error::Asn1Error)?;
83+
Ok(&output[..len])
84+
}
85+
4786
/// Verifies `signature`, using RSASSA-PKCS1-v1_5 with SHA-256.
4887
///
4988
/// `message` is the (unhashed) signed message. It is hashed

graviola/src/mid/rsa_priv.rs

+2
Original file line numberDiff line numberDiff line change
@@ -104,6 +104,8 @@ impl RsaPrivateKey {
104104
let dq = self.dq.to_bytes_asn1(dq)?;
105105
let iqmp = self.iqmp.to_bytes_asn1(iqmp)?;
106106

107+
// We are exporting this key; it is leaving the confines of this
108+
// library and becoming public data.
107109
low::ct::public_slice(p);
108110
low::ct::public_slice(q);
109111
low::ct::public_slice(d);

rustls-graviola/Cargo.toml

+1-1
Original file line numberDiff line numberDiff line change
@@ -20,7 +20,7 @@ http-body-util = "0.1"
2020
hyper = { version = "1", default-features = false }
2121
hyper-rustls = { version = "0.27", default-features = false, features = ["native-tokio", "http1", "tls12", "logging"] }
2222
hyper-util = { version = "0.1", default-features = false, features = ["server-auto"] }
23-
rustls = { version = "0.23", default-features = false, features = ["ring"] }
23+
rustls = { version = "0.23.23", default-features = false, features = ["ring"] }
2424
rustls-pemfile = "2"
2525
tokio = { version = "1.0", features = ["io-std", "macros", "net", "rt-multi-thread"] }
2626
tokio-rustls = { version = "0.26", default-features = false }

rustls-graviola/src/sign.rs

+30
Original file line numberDiff line numberDiff line change
@@ -3,6 +3,7 @@ use std::sync::Arc;
33

44
use graviola::hashing;
55
use graviola::signing::{ecdsa, rsa};
6+
use rustls::pki_types::SubjectPublicKeyInfoDer;
67
use rustls::{SignatureScheme, pki_types, sign};
78

89
#[derive(Debug)]
@@ -112,6 +113,17 @@ impl sign::SigningKey for Rsa {
112113
fn algorithm(&self) -> rustls::SignatureAlgorithm {
113114
rustls::SignatureAlgorithm::RSA
114115
}
116+
117+
fn public_key(&self) -> Option<SubjectPublicKeyInfoDer<'static>> {
118+
let size = self.0.modulus_len_bytes() + 64;
119+
let mut buffer = vec![0u8; size];
120+
121+
let pk = self.0.public_key();
122+
let used = pk.to_spki_der(&mut buffer).ok()?.len();
123+
124+
buffer.truncate(used);
125+
Some(SubjectPublicKeyInfoDer::from(buffer))
126+
}
115127
}
116128

117129
impl fmt::Debug for Rsa {
@@ -170,6 +182,15 @@ impl sign::SigningKey for EcdsaP256 {
170182
fn algorithm(&self) -> rustls::SignatureAlgorithm {
171183
rustls::SignatureAlgorithm::ECDSA
172184
}
185+
186+
fn public_key(&self) -> Option<SubjectPublicKeyInfoDer<'static>> {
187+
let mut buffer = vec![0u8; 128];
188+
189+
let used = self.0.to_spki_der(&mut buffer).ok()?.len();
190+
191+
buffer.truncate(used);
192+
Some(SubjectPublicKeyInfoDer::from(buffer))
193+
}
173194
}
174195

175196
impl sign::Signer for EcdsaP256 {
@@ -211,6 +232,15 @@ impl sign::SigningKey for EcdsaP384 {
211232
fn algorithm(&self) -> rustls::SignatureAlgorithm {
212233
rustls::SignatureAlgorithm::ECDSA
213234
}
235+
236+
fn public_key(&self) -> Option<SubjectPublicKeyInfoDer<'static>> {
237+
let mut buffer = vec![0u8; 128];
238+
239+
let used = self.0.to_spki_der(&mut buffer).ok()?.len();
240+
241+
buffer.truncate(used);
242+
Some(SubjectPublicKeyInfoDer::from(buffer))
243+
}
214244
}
215245

216246
impl sign::Signer for EcdsaP384 {

rustls-graviola/tests/pair.rs

+9
Original file line numberDiff line numberDiff line change
@@ -5,6 +5,7 @@ use rustls::crypto::ring::default_provider as baseline;
55
use rustls::crypto::{CryptoProvider, SupportedKxGroup};
66
use rustls::pki_types::pem::PemObject;
77
use rustls::pki_types::{CertificateDer, PrivateKeyDer};
8+
use rustls::sign::CertifiedKey;
89
use rustls::{
910
ClientConfig, ClientConnection, HandshakeKind, RootCertStore, ServerConfig, ServerConnection,
1011
};
@@ -20,6 +21,7 @@ fn all_suites() {
2021
rustls_graviola::suites::TLS13_CHACHA20_POLY1305_SHA256,
2122
*key_type,
2223
);
24+
test_keys_match(&rustls_graviola::default_provider(), *key_type);
2325
}
2426

2527
for key_type in KeyType::RSA {
@@ -167,6 +169,13 @@ fn exercise(client_config: Arc<ClientConfig>, server_config: Arc<ServerConfig>)
167169
server.handshake_kind().unwrap()
168170
}
169171

172+
fn test_keys_match(provider: &CryptoProvider, key_type: KeyType) {
173+
CertifiedKey::from_der(key_type.cert_chain(), key_type.key(), provider)
174+
.unwrap()
175+
.keys_match()
176+
.unwrap();
177+
}
178+
170179
#[derive(Clone, Copy, Debug)]
171180
enum KeyType {
172181
Rsa2048,

0 commit comments

Comments
 (0)