1use crate::error::ErrorStack;
12use native_ossl_sys as sys;
13use std::ffi::CStr;
14
15pub struct KdfAlg {
21 ptr: *mut sys::EVP_KDF,
22}
23
24impl KdfAlg {
25 pub fn fetch(name: &CStr) -> Result<Self, ErrorStack> {
33 let ptr = unsafe {
34 sys::EVP_KDF_fetch(std::ptr::null_mut(), name.as_ptr(), std::ptr::null())
35 };
36 if ptr.is_null() {
37 return Err(ErrorStack::drain());
38 }
39 Ok(KdfAlg { ptr })
40 }
41
42 fn as_ptr(&self) -> *mut sys::EVP_KDF {
43 self.ptr
44 }
45}
46
47impl Drop for KdfAlg {
48 fn drop(&mut self) {
49 unsafe { sys::EVP_KDF_free(self.ptr) };
50 }
51}
52
53unsafe impl Send for KdfAlg {}
55unsafe impl Sync for KdfAlg {}
56
57pub struct KdfCtx {
63 ptr: *mut sys::EVP_KDF_CTX,
64}
65
66impl KdfCtx {
67 pub fn new(alg: &KdfAlg) -> Result<Self, ErrorStack> {
71 let ptr = unsafe { sys::EVP_KDF_CTX_new(alg.as_ptr()) };
72 if ptr.is_null() {
73 return Err(ErrorStack::drain());
74 }
75 Ok(KdfCtx { ptr })
76 }
77
78 pub fn derive(
84 &mut self,
85 out: &mut [u8],
86 params: &crate::params::Params<'_>,
87 ) -> Result<(), ErrorStack> {
88 crate::ossl_call!(sys::EVP_KDF_derive(
89 self.ptr,
90 out.as_mut_ptr(),
91 out.len(),
92 params.as_ptr()
93 ))
94 }
95}
96
97impl Drop for KdfCtx {
98 fn drop(&mut self) {
99 unsafe { sys::EVP_KDF_CTX_free(self.ptr) };
100 }
101}
102
103unsafe impl Send for KdfCtx {}
104
105#[derive(Default, Clone, Copy, PartialEq, Eq)]
109pub enum HkdfMode {
110 #[default]
114 ExtractAndExpand,
115 ExtractOnly,
117 ExpandOnly,
119}
120
121impl HkdfMode {
122 fn as_uint(self) -> u32 {
123 match self {
124 HkdfMode::ExtractAndExpand => 0,
125 HkdfMode::ExtractOnly => 1,
126 HkdfMode::ExpandOnly => 2,
127 }
128 }
129}
130
131pub struct HkdfBuilder<'a> {
143 digest: &'a crate::digest::DigestAlg,
144 key: Option<&'a [u8]>,
145 salt: Option<&'a [u8]>,
146 info: Option<&'a [u8]>,
147 mode: HkdfMode,
148}
149
150impl<'a> HkdfBuilder<'a> {
151 #[must_use]
155 pub fn new(digest: &'a crate::digest::DigestAlg) -> Self {
156 HkdfBuilder {
157 digest,
158 key: None,
159 salt: None,
160 info: None,
161 mode: HkdfMode::default(),
162 }
163 }
164
165 #[must_use]
167 pub fn key(mut self, key: &'a [u8]) -> Self {
168 self.key = Some(key);
169 self
170 }
171
172 #[must_use]
174 pub fn salt(mut self, salt: &'a [u8]) -> Self {
175 self.salt = Some(salt);
176 self
177 }
178
179 #[must_use]
181 pub fn info(mut self, info: &'a [u8]) -> Self {
182 self.info = Some(info);
183 self
184 }
185
186 #[must_use]
188 pub fn mode(mut self, mode: HkdfMode) -> Self {
189 self.mode = mode;
190 self
191 }
192
193 pub fn derive(self, out: &mut [u8]) -> Result<(), ErrorStack> {
197 let name_ptr = unsafe { sys::OBJ_nid2sn(self.digest.nid()) };
198 if name_ptr.is_null() {
199 return Err(ErrorStack::drain());
200 }
201 let name = unsafe { CStr::from_ptr(name_ptr) };
202
203 let mut builder = crate::params::ParamBuilder::new()?
204 .push_utf8_string(c"digest", name)?
205 .push_uint(c"mode", self.mode.as_uint())?;
206
207 if let Some(k) = self.key {
208 builder = builder.push_octet_slice(c"key", k)?;
209 }
210 if let Some(s) = self.salt {
211 builder = builder.push_octet_slice(c"salt", s)?;
212 }
213 if let Some(i) = self.info {
214 builder = builder.push_octet_slice(c"info", i)?;
215 }
216
217 let params = builder.build()?;
218 let alg = KdfAlg::fetch(c"HKDF")?;
219 KdfCtx::new(&alg)?.derive(out, ¶ms)
220 }
221
222 pub fn derive_to_vec(self, len: usize) -> Result<Vec<u8>, ErrorStack> {
226 let mut out = vec![0u8; len];
227 self.derive(&mut out)?;
228 Ok(out)
229 }
230}
231
232pub struct Pbkdf2Builder<'a> {
242 digest: &'a crate::digest::DigestAlg,
243 password: &'a [u8],
244 salt: &'a [u8],
245 iterations: u32,
246}
247
248impl<'a> Pbkdf2Builder<'a> {
249 #[must_use]
254 pub fn new(
255 digest: &'a crate::digest::DigestAlg,
256 password: &'a [u8],
257 salt: &'a [u8],
258 ) -> Self {
259 Pbkdf2Builder {
260 digest,
261 password,
262 salt,
263 iterations: 600_000,
264 }
265 }
266
267 #[must_use]
269 pub fn iterations(mut self, n: u32) -> Self {
270 self.iterations = n;
271 self
272 }
273
274 pub fn derive(self, out: &mut [u8]) -> Result<(), ErrorStack> {
278 let name_ptr = unsafe { sys::OBJ_nid2sn(self.digest.nid()) };
279 if name_ptr.is_null() {
280 return Err(ErrorStack::drain());
281 }
282 let name = unsafe { CStr::from_ptr(name_ptr) };
283
284 let params = crate::params::ParamBuilder::new()?
285 .push_octet_slice(c"pass", self.password)?
286 .push_octet_slice(c"salt", self.salt)?
287 .push_uint(c"iter", self.iterations)?
288 .push_utf8_string(c"digest", name)?
289 .build()?;
290
291 let alg = KdfAlg::fetch(c"PBKDF2")?;
292 KdfCtx::new(&alg)?.derive(out, ¶ms)
293 }
294
295 pub fn derive_to_vec(self, len: usize) -> Result<Vec<u8>, ErrorStack> {
299 let mut out = vec![0u8; len];
300 self.derive(&mut out)?;
301 Ok(out)
302 }
303}
304
305pub struct ScryptParams {
314 pub n: u64,
316 pub r: u32,
318 pub p: u32,
320}
321
322impl Default for ScryptParams {
323 fn default() -> Self {
324 ScryptParams { n: 16_384, r: 8, p: 1 }
326 }
327}
328
329pub struct ScryptBuilder<'a> {
337 password: &'a [u8],
338 salt: &'a [u8],
339 params: ScryptParams,
340}
341
342impl<'a> ScryptBuilder<'a> {
343 #[must_use]
347 pub fn new(password: &'a [u8], salt: &'a [u8]) -> Self {
348 ScryptBuilder {
349 password,
350 salt,
351 params: ScryptParams::default(),
352 }
353 }
354
355 #[must_use]
357 pub fn params(mut self, params: ScryptParams) -> Self {
358 self.params = params;
359 self
360 }
361
362 pub fn derive(self, out: &mut [u8]) -> Result<(), ErrorStack> {
366 let params = crate::params::ParamBuilder::new()?
367 .push_octet_slice(c"pass", self.password)?
368 .push_octet_slice(c"salt", self.salt)?
369 .push_uint64(c"n", self.params.n)?
370 .push_uint(c"r", self.params.r)?
371 .push_uint(c"p", self.params.p)?
372 .build()?;
373
374 let alg = KdfAlg::fetch(c"SCRYPT")?;
375 KdfCtx::new(&alg)?.derive(out, ¶ms)
376 }
377
378 pub fn derive_to_vec(self, len: usize) -> Result<Vec<u8>, ErrorStack> {
382 let mut out = vec![0u8; len];
383 self.derive(&mut out)?;
384 Ok(out)
385 }
386}
387
388#[cfg(ossl350)]
392#[derive(Clone, Copy, Debug, PartialEq, Eq)]
393pub enum SshkdfKeyType {
394 InitialIvClientToServer,
396 InitialIvServerToClient,
398 EncryptionKeyClientToServer,
400 EncryptionKeyServerToClient,
402 IntegrityKeyClientToServer,
404 IntegrityKeyServerToClient,
406}
407
408#[cfg(ossl350)]
409impl SshkdfKeyType {
410 fn as_cstr(self) -> &'static CStr {
411 match self {
412 Self::InitialIvClientToServer => c"A",
413 Self::InitialIvServerToClient => c"B",
414 Self::EncryptionKeyClientToServer => c"C",
415 Self::EncryptionKeyServerToClient => c"D",
416 Self::IntegrityKeyClientToServer => c"E",
417 Self::IntegrityKeyServerToClient => c"F",
418 }
419 }
420}
421
422#[cfg(ossl350)]
430pub struct SshkdfBuilder<'a> {
431 digest: &'a crate::digest::DigestAlg,
432 key: &'a [u8],
433 xcghash: &'a [u8],
434 session_id: &'a [u8],
435 key_type: SshkdfKeyType,
436}
437
438#[cfg(ossl350)]
439impl<'a> SshkdfBuilder<'a> {
440 #[must_use]
448 pub fn new(
449 digest: &'a crate::digest::DigestAlg,
450 key: &'a [u8],
451 xcghash: &'a [u8],
452 session_id: &'a [u8],
453 key_type: SshkdfKeyType,
454 ) -> Self {
455 SshkdfBuilder { digest, key, xcghash, session_id, key_type }
456 }
457
458 pub fn derive(self, out: &mut [u8]) -> Result<(), ErrorStack> {
462 let name_ptr = unsafe { sys::OBJ_nid2sn(self.digest.nid()) };
463 if name_ptr.is_null() {
464 return Err(ErrorStack::drain());
465 }
466 let name = unsafe { CStr::from_ptr(name_ptr) };
467
468 let params = crate::params::ParamBuilder::new()?
469 .push_utf8_string(c"digest", name)?
470 .push_octet_slice(c"key", self.key)?
471 .push_octet_slice(c"xcghash", self.xcghash)?
472 .push_octet_slice(c"session-id", self.session_id)?
473 .push_utf8_string(c"type", self.key_type.as_cstr())?
474 .build()?;
475
476 let alg = KdfAlg::fetch(c"SSHKDF")?;
477 KdfCtx::new(&alg)?.derive(out, ¶ms)
478 }
479
480 pub fn derive_to_vec(self, len: usize) -> Result<Vec<u8>, ErrorStack> {
484 let mut out = vec![0u8; len];
485 self.derive(&mut out)?;
486 Ok(out)
487 }
488}
489
490#[cfg(ossl350)]
494#[derive(Clone, Copy, Debug, PartialEq, Eq)]
495pub enum KbkdfMode {
496 Counter,
498 Feedback,
500}
501
502#[cfg(ossl350)]
503impl KbkdfMode {
504 fn as_cstr(self) -> &'static CStr {
505 match self {
506 KbkdfMode::Counter => c"counter",
507 KbkdfMode::Feedback => c"feedback",
508 }
509 }
510}
511
512#[cfg(ossl350)]
514#[derive(Clone, Copy, Debug, PartialEq, Eq)]
515pub enum KbkdfCounterLen {
516 Bits8 = 8,
518 Bits16 = 16,
520 Bits24 = 24,
522 Bits32 = 32,
524}
525
526#[cfg(ossl350)]
527impl Default for KbkdfCounterLen {
528 fn default() -> Self { KbkdfCounterLen::Bits32 }
529}
530
531#[cfg(ossl350)]
543pub struct KbkdfBuilder<'a> {
544 mode: KbkdfMode,
545 mac: &'a crate::mac::MacAlg,
546 digest: Option<&'a crate::digest::DigestAlg>,
547 key: &'a [u8],
548 label: Option<&'a [u8]>,
549 context: Option<&'a [u8]>,
550 salt: Option<&'a [u8]>,
552 counter_len: KbkdfCounterLen,
553 use_l: Option<bool>,
554 use_separator: Option<bool>,
555}
556
557#[cfg(ossl350)]
558impl<'a> KbkdfBuilder<'a> {
559 #[must_use]
565 pub fn new(mode: KbkdfMode, mac: &'a crate::mac::MacAlg, key: &'a [u8]) -> Self {
566 KbkdfBuilder {
567 mode,
568 mac,
569 digest: None,
570 key,
571 label: None,
572 context: None,
573 salt: None,
574 counter_len: KbkdfCounterLen::default(),
575 use_l: None,
576 use_separator: None,
577 }
578 }
579
580 #[must_use]
582 pub fn digest(mut self, digest: &'a crate::digest::DigestAlg) -> Self {
583 self.digest = Some(digest);
584 self
585 }
586
587 #[must_use]
589 pub fn label(mut self, label: &'a [u8]) -> Self {
590 self.label = Some(label);
591 self
592 }
593
594 #[must_use]
596 pub fn context(mut self, context: &'a [u8]) -> Self {
597 self.context = Some(context);
598 self
599 }
600
601 #[must_use]
603 pub fn salt(mut self, salt: &'a [u8]) -> Self {
604 self.salt = Some(salt);
605 self
606 }
607
608 #[must_use]
610 pub fn counter_len(mut self, len: KbkdfCounterLen) -> Self {
611 self.counter_len = len;
612 self
613 }
614
615 #[must_use]
617 pub fn use_l(mut self, enabled: bool) -> Self {
618 self.use_l = Some(enabled);
619 self
620 }
621
622 #[must_use]
624 pub fn use_separator(mut self, enabled: bool) -> Self {
625 self.use_separator = Some(enabled);
626 self
627 }
628
629 pub fn derive(self, out: &mut [u8]) -> Result<(), ErrorStack> {
633 let mut builder = crate::params::ParamBuilder::new()?
634 .push_utf8_string(c"mode", self.mode.as_cstr())?
635 .push_utf8_string(c"mac", self.mac.name())?
636 .push_octet_slice(c"key", self.key)?
637 .push_uint(c"r", self.counter_len as u32)?;
638
639 if let Some(d) = self.digest {
640 let name_ptr = unsafe { sys::OBJ_nid2sn(d.nid()) };
641 if name_ptr.is_null() {
642 return Err(ErrorStack::drain());
643 }
644 let name = unsafe { CStr::from_ptr(name_ptr) };
645 builder = builder.push_utf8_string(c"digest", name)?;
646 }
647 if let Some(l) = self.label {
648 builder = builder.push_octet_slice(c"label", l)?;
649 }
650 if let Some(c) = self.context {
651 builder = builder.push_octet_slice(c"data", c)?;
652 }
653 if let Some(s) = self.salt {
654 builder = builder.push_octet_slice(c"salt", s)?;
655 }
656 if let Some(v) = self.use_l {
657 builder = builder.push_int(c"use-l", i32::from(v))?;
658 }
659 if let Some(v) = self.use_separator {
660 builder = builder.push_int(c"use-separator", i32::from(v))?;
661 }
662
663 let params = builder.build()?;
664 let alg = KdfAlg::fetch(c"KBKDF")?;
665 KdfCtx::new(&alg)?.derive(out, ¶ms)
666 }
667
668 pub fn derive_to_vec(self, len: usize) -> Result<Vec<u8>, ErrorStack> {
672 let mut out = vec![0u8; len];
673 self.derive(&mut out)?;
674 Ok(out)
675 }
676}
677
678#[cfg(test)]
681mod tests {
682 use super::*;
683 use crate::digest::DigestAlg;
684
685 #[test]
692 fn hkdf_sha256_rfc5869_tc1() {
693 let digest = DigestAlg::fetch(c"SHA2-256", None).unwrap();
694
695 let ikm = [0x0b_u8; 22];
696 let salt = [0x00, 0x01, 0x02, 0x03, 0x04, 0x05, 0x06, 0x07,
697 0x08, 0x09, 0x0a, 0x0b, 0x0c_u8];
698 let info = [0xf0, 0xf1, 0xf2, 0xf3, 0xf4, 0xf5, 0xf6, 0xf7,
699 0xf8, 0xf9_u8];
700
701 let okm = HkdfBuilder::new(&digest)
702 .key(&ikm)
703 .salt(&salt)
704 .info(&info)
705 .derive_to_vec(42)
706 .unwrap();
707
708 assert_eq!(
709 hex::encode(&okm),
710 "3cb25f25faacd57a90434f64d0362f2a\
711 2d2d0a90cf1a5a4c5db02d56ecc4c5bf\
712 34007208d5b887185865"
713 );
714 }
715
716 #[test]
723 fn hkdf_sha256_no_salt_no_info() {
724 let digest = DigestAlg::fetch(c"SHA2-256", None).unwrap();
725 let ikm = [0x0b_u8; 22];
726
727 let okm = HkdfBuilder::new(&digest)
728 .key(&ikm)
729 .derive_to_vec(42)
730 .unwrap();
731
732 assert_eq!(
733 hex::encode(&okm),
734 "8da4e775a563c18f715f802a063c5a31\
735 b8a11f5c5ee1879ec3454e5f3c738d2d\
736 9d201395faa4b61a96c8"
737 );
738 }
739
740 #[test]
747 fn pbkdf2_sha256_known_answer() {
748 let digest = DigestAlg::fetch(c"SHA2-256", None).unwrap();
749
750 let dk = Pbkdf2Builder::new(&digest, b"password", b"salt")
751 .iterations(1)
752 .derive_to_vec(32)
753 .unwrap();
754
755 assert_eq!(
756 hex::encode(&dk),
757 "120fb6cffcf8b32c43e7225256c4f837\
758 a86548c92ccc35480805987cb70be17b"
759 );
760 }
761
762 #[test]
766 fn scrypt_derives_nonzero_output() {
767 let dk = ScryptBuilder::new(b"password", b"salt")
768 .params(ScryptParams { n: 32, r: 1, p: 1 })
769 .derive_to_vec(32)
770 .unwrap();
771
772 assert_eq!(dk.len(), 32);
773 assert_ne!(dk, vec![0u8; 32]);
774 }
775
776 #[test]
778 fn scrypt_different_passwords_differ() {
779 let p = ScryptParams { n: 32, r: 1, p: 1 };
780
781 let dk1 = ScryptBuilder::new(b"pass1", b"salt")
782 .params(ScryptParams { n: p.n, r: p.r, p: p.p })
783 .derive_to_vec(32)
784 .unwrap();
785 let dk2 = ScryptBuilder::new(b"pass2", b"salt")
786 .params(ScryptParams { n: p.n, r: p.r, p: p.p })
787 .derive_to_vec(32)
788 .unwrap();
789
790 assert_ne!(dk1, dk2);
791 }
792}