Skip to content
This repository was archived by the owner on Jun 3, 2020. It is now read-only.

Secret Connection Integration#38

Merged
tarcieri merged 64 commits intomasterfrom
wip_secret_connection_integration
Aug 23, 2018
Merged

Secret Connection Integration#38
tarcieri merged 64 commits intomasterfrom
wip_secret_connection_integration

Conversation

@zmanian
Copy link
Contributor

@zmanian zmanian commented Jul 23, 2018

This is still a work in progress.

It depends on

To Dos

  • Implement signing
  • Implement Response serialization

I'll add additional to dos as i think of them.

But this should be much easier to get help on now as well.

src/rpc.rs Outdated
@@ -1,14 +1,15 @@
// TODO: replace this with amino
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Can probably get rid of this, eh?

@ValarDragon
Copy link
Contributor

Just made an issue to summarize where we're at (to my knowledge) per request at the tendermint meeting: tendermint/tendermint#2039

liamsi added 3 commits August 20, 2018 18:44
- fix DH key exchange: order of local and remote key were mixed up,
which made the secrent connection and the challenge signature
verification fail
- fix seal_in_place: we were calling it on `sealed_frame` which was all
zeroes -> now it contains the frame data
- fix decoding of remote_eph_pubkey (start with an empty buffer instead
of appending to it); prev. caused secret connection decryption error
- decoding chunk len specifier (4 not 2 bytes)

- switch from u32 -> usize to avoid a bunch of unnecessary casts
- delete unused code
- add a simple test
src/main.rs Outdated
@@ -1,8 +1,10 @@
//! Key Management System for Cosmos Validators
//! key Management System for Cosmos Validators
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

🧐

Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Whoops. Reverted.

