Giter Site home page Giter Site logo

cosmian / kms Goto Github PK

View Code? Open in Web Editor NEW
60.0 6.0 4.0 15.35 MB

A feature-rich, scalable, Key Management System

Home Page: https://docs.cosmian.com/cosmian_key_management_system/

License: Other

Dockerfile 0.04% Shell 1.01% Rust 97.10% Python 1.80% C 0.05%
cloudproof key-management key-management-service

kms's Introduction

Cosmian KMS

Build status

Cosmian KMS is an implementation of a high-performance, massively scalable, Key Management System that presents some unique features, such as

The KMS has an extensive online documentation

The KMS can manage keys and secrets used with a comprehensive list of common (AES, ECIES, ...) and Cosmian advanced cryptographic stacks such as Covercrypt. Keys can be wrapped and unwrapped using RSA, ECIES or RFC5649/AES KWP.

Quick start

Pre-built binaries are available for Linux, MacOS and Windows, as well as Docker images. Tu run the server binary, OpenSSL must be available in your path (see "building the KMS" below for details); other binaries do not have this requirement.

Using Docker, to quick-start a Cosmian KMS server on http://localhost:9998 that stores its data inside the container, simply run the following command:

docker run -p 9998:9998 --name kms ghcr.io/cosmian/kms:4.16.0

See the documentation for more.

Repository content

The server is written in Rust and is broken down into several binaries:

  • A server (cosmian_kms_server) which is the KMS itself
  • A CLI (ckms) to interact with this server

And also some libraries:

  • cosmian_kms_client to query the server
  • cosmian_kmip which is an implementation of the KMIP standard
  • cosmian_kms_pyo3 a KMS client in Python.

Please refer to the README of the inner directories to have more information.

Find the public documentation of the KMS in the documentation directory.

Building the KMS

To avoid the additive feature issues, the main artifacts - the CLI, the KMS server and the PKCS11 provider - should directly be built using cargo build --releasewithin their own crate, not from the project root.

In addition, the KMS server must be built against a local installation of OpenSSL 3. Other artifacts do not have this requirement.

Linux

Unless you require a FIPS certified cryptographic module, the distribution provided OpenSSL should be sufficient.

You need to have the development packages of openssl installed. On Ubuntu, you can install them with:

sudo apt install libssl-dev

You may also need to install the pkg-config package (on Ubuntu server typically).

MacOS

Install OpenSSL 3 with Homebrew:

brew install openssl@3

The builder should find it automatically; if not, you can set the OPENSSL_DIR environment variable to the OpenSSL installation directory.

Windows

  1. Install Visual Studio Community with the C++ workload and clang support.

  2. Install Strawberry Perl.

  3. Install vcpkg following these instructions

  4. Then install OpenSSL 3:

vcpkg.exe install openssl[fips]
vcpkg.exe integrate install
set VCPKGRS_DYNAMIC=1
$env:OPENSSL_DIR="<vcpkg>\installed\<archi>>"

where <vcpkg> is the path to the vcpkg installation directory, and <archi> is the architecture e.g x64-windows, arm64-windows, etc..

To run the server from the command line, add <vcpkg>\installed\<archi>\bin to the PATH environment variable.

Build the Docker container

You can build a docker containing the KMS server as follow:

docker build . --network=host -t kms

Or:

# Example with FIPS support
docker build . --network=host \
               --build-arg FEATURES="--features=fips" \
               -t kms

Setup as a Supervisor service

Supervisor (A Process Control System) is a client/server system that allows its users to monitor and control a number of processes on UNIX-like operating systems.

Concerning the KMS, copy the binary target/release/cosmian_kms_server to the remote machine folder according to cosmian_kms.ini statement ( ie: /usr/sbin/cosmian_kms_server).

Copy the cosmian_kms.ini config file as /etc/supervisord.d/cosmian_kms.ini in the remote machine.

Run:

supervisorctl reload
supervisorctl start cosmian_kms
supervisorctl status cosmian_kms

Server parameters

If a configuration file is provided, parameters are set following this order:

  • conf file (env variable COSMIAN_KMS_CONF set by default to /etc/cosmian_kms/server.toml)
  • default (set on struct)

Otherwise the parameters are set following this order:

  • args in command line
  • env var
  • default (set on struct)

