CipherShare
Introduction
CipherShare is a refined form of a secret-sharing scheme which allows for recovery of secrets in the case of losing access to said secret. These secrets may include passwords, images, files or keys. Currently a secret of any size can be derived into an encrypted secret and a key each of which can be shared with an independent trusted party. By re-collecting the encrypted secret and key it is possible to completely reconstruct the original secret. It is this possibility to reconstruct the original secret that is important - if one loses access to their original secret they can reconstruct it in future.
To emphasise, it is necessary to note that using this scheme to share secrets with trusted parties does introduce the possibility that those trusted parties, or any malicious parties that acquire access to them, can acquire access to the original secret in a manner that would not have been possible otherwise. Such access can be gained through collaboration between the parties that hold the derived secret and key. Only use CipherShare where these risks are acceptable.
Technical Specifications
One of many strengths of use of this library is the handling of data storage. Only the derived secret has to be stored at a size equal to the secret itself and the derived key is instead fixed to 60-bytes in length (when including the authentication tag). Therefore a user could entrust one party with large data storage capabilities the derived secret and the other party the derived key. Encryption and decryption is also performed at an average of 300 MB/s which remains faster than most bottlenecks that may be confronted. Using a public-key cryptosystem to achieve the same effects as performed by CipherShare would achieve an average of 20 MB/s which would mean large images for instance could take several seconds to process.
Multiple derived secrets
To derive a secret into more than two parts (the derived secret and a key) it is possible to reuse CipherShare on each derived secret or key to create multiple derived secrets that can be shared with multiple trusted parties. Performing this sequence ensures that each trusted party would have to collaborate together to reconstruct the secret meaning it would be more difficult for a malicious actor to achieve such a feat. However, because every derived secret (or key) would be required to recreate the original secret there is greater risk of data loss and more significant computational power required.
It is therefore recommended that CipherShare is used only to share a single derived secret and a single derived key between two trusted parties.
How it works conceptually
To explain how CipherShare works it is necessary to imagine two parties in communication with each other across an insecure network. One of the parties in this instance may be imagined to be a server or an API, especially one responsible for data storage. As all actions performed in CipherShare are symmetric the roles of the two parties are interchangeable.
Cipher-Sharing
Given a party desires to share a derived secret and key with two trusted parties such that the collaboration of those two parties will allow for the recovery of the original secret the following can be performed. Initially a randomised key and IV is created as the derived key in an AES cipher to encrypt the secret. Output of this (the derived secret) and the derived key (concatenated with the authentication tag) should be shared through encrypted channels with the trusted parties individually to be stored.
As long as no other party acquires access to the original party's derived key alongside the derived secret than no other party can acquire access to the contents of the original secret. The original party can decrypt the dervied secret as long as they regain knowledge of the derived key. Currently the most effective attacks against this scheme are brute-force attacks and as such this scheme can be considered secure where all previously mentioned conditions hold.
Secret-Sharing
One party intends on creating a derived secret to share with another party. Using their private-key and the other party's public-key they are able to derive a new "shared" key using the ECDH scheme. This dervied key is used alongside a unique IV (i.e. a nonce) in an AES cipher to encrypt the secret. Output of this can be shared across an insecure network with the other party to be stored.
As long as no other party acquires access to either party's private-key or the "shared" dervied key alongside the dervied secret than no other party can acquire access to the contents of the original secret. Either party can reconstruct the dervied key and therefore decrypt the dervied secret as long as they retain knowledge of the other party's public-key (and their own private-key) and the nonce. Currently the most effective attacks against this scheme are brute-force attacks and as such this scheme can be considered secure where all previously mentioned conditions hold.
How it works programmatically
Installation
Begin with installing this module into your project.
npm i cipher-share
Following installation, you can reference the CipherShare
class in your project as demonstrated below. Note that referencing will not work unless you are using ESM6 (available for up to date versions of Node JS). If any errors occur you can refer to this official documentation for more assistance.
import { CipherShare, SecretShare } from 'cipher-share'
Cipher-Sharing
Creating derived secret and key for sharing
When a new CipherShare
object is instantiated it will generate two values of importance. These two values are the equivalents to the dervied secret and key as established in the previous conceptual explanation. It is these two derived secrets that need to be shared securely with trusted parties (depending on the user's intention for CipherShare).
const encryptMessage = (message) => {
// Necessary for plaintext to be in form of a Buffer
let plaintext = Buffer.from(message)
// Perform encryption/derivation
let ciphertext = new CipherShare(plaintext)
// Information needed to recreate "derived key"
let shareKey = ciphertext.getShareKey()
let nonce = ciphertext.getNonce()
let authTag = ciphertext.getAuthTag()
console.log(shareKey, nonce, authTag)
// Information equivalent to "derived secret"
let secret = ciphertext.getSecret()
console.log(secret)
}
It is up to each individual project to determine how they decide to transfer these secrets, and what precautions they will take to guarantee safety of these secrets.
Recovering secret from derived secret and key
In the case that the user has lost access to their secret and desires to perform recovery, a similar process as to the derivation performed above is conducted. Assuming that the user has received the derived secret and the information required to regenerate the dervied key securely the following can be conducted.
const decryptMessage = (secret, shareKey, nonce, authTag) => {
// Decrypt derived secret
let plaintext = CipherShare(secret, shareKey, nonce, authTag)
// Information equivalent to original message
let message = plaintext.getSecret().toString()
console.log(message)
}
Secret-Sharing
Creating derived secret and key for sharing
When a new SecretShare
object is instantiated it will generate two values of importance. These two values are the equivalents to the dervied secret and key as established in the previous conceptual explanation. It is these two derived secrets that need to be shared securely with trusted parties (depending on the user's intention for CipherShare).
const encryptMessage = (message, privateKey, publicKey, nonce) => {
// Necessary for plaintext to be in form of a Buffer
let plaintext = Buffer.from(message)
// Perform encryption/derivation
let ciphertext = SecretShare(plaintext, privateKey, publicKey, nonce)
// Information needed to recreate "derived key"
let nonce = ciphertext.getNonce()
let authTag = ciphertext.getAuthTag()
console.log(nonce, authTag)
// Information equivalent to "derived secret"
let secret = ciphertext.getSecret()
console.log(secret)
}
It is up to each individual project to determine how they decide to transfer these secrets, and what precautions they will take to guarantee safety of these secrets.
Recovering secret from derived secret and key
In the case that the user has lost access to their secret and desires to perform recovery, a similar process as to the derivation performed above is conducted. Assuming that the user has received the derived secret and the information required to regenerate the dervied key securely the following can be conducted.
const decryptMessage = (secret, privateKey, publicKey, nonce, authTag) => {
// Decrypt derived secret
let plaintext = new CipherShare(secret, privateKey, publicKey, nonce, authTag)
// Information equivalent to original message
let message = plaintext.getSecret().toString()
console.log(message)
}
Security properties
By using the AES-256-GMC cipher with a sufficiently secure key of 32-bytes in length and a sufficiently unique nonce of 12-bytes in length on a message for encryption, a sufficiently random string equal in length to the secret can be calculated. Therefore, the security of CipherShare is dependent on the uniqueness of SHA256 hashing algorithms and the security of the AES cipher being used. Assuming that the AES-256-GMC cipher is robust and that SHA256 is sufficiently unique when applied on different strings as currently indicated by NIST, then CipherShare has 256-bit security. This is considered sufficient for any confidential information.
Exercise caution when using this module or any other cryptographic module however. It is important to note that no guarantee can be made of the robustness of this module or any others. Any unintended consequences from using this module is the responsibility of the user of this module. This is true of all cryptography. Users can exercise caution by considering if use of this module is truly necessary in their program, or if the risk is greater than the reward of using it.