1use openssl::ec::{EcGroup, EcKey};
2use openssl::nid::Nid;
3
4use crate::prelude::*;
5use std::sync::Arc;
6
7use super::Plugin;
8
9lazy_static! {
10 static ref DEFAULT_KEY_GROUP: EcGroup = {
13 let nid = Nid::X9_62_PRIME256V1; #[allow(clippy::unwrap_used)]
15 EcGroup::from_curve_name(nid).unwrap()
16 };
17}
18
19pub struct EcdhKeyGen {}
20
21impl EcdhKeyGen {
22 fn generate_key<STATE: Clone>(
24 cands: &mut [Entry<EntryInvalid, STATE>],
25 ) -> Result<(), OperationError> {
26 for cand in cands.iter_mut() {
27 if cand.attribute_equality(Attribute::Class, &EntryClass::Person.to_partialvalue())
28 && !cand.attribute_pres(Attribute::IdVerificationEcKey)
29 {
30 debug!(
31 "Generating {} for {}",
32 Attribute::IdVerificationEcKey,
33 cand.get_display_id()
34 );
35
36 let new_private_key = EcKey::generate(&DEFAULT_KEY_GROUP).map_err(|e| {
37 error!(err = ?e, "Unable to generate id verification ECDH private key");
38 OperationError::CryptographyError
39 })?;
40 cand.add_ava_if_not_exist(
41 Attribute::IdVerificationEcKey,
42 crate::value::Value::EcKeyPrivate(new_private_key),
43 )
44 }
45 }
46 Ok(())
47 }
48}
49
50impl Plugin for EcdhKeyGen {
51 fn id() -> &'static str {
52 "plugin_ecdhkey_gen"
53 }
54
55 #[instrument(level = "debug", name = "ecdhkeygen::pre_create_transform", skip_all)]
56 fn pre_create_transform(
57 _qs: &mut QueryServerWriteTransaction,
58 cand: &mut Vec<EntryInvalidNew>,
59 _ce: &CreateEvent,
60 ) -> Result<(), OperationError> {
61 Self::generate_key(cand)
62 }
63
64 #[instrument(level = "debug", name = "ecdhkeygen::pre_modify", skip_all)]
65 fn pre_modify(
66 _qs: &mut QueryServerWriteTransaction,
67 _pre_cand: &[Arc<EntrySealedCommitted>],
68 cand: &mut Vec<EntryInvalidCommitted>,
69 _me: &ModifyEvent,
70 ) -> Result<(), OperationError> {
71 Self::generate_key(cand)
72 }
73
74 #[instrument(level = "debug", name = "ecdhkeygen::pre_batch_modify", skip_all)]
75 fn pre_batch_modify(
76 _qs: &mut QueryServerWriteTransaction,
77 _pre_cand: &[Arc<EntrySealedCommitted>],
78 cand: &mut Vec<EntryInvalidCommitted>,
79 _me: &BatchModifyEvent,
80 ) -> Result<(), OperationError> {
81 Self::generate_key(cand)
82 }
83}
84
85#[cfg(test)]
86mod tests {
87 use openssl::ec::EcKey;
88 use uuid::Uuid;
89
90 use super::DEFAULT_KEY_GROUP;
91 use crate::prelude::*;
92 use crate::value::Value;
93 use crate::valueset;
94
95 #[test]
96 fn test_new_user_generate_key() {
97 let uuid = Uuid::new_v4();
98 let ea = entry_init!(
99 (Attribute::Class, EntryClass::Account.to_value()),
100 (Attribute::Class, EntryClass::Person.to_value()),
101 (Attribute::Class, EntryClass::Object.to_value()),
102 (Attribute::Name, Value::new_iname("test_name")),
103 (Attribute::Uuid, Value::Uuid(uuid)),
104 (Attribute::Description, Value::new_utf8s("testperson")),
105 (Attribute::DisplayName, Value::new_utf8s("Test Person"))
106 );
107 let preload: Vec<Entry<EntryInit, EntryNew>> = Vec::with_capacity(0);
108
109 let create = vec![ea];
110 run_create_test!(
111 Ok(()),
112 preload,
113 create,
114 None,
115 |qs: &mut QueryServerWriteTransaction| {
116 let e = qs.internal_search_uuid(uuid).expect("failed to get entry");
117
118 let key = e
119 .get_ava_single_eckey_private(Attribute::IdVerificationEcKey)
120 .expect("unable to retrieve the ecdh key");
121
122 assert!(key.check_key().is_ok())
123 }
124 );
125 }
126
127 #[test]
158 fn test_modify_purge_eckey() {
159 let private_key = EcKey::generate(&DEFAULT_KEY_GROUP).unwrap();
160 let private_key_value = Value::EcKeyPrivate(private_key.clone());
161
162 let uuid = Uuid::new_v4();
163
164 let ea = entry_init!(
165 (Attribute::Class, EntryClass::Account.to_value()),
166 (Attribute::Class, EntryClass::Person.to_value()),
167 (Attribute::Class, EntryClass::Object.to_value()),
168 (Attribute::Name, Value::new_iname("test_name")),
169 (Attribute::Uuid, Value::Uuid(uuid)),
170 (Attribute::IdVerificationEcKey, private_key_value.clone()),
171 (Attribute::Description, Value::new_utf8s("testperson")),
172 (Attribute::DisplayName, Value::new_utf8s("Test person!"))
173 );
174 let key_partialvalue = valueset::from_value_iter(std::iter::once(private_key_value))
175 .unwrap()
176 .to_partialvalue_iter()
177 .next()
178 .unwrap();
179 let preload = vec![ea];
180 run_modify_test!(
181 Ok(()),
182 preload,
183 filter!(f_eq(Attribute::Name, PartialValue::new_iname("test_name"))),
184 modlist!([m_purge(Attribute::IdVerificationEcKey)]),
185 None,
186 |_| {},
187 |qs: &mut QueryServerWriteTransaction| {
188 let e = qs.internal_search_uuid(uuid).expect("failed to get entry");
189
190 assert!(
191 !e.attribute_equality(Attribute::IdVerificationEcKey, &key_partialvalue)
192 && e.attribute_pres(Attribute::IdVerificationEcKey)
193 )
194 }
195 );
196 }
197 #[test]
198 fn test_modify_remove_eckey() {
199 let private_key = EcKey::generate(&DEFAULT_KEY_GROUP).unwrap();
200 let private_key_value = Value::EcKeyPrivate(private_key.clone());
201
202 let uuid = Uuid::new_v4();
203
204 let ea = entry_init!(
205 (Attribute::Class, EntryClass::Account.to_value()),
206 (Attribute::Class, EntryClass::Person.to_value()),
207 (Attribute::Class, EntryClass::Object.to_value()),
208 (Attribute::Name, Value::new_iname("test_name")),
209 (Attribute::Uuid, Value::Uuid(uuid)),
210 (Attribute::IdVerificationEcKey, private_key_value.clone()),
211 (Attribute::Description, Value::new_utf8s("testperson")),
212 (Attribute::DisplayName, Value::new_utf8s("Test person!"))
213 );
214 let key_partialvalue = valueset::from_value_iter(std::iter::once(private_key_value))
215 .unwrap()
216 .to_partialvalue_iter()
217 .next()
218 .unwrap();
219 let preload = vec![ea];
220 run_modify_test!(
221 Ok(()),
222 preload,
223 filter!(f_eq(Attribute::Name, PartialValue::new_iname("test_name"))),
224 modlist!([m_remove(Attribute::IdVerificationEcKey, &key_partialvalue)]),
225 None,
226 |_| {},
227 |qs: &mut QueryServerWriteTransaction| {
228 let e = qs.internal_search_uuid(uuid).expect("failed to get entry");
229
230 assert!(
231 !e.attribute_equality(Attribute::IdVerificationEcKey, &key_partialvalue)
232 && e.attribute_pres(Attribute::IdVerificationEcKey)
233 )
234 }
235 );
236 }
237}