project-oak/tink-rust

This repository holds a Rust port of Google's Tink cryptography library

This repository holds a Rust port of Google's RustCrypto crates – this repo focuses on making

Tink in Rust

This repository holds a Rust port of Google's Tink cryptography library.

The following warnings apply to use of this repo:

  • This is not an official port of Tink, and is not supported by Google's cryptography teams.
  • The repo is under construction and so details of the API and the code may change without warning.

Also, this repository does not implement cryptographic functionality itself; the underlying cryptographic operations are currently provided by the RustCrypto crates – this repo focuses on making those cryptographic operations available via the Tink API.

This means that all of the security warnings for the underlying RustCrypto crates apply to this repo too.

Disclaimer

This is not an officially supported Google product.

Usage Overview

An introduction to working with the Tink API is provided here.

Crate Structure

The tink-core crate holds common functionality and includes the trait definitions for all primitives, but includes very little cryptographic functionality.

Individual cryptographic primitives are implemented in tink-<primitive> crates, which depend on:

  • the tink-core crate for common types and helpers
  • the tink-proto crate for protobuf-derived structs
  • the RustCrypto crates to provide underlying cryptographic implementations.

For example, the tink-aead crate provides code that performs authenticated encryption with additional data (AEAD), implementing the Aead trait from tink-core.

All of the tests for the Tink crates are integration tests (i.e. only use public APIs) and reside in a separate tink-tests crate.

Rust Port Design

The Rust port of Tink has the following meta-goals:

  • Diverge as little as possible from the upstream Tink code: The Rust port is primarily based on the Go language version of upstream Tink, and aims to stay as close to it as possible so that future changes to Tink can be merged more easily. However, this does mean that some aspects of the port are not quite idiomatic Rust.
  • Don't write any crypto code: The Rust port aims to defer all cryptographic implementations to external crates (currently the RustCrypto crates).

The remainder of this section describes design decisions involved in the conversion from Go to Rust.

The Primitive Type

The Go port uses interface {} to hold an arbitrary primitive, and uses type assertions to convert to particular primitive interface types. This is not possible in Rust, and so the Rust port includes a Primitive enum that holds all of the possible primitive types (as trait objects):

enum Primitive {
    Aead(Box<dyn Aead>),
    DeterministicAead(Box<dyn DeterministicAead>),
    // ...
    Verifier(Box<dyn Verifier>),
}

However, this has the big downside that it is impossible for third parties to extend the Rust port of Tink to include new types of primitive without modifying the Tink source.

The KeyManager Registry

A KeyManager is an object that handles the translation from a Key instance to a Primitive object that uses the Key for its key material. Tink has a global registry of KeyManager instances, each indexed by a type URL that identifies the kind of keys it supports.

This registry allows an arbitrary Key to be converted to a Primitive of the relevant type, and similarly allows a Keyset to be converted to a PrimitiveSet.

  • In Go, primitives are of type interface {}, and the user of the registry uses type assertions to convert a general primitive to a more specific object that implements the interface of a particular primitive.
    • The global registry is automatically populated at start-of-day, by the use of init() methods for each particular KeyManager implementation.
  • In C++, the KeyManager<P> type is a template that is parameterized by the particular primitive type that it handles, so it returns primitives that are automatically type safe. Internally, the global registry of key manager instances maps type URL strings to a combination of (roughly) void * and type_info; the particular KeyManager<P> is then recovered via static_cast (modulo a check that the type_info is sensible).
    • The global registry has to be manually populated by calling <Primitive>Config::Register() methods before use.
  • In Rust, the Primitive type is an enum that encompasses all primitive types, and the user of the registry checks that the relevant enum variant is returned. If all of the Primitives in a PrimitiveSet are known to be of a specific primitive type, the PrimitiveSet can be converted to a TypedPrimitiveSet<T> for the relevant primitive type T.
    • The global registry has to be manually populated by calling tink_<primitive>::init() methods before use.

Error Handling

Many Go functions return values of form (ReturnType, error); the Rust equivalent of this is a Result<ReturnType, E>, where E is some type that implements the Error trait.

The Rust port uses the TinkError type for E. This type includes an optional inner Error, and the tink_core::utils module also includes the wrap_err() helper, which is used as an equivalent for the common Go pattern of wrapping errors:

x, err := library.DoSomething()
if err != nil {
	return nil, fmt.Errorf("doing something failed: %s", err)
}

like so:

let x = library::do_something().map_err(|e| wrap_err("doing something failed", e))?;

The PrivateKeyManager Type

The Go version of Tink includes a PrivateKeyManager interface which extends the KeyManager interface, and uses down-casting type assertions to see if an instance of the latter is also an instance of the former:

	km, err := registry.GetKeyManager(privKeyData.TypeUrl)
	if err != nil {
		return nil, err
	}
	pkm, ok := km.(registry.PrivateKeyManager)

Rust allows a trait definition to indicate a required trait bound (trait PrivateKeyManager: KeyManager {..}), but does not support down-casting; given a trait object of type dyn KeyManager, there is no way to determine if the object also references a concrete type that implements the dyn PrivateKeyManager trait.

As a result, there is no PrivateKeyManager trait in the Rust port. Instead, the KeyManager trait includes the public_key_data() method from Go's PrivateKeyManager, together with a supports_private_keys() method to allow discovery of whether a KeyManager trait object supports this or not. Both of these trait methods have default implementations that indicate no support for private keys.

