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 use crate::util::hex_encode;
837
838 #[test]
845 fn hkdf_sha256_rfc5869_tc1() {
846 let digest = DigestAlg::fetch(c"SHA2-256", None).unwrap();
847
848 let ikm = [0x0b_u8; 22];
849 let salt = [
850 0x00, 0x01, 0x02, 0x03, 0x04, 0x05, 0x06, 0x07, 0x08, 0x09, 0x0a, 0x0b, 0x0c_u8,
851 ];
852 let info = [
853 0xf0, 0xf1, 0xf2, 0xf3, 0xf4, 0xf5, 0xf6, 0xf7, 0xf8, 0xf9_u8,
854 ];
855
856 let okm = HkdfBuilder::new(&digest)
857 .key(&ikm)
858 .salt(&salt)
859 .info(&info)
860 .derive_to_vec(42)
861 .unwrap();
862
863 assert_eq!(
864 hex_encode(&okm),
865 "3cb25f25faacd57a90434f64d0362f2a\
866 2d2d0a90cf1a5a4c5db02d56ecc4c5bf\
867 34007208d5b887185865"
868 );
869 }
870
871 #[test]
878 fn hkdf_sha256_no_salt_no_info() {
879 let digest = DigestAlg::fetch(c"SHA2-256", None).unwrap();
880 let ikm = [0x0b_u8; 22];
881
882 let okm = HkdfBuilder::new(&digest)
883 .key(&ikm)
884 .derive_to_vec(42)
885 .unwrap();
886
887 assert_eq!(
888 hex_encode(&okm),
889 "8da4e775a563c18f715f802a063c5a31\
890 b8a11f5c5ee1879ec3454e5f3c738d2d\
891 9d201395faa4b61a96c8"
892 );
893 }
894
895 #[test]
902 fn pbkdf2_sha256_known_answer() {
903 let digest = DigestAlg::fetch(c"SHA2-256", None).unwrap();
904
905 let dk = Pbkdf2Builder::new(&digest, b"password", b"salt")
906 .iterations(1)
907 .derive_to_vec(32)
908 .unwrap();
909
910 assert_eq!(
911 hex_encode(&dk),
912 "120fb6cffcf8b32c43e7225256c4f837\
913 a86548c92ccc35480805987cb70be17b"
914 );
915 }
916
917 #[test]
921 fn scrypt_derives_nonzero_output() {
922 let dk = ScryptBuilder::new(b"password", b"salt")
923 .params(ScryptParams { n: 32, r: 1, p: 1 })
924 .derive_to_vec(32)
925 .unwrap();
926
927 assert_eq!(dk.len(), 32);
928 assert_ne!(dk, vec![0u8; 32]);
929 }
930
931 #[test]
933 fn scrypt_different_passwords_differ() {
934 let p = ScryptParams { n: 32, r: 1, p: 1 };
935
936 let dk1 = ScryptBuilder::new(b"pass1", b"salt")
937 .params(ScryptParams {
938 n: p.n,
939 r: p.r,
940 p: p.p,
941 })
942 .derive_to_vec(32)
943 .unwrap();
944 let dk2 = ScryptBuilder::new(b"pass2", b"salt")
945 .params(ScryptParams {
946 n: p.n,
947 r: p.r,
948 p: p.p,
949 })
950 .derive_to_vec(32)
951 .unwrap();
952
953 assert_ne!(dk1, dk2);
954 }
955
956 #[test]
961 fn pkcs12_kdf_basic() {
962 let sha1 = DigestAlg::fetch(c"SHA1", None).unwrap();
963 let salt = b"saltsalt";
964
965 let key = Pkcs12KdfBuilder::new(&sha1, b"password", salt, Pkcs12KdfId::Key)
966 .iterations(2048)
967 .derive_to_vec(24)
968 .unwrap();
969 assert_eq!(key.len(), 24);
970 assert_ne!(key, vec![0u8; 24]);
971
972 let key2 = Pkcs12KdfBuilder::new(&sha1, b"password", salt, Pkcs12KdfId::Key)
974 .iterations(2048)
975 .derive_to_vec(24)
976 .unwrap();
977 assert_eq!(key, key2);
978
979 let iv = Pkcs12KdfBuilder::new(&sha1, b"password", salt, Pkcs12KdfId::Iv)
981 .iterations(2048)
982 .derive_to_vec(8)
983 .unwrap();
984 assert_ne!(key[..8], iv[..]);
985 }
986
987 #[test]
989 fn pkcs12_kdf_different_passwords_differ() {
990 let sha1 = DigestAlg::fetch(c"SHA1", None).unwrap();
991 let salt = b"saltsalt";
992
993 let k1 = Pkcs12KdfBuilder::new(&sha1, b"pass1", salt, Pkcs12KdfId::Key)
994 .derive_to_vec(24)
995 .unwrap();
996 let k2 = Pkcs12KdfBuilder::new(&sha1, b"pass2", salt, Pkcs12KdfId::Key)
997 .derive_to_vec(24)
998 .unwrap();
999 assert_ne!(k1, k2);
1000 }
1001
1002 #[test]
1009 fn kdf_set_params_hkdf_mode() {
1010 use crate::params::ParamBuilder;
1011
1012 let digest = DigestAlg::fetch(c"SHA2-256", None).unwrap();
1013 let name_ptr = unsafe { sys::OBJ_nid2sn(digest.nid()) };
1014 assert!(!name_ptr.is_null());
1015 let name = unsafe { std::ffi::CStr::from_ptr(name_ptr) };
1016
1017 let ikm = [0x0b_u8; 32];
1018 let salt = b"test-salt-value!";
1019 let info = b"test-info-value!";
1020
1021 let params1 = ParamBuilder::new()
1023 .unwrap()
1024 .push_utf8_string(c"digest", name)
1025 .unwrap()
1026 .push_uint(c"mode", HkdfMode::ExtractAndExpand.as_uint())
1027 .unwrap()
1028 .push_octet_slice(c"key", &ikm)
1029 .unwrap()
1030 .push_octet_slice(c"salt", salt)
1031 .unwrap()
1032 .push_octet_slice(c"info", info)
1033 .unwrap()
1034 .build()
1035 .unwrap();
1036
1037 let alg = KdfAlg::fetch(c"HKDF").unwrap();
1038 let mut ctx = KdfCtx::new(&alg).unwrap();
1039
1040 let mut out1 = [0u8; 32];
1041 ctx.derive(&mut out1, ¶ms1).unwrap();
1042
1043 let params2 = ParamBuilder::new()
1046 .unwrap()
1047 .push_utf8_string(c"digest", name)
1048 .unwrap()
1049 .push_uint(c"mode", HkdfMode::ExpandOnly.as_uint())
1050 .unwrap()
1051 .push_octet_slice(c"key", &ikm)
1052 .unwrap()
1053 .push_octet_slice(c"info", info)
1054 .unwrap()
1055 .build()
1056 .unwrap();
1057
1058 ctx.set_params(¶ms2).unwrap();
1059
1060 let mut out2 = [0u8; 32];
1061 ctx.derive(&mut out2, ¶ms2).unwrap();
1062
1063 assert_ne!(out1, out2, "ExtractAndExpand and ExpandOnly must differ");
1065 }
1066
1067 #[test]
1072 fn kdf_get_params_mode() {
1073 use crate::params::ParamBuilder;
1074
1075 let digest = DigestAlg::fetch(c"SHA2-256", None).unwrap();
1076 let name_ptr = unsafe { sys::OBJ_nid2sn(digest.nid()) };
1077 assert!(!name_ptr.is_null());
1078 let name = unsafe { std::ffi::CStr::from_ptr(name_ptr) };
1079
1080 let params = ParamBuilder::new()
1081 .unwrap()
1082 .push_utf8_string(c"digest", name)
1083 .unwrap()
1084 .push_uint(c"mode", HkdfMode::ExtractOnly.as_uint())
1085 .unwrap()
1086 .push_octet_slice(c"key", &[0x0b_u8; 22])
1087 .unwrap()
1088 .push_octet_slice(c"salt", b"some-salt")
1089 .unwrap()
1090 .build()
1091 .unwrap();
1092
1093 let alg = KdfAlg::fetch(c"HKDF").unwrap();
1094 let mut ctx = KdfCtx::new(&alg).unwrap();
1095 let mut out = [0u8; 32];
1100 ctx.derive(&mut out, ¶ms).unwrap();
1101 assert_ne!(out, [0u8; 32], "ExtractOnly must produce non-zero PRK");
1103 }
1104
1105 #[test]
1107 fn kdf_size_hkdf() {
1108 let alg = KdfAlg::fetch(c"HKDF").unwrap();
1109 let ctx = KdfCtx::new(&alg).unwrap();
1110 assert_eq!(
1111 ctx.size(),
1112 usize::MAX,
1113 "HKDF has variable output; size() must return usize::MAX"
1114 );
1115 }
1116}