Skip to main content

csv_rs/session/
mod.rs

1// Copyright (C) Hygon Info Technologies Ltd.
2//
3// SPDX-License-Identifier: Apache-2.0
4//
5
6//! Utilities for creating a secure channel and facilitating the
7//! attestation process between the tenant and the HYGON SP.
8
9mod key;
10
11use crate::certs::{csv, Signer, Usage};
12use crate::crypto::PrivateKey;
13
14use super::*;
15
16use std::io::{Error, Result};
17
18use openssl::*;
19
20/// Represents a brand-new secure channel with the HYGON SP.
21pub struct Initialized;
22
23/// Indicates the Session is currently accepting data to include
24/// in its measurement for comparison against the HYGON SP's measurement.
25pub struct Measuring(hash::Hasher);
26
27/// Denotes an agreeable measurement with the HYGON SP.
28pub struct Verified(api::launch::Measurement);
29
30/// Describes a secure channel with the HYGON SP.
31///
32/// This is required for facilitating an CSV launch and attestation.
33pub struct Session<T> {
34    policy: api::launch::Policy,
35
36    /// Transport Encryption Key.
37    pub tek: key::Key,
38
39    /// Transport Integrity Key.
40    pub tik: key::Key,
41
42    data: T,
43}
44
45impl api::launch::Policy {
46    fn bytes(self) -> [u8; 4] {
47        unsafe { std::mem::transmute(self) }
48    }
49}
50
51impl std::convert::TryFrom<api::launch::Policy> for Session<Initialized> {
52    type Error = std::io::Error;
53
54    fn try_from(value: api::launch::Policy) -> Result<Self> {
55        Ok(Self {
56            tek: key::Key::random(16)?,
57            tik: key::Key::random(16)?,
58            data: Initialized,
59            policy: value,
60        })
61    }
62}
63
64impl Session<Initialized> {
65    fn session(
66        &self,
67        nonce: [u8; 16],
68        iv: [u8; 16],
69        z: key::Key,
70        pdh_host: &csv::Certificate,
71        prv: PrivateKey<Usage>,
72    ) -> Result<api::launch::Session> {
73        let master = z.derive(32, &nonce, "csv-master-secret")?;
74        let kek = master.derive(32, &[], "csv-kek")?;
75        let kik = master.derive(32, &[], "csv-kik")?;
76        let uid = String::from("GUEST_USER_ID");
77
78        let mut crypter = symm::Crypter::new(
79            symm::Cipher::sm4_ctr(),
80            symm::Mode::Encrypt,
81            // real key is the first 16 bytes of the derived kek.
82            &kek[..16],
83            Some(&iv),
84        )?;
85
86        let mut wrap = [0u8; 32];
87        let mut off = 0;
88        off += crypter.update(&self.tek, &mut wrap[off..])?;
89        off += crypter.update(&self.tik, &mut wrap[off..])?;
90        off += crypter.finalize(&mut wrap[off..])?;
91        assert_eq!(off, wrap.len());
92
93        let mut concatenated_iv_wrap = Vec::new();
94        concatenated_iv_wrap.extend_from_slice(&iv);
95        concatenated_iv_wrap.extend_from_slice(&wrap);
96
97        let mut ms_enc = pdh_host.encrypt(master.get_ref())?;
98        ms_enc.resize(256, 0);
99
100        let wmac = kik.mac(&concatenated_iv_wrap)?;
101
102        let mut session_data_needed_mac = Vec::new();
103        session_data_needed_mac.extend_from_slice(&self.policy.bytes());
104        session_data_needed_mac.extend_from_slice(&ms_enc);
105        // Fill with 0, as [vm_digest,pubkey_digest,...user_data] is all 0 now.
106        session_data_needed_mac.resize(420, 0);
107        let session_mac = self.tik.mac(&session_data_needed_mac)?;
108
109        let body: api::launch::SessionBody = api::launch::SessionBody {
110            session_mac,
111            wrap_mac: wmac,
112            wrap_tk: wrap,
113            wrap_iv: iv,
114            nonce,
115            key_id: [0u8; 16],
116            rnd_pub_key_data: [0u8; 148],
117            ms_enc: ms_enc.try_into().unwrap(),
118            vm_digest: [0u8; 32],
119            pubkey_digest: [0u8; 32],
120            vm_id: [0u8; 16],
121            vm_version: [0u8; 16],
122            user_data: [0u8; 64],
123        };
124
125        let mut session = api::launch::Session {
126            body,
127            sig: api::launch::SessionSig {
128                r: [0u8; 32],
129                s: [0u8; 32],
130            },
131        };
132
133        prv.sign(&mut session, uid)?;
134        Ok(session)
135    }
136
137    /// Produces data needed to initiate the CSV launch sequence.
138    pub fn start(&self, chain: certs::Chain) -> Result<api::launch::Start> {
139        use certs::*;
140
141        let pdh: &csv::Certificate = chain.verify()?;
142        let uid = String::from("GUEST_USER_ID");
143        let (crt, prv) = csv::Certificate::generate(Usage::PDH, Some(uid))?;
144        // get share key by random
145        let mut share_key = [0u8; 16];
146        rand::rand_bytes(&mut share_key)?;
147        let z = key::Key::new(share_key.to_vec());
148
149        let mut nonce = [0u8; 16];
150        let mut iv = [0u8; 16];
151        rand::rand_bytes(&mut nonce)?;
152        rand::rand_bytes(&mut iv)?;
153
154        let session = self.session(nonce, iv, z, pdh, prv)?;
155
156        (&crt, &session).verify()?;
157
158        Ok(api::launch::Start {
159            policy: self.policy,
160            cert: crt,
161            session,
162        })
163    }
164
165    /// Like the above start function, yet takes PDH as input instead of deriving it from a
166    /// certificate chain.
167    pub fn start_pdh(&self, pdh: certs::csv::Certificate) -> Result<api::launch::Start> {
168        use certs::*;
169
170        let uid = String::from("GUEST_USER_ID");
171        let (crt, prv) = csv::Certificate::generate(Usage::PDH, Some(uid.clone()))?;
172
173        // get share key by random
174        let mut share_key = [0u8; 16];
175        rand::rand_bytes(&mut share_key)?;
176        let z = key::Key::new(share_key.to_vec());
177
178        let mut nonce = [0u8; 16];
179        let mut iv = [0u8; 16];
180        rand::rand_bytes(&mut nonce)?;
181        rand::rand_bytes(&mut iv)?;
182
183        let session = self.session(nonce, iv, z, &pdh, prv)?;
184
185        Ok(api::launch::Start {
186            policy: self.policy,
187            cert: crt,
188            session,
189        })
190    }
191
192    /// Transitions to a measuring state.
193    ///
194    /// Any measureable data submitted to the HYGON SP should also be included
195    /// in the `Session` to easily compare against the HYGON SP's measurement.
196    pub fn measure(self) -> Result<Session<Measuring>> {
197        Ok(Session {
198            policy: self.policy,
199            tek: self.tek,
200            tik: self.tik,
201            data: Measuring(hash::Hasher::new(hash::MessageDigest::sm3())?),
202        })
203    }
204
205    /// Verifies the HYGON SP's measurement.
206    pub fn verify(
207        self,
208        _digest: &[u8],
209        _build: Build,
210        msr: api::launch::Measurement,
211    ) -> Result<Session<Verified>> {
212        //TODO: open it after drgonball support
213        // let key = pkey::PKey::hmac(&self.tik)?;
214        // let mut sig = sign::Signer::new(hash::MessageDigest::sm3(), &key)?;
215
216        // sig.update(&[0x04u8])?;
217        // sig.update(&[build.version.major, build.version.minor])?;
218        // sig.update(&self.policy.bytes())?;
219        // sig.update(digest)?;
220        // sig.update(&msr.mnonce)?;
221
222        // if sig.sign_to_vec()? != msr.measure {
223        //     return Err(ErrorKind::InvalidInput.into());
224        // }
225
226        Ok(Session {
227            policy: self.policy,
228            tek: self.tek,
229            tik: self.tik,
230            data: Verified(msr),
231        })
232    }
233
234    /// Skip verifying the measurement
235    ///
236    /// # Safety
237    ///
238    /// This method must only be used in tests or unattested workflows.
239    pub unsafe fn mock_verify(self, msr: api::launch::Measurement) -> Result<Session<Verified>> {
240        Ok(Session {
241            policy: self.policy,
242            tek: self.tek,
243            tik: self.tik,
244            data: Verified(msr),
245        })
246    }
247}
248
249impl Session<Measuring> {
250    /// Adds additional data to the digest.
251    ///
252    /// Everything measured by the HYGON SP should also be measured by
253    /// the `Session` to ensure both measurements are the same.
254    pub fn update_data(&mut self, data: &[u8]) -> std::io::Result<()> {
255        Ok(self.data.0.update(data)?)
256    }
257
258    /// Verifies the session's measurement against the HYGON SP's measurement.
259    pub fn verify(
260        mut self,
261        build: Build,
262        msr: api::launch::Measurement,
263    ) -> Result<Session<Verified>> {
264        let digest = self.data.0.finish()?;
265        let session = Session {
266            policy: self.policy,
267            tek: self.tek,
268            tik: self.tik,
269            data: Initialized,
270        };
271
272        session.verify(&digest, build, msr)
273    }
274
275    /// Verifies the session's measurement against the HYGON SP's measurement
276    /// using an externally generated digest.
277    pub fn verify_with_digest(
278        self,
279        build: Build,
280        msr: api::launch::Measurement,
281        digest: &[u8],
282    ) -> Result<Session<Verified>> {
283        let session = Session {
284            policy: self.policy,
285            tek: self.tek,
286            tik: self.tik,
287            data: Initialized,
288        };
289
290        session.verify(digest, build, msr)
291    }
292}
293
294impl Session<Verified> {
295    /// Creates a packet for a secret to be injected into the guest.
296    pub fn secret(
297        &self,
298        flags: api::launch::HeaderFlags,
299        data: &[u8],
300    ) -> Result<api::launch::Secret> {
301        let mut iv = [0u8; 16];
302        rand::rand_bytes(&mut iv)?;
303
304        let ciphertext = symm::encrypt(symm::Cipher::sm4_ctr(), &self.tek, Some(&iv), data)?;
305
306        let key = pkey::PKey::hmac(&self.tik)?;
307        let mut sig = sign::Signer::new(hash::MessageDigest::sm3(), &key)?;
308
309        sig.update(&[0x01u8])?;
310        sig.update(&unsafe { std::mem::transmute::<api::launch::HeaderFlags, [u8; 4]>(flags) })?;
311        sig.update(&iv)?;
312        sig.update(&(data.len() as u32).to_le_bytes())?;
313        sig.update(&(ciphertext.len() as u32).to_le_bytes())?;
314        sig.update(&ciphertext)?;
315        sig.update(&self.data.0.measure)?;
316
317        let mut mac = [0u8; 32];
318        sig.sign(&mut mac)?;
319
320        Ok(api::launch::Secret {
321            header: api::launch::Header { flags, iv, mac },
322            ciphertext,
323        })
324    }
325}
326
327#[cfg(test)]
328mod initialized {
329    use super::*;
330    use crate::{session::Session, Build, Version};
331
332    /* TODO: implementation
333    #[test]
334    fn session() {
335        let session = Session {
336            policy: api::launch::Policy::default(),
337            tek: key::Key::new(vec![0u8; 16]),
338            tik: key::Key::new(vec![0u8; 16]),
339            data: Initialized,
340        };
341
342        let launch = session
343            .session([0u8; 16], [0u8; 16], key::Key::zeroed(16))
344            .unwrap();
345
346        assert_eq!(launch.wrap_iv, [0u8; 16]);
347
348        assert_eq!(launch.nonce, [0u8; 16]);
349
350        assert_eq!(
351            launch.wrap_tk,
352            [
353                0x21, 0x37, 0xbc, 0x7f, 0x9b, 0xb8, 0xbd, 0x7c, 0x3e, 0x55, 0xa5, 0x76, 0xa1, 0x5d,
354                0x34, 0x54, 0xb3, 0x85, 0x6b, 0x8b, 0xa2, 0x7a, 0xfa, 0xdf, 0x46, 0xdc, 0xfe, 0xe9,
355                0xf0, 0x2c, 0x02, 0xc4,
356            ]
357        );
358
359        assert_eq!(
360            launch.wrap_mac,
361            [
362                0x31, 0x76, 0xc0, 0x75, 0x27, 0x38, 0xbd, 0x9d, 0x5e, 0x86, 0x68, 0x95, 0x34, 0x02,
363                0x0f, 0x52, 0x8c, 0x08, 0x8f, 0x16, 0x23, 0x88, 0x26, 0xb0, 0x00, 0xb3, 0x27, 0xde,
364                0xe6, 0xae, 0xed, 0x7d,
365            ]
366        );
367
368        assert_eq!(
369            launch.policy_mac,
370            [
371                0xaa, 0x78, 0x55, 0xe1, 0x38, 0x39, 0xdd, 0x76, 0x7c, 0xd5, 0xda, 0x7c, 0x1f, 0xf5,
372                0x03, 0x65, 0x40, 0xc9, 0x26, 0x4b, 0x7a, 0x80, 0x30, 0x29, 0x31, 0x5e, 0x55, 0x37,
373                0x52, 0x87, 0xb4, 0xaf,
374            ]
375        );
376    }
377    */
378
379    #[test]
380    fn verify() {
381        let digest = [
382            0xe3, 0xb0, 0xc4, 0x42, 0x98, 0xfc, 0x1c, 0x14, 0x9a, 0xfb, 0xf4, 0xc8, 0x99, 0x6f,
383            0xb9, 0x24, 0x27, 0xae, 0x41, 0xe4, 0x64, 0x9b, 0x93, 0x4c, 0xa4, 0x95, 0x99, 0x1b,
384            0x78, 0x52, 0xb8, 0x55,
385        ];
386        // TODO: update the measurement for sm3
387        let measurement = api::launch::Measurement {
388            measure: [
389                0x6f, 0xaa, 0xb2, 0xda, 0xae, 0x38, 0x9b, 0xcd, 0x34, 0x05, 0xa0, 0x5d, 0x6c, 0xaf,
390                0xe3, 0x3c, 0x04, 0x14, 0xf7, 0xbe, 0xdd, 0x0b, 0xae, 0x19, 0xba, 0x5f, 0x38, 0xb7,
391                0xfd, 0x16, 0x64, 0xea,
392            ],
393            mnonce: [
394                0x4f, 0xbe, 0x0b, 0xed, 0xba, 0xd6, 0xc8, 0x6a, 0xe8, 0xf6, 0x89, 0x71, 0xd1, 0x03,
395                0xe5, 0x54,
396            ],
397        };
398
399        let policy = api::launch::Policy {
400            flags: api::launch::PolicyFlags::default(),
401            minfw: Default::default(),
402        };
403
404        let tek = key::Key::new(vec![0u8; 16]);
405        let tik = key::Key::new(vec![
406            0x66, 0x32, 0x0d, 0xb7, 0x31, 0x58, 0xa3, 0x5a, 0x25, 0x5d, 0x05, 0x17, 0x58, 0xe9,
407            0x5e, 0xd4,
408        ]);
409
410        let session = Session {
411            policy,
412            tek,
413            tik,
414            data: Initialized,
415        };
416        let build = Build {
417            version: Version {
418                major: 0x00,
419                minor: 0x12,
420            },
421            build: 0x0f,
422        };
423
424        session.verify(&digest, build, measurement).unwrap();
425    }
426}