Use the KMS inside a Cosmian VM on SEV/TDX

See this README for more details about Cosmian VM.

To deploy the KMS inside a Cosmian VM, connect to the VM and follow these steps:

# Copy from resources/supervisor/cosmian_kms.ini
$ sudo vi /etc/supervisord.d/cosmian_kms.ini

# Copy the KMS server binary
$ sudo cp some_location/cosmian_kms_server /usr/sbin/cosmian_kms

# Create a conf file for the KMS (from resources/server.toml)
# Instead of using default path `/etc/cosmian_kms/server.toml`,
# we are using a path within LUKS encrypted container
$ sudo vi /var/lib/cosmian_vm/data/app.conf
$ sudo export COSMIAN_KMS_CONF="/var/lib/cosmian_vm/data/app.conf"
$ sudo supervisorctl reload
$ sudo supervisorctl start cosmian_kms

# Check logs
$ sudo tail -f /var/log/cosmian_kms.err.log
$ sudo tail -f /var/log/cosmian_kms.out.log

Now you can interact with your KMS through the KMS CLI.

You can also interact with the Cosmian VM Agent through its own CLI as follow:

# From your local machine
# Snapshot the VM (it could take a while)
$ ./cosmian_vm --url https://<DOMAIN_NAME>:<PORT> snapshot

# From time to time, verify it
$ ./cosmian_vm --url https://<DOMAIN_NAME>:<PORT> verify --snapshot ./cosmian_vm.snapshot
Reading the snapshot...
Fetching the collaterals...
[ OK ] Verifying TPM attestation
[ OK ] Verifying VM integrity (against N files)
[ OK ] Verifying TEE attestation

You can also provide the configuration file of the KMS through the Cosmian VM Agent and let it start the KMS.

  1. Check that the /etc/supervisord.d/cosmian_kms.ini contains the following line: environment=COSMIAN_KMS_CONF=/var/lib/cosmian_vm/data/app.conf
  2. Add the following lines in /etc/cosmian_vm/agent.toml
[app]
service_type = "supervisor"
service_app_name = "cosmian_kms"
app_storage = "data/"
  1. Provide the configuration (where server.toml is the configuration file of the KMS):
$ ./cosmian_vm --url https://domain.name:port app init -c server.toml
Processing init of the deployed app...
The app has been configured
  1. Save the printed key for further use
  2. In case of reboot, you will need to restart the KMS manually by sending the configuration decryption key (the key saved at step 4):
./cosmian_vm --url https://domain.name:port app restart --key 378f1f1b3b5cc92ed576edba265cc91de6872d61c00b0e01dba6d0ea80520820

Releases

All releases can be found in the public URL package.cosmian.com.

Benchmarks

To run benchmarks, go to the crate/test_server directory and run:

cargo bench

Typical values for single threaded HTTP KMIP 2.1 requests (zero network latency) are as follows

- RSA PKCSv1.5:
    - encrypt
            - 2048 bits: 128 microseconds
            - 4096 bits: 175 microseconds
    - decrypt
            - 2048 bits: 830 microseconds
            - 4096 bits: 4120 microseconds
- RSA PKCS OAEP:
    - encrypt
            - 2048 bits: 134 microseconds
            - 4096 bits: 173 microseconds
    - decrypt
            - 2048 bits: 849 microseconds
            - 4096 bits: 3823 microseconds
- RSA PKCS KEY WRP (AES):
    - encrypt
            - 2048 bits: 142 microseconds
            - 4096 bits: 198 microseconds
    - decrypt
            - 2048 bits: 824 microseconds
            - 4096 bits: 3768 microseconds
- RSA Keypair creation (saved in KMS DB)
    -  2048 bits: 33 milliseconds
    -  4096 bits: 322 milliseconds

kms's People

Contributors

adamk93 avatar bgrieder avatar ccorsin avatar grydz avatar heavenboy8 avatar josepisco avatar manuthor avatar phochard avatar tbrezot avatar thibauddauce avatar thibsg avatar weblaetitia 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

Watchers

 avatar  avatar  avatar  avatar  avatar

kms's Issues

