GithubHelp home page GithubHelp logo

tink-rust's People

Contributors

daviddrysdale avatar dependabot[bot] avatar harryfei avatar mcronce avatar

Stargazers

 avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar

Watchers

 avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar

tink-rust's Issues

About <<Effective Rust>> book

@daviddrysdale David, hello, I couldn't find any other contact information for you, so I had to reach out through an issue. I was wondering if you are the author of <>? It's a truly well-written book. I am planning to translate it into Chinese and was hoping to get your permission. If it's convenient for you, could you please open-source the source repository for me? Thank you.

Unable to build tink-rust binaries

While trying to do a cargo build on tink-rust, I'm receiving the following error blurb,

"Compiling tink-testing-server v0.1.0 (/home/dnovick/gitrepos/rust/tink-rust/testing)
error: failed to run custom build command for tink-testing-server v0.1.0 (/home/dnovick/gitrepos/rust/tink-rust/testing)

Caused by:
process didn't exit successfully: /home/dnovick/gitrepos/rust/tink-rust/target/debug/build/tink-testing-server-861acf93ba28d1bf/build-script-build (exit code: 1)
--- stdout
cargo:rerun-if-changed=proto/testing_api.proto

--- stderr
error: no override and no default toolchain set".

Unfortunately, I don't see enough info in the error to clue me in on exactly what is wrong. I'm building on an Ubuntu 20.04. I made sure I pulled the Wycheproof submodule. Are there other build tools or settings I'm unaware of? Note: the lib builds fine. This error only occurs when trying to build the binaries.

Thanks,
Dave

Migrate off Rusoto

As per RUSTSEC-2022-0071

The maintainers of Rusoto advise that all its crates are deprecated. This includes the common crates rusoto_core, rusoto_signature, rusoto_credential, and service crates such as rusoto_s3 and rusoto_ec2.

Users should migrate to the AWS SDK for Rust, which is maintained by AWS.

noncebased::Reader doesn't implement io::Read correctly

impl io::Read for noncebased::Reader follows a relatively standard pattern:

  • Return any remaining decrypted data from the previous chunk.
  • Read up to a full next chunk.
  • Decrypt the chunk.
  • Return decrypted data.

It looks like this was directly ported from the Go implementation of Tink. Unfortunately, Go and Rust have different semantics for their standard I/O libraries; the Go implementation is leveraging guarantees about reader behaviour that Rust does not provide.

Go impl semantics

The Go implementation calls io.ReadFull to read a complete segment, and then returns early if it received an error that is not io.ErrUnexpectedEOF:

https://github.com/google/tink/blob/cadf02102a05f74be11f18346851b67cea44709f/go/streamingaead/subtle/noncebased/noncebased.go#L278-L281

After handling any initial offset due to the header, it then proceeds to offset all its reads by 1 byte, using that extra final byte as a "query" to see whether there is another following segment. If no such chunk exists, the io.ErrUnexpectedEOF error (that was not previously handled) is used to trigger last-segment handling.

This relies on the following semantics of io.ReadFull:

  • It returns no error if and only if exactly the requested number of bytes were read.
  • If fewer bytes were read, but more than zero, it returns io.ErrUnexpectedEOF along with the bytes that were read.

Rust impl semantics

noncebased::Reader currently calls io::Read::read on the inner reader:

let n = self
.r
.read(&mut self.ciphertext[self.ciphertext_pos..ct_lim])?;
if n == 0 {
// No ciphertext available, so therefore no plaintext available for now.
return Ok(0);
}

io::Read::read provides no guarantee that it will fill the provided buffer if the inner reader would have sufficient bytes to do so:

It is not an error if the returned value n is smaller than the buffer size, even when the reader is not at the end of the stream yet. This may happen for example because fewer bytes are actually available right now (e. g. being close to end-of-file) or because read() was interrupted by a signal.

