Skip to content

Commit f18f0e5

Browse files
committed
Add decoder wrapper
1 parent f56027f commit f18f0e5

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::error::ErrorStack;
3-
use crate::pkey::PKeyRef;
4-
use crate::pkey_ctx::Selection;
3+
use crate::pkey::{Id, PKey, PKeyRef};
4+
use crate::pkey_ctx::{Selection, SelectionT};
55
use crate::symm::Cipher;
6-
use crate::util::c_str;
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;
@@ -320,13 +498,155 @@ mod test {
320498
}
321499
}
322500

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

326647
mod params {
327648
use super::*;
328649
use crate::dh::Dh;
329-
use crate::pkey::Id;
330650
use crate::pkey::Params;
331651
use crate::pkey_ctx::PkeyCtx;
332652

0 commit comments

Comments
 (0)