Zeroize BigUInt keys

          If I am right, you are using BigUint for storage only. Therefore it should be possible to implement the correct operation on dropping structure that own them (big integers are essentially pointers so they do not leak there content in the memory when they are passed around).

Originally posted by @tbrezot in #149 (comment)

Support for X509 v3 extensions on `Certify`

After #101, Certify can certify a public key, but no X509 V3 extension can be provided.

Support for adding extensions should be provided in the same form as OpenSSL i.e. an extensions file with sample content

[ v3_ca ]
basicConstraints=CA:TRUE,pathlen:0
keyUsage=keyCertSign,digitalSignature
extendedKeyUsage=emailProtection
crlDistributionPoints=URI:http://cse.example.com/crl.pem

Risk introducing nonce reuse

In the Redis + Findex storage back-end, in object_upsert, the Redis value is encrypted using a KDF256 of the Redis UID as nonce. This will cause a nonce reuse if the Redis table is mutated. Such mutation is allowed through the function update_object. Therefore, keys modified using this function should be considered as leaked.

You can use the UID as associated data in order to cryptographically link UIDs with their values. I don't think there is a way to avoid storing the nonce without making the whole DB immutable.

Support for Quick Cert functionality ?

Before #101 , a non-KMIP-compliant quick cert functionality existed which would allow the creation of a cert and its chain by supplying minimal infos on the subject names.
The keys would use X25519.

After #101 this should be doable with three calls:

  • a Import call to import the a PKCS#12 with the issuer private key and cert(s)

  • then for every new certificate

    • a CreateKeyPair call to generate a Key pair
    • a Certify call on the generated Public Key to generate a cert

    In my view, this is good enough to emulate the old functionality and has the benefit to be KMIP compliant while offering more flexibility on the encryption algorithms used.

    While we make the decision, the "quick cert" code, will be moved to a separate branch

Support for the `Validate` KMIP operation

When #101 is completed, automatic validation of the issuer certificate will be removed on the Certify operation as is mandated by the KMIP 2.1 specifications.

A specific Validate operation is available in the KNIP specs and should be implemented.

Change BigUint utilization for BigNum from OpenSSL

BigUint is a bigint type that does not export any big-endian padding method which means that any Vec<u8> starting with a null byte, converted to a BigUint and converted back to a Vec<u8> will not be the same and the null byte will be dropped.

This is important as sometimes (with $2^{-8}$ probability), a private / public key can legally generate with a prepended null byte and double conversion will return a shortened key.
For example, code below triggers this error:

let my_key = [0, 113, 8, 182, 191];
let my_key_be_bytes = BigUint::from_bytes_be(&my_key);
let my_key_vec = my_key_be_bytes.to_bytes_be();

println!("{:?}", my_key);
// [0, 113, 8, 182, 191]

println!("{:?}", my_key_vec);
// [113, 8, 182, 191]

Rust `KeyBlock` implementation not fully compliant with KMIP 2.1 specs

The KeyBlock struct with the current implementation and updated comments looks like this.

/// A Key Block object is a structure used to encapsulate all of the information
/// that is closely associated with a cryptographic key.
/// Section 3 of KMIP Reference 2.1
#[derive(Serialize, Deserialize, Clone, Debug, Eq, PartialEq)]
#[serde(rename_all = "PascalCase")]
pub struct KeyBlock {
    pub key_format_type: KeyFormatType,
    /// Indicates the format of the elliptic curve public key. By default, the public key is uncompressed
    #[serde(skip_serializing_if = "Option::is_none")]
    pub key_compression_type: Option<KeyCompressionType>,
    /// Byte String: for wrapped Key Value; Structure: for plaintext Key Value
    pub key_value: KeyValue,
    /// MAY be omitted only if this information is available from the Key Value.
    /// Does not apply to Secret Data  or Opaque.
    /// If present, the Cryptographic Length SHALL also be present.
    pub cryptographic_algorithm: CryptographicAlgorithm,
    /// MAY be omitted only if this information is available from the Key Value.
    /// Does not apply to Secret Data (or Opaque.
    /// If present, the Cryptographic Algorithm SHALL also be present.
    pub cryptographic_length: i32,
    /// SHALL only be present if the key is wrapped.
    #[serde(skip_serializing_if = "Option::is_none")]
    pub key_wrapping_data: Option<KeyWrappingData>,
}
  1. KeyValue should be an enumeration with 2 variants ByteString and a second variant wrapping the current structure
  2. cryptographic_algorithm should be optional
  3. cryptographic_length: should be optional

