Skip to content

Commit b9bb45c

Browse files
committed
Add decoder wrapper
1 parent f8a1abf commit b9bb45c

File tree

1 file changed

+330
-10
lines changed

1 file changed

+330
-10
lines changed

openssl/src/encdec.rs

Lines changed: 330 additions & 10 deletions
Original file line numberDiff line numberDiff line change
@@ -1,13 +1,14 @@
1-
use crate::bio::MemBio;
1+
use crate::bio::{MemBio, MemBioSlice};
22
use crate::cipher::CipherRef;
33
use crate::error::ErrorStack;
4-
use crate::pkey::PKeyRef;
5-
use crate::pkey_ctx::Selection;
6-
use crate::util::c_str;
4+
use crate::pkey::{Id, PKey, PKeyRef};
5+
use crate::pkey_ctx::{Selection, SelectionT};
6+
use crate::util::{c_str, invoke_passwd_cb, CallbackState};
77
use crate::{cvt, cvt_p};
88
use foreign_types::{ForeignType, ForeignTypeRef};
99
use openssl_macros::corresponds;
1010
use std::ffi::{CStr, CString};
11+
use std::marker::PhantomData;
1112
use std::ptr;
1213

1314
#[derive(Debug, Copy, Clone, PartialEq, Eq)]
@@ -91,23 +92,200 @@ foreign_type_and_impl_send_sync! {
9192

9293
/// A context object which can perform decode operations.
9394
pub struct DecoderCtx;
94-
/// A reference to a [`DecoderCtx`].
95+
/// A reference to a `DecoderCtx`.
9596
pub struct DecoderCtxRef;
9697
}
9798

