mirror of
https://github.com/librespot-org/librespot.git
synced 2024-12-18 17:11:53 +00:00
Almost eliminate util module
This commit is contained in:
parent
9378ae5b6f
commit
e688e7e886
9 changed files with 59 additions and 67 deletions
3
Cargo.lock
generated
3
Cargo.lock
generated
|
@ -1335,6 +1335,7 @@ dependencies = [
|
||||||
"base64",
|
"base64",
|
||||||
"block-modes",
|
"block-modes",
|
||||||
"dns-sd",
|
"dns-sd",
|
||||||
|
"form_urlencoded",
|
||||||
"futures-core",
|
"futures-core",
|
||||||
"futures-util",
|
"futures-util",
|
||||||
"hmac",
|
"hmac",
|
||||||
|
@ -1363,6 +1364,7 @@ dependencies = [
|
||||||
"byteorder",
|
"byteorder",
|
||||||
"bytes",
|
"bytes",
|
||||||
"env_logger",
|
"env_logger",
|
||||||
|
"form_urlencoded",
|
||||||
"futures-core",
|
"futures-core",
|
||||||
"futures-util",
|
"futures-util",
|
||||||
"hmac",
|
"hmac",
|
||||||
|
@ -1640,6 +1642,7 @@ dependencies = [
|
||||||
"autocfg",
|
"autocfg",
|
||||||
"num-integer",
|
"num-integer",
|
||||||
"num-traits",
|
"num-traits",
|
||||||
|
"rand",
|
||||||
]
|
]
|
||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
|
|
|
@ -11,6 +11,7 @@ edition = "2018"
|
||||||
aes-ctr = "0.6"
|
aes-ctr = "0.6"
|
||||||
base64 = "0.13"
|
base64 = "0.13"
|
||||||
block-modes = "0.7"
|
block-modes = "0.7"
|
||||||
|
form_urlencoded = "1.0"
|
||||||
futures-core = "0.3"
|
futures-core = "0.3"
|
||||||
futures-util = { version = "0.3", default_features = false }
|
futures-util = { version = "0.3", default_features = false }
|
||||||
hmac = "0.10"
|
hmac = "0.10"
|
||||||
|
|
|
@ -7,7 +7,6 @@ use crate::core::config::{ConnectConfig, VolumeCtrl};
|
||||||
use crate::core::mercury::{MercuryError, MercurySender};
|
use crate::core::mercury::{MercuryError, MercurySender};
|
||||||
use crate::core::session::Session;
|
use crate::core::session::Session;
|
||||||
use crate::core::spotify_id::{SpotifyAudioType, SpotifyId, SpotifyIdError};
|
use crate::core::spotify_id::{SpotifyAudioType, SpotifyId, SpotifyIdError};
|
||||||
use crate::core::util::url_encode;
|
|
||||||
use crate::core::util::SeqGenerator;
|
use crate::core::util::SeqGenerator;
|
||||||
use crate::core::version;
|
use crate::core::version;
|
||||||
use crate::playback::mixer::Mixer;
|
use crate::playback::mixer::Mixer;
|
||||||
|
@ -244,6 +243,10 @@ fn volume_to_mixer(volume: u16, volume_ctrl: &VolumeCtrl) -> u16 {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
fn url_encode(bytes: impl AsRef<[u8]>) -> String {
|
||||||
|
form_urlencoded::byte_serialize(bytes.as_ref()).collect()
|
||||||
|
}
|
||||||
|
|
||||||
impl Spirc {
|
impl Spirc {
|
||||||
pub fn new(
|
pub fn new(
|
||||||
config: ConnectConfig,
|
config: ConnectConfig,
|
||||||
|
@ -256,7 +259,7 @@ impl Spirc {
|
||||||
let ident = session.device_id().to_owned();
|
let ident = session.device_id().to_owned();
|
||||||
|
|
||||||
// Uri updated in response to issue #288
|
// Uri updated in response to issue #288
|
||||||
debug!("canonical_username: {}", url_encode(&session.username()));
|
debug!("canonical_username: {}", &session.username());
|
||||||
let uri = format!("hm://remote/user/{}/", url_encode(&session.username()));
|
let uri = format!("hm://remote/user/{}/", url_encode(&session.username()));
|
||||||
|
|
||||||
let subscription = Box::pin(
|
let subscription = Box::pin(
|
||||||
|
|
|
@ -17,6 +17,7 @@ aes = "0.6"
|
||||||
base64 = "0.13"
|
base64 = "0.13"
|
||||||
byteorder = "1.4"
|
byteorder = "1.4"
|
||||||
bytes = "1.0"
|
bytes = "1.0"
|
||||||
|
form_urlencoded = "1.0"
|
||||||
futures-core = { version = "0.3", default-features = false }
|
futures-core = { version = "0.3", default-features = false }
|
||||||
futures-util = { version = "0.3", default-features = false, features = ["alloc", "bilock", "unstable", "sink"] }
|
futures-util = { version = "0.3", default-features = false, features = ["alloc", "bilock", "unstable", "sink"] }
|
||||||
hmac = "0.10"
|
hmac = "0.10"
|
||||||
|
@ -25,7 +26,7 @@ http = "0.2"
|
||||||
hyper = { version = "0.14", optional = true, features = ["client", "tcp", "http1"] }
|
hyper = { version = "0.14", optional = true, features = ["client", "tcp", "http1"] }
|
||||||
hyper-proxy = { version = "0.9.1", optional = true, default-features = false }
|
hyper-proxy = { version = "0.9.1", optional = true, default-features = false }
|
||||||
log = "0.4"
|
log = "0.4"
|
||||||
num-bigint = "0.4"
|
num-bigint = { version = "0.4", features = ["rand"] }
|
||||||
num-integer = "0.1"
|
num-integer = "0.1"
|
||||||
num-traits = "0.2"
|
num-traits = "0.2"
|
||||||
once_cell = "1.5.2"
|
once_cell = "1.5.2"
|
||||||
|
|
|
@ -1,7 +1,7 @@
|
||||||
use byteorder::{BigEndian, ByteOrder, WriteBytesExt};
|
use byteorder::{BigEndian, ByteOrder, WriteBytesExt};
|
||||||
use hmac::{Hmac, Mac, NewMac};
|
use hmac::{Hmac, Mac, NewMac};
|
||||||
use protobuf::{self, Message};
|
use protobuf::{self, Message};
|
||||||
use rand::thread_rng;
|
use rand::{thread_rng, RngCore};
|
||||||
use sha1::Sha1;
|
use sha1::Sha1;
|
||||||
use std::io;
|
use std::io;
|
||||||
use tokio::io::{AsyncRead, AsyncReadExt, AsyncWrite, AsyncWriteExt};
|
use tokio::io::{AsyncRead, AsyncReadExt, AsyncWrite, AsyncWriteExt};
|
||||||
|
@ -11,7 +11,6 @@ use super::codec::ApCodec;
|
||||||
use crate::diffie_hellman::DhLocalKeys;
|
use crate::diffie_hellman::DhLocalKeys;
|
||||||
use crate::protocol;
|
use crate::protocol;
|
||||||
use crate::protocol::keyexchange::{APResponseMessage, ClientHello, ClientResponsePlaintext};
|
use crate::protocol::keyexchange::{APResponseMessage, ClientHello, ClientResponsePlaintext};
|
||||||
use crate::util;
|
|
||||||
|
|
||||||
pub async fn handshake<T: AsyncRead + AsyncWrite + Unpin>(
|
pub async fn handshake<T: AsyncRead + AsyncWrite + Unpin>(
|
||||||
mut connection: T,
|
mut connection: T,
|
||||||
|
@ -40,6 +39,9 @@ async fn client_hello<T>(connection: &mut T, gc: Vec<u8>) -> io::Result<Vec<u8>>
|
||||||
where
|
where
|
||||||
T: AsyncWrite + Unpin,
|
T: AsyncWrite + Unpin,
|
||||||
{
|
{
|
||||||
|
let mut client_nonce = vec![0; 0x10];
|
||||||
|
thread_rng().fill_bytes(&mut client_nonce);
|
||||||
|
|
||||||
let mut packet = ClientHello::new();
|
let mut packet = ClientHello::new();
|
||||||
packet
|
packet
|
||||||
.mut_build_info()
|
.mut_build_info()
|
||||||
|
@ -59,7 +61,7 @@ where
|
||||||
.mut_login_crypto_hello()
|
.mut_login_crypto_hello()
|
||||||
.mut_diffie_hellman()
|
.mut_diffie_hellman()
|
||||||
.set_server_keys_known(1);
|
.set_server_keys_known(1);
|
||||||
packet.set_client_nonce(util::rand_vec(&mut thread_rng(), 0x10));
|
packet.set_client_nonce(client_nonce);
|
||||||
packet.set_padding(vec![0x1e]);
|
packet.set_padding(vec![0x1e]);
|
||||||
|
|
||||||
let mut buffer = vec![0, 4];
|
let mut buffer = vec![0, 4];
|
||||||
|
|
|
@ -1,11 +1,11 @@
|
||||||
use num_bigint::BigUint;
|
use num_bigint::{BigUint, RandBigInt};
|
||||||
|
use num_integer::Integer;
|
||||||
|
use num_traits::{One, Zero};
|
||||||
use once_cell::sync::Lazy;
|
use once_cell::sync::Lazy;
|
||||||
use rand::Rng;
|
use rand::{CryptoRng, Rng};
|
||||||
|
|
||||||
use crate::util;
|
static DH_GENERATOR: Lazy<BigUint> = Lazy::new(|| BigUint::from_bytes_be(&[0x02]));
|
||||||
|
static DH_PRIME: Lazy<BigUint> = Lazy::new(|| {
|
||||||
pub static DH_GENERATOR: Lazy<BigUint> = Lazy::new(|| BigUint::from_bytes_be(&[0x02]));
|
|
||||||
pub static DH_PRIME: Lazy<BigUint> = Lazy::new(|| {
|
|
||||||
BigUint::from_bytes_be(&[
|
BigUint::from_bytes_be(&[
|
||||||
0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xc9, 0x0f, 0xda, 0xa2, 0x21, 0x68, 0xc2,
|
0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xc9, 0x0f, 0xda, 0xa2, 0x21, 0x68, 0xc2,
|
||||||
0x34, 0xc4, 0xc6, 0x62, 0x8b, 0x80, 0xdc, 0x1c, 0xd1, 0x29, 0x02, 0x4e, 0x08, 0x8a, 0x67,
|
0x34, 0xc4, 0xc6, 0x62, 0x8b, 0x80, 0xdc, 0x1c, 0xd1, 0x29, 0x02, 0x4e, 0x08, 0x8a, 0x67,
|
||||||
|
@ -17,17 +17,31 @@ pub static DH_PRIME: Lazy<BigUint> = Lazy::new(|| {
|
||||||
])
|
])
|
||||||
});
|
});
|
||||||
|
|
||||||
|
fn powm(base: &BigUint, exp: &BigUint, modulus: &BigUint) -> BigUint {
|
||||||
|
let mut base = base.clone();
|
||||||
|
let mut exp = exp.clone();
|
||||||
|
let mut result: BigUint = One::one();
|
||||||
|
|
||||||
|
while !exp.is_zero() {
|
||||||
|
if exp.is_odd() {
|
||||||
|
result = (result * &base) % modulus;
|
||||||
|
}
|
||||||
|
exp >>= 1;
|
||||||
|
base = (&base * &base) % modulus;
|
||||||
|
}
|
||||||
|
|
||||||
|
result
|
||||||
|
}
|
||||||
|
|
||||||
pub struct DhLocalKeys {
|
pub struct DhLocalKeys {
|
||||||
private_key: BigUint,
|
private_key: BigUint,
|
||||||
public_key: BigUint,
|
public_key: BigUint,
|
||||||
}
|
}
|
||||||
|
|
||||||
impl DhLocalKeys {
|
impl DhLocalKeys {
|
||||||
pub fn random<R: Rng>(rng: &mut R) -> DhLocalKeys {
|
pub fn random<R: Rng + CryptoRng>(rng: &mut R) -> DhLocalKeys {
|
||||||
let key_data = util::rand_vec(rng, 95);
|
let private_key = rng.gen_biguint(95 * 8);
|
||||||
|
let public_key = powm(&DH_GENERATOR, &private_key, &DH_PRIME);
|
||||||
let private_key = BigUint::from_bytes_be(&key_data);
|
|
||||||
let public_key = util::powm(&DH_GENERATOR, &private_key, &DH_PRIME);
|
|
||||||
|
|
||||||
DhLocalKeys {
|
DhLocalKeys {
|
||||||
private_key,
|
private_key,
|
||||||
|
@ -40,7 +54,7 @@ impl DhLocalKeys {
|
||||||
}
|
}
|
||||||
|
|
||||||
pub fn shared_secret(&self, remote_key: &[u8]) -> Vec<u8> {
|
pub fn shared_secret(&self, remote_key: &[u8]) -> Vec<u8> {
|
||||||
let shared_key = util::powm(
|
let shared_key = powm(
|
||||||
&BigUint::from_bytes_be(remote_key),
|
&BigUint::from_bytes_be(remote_key),
|
||||||
&self.private_key,
|
&self.private_key,
|
||||||
&DH_PRIME,
|
&DH_PRIME,
|
||||||
|
|
|
@ -14,12 +14,14 @@ pub mod cache;
|
||||||
pub mod channel;
|
pub mod channel;
|
||||||
pub mod config;
|
pub mod config;
|
||||||
mod connection;
|
mod connection;
|
||||||
|
#[doc(hidden)]
|
||||||
pub mod diffie_hellman;
|
pub mod diffie_hellman;
|
||||||
pub mod keymaster;
|
pub mod keymaster;
|
||||||
pub mod mercury;
|
pub mod mercury;
|
||||||
mod proxytunnel;
|
mod proxytunnel;
|
||||||
pub mod session;
|
pub mod session;
|
||||||
pub mod spotify_id;
|
pub mod spotify_id;
|
||||||
|
#[doc(hidden)]
|
||||||
pub mod util;
|
pub mod util;
|
||||||
pub mod version;
|
pub mod version;
|
||||||
|
|
||||||
|
|
|
@ -10,7 +10,6 @@ use bytes::Bytes;
|
||||||
use tokio::sync::{mpsc, oneshot};
|
use tokio::sync::{mpsc, oneshot};
|
||||||
|
|
||||||
use crate::protocol;
|
use crate::protocol;
|
||||||
use crate::util::url_encode;
|
|
||||||
use crate::util::SeqGenerator;
|
use crate::util::SeqGenerator;
|
||||||
|
|
||||||
mod types;
|
mod types;
|
||||||
|
@ -199,7 +198,7 @@ impl MercuryManager {
|
||||||
let header: protocol::mercury::Header = protobuf::parse_from_bytes(&header_data).unwrap();
|
let header: protocol::mercury::Header = protobuf::parse_from_bytes(&header_data).unwrap();
|
||||||
|
|
||||||
let response = MercuryResponse {
|
let response = MercuryResponse {
|
||||||
uri: url_encode(header.get_uri()),
|
uri: header.get_uri().to_string(),
|
||||||
status_code: header.get_status_code(),
|
status_code: header.get_status_code(),
|
||||||
payload: pending.parts,
|
payload: pending.parts,
|
||||||
};
|
};
|
||||||
|
@ -214,8 +213,21 @@ impl MercuryManager {
|
||||||
} else if cmd == 0xb5 {
|
} else if cmd == 0xb5 {
|
||||||
self.lock(|inner| {
|
self.lock(|inner| {
|
||||||
let mut found = false;
|
let mut found = false;
|
||||||
|
|
||||||
|
// TODO: This is just a workaround to make utf-8 encoded usernames work.
|
||||||
|
// A better solution would be to use an uri struct and urlencode it directly
|
||||||
|
// before sending while saving the subscription under its unencoded form.
|
||||||
|
let mut uri_split = response.uri.split('/');
|
||||||
|
|
||||||
|
let encoded_uri = std::iter::once(uri_split.next().unwrap().to_string())
|
||||||
|
.chain(uri_split.map(|component| {
|
||||||
|
form_urlencoded::byte_serialize(component.as_bytes()).collect::<String>()
|
||||||
|
}))
|
||||||
|
.collect::<Vec<String>>()
|
||||||
|
.join("/");
|
||||||
|
|
||||||
inner.subscriptions.retain(|&(ref prefix, ref sub)| {
|
inner.subscriptions.retain(|&(ref prefix, ref sub)| {
|
||||||
if response.uri.starts_with(prefix) {
|
if encoded_uri.starts_with(prefix) {
|
||||||
found = true;
|
found = true;
|
||||||
|
|
||||||
// if send fails, remove from list of subs
|
// if send fails, remove from list of subs
|
||||||
|
|
|
@ -1,50 +1,4 @@
|
||||||
use num_bigint::BigUint;
|
|
||||||
use num_integer::Integer;
|
|
||||||
use num_traits::{One, Zero};
|
|
||||||
use rand::Rng;
|
|
||||||
use std::mem;
|
use std::mem;
|
||||||
use std::ops::{Mul, Rem, Shr};
|
|
||||||
|
|
||||||
pub fn rand_vec<G: Rng>(rng: &mut G, size: usize) -> Vec<u8> {
|
|
||||||
::std::iter::repeat(())
|
|
||||||
.map(|()| rng.gen())
|
|
||||||
.take(size)
|
|
||||||
.collect()
|
|
||||||
}
|
|
||||||
|
|
||||||
pub fn url_encode(inp: &str) -> String {
|
|
||||||
let mut encoded = String::new();
|
|
||||||
|
|
||||||
for c in inp.as_bytes().iter() {
|
|
||||||
match *c as char {
|
|
||||||
'A'..='Z' | 'a'..='z' | '0'..='9' | '-' | '_' | '.' | '~' | ':' | '/' => {
|
|
||||||
encoded.push(*c as char)
|
|
||||||
}
|
|
||||||
c => encoded.push_str(format!("%{:02X}", c as u32).as_str()),
|
|
||||||
};
|
|
||||||
}
|
|
||||||
|
|
||||||
encoded
|
|
||||||
}
|
|
||||||
|
|
||||||
pub fn powm(base: &BigUint, exp: &BigUint, modulus: &BigUint) -> BigUint {
|
|
||||||
let mut base = base.clone();
|
|
||||||
let mut exp = exp.clone();
|
|
||||||
let mut result: BigUint = One::one();
|
|
||||||
|
|
||||||
while !exp.is_zero() {
|
|
||||||
if exp.is_odd() {
|
|
||||||
result = result.mul(&base).rem(modulus);
|
|
||||||
}
|
|
||||||
exp = exp.shr(1);
|
|
||||||
base = (&base).mul(&base).rem(modulus);
|
|
||||||
}
|
|
||||||
|
|
||||||
result
|
|
||||||
}
|
|
||||||
|
|
||||||
pub trait ReadSeek: ::std::io::Read + ::std::io::Seek {}
|
|
||||||
impl<T: ::std::io::Read + ::std::io::Seek> ReadSeek for T {}
|
|
||||||
|
|
||||||
pub trait Seq {
|
pub trait Seq {
|
||||||
fn next(&self) -> Self;
|
fn next(&self) -> Self;
|
||||||
|
|
Loading…
Reference in a new issue