Fixing these issues, implies fixes in at least the JS and Java implementations.

Utils crate dependency on kmip

utils crate should semantically not depend on kmip crate. This creates architectural inconsistencies in the KMS.

For example when writing a wrapper to zeroize the BigUint type, it cannot be written in utils and used in kmip whereas that's how you would do it.

OpenSSL ECIES for X and Ed curves

Hybrid encryption supports ECIES Salsa Sealbox for curves X25519 and Ed25519. In a concern for a growing list of supported algorithm, it would be of interest to have an OpenSSL implementation for curves X25519, X448, Ed25519 and Ed448. They would still be disabled in FIPS mode.

This would allow to completely remove cloudproof dependencies from KMS even in non-fips mode.

Zeroize crypto

At this point, many processed data in cryptographic primitives are not properly zeroized.
I propose to use the zeroize crate to create Zeroizing objects from keys and plaintexts.

Make sure to use the zeroize crate directly if it is imported through cloudproof.

Support for the creation of self-signed certificates

The Certify operation of #101 allows the creation of certificates from public keys but only supports signature by another key.

Nothing in the KMIP specification describes how this could be achieved.
The proposed solution is to pass a well-known value, say [SELF SIGNED] to both the PrivateKeyLink and CertificateLink of the Attributes in the Certify request.

Enforce consistant storage formats for Objects and enforce KMIP 2.1 default export formats

The KMIP 2.1 specification states that keys have a default Key Format Type that SHALL be produced by KMIP servers.
When requesting the export of an Object without specifying the Key Format Type, a default Key Format Type by object (and algorithm) should be used as listed in the following table:

Type Default Key Format Type
Certificate X.509
Certificate Request PKCS#10
Opaque Object Opaque
PGP Key Raw
Secret Data Raw
Symmetric Key Raw
Split Key Raw
RSA Private Key PKCS#1
RSA Public Key PKCS#1
EC Private Key Transparent EC Private Key
EC Public Key Transparent EC Public Key
DSA Private Key Transparent DSA Private Key
DSA Public Key Transparent DSA Public Key

The current version does not enforce these defaults.

Some of these formats need to be updated, and the IETF recommends using PKCS#8 and Subject Public Key Info.
So, even though this server enforces default export formats, the storage formats will be updated to use:

  • PKCS#8 DER for RSA and EC private Keys (RFC 5208 and 5958)
  • SPKI DER (RFC 5480) for RSA and EC public keys
  • X509 DER for certificates (RFC 5280)
  • PKCS#10 DER for certificate requests (RFC 2986)
  • TransparentSymmetricKey for symmetric keys
  • Raw for opaque objects and Secret Data

Users requesting keys will be encouraged to request them in the storage formats above to avoid conversion.

Support for `SetAttributes` KMIP operation