9899
impl DecoderCtx {
99-
/// Creates a new decoder context using the provided key.
100-
#[corresponds(OSSL_DECODER_CTX_new)]
100+
#[corresponds(OSSL_DECODER_CTX_new_for_pkey)]
101101
#[inline]
102102
#[allow(dead_code)]
103-
pub fn new() -> Result<Self, ErrorStack> {
103+
fn new_for_key(
104+
pkey: *mut *mut ffi::EVP_PKEY,
105+
selection: Selection,
106+
input: Option<KeyFormat>,
107+
structure: Option<Structure<'_>>,
108+
key_type: Option<Id>,
109+
) -> Result<Self, ErrorStack> {
110+
let input_ptr = input
111+
.map(|i| {
112+
let input: &CStr = i.into();
113+
input.as_ptr()
114+
})
115+
.unwrap_or_else(ptr::null);
116+
let structure_ptr = structure
117+
.map(|s| {
118+
let structure: &CStr = s.into();
119+
structure.as_ptr()
120+
})
121+
.unwrap_or_else(ptr::null);
122+
let key_type_ptr = key_type
123+
.and_then(|k| k.try_into().ok())
124+
.map(|k: &CStr| k.as_ptr())
125+
.unwrap_or_else(ptr::null);
104126
unsafe {
105-
let ptr = cvt_p(ffi::OSSL_DECODER_CTX_new())?;
127+
let ptr = cvt_p(ffi::OSSL_DECODER_CTX_new_for_pkey(
128+
pkey,
129+
input_ptr,
130+
structure_ptr,
131+
key_type_ptr,
132+
selection.into(),
133+
ptr::null_mut(),
134+
ptr::null(),
135+
))?;
106136
Ok(DecoderCtx::from_ptr(ptr))
107137
}
108138
}
109139
}
110140

141+
impl DecoderCtxRef {
142+
/// Select which parts of the key to decode.
143+
#[corresponds(OSSL_DECODER_CTX_set_selection)]
144+
#[allow(dead_code)]
145+
fn set_selection(&mut self, selection: Selection) -> Result<(), ErrorStack> {
146+
cvt(unsafe { ffi::OSSL_DECODER_CTX_set_selection(self.as_ptr(), selection.into()) })
147+
.map(|_| ())
148+
}
149+
150+
/// Set the input type for the encoded data.
151+
#[corresponds(OSSL_DECODER_CTX_set_input_type)]
152+
#[allow(dead_code)]
153+
fn set_input_type(&mut self, input: KeyFormat) -> Result<(), ErrorStack> {
154+
let input: &CStr = input.into();
155+
cvt(unsafe { ffi::OSSL_DECODER_CTX_set_input_type(self.as_ptr(), input.as_ptr()) })
156+
.map(|_| ())
157+
}
158+
159+
/// Set the input structure for the encoded data.
160+
#[corresponds(OSSL_DECODER_CTX_set_input_structure)]
161+
#[allow(dead_code)]
162+
fn set_input_structure(&mut self, structure: Structure<'_>) -> Result<(), ErrorStack> {
163+
let structure: &CStr = structure.into();
164+
cvt(unsafe { ffi::OSSL_DECODER_CTX_set_input_structure(self.as_ptr(), structure.as_ptr()) })
165+
.map(|_| ())
166+
}
167+
168+
/// Set the passphrase to decrypt the encoded data.
169+
#[corresponds(OSSL_DECODER_CTX_set_passphrase)]
170+
#[allow(dead_code)]
171+
fn set_passphrase(&mut self, passphrase: &[u8]) -> Result<(), ErrorStack> {
172+
cvt(unsafe {
173+
ffi::OSSL_DECODER_CTX_set_passphrase(
174+
self.as_ptr(),
175+
passphrase.as_ptr().cast(),
176+
passphrase.len(),
177+
)
178+
})
179+
.map(|_| ())
180+
}
181+
182+
/// Set the passphrase to decrypt the encoded data.
183+
#[corresponds(OSSL_DECODER_CTX_set_passphrase)]
184+
#[allow(dead_code)]
185+
unsafe fn set_passphrase_callback<F: FnOnce(&mut [u8]) -> Result<usize, ErrorStack>>(
186+
&mut self,
187+
callback: *mut CallbackState<F>,
188+
) -> Result<(), ErrorStack> {
189+
cvt(unsafe {
190+
ffi::OSSL_DECODER_CTX_set_pem_password_cb(
191+
self.as_ptr(),
192+
Some(invoke_passwd_cb::<F>),
193+
callback as *mut _,
194+
)
195+
})
196+
.map(|_| ())
197+
}
198+
199+
/// Decode the encoded data
200+
#[corresponds(OSSL_DECODER_from_bio)]
201+
#[allow(dead_code)]
202+
fn decode(&mut self, data: &[u8]) -> Result<(), ErrorStack> {
203+
let bio = MemBioSlice::new(data)?;
204+
205+
cvt(unsafe { ffi::OSSL_DECODER_from_bio(self.as_ptr(), bio.as_ptr()) }).map(|_| ())
206+
}
207+
}
208+
209+
#[allow(dead_code)]
210+
pub(crate) struct Decoder<'a, T: SelectionT> {
211+
selection: PhantomData<T>,
212+
key_type: Option<Id>,
213+
format: Option<KeyFormat>,
214+
structure: Option<Structure<'a>>,
215+
passphrase: Option<&'a [u8]>,
216+
#[allow(clippy::type_complexity)]
217+
passphrase_callback: Option<Box<dyn FnOnce(&mut [u8]) -> Result<usize, ErrorStack> + 'a>>,
218+
}
219+
220+
impl<'a, T: SelectionT> Decoder<'a, T> {
221+
#[allow(dead_code)]
222+
pub(crate) fn new() -> Self {
223+
Self {
224+
selection: PhantomData,
225+
key_type: None,
226+
format: None,
227+
structure: None,
228+
passphrase: None,
229+
passphrase_callback: None,
230+
}
231+
}
232+
233+
#[allow(dead_code)]
234+
pub fn set_key_type(mut self, key_type: Id) -> Self {
235+
self.key_type = Some(key_type);
236+
self
237+
}
238+
239+
#[allow(dead_code)]
240+
pub fn set_format(mut self, format: KeyFormat) -> Self {
241+
self.format = Some(format);
242+
self
243+
}
244+
245+
#[allow(dead_code)]
246+
pub fn set_structure(mut self, structure: Structure<'a>) -> Self {
247+
self.structure = Some(structure);
248+
self
249+
}
250+
251+
#[allow(dead_code)]
252+
pub fn set_passphrase(mut self, passphrase: &'a [u8]) -> Self {
253+
self.passphrase = Some(passphrase);
254+
self
255+
}
256+
257+
#[allow(dead_code)]
258+
pub fn set_passphrase_callback<F: FnOnce(&mut [u8]) -> Result<usize, ErrorStack> + 'a>(
259+
mut self,
260+
callback: F,
261+
) -> Self {
262+
self.passphrase_callback = Some(Box::new(callback));
263+
self
264+
}
265+
266+
#[allow(dead_code)]
267+
pub fn decode(self, data: &[u8]) -> Result<PKey<T>, ErrorStack> {
268+
let mut pkey_ptr = ptr::null_mut();
269+
let mut passphrase_callback_state;
270+
let mut ctx = DecoderCtx::new_for_key(
271+
&mut pkey_ptr,
272+
T::SELECTION,
273+
self.format,
274+
self.structure,
275+
self.key_type,
276+
)?;
277+
if let Some(passphrase) = self.passphrase {
278+
ctx.set_passphrase(passphrase)?;
279+
}
280+
if let Some(passphrase_callback) = self.passphrase_callback {
281+
passphrase_callback_state = CallbackState::new(passphrase_callback);
282+
unsafe { ctx.set_passphrase_callback(&mut passphrase_callback_state)? };
283+
}
284+
ctx.decode(data)?;
285+
Ok(unsafe { PKey::from_ptr(pkey_ptr) })
286+
}
287+
}
288+
111289
foreign_type_and_impl_send_sync! {
112290
type CType = ffi::OSSL_ENCODER_CTX;
113291
fn drop = ffi::OSSL_ENCODER_CTX_free;
@@ -321,13 +499,155 @@ mod test {
321499
}
322500
}
323501

