Skip to content

Commit 6da14ca

Browse files
committed
Add decoder wrapper
1 parent fe0eb9c commit 6da14ca

File tree

1 file changed

+348
-3
lines changed

1 file changed

+348
-3
lines changed

openssl/src/ossl_encdec.rs

Lines changed: 348 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -1,12 +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::{invoke_passwd_cb, CallbackState};
67
use crate::{cvt, cvt_p};
78
use foreign_types::{ForeignType, ForeignTypeRef};
89
use openssl_macros::corresponds;
910
use std::ffi::{CStr, CString};
11+
use std::marker::PhantomData;
1012
use std::ptr;
1113

1214
#[derive(Debug, Copy, Clone, PartialEq, Eq)]
@@ -95,6 +97,206 @@ impl<'a> From<Structure<'a>> for &'a CStr {
9597
}
9698
}
9799

100+
foreign_type_and_impl_send_sync! {
101+
type CType = ffi::OSSL_DECODER_CTX;
102+
fn drop = ffi::OSSL_DECODER_CTX_free;
103+
104+
/// A context object which can perform decode operations.
105+
pub struct OsslDecoderCtx;
106+
/// A reference to an `OsslDecoderCtx`.
107+
pub struct OsslDecoderCtxRef;
108+
}
109+
110+
impl OsslDecoderCtx {
111+
#[corresponds(OSSL_DECODER_CTX_new_for_pkey)]
112+
#[inline]
113+
#[allow(dead_code)]
114+
fn new_for_key(
115+
pkey: *mut *mut ffi::EVP_PKEY,
116+
selection: Selection,
117+
input: Option<KeyFormat>,
118+
structure: Option<Structure<'_>>,
119+
key_type: Option<Id>,
120+
) -> Result<Self, ErrorStack> {
121+
let input_ptr = input
122+
.map(|i| {
123+
let input: &CStr = i.into();
124+
input.as_ptr()
125+
})
126+
.unwrap_or_else(ptr::null);
127+
let structure_ptr = structure
128+
.map(|s| {
129+
let structure: &CStr = s.into();
130+
structure.as_ptr()
131+
})
132+
.unwrap_or_else(ptr::null);
133+
let key_type_ptr = key_type
134+
.and_then(|k| k.try_into().ok())
135+
.map(|k: &CStr| k.as_ptr())
136+
.unwrap_or_else(ptr::null);
137+
unsafe {
138+
let ptr = cvt_p(ffi::OSSL_DECODER_CTX_new_for_pkey(
139+
pkey,
140+
input_ptr,
141+
structure_ptr,
142+
key_type_ptr,
143+
selection.into(),
144+
ptr::null_mut(),
145+
ptr::null(),
146+
))?;
147+
Ok(Self::from_ptr(ptr))
148+
}
149+
}
150+
}
151+
152+
impl OsslDecoderCtxRef {
153+
/// Select which parts of the key to decode.
154+
#[corresponds(OSSL_DECODER_CTX_set_selection)]
155+
#[allow(dead_code)]
156+
fn set_selection(&mut self, selection: Selection) -> Result<(), ErrorStack> {
157+
cvt(unsafe { ffi::OSSL_DECODER_CTX_set_selection(self.as_ptr(), selection.into()) })
158+
.map(|_| ())
159+
}
160+
161+
/// Set the input type for the encoded data.
162+
#[corresponds(OSSL_DECODER_CTX_set_input_type)]
163+
#[allow(dead_code)]
164+
fn set_input_type(&mut self, input: KeyFormat) -> Result<(), ErrorStack> {
165+
let input: &CStr = input.into();
166+
cvt(unsafe { ffi::OSSL_DECODER_CTX_set_input_type(self.as_ptr(), input.as_ptr()) })
167+
.map(|_| ())
168+
}
169+
170+
/// Set the input structure for the encoded data.
171+
#[corresponds(OSSL_DECODER_CTX_set_input_structure)]
172+
#[allow(dead_code)]
173+
fn set_input_structure(&mut self, structure: Structure<'_>) -> Result<(), ErrorStack> {
174+
let structure: &CStr = structure.into();
175+
cvt(unsafe { ffi::OSSL_DECODER_CTX_set_input_structure(self.as_ptr(), structure.as_ptr()) })
176+
.map(|_| ())
177+
}
178+
179+
/// Set the passphrase to decrypt the encoded data.
180+
#[corresponds(OSSL_DECODER_CTX_set_passphrase)]
181+
#[allow(dead_code)]
182+
fn set_passphrase(&mut self, passphrase: &[u8]) -> Result<(), ErrorStack> {
183+
cvt(unsafe {
184+
ffi::OSSL_DECODER_CTX_set_passphrase(
185+
self.as_ptr(),
186+
passphrase.as_ptr().cast(),
187+
passphrase.len(),
188+
)
189+
})
190+
.map(|_| ())
191+
}
192+
193+
/// Set the passphrase to decrypt the encoded data.
194+
#[corresponds(OSSL_DECODER_CTX_set_passphrase)]
195+
#[allow(dead_code)]
196+
unsafe fn set_passphrase_callback<F: FnOnce(&mut [u8]) -> Result<usize, ErrorStack>>(
197+
&mut self,
198+
callback: *mut CallbackState<F>,
199+
) -> Result<(), ErrorStack> {
200+
cvt(unsafe {
201+
ffi::OSSL_DECODER_CTX_set_pem_password_cb(
202+
self.as_ptr(),
203+
Some(invoke_passwd_cb::<F>),
204+
callback as *mut _,
205+
)
206+
})
207+
.map(|_| ())
208+
}
209+
210+
/// Decode the encoded data
211+
#[corresponds(OSSL_DECODER_from_bio)]
212+
#[allow(dead_code)]
213+
fn decode(&mut self, data: &[u8]) -> Result<(), ErrorStack> {
214+
let bio = MemBioSlice::new(data)?;
215+
216+
cvt(unsafe { ffi::OSSL_DECODER_from_bio(self.as_ptr(), bio.as_ptr()) }).map(|_| ())
217+
}
218+
}
219+
220+
#[allow(dead_code)]
221+
pub(crate) struct Decoder<'a, T: SelectionT> {
222+
selection: PhantomData<T>,
223+
key_type: Option<Id>,
224+
format: Option<KeyFormat>,
225+
structure: Option<Structure<'a>>,
226+
passphrase: Option<&'a [u8]>,
227+
#[allow(clippy::type_complexity)]
228+
passphrase_callback: Option<Box<dyn FnOnce(&mut [u8]) -> Result<usize, ErrorStack> + 'a>>,
229+
}
230+
231+
impl<'a, T: SelectionT> Decoder<'a, T> {
232+
#[allow(dead_code)]
233+
pub(crate) fn new() -> Self {
234+
Self {
235+
selection: PhantomData,
236+
key_type: None,
237+
format: None,
238+
structure: None,
239+
passphrase: None,
240+
passphrase_callback: None,
241+
}
242+
}
243+
244+
#[allow(dead_code)]
245+
pub fn set_key_type(mut self, key_type: Id) -> Self {
246+
self.key_type = Some(key_type);
247+
self
248+
}
249+
250+
#[allow(dead_code)]
251+
pub fn set_format(mut self, format: KeyFormat) -> Self {
252+
self.format = Some(format);
253+
self
254+
}
255+
256+
#[allow(dead_code)]
257+
pub fn set_structure(mut self, structure: Structure<'a>) -> Self {
258+
self.structure = Some(structure);
259+
self
260+
}
261+
262+
#[allow(dead_code)]
263+
pub fn set_passphrase(mut self, passphrase: &'a [u8]) -> Self {
264+
self.passphrase = Some(passphrase);
265+
self
266+
}
267+
268+
#[allow(dead_code)]
269+
pub fn set_passphrase_callback<F: FnOnce(&mut [u8]) -> Result<usize, ErrorStack> + 'a>(
270+
mut self,
271+
callback: F,
272+
) -> Self {
273+
self.passphrase_callback = Some(Box::new(callback));
274+
self
275+
}
276+
277+
#[allow(dead_code)]
278+
pub fn decode(self, data: &[u8]) -> Result<PKey<T>, ErrorStack> {
279+
let mut pkey_ptr = ptr::null_mut();
280+
let mut passphrase_callback_state;
281+
let mut ctx = OsslDecoderCtx::new_for_key(
282+
&mut pkey_ptr,
283+
T::SELECTION,
284+
self.format,
285+
self.structure,
286+
self.key_type,
287+
)?;
288+
if let Some(passphrase) = self.passphrase {
289+
ctx.set_passphrase(passphrase)?;
290+
}
291+
if let Some(passphrase_callback) = self.passphrase_callback {
292+
passphrase_callback_state = CallbackState::new(passphrase_callback);
293+
unsafe { ctx.set_passphrase_callback(&mut passphrase_callback_state)? };
294+
}
295+
ctx.decode(data)?;
296+
Ok(unsafe { PKey::from_ptr(pkey_ptr) })
297+
}
298+
}
299+
98300
foreign_type_and_impl_send_sync! {
99301
type CType = ffi::OSSL_ENCODER_CTX;
100302
fn drop = ffi::OSSL_ENCODER_CTX_free;
@@ -308,6 +510,149 @@ mod test {
308510
}
309511
}
310512

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

0 commit comments

Comments
 (0)