init Methods

The Go port uses init() functions to register primitive factories; this is not supported in Rust, so each crate that provides a primitive has an init() function that should be called before use.

KeyManager::new_key Method

The Go port has a KeyManager.NewKey method which returns a proto.Message holding a new key. For the Rust port, the equivalent KeyManager::new_key method returns a serialized protobuf message (as a Vec<u8>) rather than a prost::Message.

This is because a returned trait object of type dyn prost::Message would not be of much use – almost all of the methods on the prost::Message trait require a self parameter that is Sized, and a bare trait object is not Sized.

Stringly-Typed Parameters

The Go port uses stringly-typed parameters to indicate enumerations in various places (e.g. hash function names, curve names). Wherever possible, the Rust port uses strongly typed enums instead:

  • When the main enumeration definition is from a protobuf file, the generated Rust code has a corresponding enum type, but fields using that type are encoded as i32 values. The enum type is used for API parameters in the Rust port, and converted to i32 values when held in a protobuf-generated struct.
  • When enumeration values are serialized to/from JSON, their i32 values are converted to/from string values that match the Go string values (see below).
  • Test vectors from Wycheproof use string names to identify enumeration values; these are converted to the relevant enum type in the relevant Wycheproof-driven test cases.

JSON Output

Tink supports the encoding of Keyset and EncryptedKeyset types as JSON when the json feature of the tink-core crate is enabled, with the following conventions:

  • Values of type bytes are serialized to base64-encoded strings (standard encoding).
  • Enum values are serialized as capitalized strings (e.g. "ASYMMETRIC_PRIVATE").

The tink_core::keyset::json_io module includes serde serialization code which matches these conventions, and the prost-build invocation that creates the Rust protobuf message definitions includes a collection of extra options to force the generation of the appropriate serde attributes.

Code Structure

This section describes the mapping between the upstream Go packages and the equivalent Rust crates and modules.

Infrastructure

Rust Crate/Module Go Package
tink_core::cryptofmt core/cryptofmt
tink_core::keyset keyset
tink_core::primitiveset core/primitiveset
tink_core::registry core/registry
tink-core tink
tink-proto *_go_proto

Common Crypto

Rust Crate/Module Go Package
kwp
tink_core::subtle::random subtle/random
tink_core::subtle subtle

Primitives

Rust Crate/Module Go Package
tink-aead aead
tink-daead daead
hybrid
tink-mac mac
tink-prf prf
tink-signature signature
tink-streaming-aead streamingaead

Testing

Rust Crate/Module Go Package Notes
tink_core::keyset::insecure insecurecleartextkeyset Gated on (non-default) insecure feature
tink_core::keyset::insecure internal Gated on (non-default) insecure feature
tink_core::keyset::insecure testkeyset Gated on (non-default) insecure feature
tink-tests testutil Depends on insecure feature of tink-core crate
tink-testing services (/testing/go/)
tink-testing::proto testing_api_go_grpc (/proto/testing/)
main (/tools/testing/go/)

Key Management Systems

Rust Crate/Module Go Package
tink-awskms integration/awskms
tink-gcpkms integration/gcpkms
integration/hcvault
Issues

Collection of the latest Issues

tarcieri

tarcieri

Comment Icon3

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

https://github.com/RustCrypto/traits/pull/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)

daviddrysdale

daviddrysdale

Comment Icon0

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.

daviddrysdale

daviddrysdale

enhancement
Comment Icon3

First, need a version of prost that includes https://github.com/danburkert/prost/commit/fdf9fdf730e2554a6c2bce693f7600e350358c7d.

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.

Information - Updated Nov 08, 2021

Stars: 46
Forks: 6
Issues: 17

Rust RPC client for Bitcoin Core JSON-RPC

This is a Rust RPC client library for calling the Bitcoin Core JSON-RPC API

Rust RPC client for Bitcoin Core JSON-RPC

jsonpointer_flatten

Rust library to flatten a JSON object using JSON Pointer field addressing as defined in

jsonpointer_flatten

JSON-RPC library designed for async/await in Rust

Designed to be the successor to tracking issue for next stable release (0

JSON-RPC library designed for async/await in Rust

tinyjson is a library to parse/generate JSON format document

Rust stable toolchain (no dependency)

tinyjson is a library to parse/generate JSON format document

Introduction to JsonPath

The library provides the basic functionality to find the set of the data according to the filtering query

Introduction to JsonPath

A simple JSON library

This is a simple JSON library I wrote for one of the

A simple JSON library

A library for writing colored JSON output to a termcolor terminal

A library for writing colored termcolor terminal

A library for writing colored JSON output to a termcolor terminal

Rust's standard library offers

display_json is a crate that allows you to easily integrate

Rust's standard library offers

A Rust library for safely extracting JSON fields while also checking data bounds

Obtain an SQL schema from the Database being used (it should contain the table creation SQL)

A Rust library for safely extracting JSON fields while also checking data bounds

jsonm implementation port for Rust

Original library written in JS is here:

jsonm implementation port for Rust

Lexical JSON number types

This is a simple library for parsing and storing JSON numbers according

Lexical JSON number types
Facebook Instagram Twitter GitHub Dribbble
Privacy