1use crate::{TelemetryError, TelemetryResult};
8
9const SOFTWARE_KEY_SLOTS: usize = 8;
10const SOFTWARE_KEY_MIN_LEN: usize = 16;
11const SOFTWARE_TAG_LEN: usize = 16;
12pub const MANAGED_CREDENTIAL_LEN: usize = 80;
13const MANAGED_CREDENTIAL_BODY_LEN: usize = 48;
14const MANAGED_CREDENTIAL_MAGIC: &[u8; 8] = b"SEDSCR1\0";
15
16pub trait CryptographyProvider: Sync {
21 fn seal(
22 &self,
23 key_id: u32,
24 nonce: &[u8],
25 aad: &[u8],
26 plaintext: &[u8],
27 ciphertext_out: &mut [u8],
28 tag_out: &mut [u8],
29 ) -> TelemetryResult<(usize, usize)>;
30
31 fn open(
32 &self,
33 key_id: u32,
34 nonce: &[u8],
35 aad: &[u8],
36 ciphertext: &[u8],
37 tag: &[u8],
38 plaintext_out: &mut [u8],
39 ) -> TelemetryResult<usize>;
40}
41
42pub fn seal_with<S: CryptographyProvider + ?Sized>(
44 provider: &S,
45 key_id: u32,
46 nonce: &[u8],
47 aad: &[u8],
48 plaintext: &[u8],
49 ciphertext_out: &mut [u8],
50 tag_out: &mut [u8],
51) -> TelemetryResult<(usize, usize)> {
52 provider.seal(key_id, nonce, aad, plaintext, ciphertext_out, tag_out)
53}
54
55pub fn open_with<S: CryptographyProvider + ?Sized>(
57 provider: &S,
58 key_id: u32,
59 nonce: &[u8],
60 aad: &[u8],
61 ciphertext: &[u8],
62 tag: &[u8],
63 plaintext_out: &mut [u8],
64) -> TelemetryResult<usize> {
65 provider.open(key_id, nonce, aad, ciphertext, tag, plaintext_out)
66}
67
68pub type CSealFn = unsafe extern "C" fn(
69 key_id: u32,
70 nonce: *const u8,
71 nonce_len: usize,
72 aad: *const u8,
73 aad_len: usize,
74 plaintext: *const u8,
75 plaintext_len: usize,
76 ciphertext_out: *mut u8,
77 ciphertext_cap: usize,
78 ciphertext_len_out: *mut usize,
79 tag_out: *mut u8,
80 tag_cap: usize,
81 tag_len_out: *mut usize,
82 user: *mut core::ffi::c_void,
83) -> i32;
84
85pub type COpenFn = unsafe extern "C" fn(
86 key_id: u32,
87 nonce: *const u8,
88 nonce_len: usize,
89 aad: *const u8,
90 aad_len: usize,
91 ciphertext: *const u8,
92 ciphertext_len: usize,
93 tag: *const u8,
94 tag_len: usize,
95 plaintext_out: *mut u8,
96 plaintext_cap: usize,
97 plaintext_len_out: *mut usize,
98 user: *mut core::ffi::c_void,
99) -> i32;
100
101#[derive(Clone, Copy)]
102pub struct CCryptographyProvider {
103 pub seal: Option<CSealFn>,
104 pub open: Option<COpenFn>,
105 pub user: *mut core::ffi::c_void,
106}
107
108static mut C_SHIM: CCryptographyProvider = CCryptographyProvider {
109 seal: None,
110 open: None,
111 user: core::ptr::null_mut(),
112};
113
114static mut RUST_SHIM: Option<&'static dyn CryptographyProvider> = None;
115
116#[derive(Clone, Copy)]
117struct SoftwareKey {
118 key_id: u32,
119 key: [u8; 32],
120}
121
122#[derive(Clone, Copy)]
123struct SoftwareKeyring {
124 keys: [Option<SoftwareKey>; SOFTWARE_KEY_SLOTS],
125}
126
127impl SoftwareKeyring {
128 const fn new() -> Self {
129 Self {
130 keys: [None; SOFTWARE_KEY_SLOTS],
131 }
132 }
133
134 fn is_registered(&self) -> bool {
135 self.keys.iter().any(Option::is_some)
136 }
137
138 fn get(&self, key_id: u32) -> Option<[u8; 32]> {
139 self.keys
140 .iter()
141 .flatten()
142 .find(|key| key.key_id == key_id)
143 .map(|key| key.key)
144 }
145
146 fn register(&mut self, key_id: u32, raw_key: &[u8]) -> TelemetryResult<()> {
147 if raw_key.len() < SOFTWARE_KEY_MIN_LEN {
148 return Err(TelemetryError::BadArg);
149 }
150 let key = normalize_software_key(raw_key);
151 if let Some(slot) = self
152 .keys
153 .iter_mut()
154 .find(|slot| slot.map(|key| key.key_id) == Some(key_id))
155 {
156 *slot = Some(SoftwareKey { key_id, key });
157 return Ok(());
158 }
159 if let Some(slot) = self.keys.iter_mut().find(|slot| slot.is_none()) {
160 *slot = Some(SoftwareKey { key_id, key });
161 return Ok(());
162 }
163 Err(TelemetryError::SizeMismatchError)
164 }
165}
166
167static mut SOFTWARE_KEYS: SoftwareKeyring = SoftwareKeyring::new();
168
169pub fn register_c_cryptography_provider(provider: CCryptographyProvider) {
172 unsafe {
173 core::ptr::addr_of_mut!(C_SHIM).write(provider);
174 }
175}
176
177pub fn clear_c_cryptography_provider() {
178 register_c_cryptography_provider(CCryptographyProvider {
179 seal: None,
180 open: None,
181 user: core::ptr::null_mut(),
182 });
183}
184
185pub fn c_cryptography_provider_registered() -> bool {
186 let provider = unsafe { core::ptr::addr_of!(C_SHIM).read() };
187 provider.seal.is_some() && provider.open.is_some()
188}
189
190pub fn register_rust_cryptography_provider(provider: &'static dyn CryptographyProvider) {
196 unsafe {
197 core::ptr::addr_of_mut!(RUST_SHIM).write(Some(provider));
198 }
199}
200
201pub fn clear_rust_cryptography_provider() {
202 unsafe {
203 core::ptr::addr_of_mut!(RUST_SHIM).write(None);
204 }
205}
206
207pub fn rust_cryptography_provider_registered() -> bool {
208 unsafe { core::ptr::addr_of!(RUST_SHIM).read().is_some() }
209}
210
211pub fn register_software_key(key_id: u32, key: &[u8]) -> TelemetryResult<()> {
217 let mut keyring = unsafe { core::ptr::addr_of!(SOFTWARE_KEYS).read() };
218 keyring.register(key_id, key)?;
219 unsafe {
220 core::ptr::addr_of_mut!(SOFTWARE_KEYS).write(keyring);
221 }
222 Ok(())
223}
224
225pub fn clear_software_keys() {
226 unsafe {
227 core::ptr::addr_of_mut!(SOFTWARE_KEYS).write(SoftwareKeyring::new());
228 }
229}
230
231pub fn software_crypto_available() -> bool {
232 unsafe { core::ptr::addr_of!(SOFTWARE_KEYS).read().is_registered() }
233}
234
235pub fn registered_crypto_available() -> bool {
236 c_cryptography_provider_registered()
237 || rust_cryptography_provider_registered()
238 || software_crypto_available()
239}
240
241#[derive(Debug, Clone, Copy, PartialEq, Eq)]
242pub struct ManagedCredential {
243 pub subject_id: u64,
244 pub key_id: u32,
245 pub epoch: u64,
246 pub not_before_ms: u64,
247 pub not_after_ms: u64,
248 pub permissions: u32,
249}
250
251pub fn issue_managed_credential(
256 root_key: &[u8],
257 credential: ManagedCredential,
258 out: &mut [u8],
259) -> TelemetryResult<usize> {
260 if root_key.len() < SOFTWARE_KEY_MIN_LEN || out.len() < MANAGED_CREDENTIAL_LEN {
261 return Err(TelemetryError::BadArg);
262 }
263 write_managed_credential_body(credential, &mut out[..MANAGED_CREDENTIAL_BODY_LEN]);
264 let key = normalize_software_key(root_key);
265 let tag = hmac_sha256(
266 &key,
267 &[
268 b"SEDS-MASTER-CREDENTIAL",
269 &out[..MANAGED_CREDENTIAL_BODY_LEN],
270 ],
271 );
272 out[MANAGED_CREDENTIAL_BODY_LEN..MANAGED_CREDENTIAL_LEN].copy_from_slice(&tag);
273 Ok(MANAGED_CREDENTIAL_LEN)
274}
275
276pub fn verify_managed_credential(
278 root_key: &[u8],
279 bytes: &[u8],
280 now_ms: u64,
281) -> TelemetryResult<ManagedCredential> {
282 if root_key.len() < SOFTWARE_KEY_MIN_LEN || bytes.len() != MANAGED_CREDENTIAL_LEN {
283 return Err(TelemetryError::BadArg);
284 }
285 let credential = read_managed_credential_body(&bytes[..MANAGED_CREDENTIAL_BODY_LEN])?;
286 if now_ms < credential.not_before_ms || now_ms > credential.not_after_ms {
287 return Err(TelemetryError::HandlerError("credential expired"));
288 }
289 let key = normalize_software_key(root_key);
290 let expected = hmac_sha256(
291 &key,
292 &[
293 b"SEDS-MASTER-CREDENTIAL",
294 &bytes[..MANAGED_CREDENTIAL_BODY_LEN],
295 ],
296 );
297 if !constant_time_eq(
298 &bytes[MANAGED_CREDENTIAL_BODY_LEN..MANAGED_CREDENTIAL_LEN],
299 &expected,
300 ) {
301 return Err(TelemetryError::HandlerError("credential signature"));
302 }
303 Ok(credential)
304}
305
306pub fn seal_with_registered_c_provider(
307 key_id: u32,
308 nonce: &[u8],
309 aad: &[u8],
310 plaintext: &[u8],
311 ciphertext_out: &mut [u8],
312 tag_out: &mut [u8],
313) -> TelemetryResult<(usize, usize)> {
314 let provider = unsafe { core::ptr::addr_of!(C_SHIM).read() };
315 let Some(seal) = provider.seal else {
316 return Err(TelemetryError::BadArg);
317 };
318 let mut ciphertext_len = 0usize;
319 let mut tag_len = 0usize;
320 let status = unsafe {
321 seal(
322 key_id,
323 nonce.as_ptr(),
324 nonce.len(),
325 aad.as_ptr(),
326 aad.len(),
327 plaintext.as_ptr(),
328 plaintext.len(),
329 ciphertext_out.as_mut_ptr(),
330 ciphertext_out.len(),
331 &mut ciphertext_len,
332 tag_out.as_mut_ptr(),
333 tag_out.len(),
334 &mut tag_len,
335 provider.user,
336 )
337 };
338 if status != 0 {
339 return Err(TelemetryError::HandlerError("crypto seal"));
340 }
341 if ciphertext_len > ciphertext_out.len() || tag_len > tag_out.len() {
342 return Err(TelemetryError::SizeMismatchError);
343 }
344 Ok((ciphertext_len, tag_len))
345}
346
347pub fn open_with_registered_c_provider(
348 key_id: u32,
349 nonce: &[u8],
350 aad: &[u8],
351 ciphertext: &[u8],
352 tag: &[u8],
353 plaintext_out: &mut [u8],
354) -> TelemetryResult<usize> {
355 let provider = unsafe { core::ptr::addr_of!(C_SHIM).read() };
356 let Some(open) = provider.open else {
357 return Err(TelemetryError::BadArg);
358 };
359 let mut plaintext_len = 0usize;
360 let status = unsafe {
361 open(
362 key_id,
363 nonce.as_ptr(),
364 nonce.len(),
365 aad.as_ptr(),
366 aad.len(),
367 ciphertext.as_ptr(),
368 ciphertext.len(),
369 tag.as_ptr(),
370 tag.len(),
371 plaintext_out.as_mut_ptr(),
372 plaintext_out.len(),
373 &mut plaintext_len,
374 provider.user,
375 )
376 };
377 if status != 0 {
378 return Err(TelemetryError::HandlerError("crypto open"));
379 }
380 if plaintext_len > plaintext_out.len() {
381 return Err(TelemetryError::SizeMismatchError);
382 }
383 Ok(plaintext_len)
384}
385
386pub fn seal_with_registered_crypto(
387 key_id: u32,
388 nonce: &[u8],
389 aad: &[u8],
390 plaintext: &[u8],
391 ciphertext_out: &mut [u8],
392 tag_out: &mut [u8],
393) -> TelemetryResult<(usize, usize)> {
394 if c_cryptography_provider_registered() {
395 return seal_with_registered_c_provider(
396 key_id,
397 nonce,
398 aad,
399 plaintext,
400 ciphertext_out,
401 tag_out,
402 );
403 }
404 if let Some(provider) = unsafe { core::ptr::addr_of!(RUST_SHIM).read() } {
405 return seal_with(
406 provider,
407 key_id,
408 nonce,
409 aad,
410 plaintext,
411 ciphertext_out,
412 tag_out,
413 );
414 }
415 seal_with_software_key(key_id, nonce, aad, plaintext, ciphertext_out, tag_out)
416}
417
418pub fn open_with_registered_crypto(
419 key_id: u32,
420 nonce: &[u8],
421 aad: &[u8],
422 ciphertext: &[u8],
423 tag: &[u8],
424 plaintext_out: &mut [u8],
425) -> TelemetryResult<usize> {
426 if c_cryptography_provider_registered() {
427 return open_with_registered_c_provider(key_id, nonce, aad, ciphertext, tag, plaintext_out);
428 }
429 if let Some(provider) = unsafe { core::ptr::addr_of!(RUST_SHIM).read() } {
430 return open_with(provider, key_id, nonce, aad, ciphertext, tag, plaintext_out);
431 }
432 open_with_software_key(key_id, nonce, aad, ciphertext, tag, plaintext_out)
433}
434
435fn seal_with_software_key(
436 key_id: u32,
437 nonce: &[u8],
438 aad: &[u8],
439 plaintext: &[u8],
440 ciphertext_out: &mut [u8],
441 tag_out: &mut [u8],
442) -> TelemetryResult<(usize, usize)> {
443 if ciphertext_out.len() < plaintext.len() || tag_out.len() < SOFTWARE_TAG_LEN {
444 return Err(TelemetryError::SizeMismatchError);
445 }
446 let key = software_key_for(key_id)?;
447 apply_hmac_stream(&key, key_id, nonce, aad, plaintext, ciphertext_out);
448 let tag = software_tag(&key, key_id, nonce, aad, &ciphertext_out[..plaintext.len()]);
449 tag_out[..SOFTWARE_TAG_LEN].copy_from_slice(&tag[..SOFTWARE_TAG_LEN]);
450 Ok((plaintext.len(), SOFTWARE_TAG_LEN))
451}
452
453fn open_with_software_key(
454 key_id: u32,
455 nonce: &[u8],
456 aad: &[u8],
457 ciphertext: &[u8],
458 tag: &[u8],
459 plaintext_out: &mut [u8],
460) -> TelemetryResult<usize> {
461 if plaintext_out.len() < ciphertext.len() || tag.len() != SOFTWARE_TAG_LEN {
462 return Err(TelemetryError::SizeMismatchError);
463 }
464 let key = software_key_for(key_id)?;
465 let expected = software_tag(&key, key_id, nonce, aad, ciphertext);
466 if !constant_time_eq(tag, &expected[..SOFTWARE_TAG_LEN]) {
467 return Err(TelemetryError::HandlerError("crypto open"));
468 }
469 apply_hmac_stream(&key, key_id, nonce, aad, ciphertext, plaintext_out);
470 Ok(ciphertext.len())
471}
472
473fn software_key_for(key_id: u32) -> TelemetryResult<[u8; 32]> {
474 unsafe { core::ptr::addr_of!(SOFTWARE_KEYS).read() }
475 .get(key_id)
476 .ok_or(TelemetryError::BadArg)
477}
478
479fn write_managed_credential_body(credential: ManagedCredential, out: &mut [u8]) {
480 out[..8].copy_from_slice(MANAGED_CREDENTIAL_MAGIC);
481 out[8..16].copy_from_slice(&credential.subject_id.to_le_bytes());
482 out[16..20].copy_from_slice(&credential.key_id.to_le_bytes());
483 out[20..28].copy_from_slice(&credential.epoch.to_le_bytes());
484 out[28..36].copy_from_slice(&credential.not_before_ms.to_le_bytes());
485 out[36..44].copy_from_slice(&credential.not_after_ms.to_le_bytes());
486 out[44..48].copy_from_slice(&credential.permissions.to_le_bytes());
487}
488
489fn read_managed_credential_body(bytes: &[u8]) -> TelemetryResult<ManagedCredential> {
490 if bytes.len() != MANAGED_CREDENTIAL_BODY_LEN || &bytes[..8] != MANAGED_CREDENTIAL_MAGIC {
491 return Err(TelemetryError::BadArg);
492 }
493 Ok(ManagedCredential {
494 subject_id: u64::from_le_bytes(bytes[8..16].try_into().unwrap()),
495 key_id: u32::from_le_bytes(bytes[16..20].try_into().unwrap()),
496 epoch: u64::from_le_bytes(bytes[20..28].try_into().unwrap()),
497 not_before_ms: u64::from_le_bytes(bytes[28..36].try_into().unwrap()),
498 not_after_ms: u64::from_le_bytes(bytes[36..44].try_into().unwrap()),
499 permissions: u32::from_le_bytes(bytes[44..48].try_into().unwrap()),
500 })
501}
502
503fn normalize_software_key(raw_key: &[u8]) -> [u8; 32] {
504 if raw_key.len() == 32 {
505 let mut key = [0u8; 32];
506 key.copy_from_slice(raw_key);
507 key
508 } else {
509 Sha256::digest(raw_key)
510 }
511}
512
513fn apply_hmac_stream(
514 key: &[u8; 32],
515 key_id: u32,
516 nonce: &[u8],
517 aad: &[u8],
518 input: &[u8],
519 output: &mut [u8],
520) {
521 let key_id_bytes = key_id.to_le_bytes();
522 let mut counter = 0u64;
523 let mut offset = 0usize;
524 while offset < input.len() {
525 let counter_bytes = counter.to_le_bytes();
526 let block = hmac_sha256(
527 key,
528 &[
529 b"SEDS-HMAC-STREAM",
530 &key_id_bytes,
531 nonce,
532 aad,
533 &counter_bytes,
534 ],
535 );
536 let remaining = input.len() - offset;
537 let take = remaining.min(block.len());
538 for idx in 0..take {
539 output[offset + idx] = input[offset + idx] ^ block[idx];
540 }
541 offset += take;
542 counter = counter.wrapping_add(1);
543 }
544}
545
546fn software_tag(
547 key: &[u8; 32],
548 key_id: u32,
549 nonce: &[u8],
550 aad: &[u8],
551 ciphertext: &[u8],
552) -> [u8; 32] {
553 let key_id_bytes = key_id.to_le_bytes();
554 hmac_sha256(
555 key,
556 &[b"SEDS-HMAC-TAG", &key_id_bytes, nonce, aad, ciphertext],
557 )
558}
559
560fn constant_time_eq(a: &[u8], b: &[u8]) -> bool {
561 if a.len() != b.len() {
562 return false;
563 }
564 let mut diff = 0u8;
565 for idx in 0..a.len() {
566 diff |= a[idx] ^ b[idx];
567 }
568 diff == 0
569}
570
571fn hmac_sha256(key: &[u8; 32], chunks: &[&[u8]]) -> [u8; 32] {
572 let mut ipad = [0x36u8; 64];
573 let mut opad = [0x5cu8; 64];
574 for idx in 0..key.len() {
575 ipad[idx] ^= key[idx];
576 opad[idx] ^= key[idx];
577 }
578
579 let mut inner = Sha256::new();
580 inner.update(&ipad);
581 for chunk in chunks {
582 inner.update(chunk);
583 }
584 let inner_hash = inner.finalize();
585
586 let mut outer = Sha256::new();
587 outer.update(&opad);
588 outer.update(&inner_hash);
589 outer.finalize()
590}
591
592#[derive(Clone)]
593struct Sha256 {
594 state: [u32; 8],
595 buffer: [u8; 64],
596 buffer_len: usize,
597 len_bits: u64,
598}
599
600impl Sha256 {
601 fn new() -> Self {
602 Self {
603 state: [
604 0x6a09e667, 0xbb67ae85, 0x3c6ef372, 0xa54ff53a, 0x510e527f, 0x9b05688c, 0x1f83d9ab,
605 0x5be0cd19,
606 ],
607 buffer: [0; 64],
608 buffer_len: 0,
609 len_bits: 0,
610 }
611 }
612
613 fn digest(input: &[u8]) -> [u8; 32] {
614 let mut hasher = Self::new();
615 hasher.update(input);
616 hasher.finalize()
617 }
618
619 fn update(&mut self, mut input: &[u8]) {
620 self.len_bits = self
621 .len_bits
622 .wrapping_add((input.len() as u64).wrapping_mul(8));
623 if self.buffer_len > 0 {
624 let take = (64 - self.buffer_len).min(input.len());
625 self.buffer[self.buffer_len..self.buffer_len + take].copy_from_slice(&input[..take]);
626 self.buffer_len += take;
627 input = &input[take..];
628 if self.buffer_len == 64 {
629 let block = self.buffer;
630 self.compress(&block);
631 self.buffer_len = 0;
632 }
633 }
634 while input.len() >= 64 {
635 let mut block = [0u8; 64];
636 block.copy_from_slice(&input[..64]);
637 self.compress(&block);
638 input = &input[64..];
639 }
640 if !input.is_empty() {
641 self.buffer[..input.len()].copy_from_slice(input);
642 self.buffer_len = input.len();
643 }
644 }
645
646 fn finalize(mut self) -> [u8; 32] {
647 self.buffer[self.buffer_len] = 0x80;
648 self.buffer_len += 1;
649 if self.buffer_len > 56 {
650 self.buffer[self.buffer_len..].fill(0);
651 let block = self.buffer;
652 self.compress(&block);
653 self.buffer_len = 0;
654 }
655 self.buffer[self.buffer_len..56].fill(0);
656 self.buffer[56..].copy_from_slice(&self.len_bits.to_be_bytes());
657 let block = self.buffer;
658 self.compress(&block);
659
660 let mut out = [0u8; 32];
661 for (idx, word) in self.state.iter().enumerate() {
662 out[idx * 4..idx * 4 + 4].copy_from_slice(&word.to_be_bytes());
663 }
664 out
665 }
666
667 fn compress(&mut self, block: &[u8; 64]) {
668 const K: [u32; 64] = [
669 0x428a2f98, 0x71374491, 0xb5c0fbcf, 0xe9b5dba5, 0x3956c25b, 0x59f111f1, 0x923f82a4,
670 0xab1c5ed5, 0xd807aa98, 0x12835b01, 0x243185be, 0x550c7dc3, 0x72be5d74, 0x80deb1fe,
671 0x9bdc06a7, 0xc19bf174, 0xe49b69c1, 0xefbe4786, 0x0fc19dc6, 0x240ca1cc, 0x2de92c6f,
672 0x4a7484aa, 0x5cb0a9dc, 0x76f988da, 0x983e5152, 0xa831c66d, 0xb00327c8, 0xbf597fc7,
673 0xc6e00bf3, 0xd5a79147, 0x06ca6351, 0x14292967, 0x27b70a85, 0x2e1b2138, 0x4d2c6dfc,
674 0x53380d13, 0x650a7354, 0x766a0abb, 0x81c2c92e, 0x92722c85, 0xa2bfe8a1, 0xa81a664b,
675 0xc24b8b70, 0xc76c51a3, 0xd192e819, 0xd6990624, 0xf40e3585, 0x106aa070, 0x19a4c116,
676 0x1e376c08, 0x2748774c, 0x34b0bcb5, 0x391c0cb3, 0x4ed8aa4a, 0x5b9cca4f, 0x682e6ff3,
677 0x748f82ee, 0x78a5636f, 0x84c87814, 0x8cc70208, 0x90befffa, 0xa4506ceb, 0xbef9a3f7,
678 0xc67178f2,
679 ];
680 let mut w = [0u32; 64];
681 for idx in 0..16 {
682 w[idx] = u32::from_be_bytes([
683 block[idx * 4],
684 block[idx * 4 + 1],
685 block[idx * 4 + 2],
686 block[idx * 4 + 3],
687 ]);
688 }
689 for idx in 16..64 {
690 let s0 =
691 w[idx - 15].rotate_right(7) ^ w[idx - 15].rotate_right(18) ^ (w[idx - 15] >> 3);
692 let s1 = w[idx - 2].rotate_right(17) ^ w[idx - 2].rotate_right(19) ^ (w[idx - 2] >> 10);
693 w[idx] = w[idx - 16]
694 .wrapping_add(s0)
695 .wrapping_add(w[idx - 7])
696 .wrapping_add(s1);
697 }
698
699 let mut a = self.state[0];
700 let mut b = self.state[1];
701 let mut c = self.state[2];
702 let mut d = self.state[3];
703 let mut e = self.state[4];
704 let mut f = self.state[5];
705 let mut g = self.state[6];
706 let mut h = self.state[7];
707
708 for idx in 0..64 {
709 let s1 = e.rotate_right(6) ^ e.rotate_right(11) ^ e.rotate_right(25);
710 let ch = (e & f) ^ ((!e) & g);
711 let temp1 = h
712 .wrapping_add(s1)
713 .wrapping_add(ch)
714 .wrapping_add(K[idx])
715 .wrapping_add(w[idx]);
716 let s0 = a.rotate_right(2) ^ a.rotate_right(13) ^ a.rotate_right(22);
717 let maj = (a & b) ^ (a & c) ^ (b & c);
718 let temp2 = s0.wrapping_add(maj);
719
720 h = g;
721 g = f;
722 f = e;
723 e = d.wrapping_add(temp1);
724 d = c;
725 c = b;
726 b = a;
727 a = temp1.wrapping_add(temp2);
728 }
729
730 self.state[0] = self.state[0].wrapping_add(a);
731 self.state[1] = self.state[1].wrapping_add(b);
732 self.state[2] = self.state[2].wrapping_add(c);
733 self.state[3] = self.state[3].wrapping_add(d);
734 self.state[4] = self.state[4].wrapping_add(e);
735 self.state[5] = self.state[5].wrapping_add(f);
736 self.state[6] = self.state[6].wrapping_add(g);
737 self.state[7] = self.state[7].wrapping_add(h);
738 }
739}
740
741#[cfg(test)]
742mod tests {
743 use super::*;
744
745 struct XorShim;
746
747 impl CryptographyProvider for XorShim {
748 fn seal(
749 &self,
750 key_id: u32,
751 _nonce: &[u8],
752 _aad: &[u8],
753 plaintext: &[u8],
754 ciphertext_out: &mut [u8],
755 tag_out: &mut [u8],
756 ) -> TelemetryResult<(usize, usize)> {
757 if ciphertext_out.len() < plaintext.len() || tag_out.len() < 4 {
758 return Err(TelemetryError::SizeMismatchError);
759 }
760 for (idx, byte) in plaintext.iter().enumerate() {
761 ciphertext_out[idx] = *byte ^ (key_id as u8);
762 }
763 tag_out[..4].copy_from_slice(b"SEDS");
764 Ok((plaintext.len(), 4))
765 }
766
767 fn open(
768 &self,
769 key_id: u32,
770 _nonce: &[u8],
771 _aad: &[u8],
772 ciphertext: &[u8],
773 tag: &[u8],
774 plaintext_out: &mut [u8],
775 ) -> TelemetryResult<usize> {
776 if plaintext_out.len() < ciphertext.len() || tag != b"SEDS" {
777 return Err(TelemetryError::SizeMismatchError);
778 }
779 for (idx, byte) in ciphertext.iter().enumerate() {
780 plaintext_out[idx] = *byte ^ (key_id as u8);
781 }
782 Ok(ciphertext.len())
783 }
784 }
785
786 #[test]
787 fn rust_encryption_roundtrips_without_c_callbacks() {
788 let provider = XorShim;
789 let plaintext = [1_u8, 2, 3, 4];
790 let mut ciphertext = [0_u8; 8];
791 let mut tag = [0_u8; 8];
792 let (ciphertext_len, tag_len) = seal_with(
793 &provider,
794 9,
795 &[0; 12],
796 b"aad",
797 &plaintext,
798 &mut ciphertext,
799 &mut tag,
800 )
801 .unwrap();
802 assert_eq!(ciphertext_len, plaintext.len());
803 assert_eq!(tag_len, 4);
804 let mut opened = [0_u8; 8];
805 let opened_len = open_with(
806 &provider,
807 9,
808 &[0; 12],
809 b"aad",
810 &ciphertext[..ciphertext_len],
811 &tag[..tag_len],
812 &mut opened,
813 )
814 .unwrap();
815 assert_eq!(opened_len, plaintext.len());
816 assert_eq!(&opened[..opened_len], plaintext);
817 }
818
819 #[test]
820 fn managed_credentials_verify_and_reject_tamper_or_expiry() {
821 let root = b"root key material with at least 32 bytes";
822 let credential = ManagedCredential {
823 subject_id: 0xAABB,
824 key_id: 7,
825 epoch: 11,
826 not_before_ms: 100,
827 not_after_ms: 1_000,
828 permissions: 0x05,
829 };
830 let mut bytes = [0u8; MANAGED_CREDENTIAL_LEN];
831 let len = issue_managed_credential(root, credential, &mut bytes).unwrap();
832 assert_eq!(len, MANAGED_CREDENTIAL_LEN);
833 assert_eq!(
834 verify_managed_credential(root, &bytes, 500).unwrap(),
835 credential
836 );
837
838 let mut tampered = bytes;
839 tampered[20] ^= 0x01;
840 assert!(verify_managed_credential(root, &tampered, 500).is_err());
841 assert!(verify_managed_credential(root, &bytes, 1_001).is_err());
842 }
843}