Last active
November 22, 2021 16:15
-
-
Save SuaYoo/07638d095852f0ce7777cb74ccee9c7b to your computer and use it in GitHub Desktop.
Encrypt and decrypt JSON files for Web3 Storage (requires Node.js >=15.7)
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
// node >=15.7.0 required for Blob usage | |
const { Blob } = require('buffer'); | |
const crypto = require('crypto'); | |
const { Web3Storage } = require('web3.storage'); | |
// Secret env variables | |
// | |
// Grab token from https://web3.storage/tokens/ | |
const WEB3_STORAGE_API_KEY = 'my_api_key'; | |
// Encryption secret must have exact length of 32 | |
// You'll want to save the output of | |
// `crypto.randomBytes(16).toString('hex')` | |
// somewhere instead of re-generating it each time | |
// so that you can decrypt your files later. | |
const ENCRYPTION_SECRET = crypto.randomBytes(16).toString('hex'); | |
// Make storage client | |
const storageClient = new Web3Storage({ | |
token: WEB3_STORAGE_API_KEY, | |
}); | |
// Encryption options | |
// Encrypt/decrypt functions based on | |
// https://attacomsian.com/blog/nodejs-encrypt-decrypt-data | |
const algorithm = 'aes-256-ctr'; | |
// Encrypt some data | |
// Example: | |
// encrypt(JSON.stringify({ a: 'b' })) | |
function encrypt(text/*: string*/) { | |
const iv = crypto.randomBytes(16); | |
const cipher = crypto.createCipheriv(algorithm, ENCRYPTION_SECRET, iv); | |
const encrypted = Buffer.concat([cipher.update(text), cipher.final()]); | |
return { | |
iv: iv.toString('hex'), | |
content: encrypted.toString('hex'), | |
}; | |
} | |
// Decrypt file blob (NOTE only works with node >=15.7.0) | |
// Example: | |
// decrypt(await web3Response.files()[0]) | |
async function decrypt(file/*: Blob*/) { | |
const hash = JSON.parse(await file.text()); | |
const decipher = crypto.createDecipheriv( | |
algorithm, | |
ENCRYPTION_SECRET, | |
Buffer.from(hash.iv, 'hex') | |
); | |
const decrypted = Buffer.concat([ | |
decipher.update(Buffer.from(hash.content, 'hex')), | |
decipher.final(), | |
]); | |
return decrypted.toString(); | |
} | |
// Store object in Web3 Storage as an encrypted JSON file | |
async function storeEncryptedData(data, fileName) { | |
const encrypted = encrypt(JSON.stringify(data)); | |
const file = new Blob([JSON.stringify(encrypted)], { | |
type: 'application/json', | |
}); | |
file.name = `${fileName}.json.enc`; | |
const cid = await storageClient.put([file]); | |
console.log('stored files with cid:', cid); | |
return cid; | |
} | |
// Retrieve encrypted JSON file from Web3 Storage and decrypt | |
async function retrieveDecryptedData(cid) { | |
const res = await storageClient.get(cid); | |
console.log(`Got a response! [${res.status}] ${res.statusText}`); | |
if (!res.ok) { | |
throw new Error(`failed to get ${cid} - [${res.status}] ${res.statusText}`); | |
} | |
// unpack File objects from the response | |
const files = await res.files(); | |
return Promise.all(files.map(decrypt)); | |
} | |
async function demo() { | |
// Store some data | |
const cid = await storeEncryptedData({ text: 'secret text' }, 'test_file'); | |
// Retrieve same data | |
const jsonArr = await retrieveDecryptedData(cid); | |
console.log('decrypted results:', jsonArr); | |
} | |
// Demo: | |
demo(); |
Is there a way to return the results without encrypting/decrypting? I'm able to store the file into web3.storage, but when I retrieve it without using the encrypting functionality it returns the file name, last modified date, and cid. How do I receive the content of the uploaded file?
@coding-island-js you could replace with encrypted
in line 64 with JSON.stringify(data)
and replace decrypt
in line 90 with async (file) => JSON.parse(await file.text())
. Try this:
async function storeEncryptedData(data, fileName) {
// const encrypted = encrypt(JSON.stringify(data));
const file = new Blob([JSON.stringify(data)], {
type: 'application/json',
});
file.name = `${fileName}.json.enc`;
const cid = await storageClient.put([file]);
console.log('stored files with cid:', cid);
return cid;
}
// Retrieve encrypted JSON file from Web3 Storage and decrypt
async function retrieveDecryptedData(cid) {
const res = await storageClient.get(cid);
console.log(`Got a response! [${res.status}] ${res.statusText}`);
if (!res.ok) {
throw new Error(`failed to get ${cid} - [${res.status}] ${res.statusText}`);
}
// unpack File objects from the response
const files = await res.files();
// return Promise.all(files.map(decrypt));
const parseFile = async (file) => JSON.parse(await file.text())
return Promise.all(files.map(parseFile));
}
I haven't tested actually tested this out, so it may need some more tweaking.
Awesome. thanks for the code! I did something similar to what you wrote above, but instead did a new File instead of Blob and it works. Again thanks for taking the time to reply back and provide a solution.
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment
To run the demo, replace
WEB3_STORAGE_API_KEY
with your actual Web3 Storage token and then:You should see something like:
You'll want to pass
WEB3_STORAGE_API_KEY
andENCRYPTION_SECRET
as environment variables during actual usage.Note:
Blob
support was added in node v15.7.0, v14.18.0, demo script only tested with v16.7.0Full write-up here: https://write.as/build-it-nice/encrypting-json-files-for-decentralized-storage