WARNING: This code relies on copyleft dependencies which may require special attention to use safely within commercial products. Please be take care to ensure you are abiding by the license terms specified in these dependencies.
Fuzzy Encryption for Secret Recovery project is an approach to provide an alternative for unmemorable user-controlled cryptographic keys composed of secret long strings of random numbers and letters. Our project presents a scheme where a user is expected to remember/securely protect a pass-phrase alone, regardless of ordering. This pass-phrase is used to generate cryptographic key material that is used to generate as well as recover cryptographic key(s). Any state information generated by the scheme in order to generate the cryptographic key material from the pass-phrase can be stored in any public repository.
The Fuzzy Vault Key Recovery System allows you to create a virtual vault of cryptographic keys. This vault has a combination consisting of a set of words that are to be randomly selected from a known set (corpus). To recover the keys the user must supply that combination (set of words) to the recovery system. The recovery words can be in any order and they may contain a limited set of errors defined by the system. In this way the user can recover the keys with some allowable errors.
This distribution contains a C++ and a Python implementation of the Fuzzy Vault key recovery scheme. The Python version is included as a demonstration to help understand the C++ implementation. The Python version not intended for general use. The C++ code is intended for general use.
We include two sample applications, demo and loadrand. Demo demonstrates the creation of a secret and then recovery of keys with a different number of errors in the recovery words. This sample application demonstrates that if the number of errors in the recovery words is less than a well defined limit then the keys can be recovered but if there are too many errors then the recovery fails. Loadrand is nearly identical to demo except for the fact that loadrand uses random numbers supplied by the application. This allows the secret generation to bypass the random number generator supplied by the operating system or cryptographic libraries. Loadrand uses what is equivalent to a one-time pad encryption scheme.
This section describes how to build the Fuzzy Vault C++ libraries and how to use them in practice.
There are 3 targets of the the C++ build process: Linux, WASM and Android. These build both the Fuzzy Vault shared libraries, some demonstration examples, and runs one of the examples to verify the build. The build is simple using the supplied scripts.
cd ./src/scripts/linux
sudo ./build.sh
The Web Assembly does not have a convenient way of generating random numbers so in this cases it is necessary to include the randomBytes key value pair in the input. To this end the WASM build uses the loadrand test to verify that the build succeeded.
cd ./src/scripts/android
sudo ./build.sh
cd ./src/scripts/android
sudo ./build.sh 23 <Debug|Release>
This section describes how to create a C++ application that uses the Fuzzy Vault libraries. This section explains the example in ./src/c++/tests/demo and ./src/c++/tests/loadrand.
These APIs throw exceptions rather than return an error code. Exceptions will be thrown if the JSON inputs are not correct. Your code should have the following form
#include "fuzzy.h"
try {
call some FuzzyVault APIs
}
except (NoSolutionException) {
handle this gracefully -- usually user error (eg. wrong recovery words)
which can happen
}
except (exception& e) {
this is bad perhaps by something nonsensical like negative corpus size
Check that the input makes sense and tell the user the problem
}
except (...) {
this is really bad probably want to bail
}
See the sample code for guidance.
The libraries (libfuzzyvault.*) expose only three functions:
These are defined in ./src/c++/fuzzyvault/fuzzy.h. Each of these functions use C++-JSON strings for both input and output. We have
generates the parameters of the vault in JSON format which is to be passed to gen_keys. It is expected that the architect of the key recovery process defines these parameters one time and then uses them for all clients.
std::string fuzzy_vault::gen_params(const std::string& input);
a JSON string representing a dictionary of the in one of the two following forms
{
"setSize" : 12,
"correctThreshold" : 9,
"corpusSize" : 7776
}
This input option is provided for use in WASM due to lack of access to random number generation.
{
"setSize" : 12,
"correctThreshold" : 9,
"corpusSize" : 7776,
"randomBytes": [
"3218C8B6681167BC81BBCA7523FE...E089FA0E2E04",
"E9DA670216EBDA73F1626012E645...B4C314729D29",
"C765880C27EC4EED06155B85C43D...F0B3E2E1EFBE"
]
}
The input json string will contain 3 or 4 key value pairs which are described here.
setSize is the number of words that must be selected from the corpus. This is equal to the number of words that are supplied at the time of the call to gen_keys.
correctThreshold is the minimum number of words that need to be correct to successfully recover the keys.
corpusSize is the number of unique words in the set that the recovery words are chosen from
randomBytes is an optional key value pair. If this field is present then the strings of upper-cases hexadecimal characters define random numbers to be used during the generation of the parameters. Each two consecutive characters in each string represents a bytes. Each string must contain an even number of characters. The number of bytes represented must be greater than or equal to 4 * (setSize + 8). This parameter is normally missing.
The return value of gen_secret is a string containing a JSON dictionary of the following form.
{
"setSize": 12,
"correctThreshold": 9,
"corpusSize": 7776,
"prime": 7789,
"extractor": [ 1223, 81, 1257, 2529, 2115, ... 5130, 416 ],
"salt": "CF339C756CFAA7715018C8FFF97343454 ... 94DABBC8D36"
}
takes the parameters generated by a previous call to gen_params as input and returns a secret in the form of a JSON string that will be passed into gen_keys at a later time.
std::string fuzzy_vault::gen_secret(
const std::string& params,
const std::string& words
);
}
words is a JSON string representing a list of setSize unique integers in the range 0 .. corpusSize - 1 as specified in params. The words JSON string looks like
{ 78, 2643, 1178, ... }
The gen_secret call outputs a string containing the secret which has the following form
{
"setSize": 12,
"correctThreshold": 9,
"corpusSize": 7776,
"prime": 7789,
"extractor": [ 1223, 81, 1257, 2529, ... 5130, 416 ],
"salt": "CF339C756CFAA7715018C8FFF97343 ... DABBC8D36",
"sketch": [ 967, 5576, 1719, 6542, 2717, 7711 ],
"hash": "73E8AB1883CB093F1C546D69DC87EC0FE658 ... FA975745"
}
It is up to the application to store the secret and guaranteed that it will not be modified. The secret will be one of the arguments
std::string fuzzy_vault::gen_keys(
const std::string& secret,
const std::string& recovery_words,
int key_count
);
Generates a list of keys
To generate keys the caller must supply a set words (integers) meets the minimum threshold for matching words specified in gen_secret. This means that the input words must be unique, the number of words must be equal to setSize, every word must be greater than or equal to zero and less than corpusSize and the number of words matching the original set must be greater that or equal to correctThreshold. If all of these conditions are met then a list of keys of size key_count is returned to the user in the form of a JSON string.
All calls to this function will return the same sequence of keys.
A JSON string returned by gen_secret.
a list of unique integers. These integers represent a guess of the original words passed into gen_secret.
key_count a positive integer specifying the number of keys to be returned
A JSON string representing a list of recovered keys. The returned string has the form
[
"B4263013BC29B964F6FB62FEB7119 ... ACBDC55A8C24A4ED78185936E76C8CD",
"23568650436339CCA498D396D9EFD ... BB4B1CD2D97D869A3745080E323D62F",
"E4CCD887D50179DD4B0BB57E95010 ... 4A35C1AA2B4C606B82C319C8A1D9B61",
"6FEABCC8DDD8FA6557C7D096FA612 ... 1419E5EE7F0ED739CA9FA4E03393E44",
"B075330F188F8C1795B715165B67F ... D11FC1B2D206D2E29D99EE3A020B150"
]
Each key is represented as a large hexadecimal string all upper case. Each string is a representation of an array of bytes. Each bytes is represented by two consecutive hexadecimal characters, the lowest byte starting at the left. A byte is as represented by two characters in the 'obvious' way. For example '08' represents a byte value of 8. Typically the keys represent 512 bits or 64 bytes so they each have a length of 128 characters.