I wrote some comments up under your initial question, but I feel that combining my comments (which describe a solution) with all the answers mentioned thus far may produce something you can actually use (versus researching on your own).
As mentioned in the comments, split-key technology is the process of making X-number of sub-keys. The most common style of implementation is to require ALL X sub-keys to recover a protected key.
In the example I discuss next I'll define the following words to ensure clarity:
key: A crypto-variable that is static (aka, shared/symmetric) or is generated based upon a password or passphrase.
password: A clear-text or otherwise non-crypto-derived value that a human typically memorizes (or, shamefully, writes down on a sticky kept under their keyboard).
passphrase: For the purposes of this example a passphrase is identical to a password.
sub-key: A single chunk of recovery information that is equal in size to the key being protected.
split-key: The concept of breaking a recovery key into various splits (aka, sub-key's or chunks).
dictionary characters: The set of characters a user can enter into a recovery mechanism (e.g., A-Za-z0-9).
Now that we have that out of the way we can look at a method to implement this solution.
Assume that my password (as defined above) is IKnowThisIsBad.
I pass the password to the OpenSSL Linux bash command sha512sum and it outputs the hashed value: d3a48c640bbd58633138e2f88b2d3979e3d609df6cd22246dd73d35a77b6755fcf580be431a93c059bc7106ab8d37491d442339879947728d8da436a9c2c60c0
I use that hashed value (note: it is NOT salted at this point, I'm trying to keep it simple...) as my key. This is the key that we are going to protect and I use this key for all my relevant cryptographic purposes.
Let us assume we'll protect the recovery process by using 3 sub-keys (any number of sub-keys greater than 1 is valid, however).
We assume here that a user cannot remember such a sequence and thus cannot (in their head) store a sub-key. Nonetheless, to successfully (and properly) use split-key technology they must provide a chunk of appropriate length. We'll use our friend sha512sum again. In this example let us pretend that the user's passphrase for deriving their recovery chunk is My Secret Recovery Phrase. The output for that iteration produces: 196822a43878ff1c263fe16b6cb2d768b157cf1018ded64e6bab4d36822262ff7f61404ab21c2d279c6cfaa85112786044231917c484dcf3a81931d34fa86edd
Now we need a chunk that will be stored somewhere else. Using your example we'll say it's stored as text to an email. Back we go to our friend sha512sum to generate this sub-key value. We'll use the passphrase: Email Is Not Very Safe To Store Key Info!!, which yields the following value: a8a73e1c6ecd3f2ddff853901f3781fac5839d42cf360833e7148163beb2c05ad5288e6d3c30fd9e7a4bb854b7f507c70991f6c04c507c58028573b5f89ce6a7
At this point we have 2 out of the three recovery chunks or sub-keys generated. Regardless of how many sub-keys you make, the last one is treated special. If we had 10 sub-keys, then we could create the first 9 following the previous examples. If we had 30 then the first 29 could be created that way. But in any case, one value is generated special. To create the last recovery sub-key we must XOR all the know values up to this point together.
Let's look into this more closely.
The main reason we use XOR in cryptography is that it is recoverable (unlike OR and AND operations). When we XOR the previous three values (i.e., the key, the user recovery sub-key generated from the recovery passphrase, and the stored-in-email sub-key) we generate a 3rd sub-key that is automatically the same length as the other 3 inputs. This 3rd chunk can be assigned to your "download to local computer" recovery option. The result of this XOR operation (which can be thought of as: key ^ sub-key1 ^ sub-key2 -OR- key XOR sub-key1 XOR sub-key2) is: 426;94fc5?68`854eaff50ij?gcao?g`d7i2j;add=3af<3`71gehf0dm`26b7fa0511<7g1b`cjgc;g7`a65ibm5>340;l6`9?0d<k?`n40d7a352mm010gk=j8ecfc
I apologize for the weird formatting of that last output. It is necessary because the resultant sub-key value contained backtick (`
) characters that had to be specially escaped.
At this point we save that final value somewhere as specified by the "download to local computer" policy.
Great. So that's a bunch of hash values, but how do you get your key back should it be lost from whatever underlying system(s) that use it? We recover all of the sub-key values and XOR them together. This will reproduce the original key value!
To prove this is the case I wrote a little Python blob that implements this XOR operation for arbitrary numbers of XOR strings. Find it below.
#!/usr/bin/env python
#KEY: d3a48c640bbd58633138e2f88b2d3979e3d609df6cd22246dd73d35a77b6755fcf580be431a93c059bc7106ab8d37491d442339879947728d8da436a9c2c60c0
#RECOV1: 196822a43878ff1c263fe16b6cb2d768b157cf1018ded64e6bab4d36822262ff7f61404ab21c2d279c6cfaa85112786044231917c484dcf3a81931d34fa86edd
#RECOV2: a8a73e1c6ecd3f2ddff853901f3781fac5839d42cf360833e7148163beb2c05ad5288e6d3c30fd9e7a4bb854b7f507c70991f6c04c507c58028573b5f89ce6a7
# RECOV3 is generated by running this script with the above 3 command-line values generated by 'sha512sum'
#RECOV3: 426;94fc5?68`854eaff50ij?gcao?g`d7i2j;add=3af<3`71gehf0dm`26b7fa0511<7g1b`cjgc;g7`a65ibm5>340;l6`9?0d<k?`n40d7a352mm010gk=j8ecfc
# Recovery of KEY is accomplished by running this script with RECOV1 RECOV2 and RECOV3 as parameters.
import sys, binascii, argparse;
parser = None;
stringlist = None;
def xor_strings(base, new):
return "".join(chr(ord(b)^ord(n)) for b, n in zip(base, new));
def SetParseArgs():
global parser, stringlist;
parser = argparse.ArgumentParser(description="XOR a bunch of strings!");
parser.add_argument("string", nargs="+");
stringlist = parser.parse_args().string;
def main():
global sys, stringlist;
SetParseArgs();
res = stringlist[0];
for index in range(len(stringlist) - 1):
#skip string #1
hexstr = stringlist[index + 1];
res = xor_strings(res, hexstr);
print "XOR result string:\n%s" %(res);
if __name__ == "__main__":
main();
else:
print "This script cannot be imported";
print "Please run as: %s <string_1> <string_2> [string_n]" %(sys.argv[0]);
print "For best (and most logically-correct) results, ensure strings are the same length";
This script can be used to create the recovery chunks, distribute them however you want, then use the script with ALL of the chunks and you will recover your original key.
This example should make it clear how recovery anything less than ALL of the chunks is just as useless as having none of them because an attacker would still have to brute-force every possible combination across the entire key address space.
I hope some of you find this useful. I enjoyed the exercise in putting it together and might actually use it myself now that the implementation is taken care of! :-)
Please comment on any typo's you observe. I used literal values for all examples above so anyone running the commands/tools described above will produce identical results. I wrote all pre-sha512sum'd strings to individual text files WITHOUT a newline at the end and fed them as parameters to sha512sum to create the key, sub-key1, and sub-key2.
Thanks and enjoy!