1#![no_std]
14#![doc(html_logo_url = "https://edp.fortanix.com/img/docs/edp-logo.svg",
15 html_favicon_url = "https://edp.fortanix.com/favicon.ico",
16 html_root_url = "https://edp.fortanix.com/docs/api/")]
17#![cfg_attr(all(feature = "sgxstd", target_env = "sgx"), feature(sgx_platform))]
18
19#[cfg(all(feature = "sgxstd", target_env = "sgx"))]
20extern crate std;
21
22#[macro_use]
23extern crate bitflags;
24
25#[cfg(feature = "serde")]
26extern crate serde;
27
28#[cfg(feature = "serde")]
29use serde::{Serialize, Deserialize};
30
31#[cfg(all(target_env = "sgx", feature = "sgxstd"))]
32use std::os::fortanix_sgx::arch;
33#[cfg(all(target_env = "sgx", not(feature = "sgxstd")))]
34mod arch;
35use core::{convert::TryFrom, num::TryFromIntError, slice};
36
37
38#[cfg(feature = "serde")]
39mod array_64 {
40 use serde::{
41 de::{Deserializer, Error, SeqAccess, Visitor},
42 ser::{SerializeTuple, Serializer},
43 };
44 use core::fmt;
45
46 const LEN: usize = 64;
47
48 pub fn serialize<S: Serializer>(array: &[u8; LEN], serializer: S) -> Result<S::Ok, S::Error> {
49 let mut seq = serializer.serialize_tuple(LEN)?;
50 for elem in &array[..] {
51 seq.serialize_element(elem)?;
52 }
53 seq.end()
54 }
55
56 pub fn deserialize<'de, D: Deserializer<'de>>(deserializer: D) -> Result<[u8; LEN], D::Error> {
57 struct ArrayVisitor;
58 impl<'de> Visitor<'de> for ArrayVisitor {
59 type Value = [u8; LEN];
60 fn expecting(&self, formatter: &mut fmt::Formatter) -> fmt::Result {
61 write!(formatter, "an array of length 64")
62 }
63 fn visit_seq<A: SeqAccess<'de>>(self, mut seq: A) -> Result<[u8; LEN], A::Error> {
64 let mut arr = [0; LEN];
65 for i in 0..LEN {
66 arr[i] = seq
67 .next_element()?
68 .ok_or_else(|| A::Error::invalid_length(i, &self))?;
69 }
70 Ok(arr)
71 }
72 }
73 deserializer.deserialize_tuple(64, ArrayVisitor)
74 }
75}
76
77#[cfg(feature = "serde")]
80fn report_reserved1() -> [u8; 28] {
81 [0u8; 28]
82}
83
84#[cfg(feature = "serde")]
85fn report_reserved2() -> [u8; 32] {
86 [0u8; 32]
87}
88
89#[cfg(feature = "serde")]
90fn report_reserved3() -> [u8; 96] {
91 [0u8; 96]
92}
93
94#[cfg(feature = "serde")]
95fn report_reserved4() -> [u8; 60] {
96 [0u8; 60]
97}
98
99#[cfg(feature = "serde")]
100fn ti_reserved1() -> [u8; 4] {
101 [0u8; 4]
102}
103
104#[cfg(feature = "serde")]
105fn ti_reserved2() -> [u8; 456] {
106 [0u8; 456]
107}
108
109#[cfg(not(feature = "large_array_derive"))]
110#[macro_use]
111mod large_array_impl;
112#[cfg(feature = "large_array_derive")]
113macro_rules! impl_default_clone_eq {
114 ($n:ident) => {};
115}
116
117macro_rules! enum_def {
118 (
119 #[derive($($derive:meta),*)]
120 #[repr($repr:ident)]
121 pub enum $name:ident {
122 $($key:ident = $val:expr,)*
123 }
124 ) => (
125 #[derive($($derive),*)]
126 #[repr($repr)]
127 pub enum $name {
128 $($key = $val,)*
129 }
130
131 impl TryFrom<$repr> for $name {
132 type Error = TryFromIntError;
133 fn try_from(v: $repr) -> Result<Self, Self::Error> {
134 match v {
135 $($val => Ok($name::$key),)*
136 _ => Err(u8::try_from(256u16).unwrap_err()),
137 }
138 }
139 }
140 )
141}
142
143macro_rules! struct_def {
144 (
145 #[repr(C $(, align($align:tt))*)]
146 $(#[cfg_attr(feature = "large_array_derive", derive($($cfgderive:meta),*))])*
147 $(#[cfg_attr(feature = "serde", derive($($serdederive:meta),*))])*
148 $(#[derive($($derive:meta),*)])*
149 pub struct $name:ident $impl:tt
150 ) => {
151 $(
152 impl_default_clone_eq!($name);
153 #[cfg_attr(feature = "large_array_derive", derive($($cfgderive),*))]
154 )*
155 #[repr(C $(, align($align))*)]
156 $(#[cfg_attr(feature = "serde", derive($($serdederive),*))])*
157 $(#[derive($($derive),*)])*
158 pub struct $name $impl
159
160 impl $name {
161 pub fn try_copy_from(src: &[u8]) -> Option<Self> {
164 if src.len() == Self::UNPADDED_SIZE {
165 unsafe {
166 let mut ret : Self = ::core::mem::zeroed();
167 ::core::ptr::copy_nonoverlapping(src.as_ptr(),
168 &mut ret as *mut _ as *mut _,
169 Self::UNPADDED_SIZE);
170 Some(ret)
171 }
172 } else {
173 None
174 }
175 }
176
177 fn _type_tests() {
180 #[repr(C)]
181 $(#[cfg_attr(feature = "serde", derive($($serdederive),*))])*
182 struct _Unaligned $impl
183
184 impl _Unaligned {
185 unsafe fn _check_size(self) -> [u8; $name::UNPADDED_SIZE] {
186 ::core::mem::transmute(self)
187 }
188 }
189
190 }
194 }
195
196 $(
197 #[test]
199 #[allow(non_snake_case)]
200 fn $name() {
201 assert_eq!($align, ::core::mem::align_of::<$name>());
202 }
203 )*
204
205 impl AsRef<[u8]> for $name {
206 fn as_ref(&self) -> &[u8] {
207 unsafe {
208 slice::from_raw_parts(self as *const $name as *const u8, Self::UNPADDED_SIZE)
209 }
210 }
211 }
212
213 struct_def!(@align bytes $($align)* name $name);
214 };
215 (@align bytes 16 name $name:ident) => {
216 struct_def!(@align type Align16 name $name);
217 };
218 (@align bytes 128 name $name:ident) => {
219 struct_def!(@align type Align128 name $name);
220 };
221 (@align bytes 512 name $name:ident) => {
222 struct_def!(@align type Align512 name $name);
223 };
224 (@align bytes $($other:tt)*) => {};
225 (@align type $ty:ident name $name:ident) => {
226 #[cfg(target_env = "sgx")]
227 impl AsRef<arch::$ty<[u8; $name::UNPADDED_SIZE]>> for $name {
228 fn as_ref(&self) -> &arch::$ty<[u8; $name::UNPADDED_SIZE]> {
229 unsafe {
230 &*(self as *const _ as *const _)
231 }
232 }
233 }
234 };
235}
236
237enum_def! {
238#[derive(Clone,Copy,Debug,PartialEq,Eq)]
239#[repr(u32)]
240pub enum Encls {
241 ECreate = 0,
242 EAdd = 1,
243 EInit = 2,
244 ERemove = 3,
245 EDbgrd = 4,
246 EDbgwr = 5,
247 EExtend = 6,
248 ELdb = 7,
249 ELdu = 8,
250 EBlock = 9,
251 EPa = 10,
252 EWb = 11,
253 ETrack = 12,
254 EAug = 13,
255 EModpr = 14,
256 EModt = 15,
257}
258}
259
260enum_def! {
261#[derive(Clone,Copy,Debug,PartialEq,Eq)]
262#[repr(u32)]
263pub enum Enclu {
264 EReport = 0,
265 EGetkey = 1,
266 EEnter = 2,
267 EResume = 3,
268 EExit = 4,
269 EAccept = 5,
270 EModpe = 6,
271 EAcceptcopy = 7,
272}
273}
274
275enum_def! {
276#[derive(Clone,Copy,Debug,PartialEq,Eq)]
277#[repr(u32)]
278pub enum ErrorCode {
279 Success = 0,
280 InvalidSigStruct = 1,
281 InvalidAttribute = 2,
282 Blkstate = 3, InvalidMeasurement = 4,
284 Notblockable = 5,
285 PgInvld = 6,
286 Lockfail = 7,
287 InvalidSignature = 8,
288 MacCompareFail = 9,
289 PageNotBlocked = 10,
290 NotTracked = 11,
291 VaSlotOccupied = 12,
292 ChildPresent = 13,
293 EnclaveAct = 14,
294 EntryepochLocked = 15,
295 InvalidEinitToken = 16,
296 PrevTrkIncmpl = 17,
297 PgIsSecs = 18,
298 PageAttributesMismatch = 19,
299 PageNotModifiable = 20,
300 PageNotDebuggable = 21,
301 InvalidCpusvn = 32,
302 InvalidIsvsvn = 64,
303 UnmaskedEvent = 128,
304 InvalidKeyname = 256,
305}
306}
307
308pub const MEAS_ECREATE: u64 = 0x0045544145524345;
309pub const MEAS_EADD: u64 = 0x0000000044444145;
310pub const MEAS_EEXTEND: u64 = 0x00444E4554584545;
311
312pub const SIGSTRUCT_HEADER1: [u8; 16] = [
313 0x06, 0x00, 0x00, 0x00, 0xE1, 0x00, 0x00, 0x00, 0x00, 0x00, 0x01, 0x00, 0x00, 0x00, 0x00, 0x00,
314];
315pub const SIGSTRUCT_HEADER2: [u8; 16] = [
316 0x01, 0x01, 0x00, 0x00, 0x60, 0x00, 0x00, 0x00, 0x60, 0x00, 0x00, 0x00, 0x01, 0x00, 0x00, 0x00,
317];
318
319enum_def! {
320#[derive(Clone,Copy,Debug,PartialEq,Eq)]
321#[repr(u8)]
322pub enum PageType {
323 Secs = 0,
324 Tcs = 1,
325 Reg = 2,
326 Va = 3,
327 Trim = 4,
328}
329}
330
331enum_def! {
332#[derive(Clone,Copy,Debug,PartialEq,Eq)]
333#[repr(u16)]
334pub enum Keyname {
335 Einittoken = 0,
336 Provision = 1,
337 ProvisionSeal = 2,
338 Report = 3,
339 Seal = 4,
340}
341}
342
343struct_def! {
344#[repr(C, align(4096))]
345#[cfg_attr(
346 feature = "large_array_derive",
347 derive(Clone, Debug, Default, Eq, PartialEq)
348)]
349pub struct Secs {
350 pub size: u64,
351 pub baseaddr: u64,
352 pub ssaframesize: u32,
353 pub miscselect: Miscselect,
354 pub _reserved1: [u8; 24],
355 pub attributes: Attributes,
356 pub mrenclave: [u8; 32],
357 pub _reserved2: [u8; 32],
358 pub mrsigner: [u8; 32],
359 pub _reserved3: [u8; 96],
360 pub isvprodid: u16,
361 pub isvsvn: u16,
362 pub padding: [u8; 3836],
363}
364}
365
366impl Secs {
367 pub const UNPADDED_SIZE: usize = 4096;
368}
369
370struct_def! {
371#[repr(C)]
372#[cfg_attr(feature = "serde", derive(Serialize, Deserialize))]
373#[derive(Clone, Copy, Debug, Default, Eq, PartialEq)]
374pub struct Attributes {
375 pub flags: AttributesFlags,
376 pub xfrm: u64,
377}
378}
379
380impl Attributes {
381 pub const UNPADDED_SIZE: usize = 16;
382}
383
384bitflags! {
385 #[repr(C)]
386 #[cfg_attr(feature = "serde", derive(Serialize, Deserialize))]
387 pub struct AttributesFlags: u64 {
388 const INIT = 0b0000_0001;
389 const DEBUG = 0b0000_0010;
390 const MODE64BIT = 0b0000_0100;
391 const PROVISIONKEY = 0b0001_0000;
392 const EINITTOKENKEY = 0b0010_0000;
393 const CET = 0b0100_0000;
394 const KSS = 0b1000_0000;
395 }
396}
397
398impl Default for AttributesFlags {
399 fn default() -> Self {
400 Self::empty()
401 }
402}
403
404bitflags! {
405 #[repr(C)]
406 #[cfg_attr(feature = "serde", derive(Serialize, Deserialize))]
407 pub struct Miscselect: u32 {
408 const EXINFO = 0b0000_0001;
409 }
410}
411
412impl Default for Miscselect {
413 fn default() -> Self {
414 Self::empty()
415 }
416}
417
418struct_def! {
419#[repr(C, align(4096))]
420#[cfg_attr(
421 feature = "large_array_derive",
422 derive(Clone, Debug, Default, Eq, PartialEq)
423)]
424pub struct Tcs {
425 pub _reserved1: u64,
426 pub flags: TcsFlags,
427 pub ossa: u64,
428 pub cssa: u32,
429 pub nssa: u32,
430 pub oentry: u64,
431 pub _reserved2: u64,
432 pub ofsbasgx: u64,
433 pub ogsbasgx: u64,
434 pub fslimit: u32,
435 pub gslimit: u32,
436 pub _reserved3: [u8; 4024],
437}
438}
439
440impl Tcs {
441 pub const UNPADDED_SIZE: usize = 4096;
442}
443
444bitflags! {
445 #[repr(C)]
446 pub struct TcsFlags: u64 {
447 const DBGOPTIN = 0b0000_0001;
448 }
449}
450
451impl Default for TcsFlags {
452 fn default() -> Self {
453 Self::empty()
454 }
455}
456
457struct_def! {
458#[repr(C, align(32))]
459#[derive(Clone, Debug, Default, Eq, PartialEq)]
460pub struct Pageinfo {
461 pub linaddr: u64,
462 pub srcpge: u64,
463 pub secinfo: u64,
464 pub secs: u64,
465}
466}
467
468impl Pageinfo {
469 pub const UNPADDED_SIZE: usize = 32;
470}
471
472struct_def! {
473#[repr(C, align(64))]
474#[cfg_attr(
475 feature = "large_array_derive",
476 derive(Clone, Debug, Default, Eq, PartialEq)
477)]
478pub struct Secinfo {
479 pub flags: SecinfoFlags,
480 pub _reserved1: [u8; 56],
481}
482}
483
484impl Secinfo {
485 pub const UNPADDED_SIZE: usize = 64;
486}
487
488bitflags! {
489 #[repr(C)]
490 pub struct SecinfoFlags: u64 {
491 const R = 0b0000_0000_0000_0001;
492 const W = 0b0000_0000_0000_0010;
493 const X = 0b0000_0000_0000_0100;
494 const PENDING = 0b0000_0000_0000_1000;
495 const MODIFIED = 0b0000_0000_0001_0000;
496 const PR = 0b0000_0000_0010_0000;
497 const PT_MASK = 0b1111_1111_0000_0000;
498 const PT_B0 = 0b0000_0001_0000_0000; const PT_B1 = 0b0000_0010_0000_0000; const PT_B2 = 0b0000_0100_0000_0000; const PT_B3 = 0b0000_1000_0000_0000; const PT_B4 = 0b0001_0000_0000_0000; const PT_B5 = 0b0010_0000_0000_0000; const PT_B6 = 0b0100_0000_0000_0000; const PT_B7 = 0b1000_0000_0000_0000; }
507}
508
509impl Default for SecinfoFlags {
510 fn default() -> Self {
511 Self::empty()
512 }
513}
514
515impl SecinfoFlags {
516 pub fn page_type(&self) -> u8 {
517 (((*self & SecinfoFlags::PT_MASK).bits) >> 8) as u8
518 }
519
520 pub fn page_type_mut(&mut self) -> &mut u8 {
521 use core::mem::transmute;
522 unsafe {
523 let page_type: &mut [u8; 8] = transmute(&mut self.bits);
524 transmute(&mut page_type[1])
525 }
526 }
527}
528
529impl From<PageType> for SecinfoFlags {
530 fn from(data: PageType) -> SecinfoFlags {
531 SecinfoFlags::from_bits_truncate((data as u64) << 8)
532 }
533}
534
535struct_def! {
536#[repr(C, align(128))]
537#[cfg_attr(
538 feature = "large_array_derive",
539 derive(Clone, Debug, Default, Eq, PartialEq)
540)]
541pub struct Pcmd {
542 pub secinfo: Secinfo,
543 pub enclaveid: u64,
544 pub _reserved1: [u8; 40],
545 pub mac: [u8; 16],
546}
547}
548
549impl Pcmd {
550 pub const UNPADDED_SIZE: usize = 128;
551}
552
553struct_def! {
554#[repr(C, align(4096))]
555#[cfg_attr(
556 feature = "large_array_derive",
557 derive(Clone, Debug, Default, Eq, PartialEq)
558)]
559pub struct Sigstruct {
560 pub header: [u8; 16],
561 pub vendor: u32,
562 pub date: u32,
563 pub header2: [u8; 16],
564 pub swdefined: u32,
565 pub _reserved1: [u8; 84],
566 pub modulus: [u8; 384],
567 pub exponent: u32,
568 pub signature: [u8; 384],
569 pub miscselect: Miscselect,
570 pub miscmask: u32,
571 pub _reserved2: [u8; 20],
572 pub attributes: Attributes,
573 pub attributemask: [u64; 2],
574 pub enclavehash: [u8; 32],
575 pub _reserved3: [u8; 32],
576 pub isvprodid: u16,
577 pub isvsvn: u16,
578 pub _reserved4: [u8; 12],
579 pub q1: [u8; 384],
580 pub q2: [u8; 384],
581}
582}
583
584impl Sigstruct {
585 pub const UNPADDED_SIZE: usize = 1808;
586
587 pub fn signature_data(&self) -> (&[u8], &[u8]) {
590 unsafe {
591 let part1_start = &(self.header) as *const _ as *const u8;
592 let part1_end = &(self.modulus) as *const _ as *const u8 as usize;
593 let part2_start = &(self.miscselect) as *const _ as *const u8;
594 let part2_end = &(self._reserved4) as *const _ as *const u8 as usize;
595
596 (
597 slice::from_raw_parts(part1_start, part1_end - (part1_start as usize)),
598 slice::from_raw_parts(part2_start, part2_end - (part2_start as usize))
599 )
600 }
601 }
602}
603
604struct_def! {
605#[repr(C, align(512))]
606#[cfg_attr(
607 feature = "large_array_derive",
608 derive(Clone, Debug, Default, Eq, PartialEq)
609)]
610pub struct Einittoken {
611 pub valid: u32,
612 pub _reserved1: [u8; 44],
613 pub attributes: Attributes,
614 pub mrenclave: [u8; 32],
615 pub _reserved2: [u8; 32],
616 pub mrsigner: [u8; 32],
617 pub _reserved3: [u8; 32],
618 pub cpusvnle: [u8; 16],
619 pub isvprodidle: u16,
620 pub isvsvnle: u16,
621 pub _reserved4: [u8; 24],
622 pub maskedmiscselectle: Miscselect,
623 pub maskedattributesle: Attributes,
624 pub keyid: [u8; 32],
625 pub mac: [u8; 16],
626}
627}
628
629impl Einittoken {
630 pub const UNPADDED_SIZE: usize = 304;
631}
632
633struct_def! {
634#[repr(C, align(512))]
635#[cfg_attr(
636 feature = "large_array_derive",
637 derive(Clone, Debug, Default, Eq, PartialEq)
638)]
639#[cfg_attr(feature = "serde", derive(Serialize, Deserialize))]
640pub struct Report {
641 pub cpusvn: [u8; 16],
642 pub miscselect: Miscselect,
643 #[cfg_attr(feature = "serde", serde(default = "report_reserved1"), serde(skip))]
644 pub _reserved1: [u8; 28],
645 pub attributes: Attributes,
646 pub mrenclave: [u8; 32],
647 #[cfg_attr(feature = "serde", serde(default = "report_reserved2"), serde(skip))]
648 pub _reserved2: [u8; 32],
649 pub mrsigner: [u8; 32],
650 #[cfg_attr(feature = "serde", serde(default = "report_reserved3"), serde(skip))]
651 pub _reserved3: [u8; 96],
652 pub isvprodid: u16,
653 pub isvsvn: u16,
654 #[cfg_attr(feature = "serde", serde(default = "report_reserved4"), serde(skip))]
655 pub _reserved4: [u8; 60],
656 #[cfg_attr(feature = "serde", serde(with = "array_64"))]
657 pub reportdata: [u8; 64],
658 pub keyid: [u8; 32],
659 pub mac: [u8; 16],
660}
661}
662
663impl Report {
664 pub const UNPADDED_SIZE: usize = 432;
665 pub const TRUNCATED_SIZE: usize = 384;
667
668 #[cfg(target_env = "sgx")]
678 pub fn for_self() -> Self {
679 let reportdata = arch::Align128([0; 64]);
680 let targetinfo = arch::Align512([0; 512]);
681 let out = arch::ereport(&targetinfo, &reportdata);
682 Report::try_copy_from(&out.0).unwrap()
684 }
685
686 #[cfg(target_env = "sgx")]
687 pub fn for_target(targetinfo: &Targetinfo, reportdata: &[u8; 64]) -> Report {
688 let reportdata = arch::Align128(*reportdata);
689 let out = arch::ereport(targetinfo.as_ref(), &reportdata);
690 Report::try_copy_from(&out.0).unwrap()
692 }
693
694 #[cfg(target_env = "sgx")]
700 pub fn verify<F, R>(&self, check_mac: F) -> R
701 where
702 F: FnOnce(&[u8; 16], &[u8; Report::TRUNCATED_SIZE], &[u8; 16]) -> R,
703 {
704 let req = Keyrequest {
705 keyname: Keyname::Report as u16,
706 keyid: self.keyid,
707 ..Default::default()
708 };
709 let key = req.egetkey().expect("Couldn't get report key");
710 check_mac(
711 &key,
712 self.mac_data(),
713 &self.mac,
714 )
715 }
716
717 pub fn mac_data(&self) -> &[u8; Report::TRUNCATED_SIZE] {
719 unsafe {
720 &*(self as *const Self as *const [u8; Report::TRUNCATED_SIZE])
721 }
722 }
723}
724
725struct_def! {
726#[repr(C, align(512))]
727#[cfg_attr(
728 feature = "large_array_derive",
729 derive(Clone, Debug, Default, Eq, PartialEq)
730)]
731#[cfg_attr(feature = "serde", derive(Serialize, Deserialize))]
732pub struct Targetinfo {
733 pub measurement: [u8; 32],
734 pub attributes: Attributes,
735 #[cfg_attr(feature = "serde", serde(default = "ti_reserved1"), serde(skip))]
736 pub _reserved1: [u8; 4],
737 pub miscselect: Miscselect,
738 #[cfg_attr(feature = "serde", serde(default = "ti_reserved2"), serde(skip))]
739 pub _reserved2: [u8; 456],
740}
741}
742
743impl Targetinfo {
744 pub const UNPADDED_SIZE: usize = 512;
745}
746
747impl From<Report> for Targetinfo {
748 fn from(r: Report) -> Targetinfo {
749 Targetinfo {
750 measurement: r.mrenclave,
751 attributes: r.attributes,
752 miscselect: r.miscselect,
753 ..Targetinfo::default()
754 }
755 }
756}
757
758struct_def! {
759#[repr(C, align(512))]
760#[cfg_attr(
761 feature = "large_array_derive",
762 derive(Clone, Debug, Default, Eq, PartialEq)
763)]
764pub struct Keyrequest {
765 pub keyname: u16,
766 pub keypolicy: Keypolicy,
767 pub isvsvn: u16,
768 pub _reserved1: u16,
769 pub cpusvn: [u8; 16],
770 pub attributemask: [u64; 2],
771 pub keyid: [u8; 32],
772 pub miscmask: u32,
773 pub _reserved2: [u8; 436],
774}
775}
776
777impl Keyrequest {
778 pub const UNPADDED_SIZE: usize = 512;
779
780 #[cfg(target_env = "sgx")]
781 pub fn egetkey(&self) -> Result<[u8; 16], ErrorCode> {
782 match arch::egetkey(self.as_ref()) {
783 Ok(k) => Ok(k.0),
784 Err(e) => Err(ErrorCode::try_from(e).unwrap())
786 }
787 }
788}
789
790bitflags! {
791 #[repr(C)]
792 pub struct Keypolicy: u16 {
793 const MRENCLAVE = 0b0000_0001;
794 const MRSIGNER = 0b0000_0010;
795 }
796}
797
798impl Default for Keypolicy {
799 fn default() -> Self {
800 Self::empty()
801 }
802}
803
804#[test]
805fn test_eq() {
806 let mut a = Keyrequest::default();
807 let mut b = Keyrequest::default();
808 assert!(a == b);
809
810 a.keyname = 22;
811 assert!(a != b);
812
813 b.keyname = 22;
814 assert!(a == b);
815
816 a.miscmask = 0xdeadbeef;
817 assert!(a != b);
818
819 b.miscmask = 0xdeadbeef;
820 assert!(a == b);
821}