chunk.clone_from_slice(
&frame_copy
[DATA_LEN_SIZE ..(DATA_LEN_SIZE + chunk_length as usize)],
&frame_copy[DATA_LEN_SIZE..(DATA_LEN_SIZE + chunk_length as usize)],
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I'd suggest using checked arithmetic throughout this module to avoid potential integer overflow issues, i.e.

&frame_copy[DATA_LEN_SIZE..(DATA_LEN_SIZE.checked_add(chunk_length).unwrap() as usize)]

Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Good point! I changed all constants which will mostly be used as usize to actually being usizes. So I changed the above to DATA_LEN_SIZE.checked_add(chunk_length as usize).unwrap(), where chunk_length is read from 4 bytes. So the inner cast should be fine.

let mut cnt = 0;
while 0 < data_copy.len() {
cnt+=1;
cnt += 1;
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Would suggest:

cnt = cnt.checked_add(1).unwrap();

Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I've deleted cnt because it wasn't used anywhere. But good to know!

liamsi added 2 commits August 22, 2018 12:30
- establish secret connection
- send message over sc (poison pill to stop process)
@liamsi
Copy link
Contributor

liamsi commented Aug 22, 2018

Interestingly, now circleci fails on subtle 0.3.0 (rustc 1.27.1).

Regarding the secret connection integration: As the signing logic isn't ready yet, I changed the integration test basically being about establishing a secret connection and and let the server handle a simple message (namely, the PoisonPillMsg which stops the server again). I'll clean up the code and add proper error handling everywhere. The secret connection should be compatible to tendermint's now!
Do we want to implement handling of signing requests here or in a separate PR?

Cargo.toml Outdated
chrono = "0.4.2"
byteorder = "^1.2"
lazy_static = "1.1.0"
x25519-dalek = "^0.1"
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

It looks like this is an old version and that's what's causing it to pull in an old version of subtle. Can you bump this to 0.3? (the ^ is also unnecessary as Cargo is semver-by-default)

Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Thanks, updated x25519-dalek to 0.3 and randto0.5`. This still fails on stable.

Cargo.toml Outdated
chrono = "0.4.2"
byteorder = "^1.2"
lazy_static = "1.1.0"
x25519-dalek = "0.3"
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

It looks like x25519-dalek enables its nightly feature (and vicariously subtle's nightly feature) by default (which is probably a mistake/oversight):

https://github.com/dalek-cryptography/x25519-dalek/blob/2abfb37/Cargo.toml#L38

I think this will fix it:

x25519-dalek = { version = "0.3", default-features = false, features = ["std", "u64_backend"] }

Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Thanks, that fixes that indeed (at least locally it works now).

// increment nonce big-endian by 2 with wraparound.
fn incr_nonce(nonce: &mut [u8; 12]) {
for i in (0..AEAD_NONCE_SIZE).rev() {
nonce[i] += 1;
Copy link
Contributor

@tarcieri tarcieri Aug 22, 2018

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

---- secret_connection::tests::test_incr_nonce stdout ----
	thread 'secret_connection::tests::test_incr_nonce' panicked at 'attempt to add with overflow', src/secret_connection.rs:356:9

I think this would be a lot clearer (and panic-free!) if you just kept an internal 64-bit counter and used BigEndian::write_u64 (from the byteorder crate) to serialize it.

That'd also make it easier to switch to little endian, if there's still time to do that 😉

@tarcieri
Copy link
Contributor

tarcieri commented Aug 22, 2018

It's green! Woohoo!

Do we want to implement handling of signing requests here or in a separate PR?

@liamsi I think it's fine to land this on master (after review) without signing support, if only because it's so long-lived and I'd really like to land some other stuff I've been working on as well. Perhaps you can at least remove "WIP"?

Will go through it and leave some notes.

}
}

impl<IoHandler: io::Read + io::Write + Send + Sync> io::Write for SecretConnection<IoHandler> {
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Suggestion:

impl<IoHandler> io::Write for SecretConnection<IoHandler>
where
    IoHandler: : io::Read + io::Write + Send + Sync>
{

if !self.recv_buffer.is_empty() {
let n = cmp::min(data.len(), self.recv_buffer.len());
data.copy_from_slice(&self.recv_buffer[..n]);
let mut leftover_portion = vec![0; self.recv_buffer.len() - n];
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Would suggest checked arithmetic everywhere (really wish clippy had a lint for this):

let mut leftover_portion = vec![0; self.recv_buffer.len().checked_sub(n).unwrap()];

}
}

impl<IoHandler: io::Read + io::Write + Send + Sync> io::Read for SecretConnection<IoHandler> {
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Suggestion:

impl<IoHandler> io::Read for SecretConnection<IoHandler>
where
    IoHandler: : io::Read + io::Write + Send + Sync
{

let in_out = &mut out[..ciphertext.len()];
in_out.copy_from_slice(ciphertext);
let len = aead::open_in_place(opening_key, nonce, authtext, 0, in_out)
.map_err(|_| ())?
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I think it'd be good to define an ErrorKind variant for MAC verification errors (perhaps VerificationError or CryptoError?) and use an Error here


fn open(
opening_key: &aead::OpeningKey,
nonce: &[u8; 12],
Copy link
Contributor

@tarcieri tarcieri Aug 22, 2018

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Given opening_key and nonce both come from SecretConnection, any reason to not make this a private method on SecretConnection? It looks like a corresponding seal() method could be useful too.

}
let chunk_length = chunk.len();

BigEndian::write_u32_into(&[chunk_length as u32], &mut frame[..DATA_LEN_SIZE]);
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

You should be able to just use BigEndian::write_u32 here and avoid boxing chunk_length in an array

// end encryption

self.io_handler.write_all(&sealed_frame)?;
n += chunk.len();
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Suggest checked arithmetic:

n = n.checked_add(chunk.len()).unwrap();

}

// increment nonce big-endian by 2 with wraparound.
fn incr_nonce(nonce: &mut [u8; 12]) {
Copy link
Contributor

@tarcieri tarcieri Aug 22, 2018

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I'd suggest replacing this with a u64 counter, and using BigEndian::write_u64 to serialize it. To fill a 96-bit nonce, you can fill the first 32-bits with zeroes, e.g.:

struct Nonce(pub [u8; 12]);

impl From<u64> for Nonce {
    fn from(counter: u64) -> Nonce {
        let mut nonce = [0u8; 12];
        BigEndian::write_u64(counter, &mut nonce[4..]);
        Nonce(nonce)
    }
}

Or if you're really into doing arithmetic on byte arrays, you could do it like this:

struct Nonce(pub [u8; 12]);

impl Default for Nonce {
    fn default() {
        Nonce([0u8; 12])
    }
}

impl Nonce {
    fn increment(&mut self) {
        let counter = BigEndian::read_u64(&self.0[4..]);
        BigEndian::write_u64(counter.checked_add(1).unwrap(), &mut self.0[4..]);
    }
}

Copy link
Contributor

@liamsi liamsi Aug 23, 2018

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Thanks!

I went with the 2nd approach but changed increment to behave exactly as the golang implementation (which makes another counter for the 1st 4 bytes necessary). If you think we should stick with 64 bits, I'll quickly change it. See 2e5d98f

Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I think it's a lot simpler to just use a 64-bit integer (and panic on overflow). The chances of overflowing one in practice actually sending network messages are practically nonexistent (and you should rekey long before that anyway).

Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

OK, will change to 64 bits and update the tests.

liamsi added 3 commits August 23, 2018 13:44
- improve increment nonce situation:
  - suggestion changed slightly to match with the behaviour in golang
- use `impl ... for ... where ...`
- add ErrorKind AuthCryptoError
  - wrap it with an io::Error in Read / Write

- add seal and open methods to SecretConnection

- cargo fmt
@liamsi liamsi changed the title WIP: Secret Connection Integration Secret Connection Integration Aug 23, 2018
Copy link
Contributor

@tarcieri tarcieri left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

:shipit:

Sign up for free to subscribe to this conversation on GitHub. Already have an account? Sign in.

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

4 participants