New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
Remove direct dependence on OpenSSL #2
Comments
Replaced the use of OpenSSL for SHA1, but it is also used for pkey, rand_bytes, and symm. For the latter this module looks like a good choice: https://crates.io/crates/aes_frast API compatible with OpenSSL and supports AES CFB, but, 0.1.2 uses new Rust language features:
not available in nightly-2017-08-31, may need to first https://github.com/iceiix/steven/issues/3 update to a newer Rust. |
aes_frast v0.1.2 compiles within steven after updating to nightly-2018-09-30 in https://github.com/iceiix/steven/issues/3, now to implement it in src/protocol/mod.rs. And src/server/mod.rs, what to replace pkey? https://crates.io/crates/rsa? (but its version 0.0.0) https://crates.io/crates/ring? rand_bytes maybe https://crates.io/crates/rand-bytes |
Made some progress porting to RustCrypto https://github.com/RustCrypto/block-ciphers/, saving here: diff --git a/Cargo.toml b/Cargo.toml
index 1959136..75bc7f2 100644
--- a/Cargo.toml
+++ b/Cargo.toml
@@ -10,6 +10,8 @@ authors = [ "Thinkofdeath <thinkofdeath@spigotmc.org>" ]
opt-level = 1
[dependencies]
+aes = "0.2.0"
+block-modes = "0.1.0"
sha1 = "0.6.0"
sdl2 = "0.31.0"
byteorder = "0.5.0"
diff --git a/src/main.rs b/src/main.rs
index ddffb7e..2e68807 100644
--- a/src/main.rs
+++ b/src/main.rs
@@ -23,6 +23,8 @@ extern crate byteorder;
extern crate serde_json;
extern crate openssl;
extern crate sha1;
+extern crate aes;
+extern crate block_modes;
extern crate hyper;
extern crate flate2;
extern crate rand;
diff --git a/src/protocol/mod.rs b/src/protocol/mod.rs
index 4ada321..b92e8e7 100644
--- a/src/protocol/mod.rs
+++ b/src/protocol/mod.rs
@@ -15,6 +15,11 @@
#![allow(dead_code)]
use openssl::crypto::symm;
+use aes::block_cipher_trait::generic_array::GenericArray;
+use aes::Aes128;
+use block_modes::{BlockMode, BlockModeIv, Cfb};
+use block_modes::block_padding::ZeroPadding;
+type Aes128Cfb = Cfb<Aes128, ZeroPadding>; // TODO: CFB doesn't use padding
use serde_json;
use hyper;
@@ -743,6 +748,7 @@ pub struct Conn {
pub state: State,
cipher: Option<symm::Crypter>,
+ cipher2: Option<Aes128Cfb>,
compression_threshold: i32,
compression_read: Option<ZlibDecoder<io::Cursor<Vec<u8>>>>,
@@ -770,6 +776,7 @@ impl Conn {
direction: Direction::Serverbound,
state: State::Handshaking,
cipher: Option::None,
+ cipher2: Option::None,
compression_threshold: -1,
compression_read: Option::None,
compression_write: Option::None,
@@ -860,6 +867,10 @@ impl Conn {
let cipher = symm::Crypter::new(symm::Type::AES_128_CFB8);
cipher.init(if decrypt { symm::Mode::Decrypt } else { symm::Mode::Encrypt }, key, key);
self.cipher = Option::Some(cipher);
+
+ let cipher2 = Aes128Cfb::new_varkey(key, GenericArray::from_slice(key)).unwrap();
+ println!("enabling encryption with key={:?}", key);
+ self.cipher2 = Option::Some(cipher2);
}
pub fn set_compresssion(&mut self, threshold: i32) {
@@ -967,10 +978,21 @@ impl Read for Conn {
Option::None => self.stream.read(buf),
Option::Some(cipher) => {
let ret = try!(self.stream.read(buf));
+
+ println!("decrypting {:?}", &buf[..ret]);
+
let data = cipher.update(&buf[..ret]);
for i in 0..ret {
buf[i] = data[i];
}
+ println!("ossl buf = {:?}", buf);
+
+ /*
+ let result = self.cipher2.as_mut().unwrap().decrypt_nopad(&mut buf[..ret]);
+ println!("result = {:?}", result);
+ println!("buf = {:?}", buf);
+ */
+
Ok(ret)
}
}
@@ -982,7 +1004,15 @@ impl Write for Conn {
match self.cipher.as_mut() {
Option::None => self.stream.write(buf),
Option::Some(cipher) => {
+ println!("encrypting buf = {:?}", buf);
let data = cipher.update(buf);
+ println!("data = {:?}", data);
+ /* TODO
+ let mut data: [u8; 32] = [0; 32];
+ data.clone_from_slice(&buf);
+ cipher.encrypt_nopad(&mut data).expect("failed to encrypt");
+ */
+
try!(self.stream.write_all(&data[..]));
Ok(buf.len())
}
@@ -1003,6 +1033,7 @@ impl Clone for Conn {
direction: self.direction,
state: self.state,
cipher: Option::None,
+ cipher2: Option::None,
compression_threshold: self.compression_threshold,
compression_read: Option::None,
compression_write: Option::None, but it fails with BlockModeError, may not be able to use block-modes crate in this way: RustCrypto/block-ciphers#28 Long story short, CFB (and CTR) modes turn a block cipher into a stream cipher. https://github.com/RustCrypto/stream-ciphers implements CTR but we need CTR. Try a different crate? Not many hits for cfb on crates.io: https://crates.io/search?q=cfb there's aes_frast again, it gets it: https://github.com/KaneGreen/aes_frast/blob/master/src/aes_with_operation_mode.rs#L226 /// CFB (Cipher Feedback) Encryption
///
/// The feedback size is fixed to 128 bits, which is the same as block size.
/// This mode doesn't require padding. |
Incomplete aes_frast attempt: diff --git a/Cargo.toml b/Cargo.toml
index 1959136..50bfb6d 100644
--- a/Cargo.toml
+++ b/Cargo.toml
@@ -10,6 +10,7 @@ authors = [ "Thinkofdeath <thinkofdeath@spigotmc.org>" ]
opt-level = 1
[dependencies]
+aes_frast = "0.1.2"
sha1 = "0.6.0"
sdl2 = "0.31.0"
byteorder = "0.5.0"
diff --git a/src/main.rs b/src/main.rs
index ddffb7e..4f5fdc5 100644
--- a/src/main.rs
+++ b/src/main.rs
@@ -23,6 +23,7 @@ extern crate byteorder;
extern crate serde_json;
extern crate openssl;
extern crate sha1;
+extern crate aes_frast;
extern crate hyper;
extern crate flate2;
extern crate rand;
diff --git a/src/protocol/mod.rs b/src/protocol/mod.rs
index 4ada321..ddad380 100644
--- a/src/protocol/mod.rs
+++ b/src/protocol/mod.rs
@@ -15,6 +15,7 @@
#![allow(dead_code)]
use openssl::crypto::symm;
+use aes_frast::{aes_core, aes_with_operation_mode};
use serde_json;
use hyper;
@@ -743,6 +744,8 @@ pub struct Conn {
pub state: State,
cipher: Option<symm::Crypter>,
+ w_keys: Vec<u32>,
+ iv: Vec<u8>,
compression_threshold: i32,
compression_read: Option<ZlibDecoder<io::Cursor<Vec<u8>>>>,
@@ -770,6 +773,8 @@ impl Conn {
direction: Direction::Serverbound,
state: State::Handshaking,
cipher: Option::None,
+ w_keys: vec![0u32; 60],
+ iv: vec![0; 16],
compression_threshold: -1,
compression_read: Option::None,
compression_write: Option::None,
@@ -860,6 +865,8 @@ impl Conn {
let cipher = symm::Crypter::new(symm::Type::AES_128_CFB8);
cipher.init(if decrypt { symm::Mode::Decrypt } else { symm::Mode::Encrypt }, key, key);
self.cipher = Option::Some(cipher);
+
+ aes_core::setkey_enc_auto(&key, &mut self.w_keys);
}
pub fn set_compresssion(&mut self, threshold: i32) {
@@ -1003,6 +1010,8 @@ impl Clone for Conn {
direction: self.direction,
state: self.state,
cipher: Option::None,
+ w_keys: vec![0u32; 60],
+ iv: vec![0; 16],
compression_threshold: self.compression_threshold,
compression_read: Option::None,
compression_write: Option::None, There is another dimension to CFB, the "segment size", we probably want 8-bit (1 byte) not 128-bit (8 byte), failed with aes_frast KaneGreen/aes_frast#2 but watch RustCrypto/block-ciphers#28 |
BTW if you are using RustCrypto crates, consider using |
Saving progress here on RustCrypto cfb-mode porting attempt: diff --git a/Cargo.toml b/Cargo.toml
index 3f4e636..73c1541 100644
--- a/Cargo.toml
+++ b/Cargo.toml
@@ -27,6 +27,8 @@ cgmath = "0.7.0"
lazy_static = "1.1.0"
collision = {git = "https://github.com/TheUnnamedDude/collision-rs", rev = "f80825e"}
openssl = "0.7.8"
+aes = "0.2.0"
+cfb-mode = "0.1.0"
# clippy = "*"
[dependencies.steven_gl]
diff --git a/src/main.rs b/src/main.rs
index b038860..7c3edf3 100644
--- a/src/main.rs
+++ b/src/main.rs
@@ -22,6 +22,8 @@ use std::time::{Instant, Duration};
extern crate byteorder;
extern crate serde_json;
extern crate openssl;
+extern crate aes;
+extern crate cfb_mode;
extern crate sha1;
extern crate hyper;
extern crate flate2;
diff --git a/src/protocol/mod.rs b/src/protocol/mod.rs
index 6625da4..7ab3f93 100644
--- a/src/protocol/mod.rs
+++ b/src/protocol/mod.rs
@@ -15,6 +15,8 @@
#![allow(dead_code)]
use openssl::crypto::symm;
+use aes::Aes128;
+use cfb_mode::Cfb;
use serde_json;
use hyper;
@@ -735,6 +737,8 @@ impl ::std::fmt::Display for Error {
}
}
+type Aes128Cfb = Cfb<Aes128>;
+
pub struct Conn {
stream: TcpStream,
pub host: String,
@@ -743,6 +747,7 @@ pub struct Conn {
pub state: State,
cipher: Option<symm::Crypter>,
+ cipher2: Option<Aes128Cfb>,
compression_threshold: i32,
compression_read: Option<ZlibDecoder<io::Cursor<Vec<u8>>>>,
@@ -770,6 +775,7 @@ impl Conn {
direction: Direction::Serverbound,
state: State::Handshaking,
cipher: Option::None,
+ cipher2: Option::None,
compression_threshold: -1,
compression_read: Option::None,
compression_write: Option::None,
@@ -860,6 +866,10 @@ impl Conn {
let cipher = symm::Crypter::new(symm::Type::AES_128_CFB8);
cipher.init(if decrypt { symm::Mode::Decrypt } else { symm::Mode::Encrypt }, key, key);
self.cipher = Option::Some(cipher);
+
+ let cipher2 = Aes128Cfb::new_var(key, key).unwrap();
+ println!("enabling encryption with key={:?}", key);
+ self.cipher2 = Option::Some(cipher2);
}
pub fn set_compresssion(&mut self, threshold: i32) {
@@ -967,10 +977,19 @@ impl Read for Conn {
Option::None => self.stream.read(buf),
Option::Some(cipher) => {
let ret = try!(self.stream.read(buf));
+
+ println!("decrypting {:?}", &buf[..ret]);
let data = cipher.update(&buf[..ret]);
+ println!("ossl = {:?}", &data);
+
+ self.cipher2.as_mut().unwrap().decrypt(&mut buf[..ret]);
+ println!("buf = {:?}", &buf[..ret]);
+
+
for i in 0..ret {
buf[i] = data[i];
}
+
Ok(ret)
}
}
@@ -982,7 +1001,14 @@ impl Write for Conn {
match self.cipher.as_mut() {
Option::None => self.stream.write(buf),
Option::Some(cipher) => {
+ println!("encrypting buf = {:?}", buf);
let data = cipher.update(buf);
+ println!("ossl data = {:?}", data);
+ /* TODO
+ self.cipher2.as_mut().unwrap().encrypt(&mut buf);
+ println!("buf = {:?}", buf);
+ */
+
try!(self.stream.write_all(&data[..]));
Ok(buf.len())
}
@@ -1003,6 +1029,7 @@ impl Clone for Conn {
direction: self.direction,
state: self.state,
cipher: Option::None,
+ cipher2: Option::None,
compression_threshold: self.compression_threshold,
compression_read: Option::None,
compression_write: Option::None, but cfb-mode currently only supports CFB128, whereas Steven requires CFB8... RustCrypto/stream-ciphers#4 (comment) |
reqwest, added in iceiix/steven#7, can use OpenSSL but only on Linux, for other platforms it uses the native TLS libraries: From https://github.com/seanmonstar/reqwest/
|
Major API change, the last of the outdated dependencies Closes https://github.com/iceiix/steven/issues/4 Note: would still like to replace the last usages of the OpenSSL crate https://github.com/iceiix/steven/issues/2 but it is needed for CFB8 until a replacement is available (maybe RustCrypto/stream-ciphers#4)
For the other functionality, https://crates.io/crates/rand-bytes uses ring: https://briansmith.org/rustdoc/ring/rand/index.html - ring also has public key signature signing and verification https://briansmith.org/rustdoc/ring/signature/index.html but can it not encrypt to a public key? Basically (besides CFB) only need to replace this snippet of code in src/server/mod.rs connect(): let rsa = Rsa::public_key_from_der(&packet.public_key.data).unwrap();
let mut shared = [0; 16];
rand_bytes(&mut shared).unwrap();
let mut shared_e = vec![0; rsa.size() as usize];
let mut token_e = vec![0; rsa.size() as usize];
rsa.public_encrypt(&shared, &mut shared_e, Padding::PKCS1)?;
rsa.public_encrypt(&packet.verify_token.data, &mut token_e, Padding::PKCS1)?;
try!(profile.join_server(&packet.server_id, &shared, &packet.public_key.data)); |
Sadly ring doesn't support CFB mode, it was actually removed! briansmith/ring@49c0ede Not easy to find these obscure crypto methods.. but it is mandatory for protocol compatibility. |
…) (Thinkofname#2) * Encrypt with both RustCrypto cfb8 and OpenSSL * Switch to RustCrypto for decrypting * Show encryption for both RustCrypto and OpenSSL, for comparison... * Correct off-by-one error in encryption, cfb8 doesn't need extra byte * Remove OpenSSL for symmetric crypto
…) (Thinkofname#2) * Encrypt with both RustCrypto cfb8 and OpenSSL * Switch to RustCrypto for decrypting * Show encryption for both RustCrypto and OpenSSL, for comparison... * Correct off-by-one error in encryption, cfb8 doesn't need extra byte * Remove OpenSSL for symmetric crypto * Update Cargo.lock
RustCrypto cfb8 now available, switched over to it in iceiix/steven#10. Now the only remaining use is RSA (and rand), https://github.com/iceiix/steven/issues/2#issuecomment-433673776 - what to use to replace |
RSA public key encryption in Rust is a problem, it appears openssl-rust is the only existing option. What is involved?
https://crates.io/crates/asn1 - last updated 3 years ago
|
Parsing with simple_asn1, extracting the bit string: extern crate simple_asn1;
use simple_asn1::{from_der, ASN1Block};
fn find_bitstrings(asns: Vec<ASN1Block>, mut result: &mut Vec<Vec<u8>>) {
for asn in asns.iter() {
match asn {
ASN1Block::BitString(_, _, _, bytes) => result.push(bytes.to_vec()),
ASN1Block::Sequence(_, _, blocks) => find_bitstrings(blocks.to_vec(), &mut result),
_ => (),
}
}
}
fn main() {
let asns: Vec<ASN1Block> = from_der(&packet_public_key_data).unwrap();
let mut result: Vec<Vec<u8>> = vec![];
find_bitstrings(asns, &mut result);
println!("result[0] = {:?}", result[0]);
} asns = [Sequence(Universal, 0, [Sequence(Universal, 3, [ObjectIdentifier(Universal, 5, OID([BigUint { data: [1] }, BigUint { data: [2] }, BigUint { data: [840] }, BigUint { data: [113549] }, BigUint { data: [1] }, BigUint { data: [1] }, BigUint { data: [1] }])), Null(Universal, 16)]), BitString(Universal, ...])])] |
https://tools.ietf.org/html/rfc8017#section-7.2
https://tools.ietf.org/html/rfc8017#section-5.1.1
https://docs.rs/num-bigint/0.2.1/num_bigint/struct.BigUint.html#method.modpow
|
iceiix/steven#12 removes the direct dependency, in Cargo.toml, but note that Cargo.lock still lists openssl, because native-tls depends it, for TLS, but it will use alternative TLS stacks if available on different platforms. Removing the immediate usage is the important part. |
If server supports TLS v1.3 you can try rustls instead of the |
Unfortunately reqwest uses native-tls so I think I'm stuck with it for now, but there's an open issue for rustls support in reqwest: seanmonstar/reqwest#378 if implemented will be 100% OpenSSL-free 👍 |
…GW on Windows, and no longer uses OpenSSL (https://github.com/iceiix/steven/issues/2)
…GW on Windows, and no longer uses OpenSSL (https://github.com/iceiix/steven/issues/2)
* Copy contents of wiki https://github.com/Thinkofname/steven/wiki/Compiling-and-or-running * Update and cleanup install instructions: now uses MSVC instead of MinGW on Windows, and no longer uses OpenSSL (https://github.com/iceiix/steven/issues/2)
…) (#2) * Encrypt with both RustCrypto cfb8 and OpenSSL * Switch to RustCrypto for decrypting * Show encryption for both RustCrypto and OpenSSL, for comparison... * Correct off-by-one error in encryption, cfb8 doesn't need extra byte * Remove OpenSSL for symmetric crypto * Update Cargo.lock
* Add handwritten RSA PKCS1 encryption using num-bigint and simple_asn1 * Add more logging to compare OpenSSL with/without side-by-side * Log message and ciphertext in hex * Print N and e as hexadecimal integers * Fix bad encryption caused by zeros in PKCS1 padding PS field in https://tools.ietf.org/html/rfc8017#section-7.2.1 Must be nonzero * Use rand fill instead of rand_bytes * Remove OpenSSL! * Update CI scripts and docs to not install OpenSSL * Remove copying OpenSSL DLLs (libeay and ssleay) in AppVeyor script * Change rsa_public_encrypt_pkcs1 to return a Result<Vec<u8>, String> * Add error checking, returning Err<String> on failure; RFC comments * Add the required message representative range checking * Use expect() instead of unwrap() on from_der * Map the ASN.1 error to a String to return it from rsa_public_encrypt_pkcs1() instead of panicking * Move RSA to a new crate, rsa_public_encrypt_pkcs1 https://github.com/iceiix/rsa_public_encrypt_pkcs1 * Update to rsa_public_encrypt_pkcs1 with simple_asn 0.1.0 iceiix/rsa_public_encrypt_pkcs1#1 * Update to published version of rsa_public_encrypt_pkcs1, 0.1.0 * Remove unnecessarily added blank line * Remove libssl-dev from .travis.yml
…) (#2) * Encrypt with both RustCrypto cfb8 and OpenSSL * Switch to RustCrypto for decrypting * Show encryption for both RustCrypto and OpenSSL, for comparison... * Correct off-by-one error in encryption, cfb8 doesn't need extra byte * Remove OpenSSL for symmetric crypto * Update Cargo.lock
* Add handwritten RSA PKCS1 encryption using num-bigint and simple_asn1 * Add more logging to compare OpenSSL with/without side-by-side * Log message and ciphertext in hex * Print N and e as hexadecimal integers * Fix bad encryption caused by zeros in PKCS1 padding PS field in https://tools.ietf.org/html/rfc8017#section-7.2.1 Must be nonzero * Use rand fill instead of rand_bytes * Remove OpenSSL! * Update CI scripts and docs to not install OpenSSL * Remove copying OpenSSL DLLs (libeay and ssleay) in AppVeyor script * Change rsa_public_encrypt_pkcs1 to return a Result<Vec<u8>, String> * Add error checking, returning Err<String> on failure; RFC comments * Add the required message representative range checking * Use expect() instead of unwrap() on from_der * Map the ASN.1 error to a String to return it from rsa_public_encrypt_pkcs1() instead of panicking * Move RSA to a new crate, rsa_public_encrypt_pkcs1 https://github.com/iceiix/rsa_public_encrypt_pkcs1 * Update to rsa_public_encrypt_pkcs1 with simple_asn 0.1.0 iceiix/rsa_public_encrypt_pkcs1#1 * Update to published version of rsa_public_encrypt_pkcs1, 0.1.0 * Remove unnecessarily added blank line * Remove libssl-dev from .travis.yml
…) (#2) * Encrypt with both RustCrypto cfb8 and OpenSSL * Switch to RustCrypto for decrypting * Show encryption for both RustCrypto and OpenSSL, for comparison... * Correct off-by-one error in encryption, cfb8 doesn't need extra byte * Remove OpenSSL for symmetric crypto * Update Cargo.lock
* Add handwritten RSA PKCS1 encryption using num-bigint and simple_asn1 * Add more logging to compare OpenSSL with/without side-by-side * Log message and ciphertext in hex * Print N and e as hexadecimal integers * Fix bad encryption caused by zeros in PKCS1 padding PS field in https://tools.ietf.org/html/rfc8017#section-7.2.1 Must be nonzero * Use rand fill instead of rand_bytes * Remove OpenSSL! * Update CI scripts and docs to not install OpenSSL * Remove copying OpenSSL DLLs (libeay and ssleay) in AppVeyor script * Change rsa_public_encrypt_pkcs1 to return a Result<Vec<u8>, String> * Add error checking, returning Err<String> on failure; RFC comments * Add the required message representative range checking * Use expect() instead of unwrap() on from_der * Map the ASN.1 error to a String to return it from rsa_public_encrypt_pkcs1() instead of panicking * Move RSA to a new crate, rsa_public_encrypt_pkcs1 https://github.com/iceiix/rsa_public_encrypt_pkcs1 * Update to rsa_public_encrypt_pkcs1 with simple_asn 0.1.0 iceiix/rsa_public_encrypt_pkcs1#1 * Update to published version of rsa_public_encrypt_pkcs1, 0.1.0 * Remove unnecessarily added blank line * Remove libssl-dev from .travis.yml
…) (#2) * Encrypt with both RustCrypto cfb8 and OpenSSL * Switch to RustCrypto for decrypting * Show encryption for both RustCrypto and OpenSSL, for comparison... * Correct off-by-one error in encryption, cfb8 doesn't need extra byte * Remove OpenSSL for symmetric crypto * Update Cargo.lock
* Add handwritten RSA PKCS1 encryption using num-bigint and simple_asn1 * Add more logging to compare OpenSSL with/without side-by-side * Log message and ciphertext in hex * Print N and e as hexadecimal integers * Fix bad encryption caused by zeros in PKCS1 padding PS field in https://tools.ietf.org/html/rfc8017#section-7.2.1 Must be nonzero * Use rand fill instead of rand_bytes * Remove OpenSSL! * Update CI scripts and docs to not install OpenSSL * Remove copying OpenSSL DLLs (libeay and ssleay) in AppVeyor script * Change rsa_public_encrypt_pkcs1 to return a Result<Vec<u8>, String> * Add error checking, returning Err<String> on failure; RFC comments * Add the required message representative range checking * Use expect() instead of unwrap() on from_der * Map the ASN.1 error to a String to return it from rsa_public_encrypt_pkcs1() instead of panicking * Move RSA to a new crate, rsa_public_encrypt_pkcs1 https://github.com/iceiix/rsa_public_encrypt_pkcs1 * Update to rsa_public_encrypt_pkcs1 with simple_asn 0.1.0 iceiix/rsa_public_encrypt_pkcs1#1 * Update to published version of rsa_public_encrypt_pkcs1, 0.1.0 * Remove unnecessarily added blank line * Remove libssl-dev from .travis.yml
OpenSSL is an annoying dependency, because it has to be installed separately, and has to be a precise version. Ubuntu 18.04.1 for example comes with OpenSSL 1.1, which isn't compatible with 1.0 required by rust-openssl 0.7.8, and the Rust crate itself has significantly changed in .8 and .10: https://crates.io/crates/openssl - it is a large cumbersome dependency to lug around.
Should look into replacing this dependency with native Rust cryptography crates, if possible.
The text was updated successfully, but these errors were encountered: