diff --git a/src/core/license.rs b/src/core/license.rs index f2c6f3d..d8682fc 100644 --- a/src/core/license.rs +++ b/src/core/license.rs @@ -7,7 +7,7 @@ use crate::model::error::{Error, RdpError, RdpErrorKind, RdpResult}; use crate::model::rnd::random; use crate::model::unicode; use num_enum::TryFromPrimitive; -use std::convert::TryFrom; +use std::ffi::CStr; use std::ffi::CString; use std::io::{self, Cursor, Read, Write}; @@ -100,25 +100,6 @@ pub enum StateTransition { StResendLastMessage = 0x00000004, } -/// ClientLicenseState represents the state machine for a client -/// performing the licensing negotiation. -/// -/// See MS-RDPELE section 3.3.1.11. -enum ClientLicenseState { - /// The initial state for a client attempting to obtain a license. - Await, - /// The client has received a license request from the server. - /// In this state, the client sends existing license information - /// or a new license request depending on the availability of - /// a license as requested by the server. - ProcessLicensing(SessionEncryptionData), - /// The licensing process is aborted if a client sends invalid or - /// out-of-sequence messages. - Aborted, - /// The licensing negotiation is complete. - Completed, -} - #[repr(u16)] #[derive(TryFromPrimitive)] pub enum BlobType { @@ -150,7 +131,7 @@ pub enum ClientOsId { } pub enum LicenseMessage { - NewLicense(NewLicense), + NewLicense(ServerNewLicense), LicenseRequest(ServerLicenseRequest), ServerPlatformChallenge(PlatformChallenge), UpgradeLicense(UpgradeLicense), @@ -186,10 +167,10 @@ impl LicenseMessage { let mut msg_data = Cursor::new(cast!(DataType::Slice, license_message["message"])?); let message_type = cast!(DataType::U8, license_message["bMsgtype"])?; - println!("!! [LIC] recieved license message {}", message_type); - match MessageType::try_from(message_type)? { - MessageType::NewLicense => Ok(Self::NewLicense(NewLicense::from_bytes(&mut msg_data)?)), + MessageType::NewLicense => Ok(Self::NewLicense(ServerNewLicense::from_bytes( + &mut msg_data, + )?)), MessageType::LicenseRequest => Ok(Self::LicenseRequest( ServerLicenseRequest::from_bytes(&mut msg_data)?, )), @@ -336,12 +317,16 @@ impl ServerCertificate { } } -pub struct NewLicense { +/// ServerNewLicense is sent from server to client when +/// a new license is issued to the client. +/// +/// See MS-RDPELE section 2.2.2.7. +pub struct ServerNewLicense { mac_data: Vec, encrypted_license_data: Vec, } -impl NewLicense { +impl ServerNewLicense { fn from_bytes(raw: &mut dyn Read) -> RdpResult { let mut message = component![ "EncryptedLicenseInfo" => component![ @@ -365,6 +350,19 @@ impl NewLicense { mac_data, }) } + + fn decrypted_license( + &self, + session_encryption_data: &SessionEncryptionData, + ) -> RdpResult { + License::new( + session_encryption_data, + &ServerNewLicense { + mac_data: self.mac_data.clone(), + encrypted_license_data: self.encrypted_license_data.clone(), + }, + ) + } } pub struct ErrorAlert { @@ -426,76 +424,94 @@ impl BinaryBlob { } } -#[allow(dead_code)] -pub struct UpgradeLicense { - mac_data: Vec, - encrypted_license_data: Vec, -} - -impl UpgradeLicense { - fn from_bytes(raw: &mut dyn Read) -> RdpResult { - let mut message = component![ - "EncryptedLicenseInfo" => component![ - "wBlobType" => U16::LE(0), - "wBlobLen" => DynOption::new(U16::LE(0), | size | MessageOption::Size("blobData".to_string(), size.inner() as usize)), - "blobData" => Vec::::new() - ], - "MACData" => vec![0_u8; 16] - ]; - - message.read(raw)?; - - let encrypted_license_data = cast!( - DataType::Slice, - cast!(DataType::Component, message["EncryptedLicenseInfo"])?["blobData"] - )? - .to_vec(); - let mac_data = cast!(DataType::Slice, message["MACData"])?.to_vec(); - Ok(Self { - encrypted_license_data, - mac_data, - }) - } - - fn decrypted_license( - &self, - session_encryption_data: &SessionEncryptionData, - ) -> RdpResult { - License::new( - session_encryption_data, - &NewLicense { - mac_data: self.mac_data.clone(), - encrypted_license_data: self.encrypted_license_data.clone(), - }, - ) - } -} +/// ServerUpgradeLicense is sent from server to client if the client +/// presents an existing license and the server determines that this +/// license should be upgraded. +/// +/// The structure and content of this message is identical to the +/// ServerNewLicense message. +/// +/// See MS-RDPELE section 2.2.2.6. +type UpgradeLicense = ServerNewLicense; /// License data that has been obtained from the sever #[allow(dead_code)] struct License { - data: Vec, + // The version, scope, company name, and product ID fields are used + // by the client to index the licenses in the client's license store. + version_major: u16, + version_minor: u16, + scope: String, + company_name: String, + product_id: String, + + // In theory, this is a DER-encoded X.509 certificate that has some + // helpful data in its certificate extensions: + // + // https://learn.microsoft.com/en-us/openspecs/windows_protocols/ms-rdpele/99e6bdc5-4517-45a3-be72-a254e8389546 + // + // In practice, something about this cert is non-standard: both Go's crypto/x509 package + // and Rust's x509_parser crate choke when attempting to parse it. + // (Note that `openssl asn1parse -inform DER` does work) + cert_data: Vec, } impl License { fn new( session_encryption_data: &SessionEncryptionData, - new_license: &NewLicense, + new_license: &ServerNewLicense, ) -> RdpResult { - let mut rc4 = Rc4::new(Key::::from_slice( - &session_encryption_data.license_encryption_key, - )); - - let mut data: Vec = new_license.encrypted_license_data.clone(); - rc4.apply_keystream(&mut data); - + // Decrypt and validate the MAC + let data = session_encryption_data.decrypt_message(&new_license.encrypted_license_data); if session_encryption_data.generate_mac_data(&data) != new_license.mac_data { return Err(Error::RdpError(RdpError::new( RdpErrorKind::InvalidData, "new license MAC is different than MAC from encrypted message", ))); } - Ok(Self { data }) + + Self::from_decrypted_data(&mut Cursor::new(&data)) + } + + fn from_decrypted_data(reader: &mut dyn Read) -> RdpResult { + // The decrypted data contains a New License Information packet. + // (See MS-RDPELE section 2.2.2.6.1) + let mut license_info = component![ + "dwVersion" => U32::LE(0), + "cbScope" => DynOption::new(U32::LE(0), | size | MessageOption::Size("pbScope".to_string(), size.inner() as usize)), + "pbScope" => Vec::::new(), + "cbCompanyName" => DynOption::new(U32::LE(0), | size | MessageOption::Size("pbCompanyName".to_string(), size.inner() as usize)), + "pbCompanyName" => Vec::::new(), + "cbProductId" => DynOption::new(U32::LE(0), | size | MessageOption::Size("pbProductId".to_string(), size.inner() as usize)), + "pbProductId" => Vec::::new(), + "cbLicenseInfo" => DynOption::new(U32::LE(0), | size | MessageOption::Size("pbLicenseInfo".to_string(), size.inner() as usize)), + "pbLicenseInfo" => Vec::::new() + ]; + + license_info.read(reader)?; + + let version = cast!(DataType::U32, license_info["dwVersion"])?; + let scope = cast!(DataType::Slice, license_info["pbScope"])?; + + Ok(Self { + version_major: (version >> 16) as u16, + version_minor: (version & 0xFFFF) as u16, + scope: String::from( + CStr::from_bytes_with_nul(&scope) + .unwrap_or_default() + .to_str() + .unwrap_or_default(), + ), + company_name: unicode::parse_utf16le(cast!( + DataType::Slice, + license_info["pbCompanyName"] + )?), + product_id: unicode::parse_utf16le(cast!( + DataType::Slice, + license_info["pbProductId"] + )?), + cert_data: cast!(DataType::Slice, license_info["pbLicenseInfo"])?.to_vec(), + }) } } @@ -536,6 +552,8 @@ pub struct ServerLicenseRequest { company_name: String, product_id: String, + + scopes: Vec, } impl ServerLicenseRequest { @@ -557,8 +575,7 @@ impl ServerLicenseRequest { "wBlobLen" => DynOption::new(U16::LE(0), | size | MessageOption::Size("blobData".to_string(), size.inner() as usize)), "blobData" => Vec::::new() ], - "ScopeCount" => DynOption::new(U32::LE(0), | size | MessageOption::Size("ScopeArray".to_string(), size.inner() as usize)), - "ScopeArray" => Vec::::new() + "ScopeCount" => U32::LE(0) ]; message.read(raw)?; @@ -566,6 +583,21 @@ impl ServerLicenseRequest { let version = cast!(DataType::U32, message["dwVersion"])?; let server_certificate = cast!(DataType::Component, message["ServerCertificate"])?; let mut blob_data = cast!(DataType::Slice, server_certificate["blobData"])?; + let scope_count = cast!(DataType::U32, message["ScopeCount"])?; + + let mut scopes = Vec::with_capacity(scope_count as usize); + for _i in 0..scope_count { + let mut scope = BinaryBlob::new(BlobType::Scope, vec![]).component(); + scope.read(raw)?; + + let issuer = cast!(DataType::Slice, scope["blobData"])?; + scopes.push(String::from( + CStr::from_bytes_with_nul(&issuer) + .unwrap_or_default() + .to_str() + .unwrap_or_default(), + )); + } Ok(Self { server_random: Vec::from(server_random), @@ -574,6 +606,7 @@ impl ServerLicenseRequest { product_id: unicode::parse_utf16le(cast!(DataType::Slice, message["pbProductId"])?), version_major: (version >> 16) as u16, version_minor: (version & 0xFFFF) as u16, + scopes, }) } } @@ -899,9 +932,79 @@ impl<'a> ClientLicenseInfo<'a> { } } +/// LicensedProductInfo contains information specific to a license +/// issued to a client. This information is stored inside the license +/// certificate in the certificate extension with OID 1.3.6.1.4.1.311.18.5. +/// +/// See MS-RDPELE section 2.2.2.9.1. +struct LicensedProductInfo { + count: u32, // the number of licenses issued to the client + platform_id: u32, + + requested_product_id: String, + adjusted_product_id: String, + + product_major_version: u16, + product_minor_version: u16, + + flags: u32, +} + +#[repr(u32)] +#[derive(Clone, Copy, TryFromPrimitive)] +enum ProductLicenseFlags { + /// Indicates that license is enforced. + LicenseEnforced = 0x00008000, + /// Indicates that the license is an RTM license. If this flag + /// is not set, then the license is a beta license. + RtmLicense = 0x00800000, + /// Indicates that the license is a temporary license. If this + /// flag is not set, then the license is a permanent license. + TemporaryLicense = 0x80000000, +} + +impl LicensedProductInfo { + fn from_bytes(raw: &mut dyn Read) -> RdpResult { + let mut message = component![ + "Version" => U32::LE(0), + "LicenseCount" => U32::LE(0), + "PlatformId" => U32::LE(0), + "LicensedLanguageId" => U32::LE(0), + "RequestedProductIdOffset" => U16::LE(0), + "RequestedProductIdByteCount" => DynOption::new(U16::LE(0), | size | MessageOption::Size("RequestedProductId".to_string(), size.inner() as usize)), + "AdjustedProductIdOffset" => U16::LE(0), + "AdjustedProductIdByteCount" => DynOption::new(U16::LE(0), | size | MessageOption::Size("AdjustedProductId".to_string(), size.inner() as usize)), + "LicensedVersionInfoOffset" => U16::LE(0), + "LicensedVersionInfoCount" => U16::LE(0), + "RequestedProductId" => Vec::::new(), + "AdjustedProductId" => Vec::::new(), + "ProductLicenseMajorVersion" => U16::LE(0), + "ProductLicenseMinorVersion" => U16::LE(0), + "ProductLicenseFlags" => U32::LE(0) + ]; + + message.read(raw)?; + + Ok(Self { + count: cast!(DataType::U32, message["LicenseCount"])?, + platform_id: cast!(DataType::U32, message["PlatformId"])?, + requested_product_id: unicode::parse_utf16le(cast!( + DataType::Slice, + message["RequestedProductId"] + )?), + adjusted_product_id: unicode::parse_utf16le(cast!( + DataType::Slice, + message["AdjustedProductId"] + )?), + product_major_version: cast!(DataType::U16, message["ProductLicenseMajorVersion"])?, + product_minor_version: cast!(DataType::U16, message["ProductLicenseMinorVersion"])?, + flags: cast!(DataType::U32, message["ProductLicenseFlags"])?, + }) + } +} + /// Wraps license message with security and preamble headers fn license_response(message_type: MessageType, data: Vec) -> RdpResult> { - println!("!! [LIC] writing message {:?}", message_type); let message = trame![ U16::LE(SecurityFlag::SecLicensePkt as u16), U16::LE(0), @@ -927,12 +1030,7 @@ pub fn client_connect( // and (in binary form) the hardware identifier for the client. let client_uuid = Uuid::try_parse(client_machine)?; - // let mut current_state = ClientLicenseState::Await; - let (channel, payload) = mcs.read()?; - - println!("!! [LIC] license code got message on channel {}", channel); - let session_encryption_data = match LicenseMessage::new(payload)? { // When we get the `NewLicense` message at the start of the // license flow it means that we don't have to continue @@ -948,8 +1046,8 @@ pub fn client_connect( ); let client_new_license_response = ClientNewLicense::new( &session_encryption_data, - CString::new(client_machine).unwrap_or_else(|_| CString::new(".").unwrap()), CString::new(username).unwrap_or_else(|_| CString::new("default").unwrap()), + CString::new(client_machine).unwrap_or_else(|_| CString::new(".").unwrap()), )?; mcs.write( &channel, @@ -970,7 +1068,6 @@ pub fn client_connect( }; let (channel, payload) = mcs.read()?; - println!("!!! [LIC] license code got message on channel {}", channel); match LicenseMessage::new(payload)? { LicenseMessage::ServerPlatformChallenge(platform_challenge) => { // validate the integrity of the server challenge and create our response @@ -997,7 +1094,6 @@ pub fn client_connect( } let (_channel, payload) = mcs.read()?; - println!("!!!! [LIC] license code got message on channel {}", channel); match LicenseMessage::new(payload)? { LicenseMessage::NewLicense(new_license) => { // At the moment we're not storing license client-side, @@ -1282,6 +1378,213 @@ mod tests { String::from("Microsoft Corporation") ); assert_eq!(server_license_request.product_id, String::from("A02")); + assert_eq!(1, server_license_request.scopes.len()); + assert_eq!( + String::from("microsoft.com"), + server_license_request.scopes[0] + ); + } + + #[test] + fn test_new_license() { + // https://learn.microsoft.com/en-us/openspecs/windows_protocols/ms-rdpele/73c596f1-9550-4649-b880-2947c36c1bb6 + #[cfg_attr(rustfmt, rustfmt_skip)] + let payload = vec![ + 0x00, 0x00, 0x06, 0x00, // dwVersion + 0x0e, 0x00, 0x00, 0x00, // cbScope + 0x6d, 0x69, 0x63, 0x72, 0x6f, 0x73, 0x6f, 0x66, 0x74, 0x2e, 0x63, 0x6f, 0x6d, 0x00, // pbScope + 0x2c, 0x00, 0x00, 0x00, // cbCompanyName + + // pbCompanyname + 0x4d, 0x00, 0x69, 0x00, 0x63, 0x00, 0x72, 0x00, + 0x6f, 0x00, 0x73, 0x00, 0x6f, 0x00, 0x66, 0x00, + 0x74, 0x00, 0x20, 0x00, 0x43, 0x00, 0x6f, 0x00, + 0x72, 0x00, 0x70, 0x00, 0x6f, 0x00, 0x72, 0x00, + 0x61, 0x00, 0x74, 0x00, 0x69, 0x00, 0x6f, 0x00, + 0x6e, 0x00, 0x00, 0x00, + + + 0x08, 0x00, 0x00, 0x00, // cbProductId + 0x41, 0x00, 0x30, 0x00, 0x32, 0x00, 0x00, 0x00, // pbProductId + + 0x99, 0x07, 0x00, 0x00, // cbLicenseInfo + + // pbLicenseInfo + 0x30, 0x82, 0x07, 0x95, 0x06, 0x09, 0x2a, 0x86, 0x48, 0x86, 0xf7, 0x0d, 0x01, 0x07, 0x02, 0xa0, + 0x82, 0x07, 0x86, 0x30, 0x82, 0x07, 0x82, 0x02, 0x01, 0x01, 0x31, 0x00, 0x30, 0x0b, 0x06, 0x09, + 0x2a, 0x86, 0x48, 0x86, 0xf7, 0x0d, 0x01, 0x07, 0x01, 0xa0, 0x82, 0x07, 0x6a, 0x30, 0x82, 0x02, + 0xf1, 0x30, 0x82, 0x01, 0xdd, 0xa0, 0x03, 0x02, 0x01, 0x02, 0x02, 0x08, 0x01, 0x9e, 0x27, 0x4d, + 0x68, 0xac, 0xed, 0x20, 0x30, 0x09, 0x06, 0x05, 0x2b, 0x0e, 0x03, 0x02, 0x1d, 0x05, 0x00, 0x30, + 0x32, 0x31, 0x30, 0x30, 0x13, 0x06, 0x03, 0x55, 0x04, 0x03, 0x1e, 0x0c, 0x00, 0x52, 0x00, 0x4f, + 0x00, 0x44, 0x00, 0x45, 0x00, 0x4e, 0x00, 0x54, 0x30, 0x19, 0x06, 0x03, 0x55, 0x04, 0x07, 0x1e, + 0x12, 0x00, 0x57, 0x00, 0x4f, 0x00, 0x52, 0x00, 0x4b, 0x00, 0x47, 0x00, 0x52, 0x00, 0x4f, 0x00, + 0x55, 0x00, 0x50, 0x30, 0x1e, 0x17, 0x0d, 0x37, 0x30, 0x30, 0x35, 0x33, 0x30, 0x31, 0x30, 0x33, + 0x36, 0x31, 0x38, 0x5a, 0x17, 0x0d, 0x34, 0x39, 0x30, 0x35, 0x33, 0x30, 0x31, 0x30, 0x33, 0x36, + 0x31, 0x38, 0x5a, 0x30, 0x32, 0x31, 0x30, 0x30, 0x13, 0x06, 0x03, 0x55, 0x04, 0x03, 0x1e, 0x0c, + 0x00, 0x52, 0x00, 0x4f, 0x00, 0x44, 0x00, 0x45, 0x00, 0x4e, 0x00, 0x54, 0x30, 0x19, 0x06, 0x03, + 0x55, 0x04, 0x07, 0x1e, 0x12, 0x00, 0x57, 0x00, 0x4f, 0x00, 0x52, 0x00, 0x4b, 0x00, 0x47, 0x00, + 0x52, 0x00, 0x4f, 0x00, 0x55, 0x00, 0x50, 0x30, 0x82, 0x01, 0x22, 0x30, 0x0d, 0x06, 0x09, 0x2a, + 0x86, 0x48, 0x86, 0xf7, 0x0d, 0x01, 0x01, 0x01, 0x05, 0x00, 0x03, 0x82, 0x01, 0x0f, 0x00, 0x30, + 0x82, 0x01, 0x0a, 0x02, 0x82, 0x01, 0x01, 0x00, 0x88, 0xad, 0x7c, 0x8f, 0x8b, 0x82, 0x76, 0x5a, + 0xbd, 0x8f, 0x6f, 0x62, 0x18, 0xe1, 0xd9, 0xaa, 0x41, 0xfd, 0xed, 0x68, 0x01, 0xc6, 0x34, 0x35, + 0xb0, 0x29, 0x04, 0xca, 0x4a, 0x4a, 0x1c, 0x7e, 0x80, 0x14, 0xf7, 0x8e, 0x77, 0xb8, 0x25, 0xff, + 0x16, 0x47, 0x6f, 0xbd, 0xe2, 0x34, 0x3d, 0x2e, 0x02, 0xb9, 0x53, 0xe4, 0x33, 0x75, 0xad, 0x73, + 0x28, 0x80, 0xa0, 0x4d, 0xfc, 0x6c, 0xc0, 0x22, 0x53, 0x1b, 0x2c, 0xf8, 0xf5, 0x01, 0x60, 0x19, + 0x7e, 0x79, 0x19, 0x39, 0x8d, 0xb5, 0xce, 0x39, 0x58, 0xdd, 0x55, 0x24, 0x3b, 0x55, 0x7b, 0x43, + 0xc1, 0x7f, 0x14, 0x2f, 0xb0, 0x64, 0x3a, 0x54, 0x95, 0x2b, 0x88, 0x49, 0x0c, 0x61, 0x2d, 0xac, + 0xf8, 0x45, 0xf5, 0xda, 0x88, 0x18, 0x5f, 0xae, 0x42, 0xf8, 0x75, 0xc7, 0x26, 0x6d, 0xb5, 0xbb, + 0x39, 0x6f, 0xcc, 0x55, 0x1b, 0x32, 0x11, 0x38, 0x8d, 0xe4, 0xe9, 0x44, 0x84, 0x11, 0x36, 0xa2, + 0x61, 0x76, 0xaa, 0x4c, 0xb4, 0xe3, 0x55, 0x0f, 0xe4, 0x77, 0x8e, 0xde, 0xe3, 0xa9, 0xea, 0xb7, + 0x41, 0x94, 0x00, 0x58, 0xaa, 0xc9, 0x34, 0xa2, 0x98, 0xc6, 0x01, 0x1a, 0x76, 0x14, 0x01, 0xa8, + 0xdc, 0x30, 0x7c, 0x77, 0x5a, 0x20, 0x71, 0x5a, 0xa2, 0x3f, 0xaf, 0x13, 0x7e, 0xe8, 0xfd, 0x84, + 0xa2, 0x5b, 0xcf, 0x25, 0xe9, 0xc7, 0x8f, 0xa8, 0xf2, 0x8b, 0x84, 0xc7, 0x04, 0x5e, 0x53, 0x73, + 0x4e, 0x0e, 0x89, 0xa3, 0x3c, 0xe7, 0x68, 0x5c, 0x24, 0xb7, 0x80, 0x53, 0x3c, 0x54, 0xc8, 0xc1, + 0x53, 0xaa, 0x71, 0x71, 0x3d, 0x36, 0x15, 0xd6, 0x6a, 0x9d, 0x7d, 0xde, 0xae, 0xf9, 0xe6, 0xaf, + 0x57, 0xae, 0xb9, 0x01, 0x96, 0x5d, 0xe0, 0x4d, 0xcd, 0xed, 0xc8, 0xd7, 0xf3, 0x01, 0x03, 0x38, + 0x10, 0xbe, 0x7c, 0x42, 0x67, 0x01, 0xa7, 0x23, 0x02, 0x03, 0x01, 0x00, 0x01, 0xa3, 0x13, 0x30, + 0x11, 0x30, 0x0f, 0x06, 0x03, 0x55, 0x1d, 0x13, 0x04, 0x08, 0x30, 0x06, 0x01, 0x01, 0xff, 0x02, + 0x01, 0x00, 0x30, 0x09, 0x06, 0x05, 0x2b, 0x0e, 0x03, 0x02, 0x1d, 0x05, 0x00, 0x03, 0x82, 0x01, + 0x01, 0x00, 0x70, 0xdb, 0x21, 0x2b, 0x84, 0x9a, 0x7a, 0xc3, 0xb1, 0x68, 0xfa, 0xc0, 0x00, 0x8b, + 0x71, 0xab, 0x43, 0x9f, 0xb6, 0x7b, 0xb7, 0x1f, 0x20, 0x83, 0xac, 0x0a, 0xb5, 0x0e, 0xad, 0xb6, + 0x36, 0xef, 0x65, 0x17, 0x99, 0x86, 0x8a, 0x3d, 0xba, 0x0c, 0x53, 0x2e, 0xa3, 0x75, 0xa0, 0xf3, + 0x11, 0x3d, 0xe7, 0x65, 0x4b, 0xae, 0x3c, 0x42, 0x70, 0x11, 0xdc, 0xca, 0x83, 0xc0, 0xbe, 0x3e, + 0x97, 0x71, 0x84, 0x69, 0xd6, 0xa8, 0x27, 0x33, 0x9b, 0x3e, 0x17, 0x3c, 0xa0, 0x4c, 0x64, 0xca, + 0x20, 0x37, 0xa4, 0x11, 0xa9, 0x28, 0x8f, 0xb7, 0x18, 0x96, 0x69, 0x15, 0x0d, 0x74, 0x04, 0x75, + 0x2a, 0x00, 0xc7, 0xa6, 0x6a, 0xbe, 0xac, 0xb3, 0xf2, 0xfb, 0x06, 0x1b, 0x6c, 0x11, 0xbd, 0x96, + 0xe2, 0x34, 0x74, 0x5d, 0xf5, 0x98, 0x8f, 0x3a, 0x8d, 0x69, 0x08, 0x6f, 0x53, 0x12, 0x4e, 0x39, + 0x80, 0x90, 0xce, 0x8b, 0x5e, 0x88, 0x23, 0x2d, 0xfd, 0x55, 0xfd, 0x58, 0x3d, 0x39, 0x27, 0xb3, + 0x7c, 0x57, 0xfe, 0x3b, 0xab, 0x62, 0x26, 0x60, 0xe2, 0xd0, 0xc8, 0xf4, 0x02, 0x23, 0x16, 0xc3, + 0x52, 0x5d, 0x9f, 0x05, 0x49, 0xa2, 0x71, 0x2d, 0x6d, 0x5b, 0x90, 0xdd, 0xbf, 0xe5, 0xa9, 0x2e, + 0xf1, 0x85, 0x8a, 0x8a, 0xb8, 0xa9, 0x6b, 0x13, 0xcc, 0x8d, 0x4c, 0x22, 0x41, 0xad, 0x32, 0x1e, + 0x3b, 0x4b, 0x89, 0x37, 0x66, 0xdf, 0x1e, 0xa5, 0x4a, 0x03, 0x52, 0x1c, 0xd9, 0x19, 0x79, 0x22, + 0xd4, 0xa7, 0x3b, 0x47, 0x93, 0xa9, 0x0c, 0x03, 0x6a, 0xd8, 0x5f, 0xfc, 0xc0, 0x75, 0x33, 0xe5, + 0x26, 0xda, 0xf7, 0x4a, 0x77, 0xd8, 0xf1, 0x30, 0x80, 0x39, 0x38, 0x1e, 0x86, 0x1d, 0x97, 0x00, + 0x9c, 0x0e, 0xba, 0x00, 0x54, 0x8a, 0xc0, 0x12, 0x32, 0x6f, 0x3d, 0xc4, 0x15, 0xf9, 0x50, 0xf8, + 0xce, 0x95, 0x30, 0x82, 0x04, 0x71, 0x30, 0x82, 0x03, 0x5d, 0xa0, 0x03, 0x02, 0x01, 0x02, 0x02, + 0x05, 0x03, 0x00, 0x00, 0x00, 0x0f, 0x30, 0x09, 0x06, 0x05, 0x2b, 0x0e, 0x03, 0x02, 0x1d, 0x05, + 0x00, 0x30, 0x32, 0x31, 0x30, 0x30, 0x13, 0x06, 0x03, 0x55, 0x04, 0x03, 0x1e, 0x0c, 0x00, 0x52, + 0x00, 0x4f, 0x00, 0x44, 0x00, 0x45, 0x00, 0x4e, 0x00, 0x54, 0x30, 0x19, 0x06, 0x03, 0x55, 0x04, + 0x07, 0x1e, 0x12, 0x00, 0x57, 0x00, 0x4f, 0x00, 0x52, 0x00, 0x4b, 0x00, 0x47, 0x00, 0x52, 0x00, + 0x4f, 0x00, 0x55, 0x00, 0x50, 0x30, 0x1e, 0x17, 0x0d, 0x30, 0x37, 0x30, 0x36, 0x32, 0x30, 0x31, + 0x34, 0x35, 0x31, 0x33, 0x35, 0x5a, 0x17, 0x0d, 0x30, 0x37, 0x30, 0x39, 0x31, 0x38, 0x31, 0x34, + 0x35, 0x31, 0x33, 0x35, 0x5a, 0x30, 0x7f, 0x31, 0x7d, 0x30, 0x13, 0x06, 0x03, 0x55, 0x04, 0x03, + 0x1e, 0x0c, 0x00, 0x52, 0x00, 0x4f, 0x00, 0x44, 0x00, 0x45, 0x00, 0x4e, 0x00, 0x54, 0x30, 0x21, + 0x06, 0x03, 0x55, 0x04, 0x07, 0x1e, 0x1a, 0x00, 0x41, 0x00, 0x64, 0x00, 0x6d, 0x00, 0x69, 0x00, + 0x6e, 0x00, 0x69, 0x00, 0x73, 0x00, 0x74, 0x00, 0x72, 0x00, 0x61, 0x00, 0x74, 0x00, 0x6f, 0x00, + 0x72, 0x30, 0x43, 0x06, 0x03, 0x55, 0x04, 0x05, 0x1e, 0x3c, 0x00, 0x31, 0x00, 0x42, 0x00, 0x63, + 0x00, 0x4b, 0x00, 0x65, 0x00, 0x64, 0x00, 0x79, 0x00, 0x32, 0x00, 0x6b, 0x00, 0x72, 0x00, 0x4f, + 0x00, 0x34, 0x00, 0x2f, 0x00, 0x4d, 0x00, 0x43, 0x00, 0x44, 0x00, 0x4c, 0x00, 0x49, 0x00, 0x31, + 0x00, 0x41, 0x00, 0x48, 0x00, 0x5a, 0x00, 0x63, 0x00, 0x50, 0x00, 0x69, 0x00, 0x61, 0x00, 0x73, + 0x00, 0x3d, 0x00, 0x0d, 0x00, 0x0a, 0x30, 0x82, 0x01, 0x22, 0x30, 0x0d, 0x06, 0x09, 0x2a, 0x86, + 0x48, 0x86, 0xf7, 0x0d, 0x01, 0x01, 0x01, 0x05, 0x00, 0x03, 0x82, 0x01, 0x0f, 0x00, 0x30, 0x82, + 0x01, 0x0a, 0x02, 0x82, 0x01, 0x01, 0x00, 0x88, 0xad, 0x7c, 0x8f, 0x8b, 0x82, 0x76, 0x5a, 0xbd, + 0x8f, 0x6f, 0x62, 0x18, 0xe1, 0xd9, 0xaa, 0x41, 0xfd, 0xed, 0x68, 0x01, 0xc6, 0x34, 0x35, 0xb0, + 0x29, 0x04, 0xca, 0x4a, 0x4a, 0x1c, 0x7e, 0x80, 0x14, 0xf7, 0x8e, 0x77, 0xb8, 0x25, 0xff, 0x16, + 0x47, 0x6f, 0xbd, 0xe2, 0x34, 0x3d, 0x2e, 0x02, 0xb9, 0x53, 0xe4, 0x33, 0x75, 0xad, 0x73, 0x28, + 0x80, 0xa0, 0x4d, 0xfc, 0x6c, 0xc0, 0x22, 0x53, 0x1b, 0x2c, 0xf8, 0xf5, 0x01, 0x60, 0x19, 0x7e, + 0x79, 0x19, 0x39, 0x8d, 0xb5, 0xce, 0x39, 0x58, 0xdd, 0x55, 0x24, 0x3b, 0x55, 0x7b, 0x43, 0xc1, + 0x7f, 0x14, 0x2f, 0xb0, 0x64, 0x3a, 0x54, 0x95, 0x2b, 0x88, 0x49, 0x0c, 0x61, 0x2d, 0xac, 0xf8, + 0x45, 0xf5, 0xda, 0x88, 0x18, 0x5f, 0xae, 0x42, 0xf8, 0x75, 0xc7, 0x26, 0x6d, 0xb5, 0xbb, 0x39, + 0x6f, 0xcc, 0x55, 0x1b, 0x32, 0x11, 0x38, 0x8d, 0xe4, 0xe9, 0x44, 0x84, 0x11, 0x36, 0xa2, 0x61, + 0x76, 0xaa, 0x4c, 0xb4, 0xe3, 0x55, 0x0f, 0xe4, 0x77, 0x8e, 0xde, 0xe3, 0xa9, 0xea, 0xb7, 0x41, + 0x94, 0x00, 0x58, 0xaa, 0xc9, 0x34, 0xa2, 0x98, 0xc6, 0x01, 0x1a, 0x76, 0x14, 0x01, 0xa8, 0xdc, + 0x30, 0x7c, 0x77, 0x5a, 0x20, 0x71, 0x5a, 0xa2, 0x3f, 0xaf, 0x13, 0x7e, 0xe8, 0xfd, 0x84, 0xa2, + 0x5b, 0xcf, 0x25, 0xe9, 0xc7, 0x8f, 0xa8, 0xf2, 0x8b, 0x84, 0xc7, 0x04, 0x5e, 0x53, 0x73, 0x4e, + 0x0e, 0x89, 0xa3, 0x3c, 0xe7, 0x68, 0x5c, 0x24, 0xb7, 0x80, 0x53, 0x3c, 0x54, 0xc8, 0xc1, 0x53, + 0xaa, 0x71, 0x71, 0x3d, 0x36, 0x15, 0xd6, 0x6a, 0x9d, 0x7d, 0xde, 0xae, 0xf9, 0xe6, 0xaf, 0x57, + 0xae, 0xb9, 0x01, 0x96, 0x5d, 0xe0, 0x4d, 0xcd, 0xed, 0xc8, 0xd7, 0xf3, 0x01, 0x03, 0x38, 0x10, + 0xbe, 0x7c, 0x42, 0x67, 0x01, 0xa7, 0x23, 0x02, 0x03, 0x01, 0x00, 0x01, 0xa3, 0x82, 0x01, 0x47, + 0x30, 0x82, 0x01, 0x43, 0x30, 0x14, 0x06, 0x09, 0x2b, 0x06, 0x01, 0x04, 0x01, 0x82, 0x37, 0x12, + 0x04, 0x01, 0x01, 0xff, 0x04, 0x04, 0x01, 0x00, 0x05, 0x00, 0x30, 0x3c, 0x06, 0x09, 0x2b, 0x06, + 0x01, 0x04, 0x01, 0x82, 0x37, 0x12, 0x02, 0x01, 0x01, 0xff, 0x04, 0x2c, 0x4d, 0x00, 0x69, 0x00, + 0x63, 0x00, 0x72, 0x00, 0x6f, 0x00, 0x73, 0x00, 0x6f, 0x00, 0x66, 0x00, 0x74, 0x00, 0x20, 0x00, + 0x43, 0x00, 0x6f, 0x00, 0x72, 0x00, 0x70, 0x00, 0x6f, 0x00, 0x72, 0x00, 0x61, 0x00, 0x74, 0x00, + 0x69, 0x00, 0x6f, 0x00, 0x6e, 0x00, 0x00, 0x00, 0x30, 0x56, 0x06, 0x09, 0x2b, 0x06, 0x01, 0x04, + 0x01, 0x82, 0x37, 0x12, 0x05, 0x01, 0x01, 0xff, 0x04, 0x46, 0x00, 0x30, 0x00, 0x00, 0x01, 0x00, + 0x00, 0x00, 0xff, 0x00, 0x00, 0x00, 0x00, 0x04, 0x00, 0x00, 0x1c, 0x00, 0x08, 0x00, 0x24, 0x00, + 0x16, 0x00, 0x3a, 0x00, 0x01, 0x00, 0x41, 0x00, 0x30, 0x00, 0x32, 0x00, 0x00, 0x00, 0x41, 0x00, + 0x30, 0x00, 0x32, 0x00, 0x2d, 0x00, 0x36, 0x00, 0x2e, 0x00, 0x30, 0x00, 0x30, 0x00, 0x2d, 0x00, + 0x53, 0x00, 0x00, 0x00, 0x06, 0x00, 0x00, 0x00, 0x00, 0x80, 0x64, 0x80, 0x00, 0x00, 0x00, 0x00, + 0x30, 0x6e, 0x06, 0x09, 0x2b, 0x06, 0x01, 0x04, 0x01, 0x82, 0x37, 0x12, 0x06, 0x01, 0x01, 0xff, + 0x04, 0x5e, 0x00, 0x30, 0x00, 0x00, 0x00, 0x00, 0x0e, 0x00, 0x3e, 0x00, 0x52, 0x00, 0x4f, 0x00, + 0x44, 0x00, 0x45, 0x00, 0x4e, 0x00, 0x54, 0x00, 0x00, 0x00, 0x37, 0x00, 0x38, 0x00, 0x34, 0x00, + 0x34, 0x00, 0x30, 0x00, 0x2d, 0x00, 0x30, 0x00, 0x30, 0x00, 0x36, 0x00, 0x2d, 0x00, 0x35, 0x00, + 0x38, 0x00, 0x36, 0x00, 0x37, 0x00, 0x30, 0x00, 0x34, 0x00, 0x35, 0x00, 0x2d, 0x00, 0x37, 0x00, + 0x30, 0x00, 0x33, 0x00, 0x34, 0x00, 0x37, 0x00, 0x00, 0x00, 0x57, 0x00, 0x4f, 0x00, 0x52, 0x00, + 0x4b, 0x00, 0x47, 0x00, 0x52, 0x00, 0x4f, 0x00, 0x55, 0x00, 0x50, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x30, 0x25, 0x06, 0x03, 0x55, 0x1d, 0x23, 0x01, 0x01, 0xff, 0x04, 0x1b, 0x30, 0x19, 0xa1, 0x10, + 0xa4, 0x0e, 0x52, 0x00, 0x4f, 0x00, 0x44, 0x00, 0x45, 0x00, 0x4e, 0x00, 0x54, 0x00, 0x00, 0x00, + 0x82, 0x05, 0x03, 0x00, 0x00, 0x00, 0x0f, 0x30, 0x09, 0x06, 0x05, 0x2b, 0x0e, 0x03, 0x02, 0x1d, + 0x05, 0x00, 0x03, 0x82, 0x01, 0x01, 0x00, 0x13, 0x1b, 0xdc, 0x89, 0xd2, 0xfc, 0x54, 0x0c, 0xee, + 0x82, 0x45, 0x68, 0x6a, 0x72, 0xc3, 0x3e, 0x17, 0x73, 0x96, 0x53, 0x44, 0x39, 0x50, 0x0e, 0x0b, + 0x9f, 0x95, 0xd6, 0x2c, 0x6b, 0x53, 0x14, 0x9c, 0xe5, 0x55, 0xed, 0x65, 0xdf, 0x2a, 0xeb, 0x5c, + 0x64, 0x85, 0x70, 0x1f, 0xbc, 0x96, 0xcf, 0xa3, 0x76, 0xb1, 0x72, 0x3b, 0xe1, 0xf6, 0xad, 0xad, + 0xad, 0x2a, 0x14, 0xaf, 0xba, 0xd0, 0xd6, 0xd5, 0x6d, 0x55, 0xec, 0x1e, 0xc3, 0x4b, 0xba, 0x06, + 0x9c, 0x59, 0x78, 0x93, 0x64, 0x87, 0x4b, 0x03, 0xf9, 0xee, 0x4c, 0xdd, 0x36, 0x5b, 0xbd, 0xd4, + 0xe5, 0x4c, 0x4e, 0xda, 0x7b, 0xc1, 0xae, 0x23, 0x28, 0x9e, 0x77, 0x6f, 0x0f, 0xe6, 0x94, 0xfe, + 0x05, 0x22, 0x00, 0xab, 0x63, 0x5b, 0xe1, 0x82, 0x45, 0xa6, 0xec, 0x1f, 0x6f, 0x2c, 0x7b, 0x56, + 0xde, 0x78, 0x25, 0x7d, 0x10, 0x60, 0x0e, 0x53, 0x42, 0x4b, 0x6c, 0x7a, 0x6b, 0x5d, 0xc9, 0xd5, + 0xa6, 0xae, 0xc8, 0xc8, 0x52, 0x29, 0xd6, 0x42, 0x56, 0x02, 0xec, 0xf9, 0x23, 0xa8, 0x8c, 0x8d, + 0x89, 0xc9, 0x7c, 0x84, 0x07, 0xfc, 0x33, 0xe1, 0x1e, 0xea, 0xe2, 0x8f, 0x2b, 0xbe, 0x8f, 0xa9, + 0xd3, 0xd1, 0xe1, 0x5e, 0x0b, 0xdc, 0xb6, 0x43, 0x6e, 0x33, 0x0a, 0xf4, 0x2e, 0x9d, 0x0c, 0xc9, + 0x58, 0x54, 0x34, 0xaa, 0xe1, 0xd2, 0xa2, 0xe4, 0x90, 0x02, 0x23, 0x26, 0xa0, 0x92, 0x26, 0x26, + 0x0a, 0x83, 0xb4, 0x4d, 0xd9, 0x4b, 0xef, 0xeb, 0x9d, 0xa9, 0x24, 0x3f, 0x92, 0x8b, 0xdb, 0x04, + 0x7b, 0x9d, 0x64, 0x91, 0xa4, 0x4b, 0xd2, 0x6e, 0x51, 0x05, 0x08, 0xc9, 0x91, 0xaf, 0x31, 0x26, + 0x55, 0x21, 0xb1, 0xea, 0xce, 0xa3, 0xa4, 0x0d, 0x5e, 0x4c, 0x46, 0xdb, 0x16, 0x2d, 0x98, 0xdc, + 0x60, 0x19, 0xb8, 0x1b, 0xb9, 0xcd, 0xfb, 0x31, 0x00 + ]; + + let license = License::from_decrypted_data(&mut Cursor::new(payload)).unwrap(); + assert_eq!(6, license.version_major); + assert_eq!(0, license.version_minor); + assert_eq!(String::from("microsoft.com"), license.scope); + assert_eq!(String::from("Microsoft Corporation"), license.company_name); + assert_eq!(String::from("A02"), license.product_id); + assert_eq!(1945, license.cert_data.len()); + } + + #[test] + fn test_licensed_product_info() { + // this payload was extracted from the decrypted certificate at + // https://learn.microsoft.com/en-us/openspecs/windows_protocols/ms-rdpele/73c596f1-9550-4649-b880-2947c36c1bb6, + // then run through `openssl x509 -noout -text -inform der -in rdplicense.der -certopt ext_dump` to get the + // value of the 1.3.6.1.4.1.311.18.5 extension + let payload = vec![ + 0x00, 0x30, 0x00, 0x00, // version + 0x01, 0x00, 0x00, 0x00, // license count + 0xff, 0x00, 0x00, 0x00, // platform ID + 0x00, 0x04, 0x00, 0x00, // language ID + 0x1c, 0x00, 0x08, 0x00, // requested product ID (offset=28, count=8) + 0x24, 0x00, 0x16, 0x00, // adjusted product ID (offset=36, count=22) + 0x3a, 0x00, 0x01, 0x00, // version info (offset=58, count=1) + 0x41, 0x00, 0x30, 0x00, // requested product ID + 0x32, 0x00, 0x00, 0x00, // requested product ID + 0x41, 0x00, 0x30, 0x00, // + 0x32, 0x00, 0x2d, 0x00, // + 0x36, 0x00, 0x2e, 0x00, // adjusted product ID + 0x30, 0x00, 0x30, 0x00, // + 0x2d, 0x00, 0x53, 0x00, // + 0x00, 0x00, // + 0x06, 0x00, 0x00, 0x00, // version info (major=6 minor=0) + 0x00, 0x80, 0x64, 0x80, // version info (flags=0x80648000) + 0x00, 0x00, 0x00, 0x00, + ]; + + let info = LicensedProductInfo::from_bytes(&mut Cursor::new(payload)).unwrap(); + assert_eq!(1, info.count); + assert_eq!(0xFF, info.platform_id); + assert_eq!(String::from("A02"), info.requested_product_id); + assert_eq!(String::from("A02-6.00-S"), info.adjusted_product_id); + + let license_flags = info.flags as u32; + assert_eq!( + true, + license_flags & ProductLicenseFlags::LicenseEnforced as u32 != 0 + ); + assert_eq!( + true, + license_flags & ProductLicenseFlags::TemporaryLicense as u32 != 0 + ); } #[test]