This means that the subsequent logic to check for the presence of the last segment can be incorrectly triggered by partial reads of middle segments, in addition to the last-segment read:

if n != (ct_lim - self.ciphertext_pos) {
// Read less than a full segment, so this should be the last segment.
last_segment = true;
segment = self.ciphertext_pos + n;

The Rust API that more closely matches io.ReadFull is io::Read::read_exact, but that doesn't have the same semantics for partial reads:

If this function encounters an "end of file" before completely filling the buffer, it returns an error of the kind ErrorKind::UnexpectedEof. The contents of buf are unspecified in this case.

Fix

The Rust implementation needs to loop on io::Read::read until either a full segment has been read (plus the query byte), or the inner reader returns Ok(0). See e.g. my implementation of io::Read for STREAM in age. (I don't currently implement the query-byte strategy, because in age all chunks are the same size except possibly the final chunk, so I just handle the last-chunk-is-full case by decrypting twice.)

Lack of Send and Sync on Box<dyn Primitive> makes library very difficult to use in async code

The lack of Send and Sync make the library almost unusable. I've had to resort to weird thread local tricks like the one below:

mod hello {
    tonic::include_proto!("hello");
}

use std::cell::Cell;

use hello::{HelloRequest,HelloResponse};
use hello::hello_service_server::{HelloService,HelloServiceServer};
use tonic::transport::Server;
use tonic::{Request, Response, Status};

// This would be passed through the environment to the container.
static KEY: &str = "my encryption key";

thread_local! {
    static DECRYPTER: Cell<Option<Box<dyn tink_core::Aead>>> = Cell::new(None)
}

fn parse_encryption_key(key: &str) -> Option<Box<dyn tink_core::Aead>> {
    if key != "" {
        let mut key_reader = tink_core::keyset::BinaryReader::new(key.as_bytes());
        match tink_core::keyset::insecure::read(&mut key_reader) {
            Ok(h) => Some(tink_aead::new(&h).unwrap()),
            Err(e) => None        }
    } else {
        None
    }
}

#[tokio::main]
async fn main() -> Result<(), Box<dyn std::error::Error>> {
    let addr = "[::1]:50051";
    let service = MyServer { };

    Server::builder()
        .add_service(HelloServiceServer::new(service))
        .serve(addr.parse().unwrap())
        .await?;

    Ok(())
}

struct MyServer {
}

#[tonic::async_trait]
impl HelloService for MyServer {
    async fn hello(
        &self,
        req: Request<HelloRequest>,
    ) -> Result<Response<HelloResponse>, Status> {
        DECRYPTER.with(|c| {
            let decrypter = match c.take() {
                Some(d) => d,
                None => parse_encryption_key(KEY).unwrap()
            };
            c.set(Some(decrypter));
        });

        Ok(Response::new(HelloResponse::default()))
    }
}

What I'd like to be able to do is just:

#[tokio::main]
async fn main() -> Result<(), Box<dyn std::error::Error>> {
    let decrypter = parse_encryption_key(KEY).unwrap();
    let addr = "[::1]:50051";
    let service = MyServer { decrypter };

    Server::builder()
        .add_service(HelloServiceServer::new(service))
        .serve(addr.parse().unwrap())
        .await?;

    Ok(())
}

struct MyServer {
    decrypter: ...
}

The types should be such that this is easily supported. Most DB client libraries etc. play well together with Tonic. Not having Send and Sync also makes it impossible to hold a reference across await points.

Shift `StreamingAead` to use RustCrypto `aead::stream` module

FYI, there's an open PR to add a stream module to the RustCrypto aead crate which StreamingAead could potentially benefit from:

RustCrypto/traits#436

In particular I think it'd be nice if rage and tink-rust could potentially share code, particularly around things like async and/or parallel stream readers/writers.

Anyway, heads up we're working on some common abstractions for this sort of thing and would love your input, in particular if you think it would be helpful for things like StreamingAead, and if you have any concerns about the proposed design.

I think age and Tink (in all forms) might also use a common "flavor" of STREAM, although I haven't confirmed that.

Sidebar: STREAM isn't actually OAE2, but rather "nonce-based OAE" (nOAE). CHAIN is required for OAE2. (Edit: I now see the noncebased streaming module, never mind)

Decryption Error in GcpAEAD tests

Hello,

I tried testing gcpkms_example() test from integration_test, and "test_gcpkms_basic_aead()" test from gcp_kms_aead_test with valid gcp key uri and credential data, and it returns decryption error with following message:
"thread 'gcpkms::integration_test::gcpkms_example' panicked at 'called Result::unwrap() on an Err value: TinkError { msg: "aead::decrypt: decryption failed", src: None }', tests/tests/gcpkms/integration_test.rs"
This appears to be from aead_factory.rs, line 112.

Is this a known issue? Any suggestion how to solve this?

Run cross-language tests in CI

The upstream Tink codebase includes a collection of cross-language tests which would be good to run in CI.

Probably involves:

  • getting a local checkout of the upstream code
  • applying patches to the upstream code to mark 'rust' as being supported for various operations
  • building all of the different versions of the testing servers (Java, C++, Python, Go)
  • running the tests (with scripts/run-tests.sh).

I suspect that the main stumbling block will be that builds of all of the different testing servers will take too long.

Non-blocking API

A non-blocking API would be great. The crate is not that ergonomic to use in a project that relies on the tokio runtime. This is due to the fact that you can not spawn a tokio::Runtime from within a runtime.

    #[tokio::test]
    async fn test_construction() -> Result<(), Box<dyn std::error::Error>> {
        tink_aead::init();
        dotenv::dotenv().unwrap();
        let key_uri = std::env::var("GOOGLE_KMS_KEY_URI")?;
        let credential_path_var = std::env::var("GOOGLE_APPLICATION_CREDENTIALS").unwrap();
        let credential_path = std::path::Path::new(&credential_path_var);
        let client =
            tink_gcpkms::GcpClient::new_with_credentials(&key_uri, credential_path).unwrap();
        let backend = client.get_aead(&key_uri).unwrap();
        // ...
        Ok(())
    }
thread 'key_manager::tests::test_construction' panicked at 'Cannot start a runtime from within a runtime. This happens because a function (like `block_on`) attempted to block the current thread while the thread is being used to drive asynchronous tasks.'

Also, dyn <Primitive> are cumbersome without Send + Sync.

/// Returns a [`tink_core::Aead`] primitive from the given keyset handle.
pub fn new(h: &tink_core::keyset::Handle) -> Result<Box<dyn tink_core::Aead>, TinkError> {
    new_with_key_manager(h, None)
}

I can get around the first issue with an actor and spawning a thread, but lack of Send + Sync on the primitives is a tough one.

I believe there are a few changes that would need to be made:

  1. Create a feature flag, something like "non-blocking" to avoid breaking changes.
  2. Add async-trait as a dependency
  3. Add an AeadEnvelope trait:
#[async_trait::async_trait]
pub trait AeadEnvelope {
    async fn encrypt(
        &self,
        plaintext: &[u8],
        additional_data: &[u8],
    ) -> Result<Vec<u8>, TinkError>;

    async fn decrypt(
        &self,
        ciphertext: &[u8],
        additional_data: &[u8],
    ) -> Result<Vec<u8>, TinkError>;
}
  1. A good bit of the dyn <Primitive> will likely need to be dyn 'static + <Primitive> + Send + Sync
  2. Possibly provide implementations for the integrations but that's not as important as the above.

Thank you for your work and effort porting this.

Support `no_std` in (most) crates

First, need a version of prost that includes danburkert/prost@fdf9fdf.

Then the obvious changes needed to make Tink no_std compatible would include the following (but there are bound to be others):

  • Depend on core + alloc instead of std, and have a std feature for those things that definitely need std:
    • keyset I/O (both binary and JSON-based)
    • streaming AEAD
  • Changes to use core / alloc types:
    • Box => alloc::boxed::Box
    • String => alloc::string::String
    • Vec => alloc::vec::Vec
    • std::sync::Arc => alloc::sync::Arc
    • std::fmt::* => core::fmt::*
    • std::collections::HashMap => alloc::collections::BTreeMap
    • std::sync::RwLock => spin::RwLock
    • std::convert::From => core::convert::From
    • Move TinkError to wrap something that just implements core::fmt::Debug rather than std::error::Error.

tink-gcpkms: remove need for old dependencies

The tink-gcpkms crate currently uses the (auto-generated) google-cloudkms1 crate for GCP client functionality. However, the latest version of this crate (1.0.14, from July 2020) depends on old versions of other crates:

  • hyper ^0.10: most recent matching version is 0.10.16 from April 2019; current version is 0.14.2
  • hyper-rustls ^0.6: most recent matching version is 0.6.2 from July 2018; current version is 0.22.1

These old dependencies make it very hard to use this crate in anything that uses more up-to-date versions of the deps, particularly since a native library (of which There Can Be Only One) ends up in the deps (ring-asm from ring).

Possible options:

  • Persuade upstream to update the google-cloudkms1 crate.
  • Use a local copy of the google-cloudkms1 crate and update that.
  • Drop the google-cloudkms1 dependency and find another mechanism/library for GCP client functionality.

build error for 32bit target

tink-aead has a error when building for 32 bit target

cargo check --target i686-unknown-linux-gnu
error: any use of this value will cause an error
  --> aead/src/subtle/aes_gcm.rs:27:43
   |
27 | const MAX_AES_GCM_PLAINTEXT_SIZE: usize = (1 << 36) - 32;
   | ------------------------------------------^^^^^^^^^------
   |                                           |
   |                                           attempt to shift left by `36_i32`, which would overflow
   |
   = note: `#[deny(const_err)]` on by default
   = warning: this was previously accepted by the compiler but is being phased out; it will become a hard error in a future release!
   = note: for more information, see issue #71800 <https://github.com/rust-lang/rust/issues/71800>

error: could not compile `tink-aead` due to previous error

Check error behaviour for information leakage

While the Rust port is under development, it's helpful to have error message that indicate what's gone wrong.

However, this is unwise for a working system if the error details can leak out to attackers.

Recommend Projects

  • React photo React

    A declarative, efficient, and flexible JavaScript library for building user interfaces.

  • Vue.js photo Vue.js

    ๐Ÿ–– Vue.js is a progressive, incrementally-adoptable JavaScript framework for building UI on the web.

  • Typescript photo Typescript

    TypeScript is a superset of JavaScript that compiles to clean JavaScript output.

  • TensorFlow photo TensorFlow

    An Open Source Machine Learning Framework for Everyone

  • Django photo Django

    The Web framework for perfectionists with deadlines.

  • D3 photo D3

    Bring data to life with SVG, Canvas and HTML. ๐Ÿ“Š๐Ÿ“ˆ๐ŸŽ‰

Recommend Topics

  • javascript

    JavaScript (JS) is a lightweight interpreted programming language with first-class functions.

  • web

    Some thing interesting about web. New door for the world.

  • server

    A server is a program made to process requests and deliver data to clients.

  • Machine learning

    Machine learning is a way of modeling and interpreting data that allows a piece of software to respond intelligently.

  • Game

    Some thing interesting about game, make everyone happy.

Recommend Org

  • Facebook photo Facebook

    We are working to build community through open source technology. NB: members must have two-factor auth.

  • Microsoft photo Microsoft

    Open source projects and samples from Microsoft.

  • Google photo Google

    Google โค๏ธ Open Source for everyone.

  • D3 photo D3

    Data-Driven Documents codes.