mach_siegbert_vogt_dxcsa/
lib.rs

1const PADDING: [u8; 64] = [
2    0x80, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
3    0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
4    0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
5    0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
6];
7
8#[derive(Clone)]
9struct Context {
10    input: [u8; 64],
11    i: [u32; 2],
12    buf: [u32; 4],
13}
14
15fn transform(state: &mut [u32; 4], input: &[u32; 16]) {
16    let (mut a, mut b, mut c, mut d) = (state[0], state[1], state[2], state[3]);
17    macro_rules! add(
18        ($a:expr, $b:expr) => ($a.wrapping_add($b));
19    );
20    macro_rules! rotate(
21        ($x:expr, $n:expr) => (($x << $n) | ($x >> (32 - $n)));
22    );
23    {
24        macro_rules! F(
25            ($x:expr, $y:expr, $z:expr) => (($x & $y) | (!$x & $z));
26        );
27        macro_rules! T(
28            ($a:expr, $b:expr, $c:expr, $d:expr, $x:expr, $s:expr, $ac:expr) => ({
29                $a = add!(add!(add!($a, F!($b, $c, $d)), $x), $ac);
30                $a = rotate!($a, $s);
31                $a = add!($a, $b);
32            });
33        );
34        const S1: u32 = 7;
35        const S2: u32 = 12;
36        const S3: u32 = 17;
37        const S4: u32 = 22;
38        T!(a, b, c, d, input[0], S1, 3614090360);
39        T!(d, a, b, c, input[1], S2, 3905402710);
40        T!(c, d, a, b, input[2], S3, 606105819);
41        T!(b, c, d, a, input[3], S4, 3250441966);
42        T!(a, b, c, d, input[4], S1, 4118548399);
43        T!(d, a, b, c, input[5], S2, 1200080426);
44        T!(c, d, a, b, input[6], S3, 2821735955);
45        T!(b, c, d, a, input[7], S4, 4249261313);
46        T!(a, b, c, d, input[8], S1, 1770035416);
47        T!(d, a, b, c, input[9], S2, 2336552879);
48        T!(c, d, a, b, input[10], S3, 4294925233);
49        T!(b, c, d, a, input[11], S4, 2304563134);
50        T!(a, b, c, d, input[12], S1, 1804603682);
51        T!(d, a, b, c, input[13], S2, 4254626195);
52        T!(c, d, a, b, input[14], S3, 2792965006);
53        T!(b, c, d, a, input[15], S4, 1236535329);
54    }
55    {
56        macro_rules! F(
57            ($x:expr, $y:expr, $z:expr) => (($x & $z) | ($y & !$z));
58        );
59        macro_rules! T(
60            ($a:expr, $b:expr, $c:expr, $d:expr, $x:expr, $s:expr, $ac:expr) => ({
61                $a = add!(add!(add!($a, F!($b, $c, $d)), $x), $ac);
62                $a = rotate!($a, $s);
63                $a = add!($a, $b);
64            });
65        );
66        const S1: u32 = 5;
67        const S2: u32 = 9;
68        const S3: u32 = 14;
69        const S4: u32 = 20;
70        T!(a, b, c, d, input[1], S1, 4129170786);
71        T!(d, a, b, c, input[6], S2, 3225465664);
72        T!(c, d, a, b, input[11], S3, 643717713);
73        T!(b, c, d, a, input[0], S4, 3921069994);
74        T!(a, b, c, d, input[5], S1, 3593408605);
75        T!(d, a, b, c, input[10], S2, 38016083);
76        T!(c, d, a, b, input[15], S3, 3634488961);
77        T!(b, c, d, a, input[4], S4, 3889429448);
78        T!(a, b, c, d, input[9], S1, 568446438);
79        T!(d, a, b, c, input[14], S2, 3275163606);
80        T!(c, d, a, b, input[3], S3, 4107603335);
81        T!(b, c, d, a, input[8], S4, 1163531501);
82        T!(a, b, c, d, input[13], S1, 2850285829);
83        T!(d, a, b, c, input[2], S2, 4243563512);
84        T!(c, d, a, b, input[7], S3, 1735328473);
85        T!(b, c, d, a, input[12], S4, 2368359562);
86    }
87    {
88        macro_rules! F(
89            ($x:expr, $y:expr, $z:expr) => ($x ^ $y ^ $z);
90        );
91        macro_rules! T(
92            ($a:expr, $b:expr, $c:expr, $d:expr, $x:expr, $s:expr, $ac:expr) => ({
93                $a = add!(add!(add!($a, F!($b, $c, $d)), $x), $ac);
94                $a = rotate!($a, $s);
95                $a = add!($a, $b);
96            });
97        );
98        const S1: u32 = 4;
99        const S2: u32 = 11;
100        const S3: u32 = 16;
101        const S4: u32 = 23;
102        T!(a, b, c, d, input[5], S1, 4294588738);
103        T!(d, a, b, c, input[8], S2, 2272392833);
104        T!(c, d, a, b, input[11], S3, 1839030562);
105        T!(b, c, d, a, input[14], S4, 4259657740);
106        T!(a, b, c, d, input[1], S1, 2763975236);
107        T!(d, a, b, c, input[4], S2, 1272893353);
108        T!(c, d, a, b, input[7], S3, 4139469664);
109        T!(b, c, d, a, input[10], S4, 3200236656);
110        T!(a, b, c, d, input[13], S1, 681279174);
111        T!(d, a, b, c, input[0], S2, 3936430074);
112        T!(c, d, a, b, input[3], S3, 3572445317);
113        T!(b, c, d, a, input[6], S4, 76029189);
114        T!(a, b, c, d, input[9], S1, 3654602809);
115        T!(d, a, b, c, input[12], S2, 3873151461);
116        T!(c, d, a, b, input[15], S3, 530742520);
117        T!(b, c, d, a, input[2], S4, 3299628645);
118    }
119    {
120        macro_rules! F(
121            ($x:expr, $y:expr, $z:expr) => ($y ^ ($x | !$z));
122        );
123        macro_rules! T(
124            ($a:expr, $b:expr, $c:expr, $d:expr, $x:expr, $s:expr, $ac:expr) => ({
125                $a = add!(add!(add!($a, F!($b, $c, $d)), $x), $ac);
126                $a = rotate!($a, $s);
127                $a = add!($a, $b);
128            });
129        );
130        const S1: u32 = 6;
131        const S2: u32 = 10;
132        const S3: u32 = 15;
133        const S4: u32 = 21;
134        T!(a, b, c, d, input[0], S1, 4096336452);
135        T!(d, a, b, c, input[7], S2, 1126891415);
136        T!(c, d, a, b, input[14], S3, 2878612391);
137        T!(b, c, d, a, input[5], S4, 4237533241);
138        T!(a, b, c, d, input[12], S1, 1700485571);
139        T!(d, a, b, c, input[3], S2, 2399980690);
140        T!(c, d, a, b, input[10], S3, 4293915773);
141        T!(b, c, d, a, input[1], S4, 2240044497);
142        T!(a, b, c, d, input[8], S1, 1873313359);
143        T!(d, a, b, c, input[15], S2, 4264355552);
144        T!(c, d, a, b, input[6], S3, 2734768916);
145        T!(b, c, d, a, input[13], S4, 1309151649);
146        T!(a, b, c, d, input[4], S1, 4149444226);
147        T!(d, a, b, c, input[11], S2, 3174756917);
148        T!(c, d, a, b, input[2], S3, 718787259);
149        T!(b, c, d, a, input[9], S4, 3951481745);
150    }
151    state[0] = add!(state[0], a);
152    state[1] = add!(state[1], b);
153    state[2] = add!(state[2], c);
154    state[3] = add!(state[3], d);
155}
156
157impl Context {
158    fn new() -> Self {
159        Context {
160            input: [0u8; 64],
161            i: [0, 0],
162            buf: [0x67452301, 0xefcdab89, 0x98badcfe, 0x10325476],
163        }
164    }
165
166    fn update(&mut self, buf: &[u8]) {
167        let mut input = [0u32; 16];
168        let mut mdi = ((self.i[0] >> 3) & 0x3f) as usize;
169
170        // Update # of bits
171        let length = buf.len() as u32;
172
173        if self.i[0].wrapping_add(length) << 3 < self.i[0] {
174            self.i[1] = self.i[1].wrapping_add(1);
175        }
176
177        self.i[0] = self.i[0].wrapping_add(length << 3);
178        self.i[1] = self.i[1].wrapping_add(length >> 29);
179
180        for &byte in buf {
181            self.input[mdi] = byte;
182            mdi += 1;
183
184            if mdi == 0x40 {
185                let mut j = 0;
186                for i in 0..16 {
187                    input[i] = (self.input[j + 3] as u32) << 24;
188                    input[i] |= (self.input[j + 2] as u32) << 16;
189                    input[i] |= (self.input[j + 1] as u32) << 8;
190                    input[i] |= self.input[j] as u32;
191                    j += 4;
192                }
193
194                transform(&mut self.buf, &input);
195                mdi = 0;
196            }
197        }
198    }
199}
200
201/// Sign the DXIL blob with Mach-Siegbert-Vogt DXCSA
202pub fn sign(blob: &[u8], out: &mut [u32; 4]) {
203    const SECRET_HASH_OFFSET: usize = 20;
204    let mut context = Context::new();
205
206    // Note: first 4 bytes of bin are "DXBC" (IL) header/file-magic, then 16-byte signing hash,
207    // then remainder of the file contents.
208    let blob = &blob[SECRET_HASH_OFFSET..];
209
210    let len = blob.len() as u32;
211    let num_bits = len * 8;
212    let full_chunks_size = len & 0xffffffc0;
213
214    context.update(&blob[..full_chunks_size as usize]);
215
216    let last_chunk_data = &blob[full_chunks_size as usize..];
217    let padding_size = 64 - (blob.len() - full_chunks_size as usize);
218
219    if last_chunk_data.len() >= 56 {
220        context.update(last_chunk_data);
221
222        // Pad to 56 mod 64
223        context.update(&PADDING[0..padding_size]);
224
225        let mut input = [0u32; 16];
226        input[0] = num_bits;
227        input[15] = (num_bits >> 2) | 1;
228        transform(&mut context.buf, &input);
229    } else {
230        let num_bits_bytes = bytemuck::bytes_of(&num_bits);
231        context.update(num_bits_bytes);
232        if last_chunk_data.len() != 0 {
233            context.update(last_chunk_data);
234        }
235
236        // Adjust for the space used for num_bits
237        let padding_size = padding_size - ::core::mem::size_of::<u32>();
238
239        // Pad to 56 mod 64
240        context.input[last_chunk_data.len() + ::core::mem::size_of::<u32>()..]
241            .copy_from_slice(&PADDING[0..padding_size]);
242
243        let num_bits_shift = num_bits >> 2 | 1;
244
245        bytemuck::cast_slice_mut::<_, u32>(&mut context.input)[15] = num_bits_shift;
246
247        let mut input = [0u32; 16];
248        input.copy_from_slice(bytemuck::cast_slice(&context.input));
249        transform(&mut context.buf, &input);
250    }
251
252    out.copy_from_slice(&context.buf);
253}
254
255/// Sign the DXIL blob in place with Mach-Siegbert-Vogt DXCSA
256pub fn sign_in_place(blob: &mut [u8]) {
257    let mut signature = [0u32; 4];
258    sign(&blob, &mut signature);
259    blob[4..20].copy_from_slice(bytemuck::cast_slice(&signature));
260}
261
262#[cfg(test)]
263mod test {
264    use crate::sign;
265
266    const REAL_SIGNED_BLOB: &[u8] = include_bytes!("../mipmap.dxil.blob");
267    const REAL_SIGNED_COMPLEX_BLOB: &[u8] = include_bytes!("../realsigned_complex.blob");
268
269    #[test]
270    pub fn test() {
271        let sig = &REAL_SIGNED_BLOB[4..20];
272        let mut out_sig = [0u32; 4];
273
274        sign(&REAL_SIGNED_BLOB, &mut out_sig);
275
276        assert_eq!(sig, bytemuck::cast_slice(&out_sig));
277    }
278
279    #[test]
280    pub fn test_complex() {
281        let sig = &REAL_SIGNED_COMPLEX_BLOB[4..20];
282        let mut out_sig = [0u32; 4];
283
284        sign(&REAL_SIGNED_COMPLEX_BLOB, &mut out_sig);
285
286        assert_eq!(sig, bytemuck::cast_slice(&out_sig));
287    }
288}