2019-10-08 09:31:18 +00:00
|
|
|
use aes::Aes192;
|
2016-01-01 23:16:12 +00:00
|
|
|
use byteorder::{BigEndian, ByteOrder};
|
2018-07-23 13:41:39 +00:00
|
|
|
use hmac::Hmac;
|
|
|
|
use pbkdf2::pbkdf2;
|
2016-03-13 20:45:31 +00:00
|
|
|
use protobuf::ProtobufEnum;
|
2019-10-08 09:31:18 +00:00
|
|
|
use sha1::{Digest, Sha1};
|
2021-01-23 21:37:41 +00:00
|
|
|
use std::io::{self, Read};
|
2016-01-01 23:16:12 +00:00
|
|
|
|
2019-10-08 09:31:18 +00:00
|
|
|
use crate::protocol::authentication::AuthenticationType;
|
2021-01-18 13:30:24 +00:00
|
|
|
use crate::protocol::keyexchange::{APLoginFailed, ErrorCode};
|
2016-01-01 23:16:12 +00:00
|
|
|
|
2018-02-11 11:37:08 +00:00
|
|
|
#[derive(Debug, Clone, Serialize, Deserialize)]
|
2016-03-13 20:45:31 +00:00
|
|
|
pub struct Credentials {
|
|
|
|
pub username: String,
|
2016-07-06 01:29:38 +00:00
|
|
|
|
2018-02-11 11:37:08 +00:00
|
|
|
#[serde(serialize_with = "serialize_protobuf_enum")]
|
|
|
|
#[serde(deserialize_with = "deserialize_protobuf_enum")]
|
2016-03-13 20:45:31 +00:00
|
|
|
pub auth_type: AuthenticationType,
|
2016-01-01 23:16:12 +00:00
|
|
|
|
2019-03-20 20:17:23 +00:00
|
|
|
#[serde(alias = "encoded_auth_blob")]
|
2018-02-11 11:37:08 +00:00
|
|
|
#[serde(serialize_with = "serialize_base64")]
|
|
|
|
#[serde(deserialize_with = "deserialize_base64")]
|
2016-07-06 01:29:38 +00:00
|
|
|
pub auth_data: Vec<u8>,
|
2016-03-13 22:35:09 +00:00
|
|
|
}
|
|
|
|
|
2016-03-13 20:45:31 +00:00
|
|
|
impl Credentials {
|
|
|
|
pub fn with_password(username: String, password: String) -> Credentials {
|
|
|
|
Credentials {
|
|
|
|
username: username,
|
|
|
|
auth_type: AuthenticationType::AUTHENTICATION_USER_PASS,
|
|
|
|
auth_data: password.into_bytes(),
|
|
|
|
}
|
2016-01-26 23:01:45 +00:00
|
|
|
}
|
|
|
|
|
2016-03-13 20:45:31 +00:00
|
|
|
pub fn with_blob(username: String, encrypted_blob: &str, device_id: &str) -> Credentials {
|
|
|
|
fn read_u8<R: Read>(stream: &mut R) -> io::Result<u8> {
|
|
|
|
let mut data = [0u8];
|
2019-10-08 09:31:18 +00:00
|
|
|
stream.read_exact(&mut data)?;
|
2016-03-13 20:45:31 +00:00
|
|
|
Ok(data[0])
|
|
|
|
}
|
2016-01-26 23:01:45 +00:00
|
|
|
|
2016-03-13 20:45:31 +00:00
|
|
|
fn read_int<R: Read>(stream: &mut R) -> io::Result<u32> {
|
2019-10-08 09:31:18 +00:00
|
|
|
let lo = read_u8(stream)? as u32;
|
2016-03-13 20:45:31 +00:00
|
|
|
if lo & 0x80 == 0 {
|
|
|
|
return Ok(lo);
|
2016-01-01 23:16:12 +00:00
|
|
|
}
|
2016-01-26 23:01:45 +00:00
|
|
|
|
2019-10-08 09:31:18 +00:00
|
|
|
let hi = read_u8(stream)? as u32;
|
2016-03-13 20:45:31 +00:00
|
|
|
Ok(lo & 0x7f | hi << 7)
|
|
|
|
}
|
2016-01-01 23:16:12 +00:00
|
|
|
|
2016-03-13 20:45:31 +00:00
|
|
|
fn read_bytes<R: Read>(stream: &mut R) -> io::Result<Vec<u8>> {
|
2019-10-08 09:31:18 +00:00
|
|
|
let length = read_int(stream)?;
|
2016-03-13 20:45:31 +00:00
|
|
|
let mut data = vec![0u8; length as usize];
|
2019-10-08 09:31:18 +00:00
|
|
|
stream.read_exact(&mut data)?;
|
2016-01-01 23:16:12 +00:00
|
|
|
|
2016-03-13 20:45:31 +00:00
|
|
|
Ok(data)
|
2016-01-01 23:16:12 +00:00
|
|
|
}
|
|
|
|
|
2018-07-23 13:41:39 +00:00
|
|
|
let secret = Sha1::digest(device_id.as_bytes());
|
2016-01-01 23:16:12 +00:00
|
|
|
|
|
|
|
let key = {
|
2018-07-23 13:41:39 +00:00
|
|
|
let mut key = [0u8; 24];
|
|
|
|
pbkdf2::<Hmac<Sha1>>(&secret, username.as_bytes(), 0x100, &mut key[0..20]);
|
|
|
|
|
|
|
|
let hash = &Sha1::digest(&key[..20]);
|
|
|
|
key[..20].copy_from_slice(hash);
|
|
|
|
BigEndian::write_u32(&mut key[20..], 20);
|
|
|
|
key
|
2016-01-01 23:16:12 +00:00
|
|
|
};
|
|
|
|
|
2019-03-16 15:30:10 +00:00
|
|
|
// decrypt data using ECB mode without padding
|
2016-01-01 23:16:12 +00:00
|
|
|
let blob = {
|
2019-03-16 15:30:10 +00:00
|
|
|
use aes::block_cipher_trait::generic_array::typenum::Unsigned;
|
2019-10-08 09:31:18 +00:00
|
|
|
use aes::block_cipher_trait::generic_array::GenericArray;
|
|
|
|
use aes::block_cipher_trait::BlockCipher;
|
2019-03-16 15:30:10 +00:00
|
|
|
|
|
|
|
let mut data = base64::decode(encrypted_blob).unwrap();
|
|
|
|
let cipher = Aes192::new(GenericArray::from_slice(&key));
|
|
|
|
let block_size = <Aes192 as BlockCipher>::BlockSize::to_usize();
|
|
|
|
assert_eq!(data.len() % block_size, 0);
|
|
|
|
// replace to chunks_exact_mut with MSRV bump to 1.31
|
|
|
|
for chunk in data.chunks_mut(block_size) {
|
|
|
|
cipher.decrypt_block(GenericArray::from_mut_slice(chunk));
|
|
|
|
}
|
2018-07-23 13:41:39 +00:00
|
|
|
|
|
|
|
let l = data.len();
|
2016-01-02 15:19:39 +00:00
|
|
|
for i in 0..l - 0x10 {
|
2016-01-01 23:16:12 +00:00
|
|
|
data[l - i - 1] ^= data[l - i - 0x11];
|
|
|
|
}
|
|
|
|
|
|
|
|
data
|
|
|
|
};
|
|
|
|
|
|
|
|
let mut cursor = io::Cursor::new(&blob);
|
|
|
|
read_u8(&mut cursor).unwrap();
|
|
|
|
read_bytes(&mut cursor).unwrap();
|
|
|
|
read_u8(&mut cursor).unwrap();
|
|
|
|
let auth_type = read_int(&mut cursor).unwrap();
|
|
|
|
let auth_type = AuthenticationType::from_i32(auth_type as i32).unwrap();
|
|
|
|
read_u8(&mut cursor).unwrap();
|
2018-02-11 11:37:08 +00:00
|
|
|
let auth_data = read_bytes(&mut cursor).unwrap();
|
2016-01-01 23:16:12 +00:00
|
|
|
|
2016-03-13 20:45:31 +00:00
|
|
|
Credentials {
|
|
|
|
username: username,
|
|
|
|
auth_type: auth_type,
|
|
|
|
auth_data: auth_data,
|
|
|
|
}
|
2016-01-01 23:16:12 +00:00
|
|
|
}
|
2016-03-13 22:35:09 +00:00
|
|
|
}
|
|
|
|
|
2017-01-29 12:50:18 +00:00
|
|
|
fn serialize_protobuf_enum<T, S>(v: &T, ser: S) -> Result<S::Ok, S::Error>
|
2018-02-11 11:37:08 +00:00
|
|
|
where
|
|
|
|
T: ProtobufEnum,
|
|
|
|
S: serde::Serializer,
|
|
|
|
{
|
2016-07-06 01:29:38 +00:00
|
|
|
serde::Serialize::serialize(&v.value(), ser)
|
2016-01-01 23:16:12 +00:00
|
|
|
}
|
2016-03-13 22:35:09 +00:00
|
|
|
|
2019-03-15 14:50:15 +00:00
|
|
|
fn deserialize_protobuf_enum<'de, T, D>(de: D) -> Result<T, D::Error>
|
2018-02-11 11:37:08 +00:00
|
|
|
where
|
|
|
|
T: ProtobufEnum,
|
2019-03-15 14:50:15 +00:00
|
|
|
D: serde::Deserializer<'de>,
|
2018-02-11 11:37:08 +00:00
|
|
|
{
|
2019-10-08 09:31:18 +00:00
|
|
|
let v: i32 = serde::Deserialize::deserialize(de)?;
|
2017-02-09 01:32:18 +00:00
|
|
|
T::from_i32(v).ok_or_else(|| serde::de::Error::custom("Invalid enum value"))
|
2016-07-06 01:29:38 +00:00
|
|
|
}
|
|
|
|
|
2017-01-29 12:50:18 +00:00
|
|
|
fn serialize_base64<T, S>(v: &T, ser: S) -> Result<S::Ok, S::Error>
|
2018-02-11 11:37:08 +00:00
|
|
|
where
|
|
|
|
T: AsRef<[u8]>,
|
|
|
|
S: serde::Serializer,
|
|
|
|
{
|
2017-02-09 01:27:52 +00:00
|
|
|
serde::Serialize::serialize(&base64::encode(v.as_ref()), ser)
|
2016-07-06 01:29:38 +00:00
|
|
|
}
|
|
|
|
|
2019-03-15 14:50:15 +00:00
|
|
|
fn deserialize_base64<'de, D>(de: D) -> Result<Vec<u8>, D::Error>
|
2018-02-11 11:37:08 +00:00
|
|
|
where
|
2019-03-15 14:50:15 +00:00
|
|
|
D: serde::Deserializer<'de>,
|
2018-02-11 11:37:08 +00:00
|
|
|
{
|
2019-10-08 09:31:18 +00:00
|
|
|
let v: String = serde::Deserialize::deserialize(de)?;
|
2017-02-09 01:27:52 +00:00
|
|
|
base64::decode(&v).map_err(|e| serde::de::Error::custom(e.to_string()))
|
2016-03-13 22:35:09 +00:00
|
|
|
}
|
|
|
|
|
2018-02-25 04:40:00 +00:00
|
|
|
pub fn get_credentials<F: FnOnce(&String) -> String>(
|
2018-02-11 11:37:08 +00:00
|
|
|
username: Option<String>,
|
|
|
|
password: Option<String>,
|
|
|
|
cached_credentials: Option<Credentials>,
|
2018-02-25 04:40:00 +00:00
|
|
|
prompt: F,
|
2018-02-11 11:37:08 +00:00
|
|
|
) -> Option<Credentials> {
|
2017-01-18 17:07:20 +00:00
|
|
|
match (username, password, cached_credentials) {
|
2018-02-11 11:37:08 +00:00
|
|
|
(Some(username), Some(password), _) => Some(Credentials::with_password(username, password)),
|
2017-01-06 16:18:41 +00:00
|
|
|
|
2018-02-11 11:37:08 +00:00
|
|
|
(Some(ref username), _, Some(ref credentials)) if *username == credentials.username => {
|
|
|
|
Some(credentials.clone())
|
|
|
|
}
|
2017-01-06 16:18:41 +00:00
|
|
|
|
2019-10-08 09:31:18 +00:00
|
|
|
(Some(username), None, _) => Some(Credentials::with_password(
|
|
|
|
username.clone(),
|
|
|
|
prompt(&username),
|
|
|
|
)),
|
2017-01-06 16:18:41 +00:00
|
|
|
|
2018-02-11 11:37:08 +00:00
|
|
|
(None, _, Some(credentials)) => Some(credentials),
|
2017-01-06 16:18:41 +00:00
|
|
|
|
2017-02-22 04:17:04 +00:00
|
|
|
(None, _, None) => None,
|
2017-01-06 16:18:41 +00:00
|
|
|
}
|
|
|
|
}
|
2021-01-18 13:30:24 +00:00
|
|
|
|
|
|
|
error_chain! {
|
|
|
|
types {
|
|
|
|
AuthenticationError, AuthenticationErrorKind, AuthenticationResultExt, AuthenticationResult;
|
|
|
|
}
|
|
|
|
|
|
|
|
foreign_links {
|
|
|
|
Io(::std::io::Error);
|
|
|
|
}
|
|
|
|
|
|
|
|
errors {
|
|
|
|
BadCredentials {
|
|
|
|
description("Bad credentials")
|
|
|
|
display("Authentication failed with error: Bad credentials")
|
|
|
|
}
|
|
|
|
PremiumAccountRequired {
|
|
|
|
description("Premium account required")
|
|
|
|
display("Authentication failed with error: Premium account required")
|
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
impl From<APLoginFailed> for AuthenticationError {
|
|
|
|
fn from(login_failure: APLoginFailed) -> Self {
|
|
|
|
let error_code = login_failure.get_error_code();
|
|
|
|
match error_code {
|
|
|
|
ErrorCode::BadCredentials => Self::from_kind(AuthenticationErrorKind::BadCredentials),
|
|
|
|
ErrorCode::PremiumAccountRequired => {
|
|
|
|
Self::from_kind(AuthenticationErrorKind::PremiumAccountRequired)
|
|
|
|
}
|
|
|
|
_ => format!("Authentication failed with error: {:?}", error_code).into(),
|
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|