Supporting the SetAttribute operation will greatly help

  • linking objects together in the KMS (links are essential in the PKI and PKCS#12 parts of the server)
  • adding tags to Objects

Much likely easier to handle after fixing #88

After #101 , GetAttributes will be available (this includes tags as well)

Fix Panic if AuthenticatedEncryptionTag is missing

I encountered this, writing a Decrypt request from Js library.
If the Authenticated Encryption Tag parameter is missing or null in a Decrypt request, Rust is panicking :

thread 'actix-rt|system:0|arbiter:3' panicked at /root/.cargo/registry/src/index.crates.io-6f17d22bba15001f/generic-array-0.14.7/src/lib.rs:572:9: assertion left == right failed left: 0 right: 16

Expected :

  • Rust should not panic and handle this error properly
  • This parameter is also not required from KMIP standard

Make RSA_OAEP_AES a generalized Encryption System for use in all KMS and not only for key wrapping.

KMS Encryption Systems only choices are CoverCrypt or HybridEncryptionSystem which are not specified in any FIPS document for hybrid encryption hence by default not allowed in FIPS mode.

However, the implementation of RSA_OAEP_AES_KWP in crate/utils/src/crypto/wrap in built on primitives in compliance with FIPS 140-3. Thus, I belived it should be generalized as an RSA hybrid encryption scheme and derived with AES-GCM and not only AES-KWP.

Improve KMIP `Attributes` support

Kmip managed objects (Objects) are associated with attributes ( Attributes).
These attributes contain meta information on the object, in particular links to other objects.

To store the attributes, the current implementation relies on a nested Attributes property inside the KeyValue, located inside a KeyBlock which is itself inside the Object.

This implementation is problematic for a few reasons:

  • Some Objects such as Certificate do not have a nested Attributes property where to store them, and attributes are lost on Import or not generated on Create. The current implementation partially compensates with the use of tags, but this is limited and non-standard.
  • The current implementation prevents the use of the Locate operation to find Object that is not holding attributes.
  • With regards to the Attributes nested inside the KeyValue, KMIP specifies they should merely be a copy of the "main" attributes

This attribute information differs from the attributes associated with Managed Objects, and is obtained via the Get Attributes operation, only by the fact that it is encapsulated with (and possibly wrapped with) the key material itself.

Proposed change

This ticket proposes to change the storage layer so that it stores the tuple (Object, Attributes) rather than just the Object. This would affect the Database trait in crate/server/src/database/database_trait.rs and its specific DB implementations.

The Locate operation should be updated to search these top-level Attributes.
The Import operation should be updated accordingly.

Move serialization out of KMIP crate

In the KMIP create, the presence of the file data_to_encrypt.rs which is basically only serialization could be moved elsewhere.

Then the function could bubble-up appropriate errors instead of having an error variant about serialization problem in KMIP error enum.

Database trait `filename()` should return an option

pub trait Database {
    /// Return the filename of the database if supported
    fn filename(&self, group_id: u128) -> PathBuf;
   ...
}

should be

pub trait Database {
    /// Return the filename of the database if supported
    fn filename(&self, group_id: u128) -> Option<PathBuf>;
   ...
}

JWKS refresh

Google changes its JWKS regularly.
We, therefore, need to refresh them in the auth. Module.
I suggest the following approach: if a valid JWT errors with "key not found in set" (of certificates), we refresh the JWKS and retry authentication.

Migrate from 'num-bigint' to ´num-bigint-dig'

´num-bigint' is not constant time. BigUints are used to store key material data (e.g. RSA).
This is only used for storage, not computation so there should not be any security issue.
However, to prevent potential misuse, use of the constant time version would be preferred.

Note: this change is likely to create havoc in the ser/de of TTLV

Build a generic database upgrade mechanism

A generic database upgrade mechanism on startup should be built.

This involves:

  • Creating a context table with a single record holding the last version of the software run (when the table is not present, assume the version run is the one before the version implementing the change)
  • On server startup:
    • the server checks if the software version is greater than the last version run
    • if no, it simply starts
    • if yes,
      • it looks for all upgrades to apply in order from the last version run to this version (this is done using a static function hardcoding the upgrade functions to call for each version)
      • if there is any to run, it sets an upgrading flag on the db state field in the context table
      • it runs all the upgrades in order
      • it sets the flag from upgrading to ready
  • On every call to the DB, a check must be performed on the db state field to check if the DB is upgrading. If yes, calls should fail. Performance can be maintained using a secondary write-through cache of the context table

Upgrades should be programmed to resist being interrupted in the middle and resumed from start.

Removal of openssl in the CLI

This will make the CLI pure Rust and facilitate compiling to various targets.

After #101 , only the verify.rs need to be ported to OpenSSL.

Critical: better salt for password derivation

Passwords can be derived into secure keys via the derive_key_from_password() method in crate/utils.
There is an issue where one would not find back the same key from a password twice and because the salt is not saved.
This could be fixed by adding an Option(&[u8]) parameter to derive_key_from_password() to both FIPS (PBKDF2 with HMAC256) and non-FIPS (Argon2).

Adding support for non-deterministic salt to Argon2 is primordial since for the moment the salt is a constant string, which means that two identical password derive to the same key and this need to be avoided.

Don't forget to ad tests to derive_key_from_password() with optional salt.

Accurate CryptographicAlgorithm for KMIP key creation on elliptic curves

As mentionned by @tbrezot in #149 (more precisely this comment), Curves such as X25519 or Ed25519 are created for CryptographicAlgorithm::ECDH in FIPS mode whereas it is not approved by the standard.

I think we should rearrange dynamic algorithm to pass as an arguemnt to the create_X_key_pair() function that generates elliptic curve keys.

pub fn create_x25519_key_pair(
    private_key_uid: &str,
    public_key_uid: &str,
    cryptographic_algorithm: CryptographicAlgorithm,
) -> Result<KeyPair, KmipUtilsError> {}

FIPS version of the Server

Create a special FIPS version of the server that will only enable FIPS-140 certified crypto modules.

The choice is to use OpenSSL (only) server side in one of its FIPS-140 approved modes.

Improve performance of objects retrieval for KMIP operations

When a user retrieves an object to perform an operation (Encrypt, Decrypt, Certify, etc.), the decision rationale is that the user has the access right to operate for that object, or it can read the entire object (and hence export it).

The current implementation in retrieve_object_utils.rs potentially issues 2 calls to the DB. This could be improved by supporting a single retrieve call that would take a list of (any) operations.

/// Retrieve a single object for a given operation type
/// or the Get operation if not found.
///
/// This function assumes that if the user can `Get` the object,
/// then it can also do any other operation with it.
pub async fn retrieve_object_for_operation(
    uid_or_tags: &str,
    operation_type: ObjectOperationType,
    kms: &KMS,
    user: &str,
    params: Option<&ExtraDatabaseParams>,
) -> KResult<Object> {
    //TODO: we could improve the retrieve() DB calls to support a list of Any(operation..)
    Ok(
        match _retrieve_object(uid_or_tags, operation_type, kms, user, params).await {
            Ok(key) => key,
            Err(_) => {
                // see if we can Get it which is also acceptable in this case
                _retrieve_object(uid_or_tags, ObjectOperationType::Get, kms, user, params).await?
            }
        },
    )
}

Empty `Attributes` cause an `Object` deserialisation issue

A complete test is available in crate/kmip/src/kmip/ttlv/tests/mod.rs

#[test]
fn test_issue_deserialize_object_with_empty_attributes() {
    log_init("info,hyper=info,reqwest=info");

    // this works
    let _: KeyBlock = serialize_deserialize(get_key_block()).unwrap();
    println!("KeyBlock serialize/deserialize OK");

    // this should work too but does not deserialize
    // because of the empty Attributes in the KeyValue
    let object = Object::SymmetricKey {
        key_block: get_key_block(),
    };
    let res: Result<Object, KmipError> = serialize_deserialize(object);
    assert!(res.is_err());
}

fn serialize_deserialize<T: DeserializeOwned + Serialize>(object: T) -> Result<T, KmipError> {
    // serialize
    let object_ttlv = to_ttlv(&object)?;
    let json = serde_json::to_string_pretty(&object_ttlv)?;
    // deserialize
    let ttlv: TTLV = serde_json::from_str(&json)?;
    let t: T = from_ttlv(&ttlv)?;
    Ok(t)
}

fn get_key_block() -> KeyBlock {
    KeyBlock {
        key_format_type: KeyFormatType::TransparentSymmetricKey,
        key_compression_type: None,
        key_value: KeyValue {
            key_material: KeyMaterial::TransparentSymmetricKey {
                key: hex::decode(
                    b"EC189A82797F0AED1E5AEF9EB0D232E6079A1D3E5C00526DDEE59BCA16242604",
                )
                .unwrap(),
            },
            //TODO:: Empty attributes causes a deserialization issue for `Object`; `None` works
            attributes: Some(Attributes::default()),
        },
        cryptographic_algorithm: Some(CryptographicAlgorithm::AES),
        cryptographic_length: Some(256),
        key_wrapping_data: None,
    }
}

Better SQLCipher support

  • Allow the user to change his database secret
  • Improve error messages (for a better user experience) concerning sqlcipher: bad password, bad group id, etc.
  • Remove all references to EdgelessDB: CI, docs, etc.
  • Add the cli command configure in the public documentation
  • When sqlcipher will include sqlite>=3.8, remove the feature sqlcipher from Cargo.toml and enable libsqlite3-sys/bundled-sqlcipher-vendored-openssl by default. Follow: sqlcipher/sqlcipher#427

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.