502+
mod decoder {
503+
use super::*;
504+
505+
mod params {
506+
use super::*;
507+
use crate::pkey::Params;
508+
509+
#[test]
510+
fn test_dh_pem() {
511+
Decoder::<Params>::new()
512+
.set_key_type(Id::DH)
513+
.set_format(KeyFormat::Pem)
514+
.set_structure(Structure::TypeSpecific)
515+
.decode(include_bytes!("../test/dhparams.pem"))
516+
.unwrap()
517+
.dh()
518+
.unwrap();
519+
}
520+
521+
#[test]
522+
fn test_dh_der() {
523+
Decoder::<Params>::new()
524+
.set_key_type(Id::DH)
525+
.set_format(KeyFormat::Der)
526+
.set_structure(Structure::TypeSpecific)
527+
.decode(include_bytes!("../test/dhparams.der"))
528+
.unwrap()
529+
.dh()
530+
.unwrap();
531+
}
532+
}
533+
mod public {
534+
use super::*;
535+
use crate::pkey::Public;
536+
537+
#[test]
538+
fn test_rsa_pem() {
539+
Decoder::<Public>::new()
540+
.set_key_type(Id::RSA)
541+
.set_format(KeyFormat::Pem)
542+
.set_structure(Structure::SubjectPublicKeyInfo)
543+
.decode(include_bytes!("../test/rsa.pem.pub"))
544+
.unwrap()
545+
.rsa()
546+
.unwrap();
547+
}
548+
549+
#[test]
550+
fn test_rsa_pem_pkcs1() {
551+
Decoder::<Public>::new()
552+
.set_key_type(Id::RSA)
553+
.set_format(KeyFormat::Pem)
554+
.set_structure(Structure::PKCS1)
555+
.decode(include_bytes!("../test/pkcs1.pem.pub"))
556+
.unwrap()
557+
.rsa()
558+
.unwrap();
559+
}
560+
561+
#[test]
562+
fn test_rsa_der() {
563+
Decoder::<Public>::new()
564+
.set_key_type(Id::RSA)
565+
.set_format(KeyFormat::Der)
566+
.set_structure(Structure::SubjectPublicKeyInfo)
567+
.decode(include_bytes!("../test/key.der.pub"))
568+
.unwrap()
569+
.rsa()
570+
.unwrap();
571+
}
572+
573+
#[test]
574+
fn test_rsa_der_pkcs1() {
575+
Decoder::<Public>::new()
576+
.set_key_type(Id::RSA)
577+
.set_format(KeyFormat::Der)
578+
.set_structure(Structure::PKCS1)
579+
.decode(include_bytes!("../test/pkcs1.der.pub"))
580+
.unwrap()
581+
.rsa()
582+
.unwrap();
583+
}
584+
}
585+
mod private {
586+
use super::*;
587+
use crate::pkey::Private;
588+
589+
#[test]
590+
fn test_rsa_pem() {
591+
Decoder::<Private>::new()
592+
.set_key_type(Id::RSA)
593+
.set_format(KeyFormat::Pem)
594+
.set_structure(Structure::PKCS1)
595+
.decode(include_bytes!("../test/rsa.pem"))
596+
.unwrap()
597+
.rsa()
598+
.unwrap();
599+
}
600+
601+
#[test]
602+
fn test_rsa_pem_passphrase() {
603+
Decoder::<Private>::new()
604+
.set_key_type(Id::RSA)
605+
.set_format(KeyFormat::Pem)
606+
.set_structure(Structure::PKCS1)
607+
.set_passphrase(b"mypass")
608+
.decode(include_bytes!("../test/rsa-encrypted.pem"))
609+
.unwrap()
610+
.rsa()
611+
.unwrap();
612+
}
613+
614+
#[test]
615+
fn test_rsa_pem_callback() {
616+
let mut password_queried = false;
617+
Decoder::<Private>::new()
618+
.set_key_type(Id::RSA)
619+
.set_format(KeyFormat::Pem)
620+
.set_structure(Structure::PKCS1)
621+
.set_passphrase_callback(|password| {
622+
password_queried = true;
623+
password[..6].copy_from_slice(b"mypass");
624+
Ok(6)
625+
})
626+
.decode(include_bytes!("../test/rsa-encrypted.pem"))
627+
.unwrap();
628+
assert!(password_queried);
629+
}
630+
631+
#[test]
632+
fn test_rsa_der() {
633+
Decoder::<Private>::new()
634+
.set_key_type(Id::RSA)
635+
.set_format(KeyFormat::Der)
636+
.set_structure(Structure::PKCS1)
637+
.decode(include_bytes!("../test/key.der"))
638+
.unwrap()
639+
.rsa()
640+
.unwrap();
641+
}
642+
}
643+
}
644+
324645
mod encoder {
325646
use super::*;
326647

327648
mod params {
328649
use super::*;
329650
use crate::dh::Dh;
330-
use crate::pkey::Id;
331651
use crate::pkey::Params;
332652
use crate::pkey_ctx::PkeyCtx;
333653

0 commit comments

Comments
 (0)