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 =
34 unsafe { sys::EVP_KDF_fetch(std::ptr::null_mut(), name.as_ptr(), std::ptr::null()) };
35 if ptr.is_null() {
36 return Err(ErrorStack::drain());
37 }
38 Ok(KdfAlg { ptr })
39 }
40
41 fn as_ptr(&self) -> *mut sys::EVP_KDF {
42 self.ptr
43 }
44}
45
46impl Drop for KdfAlg {
47 fn drop(&mut self) {
48 unsafe { sys::EVP_KDF_free(self.ptr) };
49 }
50}
51
52unsafe impl Send for KdfAlg {}
54unsafe impl Sync for KdfAlg {}
55
56pub struct KdfCtx {
62 ptr: *mut sys::EVP_KDF_CTX,
63}
64
65impl KdfCtx {
66 pub fn new(alg: &KdfAlg) -> Result<Self, ErrorStack> {
70 let ptr = unsafe { sys::EVP_KDF_CTX_new(alg.as_ptr()) };
71 if ptr.is_null() {
72 return Err(ErrorStack::drain());
73 }
74 Ok(KdfCtx { ptr })
75 }
76
77 pub fn derive(
83 &mut self,
84 out: &mut [u8],
85 params: &crate::params::Params<'_>,
86 ) -> Result<(), ErrorStack> {
87 crate::ossl_call!(sys::EVP_KDF_derive(
88 self.ptr,
89 out.as_mut_ptr(),
90 out.len(),
91 params.as_ptr()
92 ))
93 }
94
95 pub fn set_params(&mut self, params: &crate::params::Params<'_>) -> Result<(), ErrorStack> {
105 crate::ossl_call!(sys::EVP_KDF_CTX_set_params(self.ptr, params.as_ptr()))
110 }
111
112 pub fn get_params(&self, params: &mut crate::params::Params<'_>) -> Result<(), ErrorStack> {
121 crate::ossl_call!(sys::EVP_KDF_CTX_get_params(self.ptr, params.as_mut_ptr()))
126 }
127
128 #[must_use]
130 pub fn size(&self) -> usize {
131 unsafe { sys::EVP_KDF_CTX_get_kdf_size(self.ptr) }
133 }
134}
135
136impl Drop for KdfCtx {
137 fn drop(&mut self) {
138 unsafe { sys::EVP_KDF_CTX_free(self.ptr) };
139 }
140}
141
142unsafe impl Send for KdfCtx {}
143
144#[derive(Default, Clone, Copy, PartialEq, Eq)]
148pub enum HkdfMode {
149 #[default]
153 ExtractAndExpand,
154 ExtractOnly,
156 ExpandOnly,
158}
159
160impl HkdfMode {
161 fn as_uint(self) -> u32 {
162 match self {
163 HkdfMode::ExtractAndExpand => 0,
164 HkdfMode::ExtractOnly => 1,
165 HkdfMode::ExpandOnly => 2,
166 }
167 }
168}
169
170pub struct HkdfBuilder<'a> {
182 digest: &'a crate::digest::DigestAlg,
183 key: Option<&'a [u8]>,
184 salt: Option<&'a [u8]>,
185 info: Option<&'a [u8]>,
186 mode: HkdfMode,
187}
188
189impl<'a> HkdfBuilder<'a> {
190 #[must_use]
194 pub fn new(digest: &'a crate::digest::DigestAlg) -> Self {
195 HkdfBuilder {
196 digest,
197 key: None,
198 salt: None,
199 info: None,
200 mode: HkdfMode::default(),
201 }
202 }
203
204 #[must_use]
206 pub fn key(mut self, key: &'a [u8]) -> Self {
207 self.key = Some(key);
208 self
209 }
210
211 #[must_use]
213 pub fn salt(mut self, salt: &'a [u8]) -> Self {
214 self.salt = Some(salt);
215 self
216 }
217
218 #[must_use]
220 pub fn info(mut self, info: &'a [u8]) -> Self {
221 self.info = Some(info);
222 self
223 }
224
225 #[must_use]
227 pub fn mode(mut self, mode: HkdfMode) -> Self {
228 self.mode = mode;
229 self
230 }
231
232 pub fn derive(self, out: &mut [u8]) -> Result<(), ErrorStack> {
236 let name_ptr = unsafe { sys::OBJ_nid2sn(self.digest.nid()) };
237 if name_ptr.is_null() {
238 return Err(ErrorStack::drain());
239 }
240 let name = unsafe { CStr::from_ptr(name_ptr) };
241
242 let mut builder = crate::params::ParamBuilder::new()?
243 .push_utf8_string(c"digest", name)?
244 .push_uint(c"mode", self.mode.as_uint())?;
245
246 if let Some(k) = self.key {
247 builder = builder.push_octet_slice(c"key", k)?;
248 }
249 if let Some(s) = self.salt {
250 builder = builder.push_octet_slice(c"salt", s)?;
251 }
252 if let Some(i) = self.info {
253 builder = builder.push_octet_slice(c"info", i)?;
254 }
255
256 let params = builder.build()?;
257 let alg = KdfAlg::fetch(c"HKDF")?;
258 KdfCtx::new(&alg)?.derive(out, ¶ms)
259 }
260
261 pub fn derive_to_vec(self, len: usize) -> Result<Vec<u8>, ErrorStack> {
265 let mut out = vec![0u8; len];
266 self.derive(&mut out)?;
267 Ok(out)
268 }
269}
270
271pub struct Pbkdf2Builder<'a> {
281 digest: &'a crate::digest::DigestAlg,
282 password: &'a [u8],
283 salt: &'a [u8],
284 iterations: u32,
285}
286
287impl<'a> Pbkdf2Builder<'a> {
288 #[must_use]
293 pub fn new(digest: &'a crate::digest::DigestAlg, password: &'a [u8], salt: &'a [u8]) -> Self {
294 Pbkdf2Builder {
295 digest,
296 password,
297 salt,
298 iterations: 600_000,
299 }
300 }
301
302 #[must_use]
304 pub fn iterations(mut self, n: u32) -> Self {
305 self.iterations = n;
306 self
307 }
308
309 pub fn derive(self, out: &mut [u8]) -> Result<(), ErrorStack> {
313 let name_ptr = unsafe { sys::OBJ_nid2sn(self.digest.nid()) };
314 if name_ptr.is_null() {
315 return Err(ErrorStack::drain());
316 }
317 let name = unsafe { CStr::from_ptr(name_ptr) };
318
319 let params = crate::params::ParamBuilder::new()?
320 .push_octet_slice(c"pass", self.password)?
321 .push_octet_slice(c"salt", self.salt)?
322 .push_uint(c"iter", self.iterations)?
323 .push_utf8_string(c"digest", name)?
324 .build()?;
325
326 let alg = KdfAlg::fetch(c"PBKDF2")?;
327 KdfCtx::new(&alg)?.derive(out, ¶ms)
328 }
329
330 pub fn derive_to_vec(self, len: usize) -> Result<Vec<u8>, ErrorStack> {
334 let mut out = vec![0u8; len];
335 self.derive(&mut out)?;
336 Ok(out)
337 }
338}
339
340pub struct ScryptParams {
349 pub n: u64,
351 pub r: u32,
353 pub p: u32,
355}
356
357impl Default for ScryptParams {
358 fn default() -> Self {
359 ScryptParams {
361 n: 16_384,
362 r: 8,
363 p: 1,
364 }
365 }
366}
367
368pub struct ScryptBuilder<'a> {
376 password: &'a [u8],
377 salt: &'a [u8],
378 params: ScryptParams,
379}
380
381impl<'a> ScryptBuilder<'a> {
382 #[must_use]
386 pub fn new(password: &'a [u8], salt: &'a [u8]) -> Self {
387 ScryptBuilder {
388 password,
389 salt,
390 params: ScryptParams::default(),
391 }
392 }
393
394 #[must_use]
396 pub fn params(mut self, params: ScryptParams) -> Self {
397 self.params = params;
398 self
399 }
400
401 pub fn derive(self, out: &mut [u8]) -> Result<(), ErrorStack> {
405 let params = crate::params::ParamBuilder::new()?
406 .push_octet_slice(c"pass", self.password)?
407 .push_octet_slice(c"salt", self.salt)?
408 .push_uint64(c"n", self.params.n)?
409 .push_uint(c"r", self.params.r)?
410 .push_uint(c"p", self.params.p)?
411 .build()?;
412
413 let alg = KdfAlg::fetch(c"SCRYPT")?;
414 KdfCtx::new(&alg)?.derive(out, ¶ms)
415 }
416
417 pub fn derive_to_vec(self, len: usize) -> Result<Vec<u8>, ErrorStack> {
421 let mut out = vec![0u8; len];
422 self.derive(&mut out)?;
423 Ok(out)
424 }
425}
426
427#[cfg(ossl350)]
431#[derive(Clone, Copy, Debug, PartialEq, Eq)]
432pub enum SshkdfKeyType {
433 InitialIvClientToServer,
435 InitialIvServerToClient,
437 EncryptionKeyClientToServer,
439 EncryptionKeyServerToClient,
441 IntegrityKeyClientToServer,
443 IntegrityKeyServerToClient,
445}
446
447#[cfg(ossl350)]
448impl SshkdfKeyType {
449 fn as_cstr(self) -> &'static CStr {
450 match self {
451 Self::InitialIvClientToServer => c"A",
452 Self::InitialIvServerToClient => c"B",
453 Self::EncryptionKeyClientToServer => c"C",
454 Self::EncryptionKeyServerToClient => c"D",
455 Self::IntegrityKeyClientToServer => c"E",
456 Self::IntegrityKeyServerToClient => c"F",
457 }
458 }
459}
460
461#[cfg(ossl350)]
469pub struct SshkdfBuilder<'a> {
470 digest: &'a crate::digest::DigestAlg,
471 key: &'a [u8],
472 xcghash: &'a [u8],
473 session_id: &'a [u8],
474 key_type: SshkdfKeyType,
475}
476
477#[cfg(ossl350)]
478impl<'a> SshkdfBuilder<'a> {
479 #[must_use]
487 pub fn new(
488 digest: &'a crate::digest::DigestAlg,
489 key: &'a [u8],
490 xcghash: &'a [u8],
491 session_id: &'a [u8],
492 key_type: SshkdfKeyType,
493 ) -> Self {
494 SshkdfBuilder {
495 digest,
496 key,
497 xcghash,
498 session_id,
499 key_type,
500 }
501 }
502
503 pub fn derive(self, out: &mut [u8]) -> Result<(), ErrorStack> {
507 let name_ptr = unsafe { sys::OBJ_nid2sn(self.digest.nid()) };
508 if name_ptr.is_null() {
509 return Err(ErrorStack::drain());
510 }
511 let name = unsafe { CStr::from_ptr(name_ptr) };
512
513 let params = crate::params::ParamBuilder::new()?
514 .push_utf8_string(c"digest", name)?
515 .push_octet_slice(c"key", self.key)?
516 .push_octet_slice(c"xcghash", self.xcghash)?
517 .push_octet_slice(c"session-id", self.session_id)?
518 .push_utf8_string(c"type", self.key_type.as_cstr())?
519 .build()?;
520
521 let alg = KdfAlg::fetch(c"SSHKDF")?;
522 KdfCtx::new(&alg)?.derive(out, ¶ms)
523 }
524
525 pub fn derive_to_vec(self, len: usize) -> Result<Vec<u8>, ErrorStack> {
529 let mut out = vec![0u8; len];
530 self.derive(&mut out)?;
531 Ok(out)
532 }
533}
534
535#[cfg(ossl350)]
539#[derive(Clone, Copy, Debug, PartialEq, Eq)]
540pub enum KbkdfMode {
541 Counter,
543 Feedback,
545}
546
547#[cfg(ossl350)]
548impl KbkdfMode {
549 fn as_cstr(self) -> &'static CStr {
550 match self {
551 KbkdfMode::Counter => c"counter",
552 KbkdfMode::Feedback => c"feedback",
553 }
554 }
555}
556
557#[cfg(ossl350)]
559#[derive(Clone, Copy, Debug, PartialEq, Eq)]
560#[cfg_attr(ossl350, derive(Default))]
561pub enum KbkdfCounterLen {
562 Bits8 = 8,
564 Bits16 = 16,
566 Bits24 = 24,
568 #[cfg_attr(ossl350, default)]
570 Bits32 = 32,
571}
572
573#[cfg(ossl350)]
585pub struct KbkdfBuilder<'a> {
586 mode: KbkdfMode,
587 mac: &'a crate::mac::MacAlg,
588 digest: Option<&'a crate::digest::DigestAlg>,
589 key: &'a [u8],
590 label: Option<&'a [u8]>,
591 context: Option<&'a [u8]>,
592 salt: Option<&'a [u8]>,
594 counter_len: KbkdfCounterLen,
595 use_l: Option<bool>,
596 use_separator: Option<bool>,
597}
598
599#[cfg(ossl350)]
600impl<'a> KbkdfBuilder<'a> {
601 #[must_use]
607 pub fn new(mode: KbkdfMode, mac: &'a crate::mac::MacAlg, key: &'a [u8]) -> Self {
608 KbkdfBuilder {
609 mode,
610 mac,
611 digest: None,
612 key,
613 label: None,
614 context: None,
615 salt: None,
616 counter_len: KbkdfCounterLen::default(),
617 use_l: None,
618 use_separator: None,
619 }
620 }
621
622 #[must_use]
624 pub fn digest(mut self, digest: &'a crate::digest::DigestAlg) -> Self {
625 self.digest = Some(digest);
626 self
627 }
628
629 #[must_use]
631 pub fn label(mut self, label: &'a [u8]) -> Self {
632 self.label = Some(label);
633 self
634 }
635
636 #[must_use]
638 pub fn context(mut self, context: &'a [u8]) -> Self {
639 self.context = Some(context);
640 self
641 }
642
643 #[must_use]
645 pub fn salt(mut self, salt: &'a [u8]) -> Self {
646 self.salt = Some(salt);
647 self
648 }
649
650 #[must_use]
652 pub fn counter_len(mut self, len: KbkdfCounterLen) -> Self {
653 self.counter_len = len;
654 self
655 }
656
657 #[must_use]
659 pub fn use_l(mut self, enabled: bool) -> Self {
660 self.use_l = Some(enabled);
661 self
662 }
663
664 #[must_use]
666 pub fn use_separator(mut self, enabled: bool) -> Self {
667 self.use_separator = Some(enabled);
668 self
669 }
670
671 pub fn derive(self, out: &mut [u8]) -> Result<(), ErrorStack> {
675 let mut builder = crate::params::ParamBuilder::new()?
676 .push_utf8_string(c"mode", self.mode.as_cstr())?
677 .push_utf8_string(c"mac", self.mac.name())?
678 .push_octet_slice(c"key", self.key)?
679 .push_uint(c"r", self.counter_len as u32)?;
680
681 if let Some(d) = self.digest {
682 let name_ptr = unsafe { sys::OBJ_nid2sn(d.nid()) };
683 if name_ptr.is_null() {
684 return Err(ErrorStack::drain());
685 }
686 let name = unsafe { CStr::from_ptr(name_ptr) };
687 builder = builder.push_utf8_string(c"digest", name)?;
688 }
689 if let Some(l) = self.label {
690 builder = builder.push_octet_slice(c"label", l)?;
691 }
692 if let Some(c) = self.context {
693 builder = builder.push_octet_slice(c"data", c)?;
694 }
695 if let Some(s) = self.salt {
696 builder = builder.push_octet_slice(c"salt", s)?;
697 }
698 if let Some(v) = self.use_l {
699 builder = builder.push_int(c"use-l", i32::from(v))?;
700 }
701 if let Some(v) = self.use_separator {
702 builder = builder.push_int(c"use-separator", i32::from(v))?;
703 }
704
705 let params = builder.build()?;
706 let alg = KdfAlg::fetch(c"KBKDF")?;
707 KdfCtx::new(&alg)?.derive(out, ¶ms)
708 }
709
710 pub fn derive_to_vec(self, len: usize) -> Result<Vec<u8>, ErrorStack> {
714 let mut out = vec![0u8; len];
715 self.derive(&mut out)?;
716 Ok(out)
717 }
718}
719
720#[derive(Clone, Copy, Debug, PartialEq, Eq)]
726pub enum Pkcs12KdfId {
727 Key = 1,
729 Iv = 2,
731 Mac = 3,
733}
734
735pub struct Pkcs12KdfBuilder<'a> {
748 md: &'a crate::digest::DigestAlg,
749 password: &'a [u8],
750 salt: &'a [u8],
751 id: Pkcs12KdfId,
752 iter: u32,
753}
754
755impl<'a> Pkcs12KdfBuilder<'a> {
756 #[must_use]
766 pub fn new(
767 md: &'a crate::digest::DigestAlg,
768 password: &'a [u8],
769 salt: &'a [u8],
770 id: Pkcs12KdfId,
771 ) -> Self {
772 Self {
773 md,
774 password,
775 salt,
776 id,
777 iter: 2048,
778 }
779 }
780
781 #[must_use]
783 pub fn iterations(mut self, n: u32) -> Self {
784 self.iter = n;
785 self
786 }
787
788 pub fn derive(&self, out: &mut [u8]) -> Result<(), ErrorStack> {
797 let rc = unsafe {
801 sys::PKCS12_key_gen_utf8(
802 self.password.as_ptr().cast(),
803 i32::try_from(self.password.len()).expect("password too long"),
804 self.salt.as_ptr().cast_mut(),
805 i32::try_from(self.salt.len()).expect("salt too long"),
806 self.id as std::ffi::c_int,
807 self.iter.cast_signed(),
808 i32::try_from(out.len()).expect("output too long"),
809 out.as_mut_ptr(),
810 self.md.as_ptr(),
811 )
812 };
813 if rc != 1 {
814 return Err(ErrorStack::drain());
815 }
816 Ok(())
817 }
818
819 pub fn derive_to_vec(&self, len: usize) -> Result<Vec<u8>, ErrorStack> {
824 let mut out = vec![0u8; len];
825 self.derive(&mut out)?;
826 Ok(out)
827 }
828}
829
830#[cfg(test)]
833mod tests {
834 use super::*;
835 use crate::digest::DigestAlg;
836
837 #[test]
844 fn hkdf_sha256_rfc5869_tc1() {
845 let digest = DigestAlg::fetch(c"SHA2-256", None).unwrap();
846
847 let ikm = [0x0b_u8; 22];
848 let salt = [
849 0x00, 0x01, 0x02, 0x03, 0x04, 0x05, 0x06, 0x07, 0x08, 0x09, 0x0a, 0x0b, 0x0c_u8,
850 ];
851 let info = [
852 0xf0, 0xf1, 0xf2, 0xf3, 0xf4, 0xf5, 0xf6, 0xf7, 0xf8, 0xf9_u8,
853 ];
854
855 let okm = HkdfBuilder::new(&digest)
856 .key(&ikm)
857 .salt(&salt)
858 .info(&info)
859 .derive_to_vec(42)
860 .unwrap();
861
862 assert_eq!(
863 hex::encode(&okm),
864 "3cb25f25faacd57a90434f64d0362f2a\
865 2d2d0a90cf1a5a4c5db02d56ecc4c5bf\
866 34007208d5b887185865"
867 );
868 }
869
870 #[test]
877 fn hkdf_sha256_no_salt_no_info() {
878 let digest = DigestAlg::fetch(c"SHA2-256", None).unwrap();
879 let ikm = [0x0b_u8; 22];
880
881 let okm = HkdfBuilder::new(&digest)
882 .key(&ikm)
883 .derive_to_vec(42)
884 .unwrap();
885
886 assert_eq!(
887 hex::encode(&okm),
888 "8da4e775a563c18f715f802a063c5a31\
889 b8a11f5c5ee1879ec3454e5f3c738d2d\
890 9d201395faa4b61a96c8"
891 );
892 }
893
894 #[test]
901 fn pbkdf2_sha256_known_answer() {
902 let digest = DigestAlg::fetch(c"SHA2-256", None).unwrap();
903
904 let dk = Pbkdf2Builder::new(&digest, b"password", b"salt")
905 .iterations(1)
906 .derive_to_vec(32)
907 .unwrap();
908
909 assert_eq!(
910 hex::encode(&dk),
911 "120fb6cffcf8b32c43e7225256c4f837\
912 a86548c92ccc35480805987cb70be17b"
913 );
914 }
915
916 #[test]
920 fn scrypt_derives_nonzero_output() {
921 let dk = ScryptBuilder::new(b"password", b"salt")
922 .params(ScryptParams { n: 32, r: 1, p: 1 })
923 .derive_to_vec(32)
924 .unwrap();
925
926 assert_eq!(dk.len(), 32);
927 assert_ne!(dk, vec![0u8; 32]);
928 }
929
930 #[test]
932 fn scrypt_different_passwords_differ() {
933 let p = ScryptParams { n: 32, r: 1, p: 1 };
934
935 let dk1 = ScryptBuilder::new(b"pass1", b"salt")
936 .params(ScryptParams {
937 n: p.n,
938 r: p.r,
939 p: p.p,
940 })
941 .derive_to_vec(32)
942 .unwrap();
943 let dk2 = ScryptBuilder::new(b"pass2", b"salt")
944 .params(ScryptParams {
945 n: p.n,
946 r: p.r,
947 p: p.p,
948 })
949 .derive_to_vec(32)
950 .unwrap();
951
952 assert_ne!(dk1, dk2);
953 }
954
955 #[test]
960 fn pkcs12_kdf_basic() {
961 let sha1 = DigestAlg::fetch(c"SHA1", None).unwrap();
962 let salt = b"saltsalt";
963
964 let key = Pkcs12KdfBuilder::new(&sha1, b"password", salt, Pkcs12KdfId::Key)
965 .iterations(2048)
966 .derive_to_vec(24)
967 .unwrap();
968 assert_eq!(key.len(), 24);
969 assert_ne!(key, vec![0u8; 24]);
970
971 let key2 = Pkcs12KdfBuilder::new(&sha1, b"password", salt, Pkcs12KdfId::Key)
973 .iterations(2048)
974 .derive_to_vec(24)
975 .unwrap();
976 assert_eq!(key, key2);
977
978 let iv = Pkcs12KdfBuilder::new(&sha1, b"password", salt, Pkcs12KdfId::Iv)
980 .iterations(2048)
981 .derive_to_vec(8)
982 .unwrap();
983 assert_ne!(key[..8], iv[..]);
984 }
985
986 #[test]
988 fn pkcs12_kdf_different_passwords_differ() {
989 let sha1 = DigestAlg::fetch(c"SHA1", None).unwrap();
990 let salt = b"saltsalt";
991
992 let k1 = Pkcs12KdfBuilder::new(&sha1, b"pass1", salt, Pkcs12KdfId::Key)
993 .derive_to_vec(24)
994 .unwrap();
995 let k2 = Pkcs12KdfBuilder::new(&sha1, b"pass2", salt, Pkcs12KdfId::Key)
996 .derive_to_vec(24)
997 .unwrap();
998 assert_ne!(k1, k2);
999 }
1000
1001 #[test]
1008 fn kdf_set_params_hkdf_mode() {
1009 use crate::params::ParamBuilder;
1010
1011 let digest = DigestAlg::fetch(c"SHA2-256", None).unwrap();
1012 let name_ptr = unsafe { sys::OBJ_nid2sn(digest.nid()) };
1013 assert!(!name_ptr.is_null());
1014 let name = unsafe { std::ffi::CStr::from_ptr(name_ptr) };
1015
1016 let ikm = [0x0b_u8; 32];
1017 let salt = b"test-salt-value!";
1018 let info = b"test-info-value!";
1019
1020 let params1 = ParamBuilder::new()
1022 .unwrap()
1023 .push_utf8_string(c"digest", name)
1024 .unwrap()
1025 .push_uint(c"mode", HkdfMode::ExtractAndExpand.as_uint())
1026 .unwrap()
1027 .push_octet_slice(c"key", &ikm)
1028 .unwrap()
1029 .push_octet_slice(c"salt", salt)
1030 .unwrap()
1031 .push_octet_slice(c"info", info)
1032 .unwrap()
1033 .build()
1034 .unwrap();
1035
1036 let alg = KdfAlg::fetch(c"HKDF").unwrap();
1037 let mut ctx = KdfCtx::new(&alg).unwrap();
1038
1039 let mut out1 = [0u8; 32];
1040 ctx.derive(&mut out1, ¶ms1).unwrap();
1041
1042 let params2 = ParamBuilder::new()
1045 .unwrap()
1046 .push_utf8_string(c"digest", name)
1047 .unwrap()
1048 .push_uint(c"mode", HkdfMode::ExpandOnly.as_uint())
1049 .unwrap()
1050 .push_octet_slice(c"key", &ikm)
1051 .unwrap()
1052 .push_octet_slice(c"info", info)
1053 .unwrap()
1054 .build()
1055 .unwrap();
1056
1057 ctx.set_params(¶ms2).unwrap();
1058
1059 let mut out2 = [0u8; 32];
1060 ctx.derive(&mut out2, ¶ms2).unwrap();
1061
1062 assert_ne!(out1, out2, "ExtractAndExpand and ExpandOnly must differ");
1064 }
1065
1066 #[test]
1071 fn kdf_get_params_mode() {
1072 use crate::params::ParamBuilder;
1073
1074 let digest = DigestAlg::fetch(c"SHA2-256", None).unwrap();
1075 let name_ptr = unsafe { sys::OBJ_nid2sn(digest.nid()) };
1076 assert!(!name_ptr.is_null());
1077 let name = unsafe { std::ffi::CStr::from_ptr(name_ptr) };
1078
1079 let params = ParamBuilder::new()
1080 .unwrap()
1081 .push_utf8_string(c"digest", name)
1082 .unwrap()
1083 .push_uint(c"mode", HkdfMode::ExtractOnly.as_uint())
1084 .unwrap()
1085 .push_octet_slice(c"key", &[0x0b_u8; 22])
1086 .unwrap()
1087 .push_octet_slice(c"salt", b"some-salt")
1088 .unwrap()
1089 .build()
1090 .unwrap();
1091
1092 let alg = KdfAlg::fetch(c"HKDF").unwrap();
1093 let mut ctx = KdfCtx::new(&alg).unwrap();
1094 let mut out = [0u8; 32];
1099 ctx.derive(&mut out, ¶ms).unwrap();
1100 assert_ne!(out, [0u8; 32], "ExtractOnly must produce non-zero PRK");
1102 }
1103
1104 #[test]
1106 fn kdf_size_hkdf() {
1107 let alg = KdfAlg::fetch(c"HKDF").unwrap();
1108 let ctx = KdfCtx::new(&alg).unwrap();
1109 assert_eq!(
1110 ctx.size(),
1111 usize::MAX,
1112 "HKDF has variable output; size() must return usize::MAX"
1113 );
1114 }
1115}