add file name support for encryptFile & decryptFile
also adds supporting methods l/e int32 byteArray2int & int2byteArray creates TextEncoder, TextDecoder objects in global module scope creates magic value METADATA_LENGTH of 1020 + 4 (4 bytes * 255 chars, 4 bytes for int32)
This commit is contained in:
parent
fa44bfe2eb
commit
4fcdcb65bd
78
crypto.js
78
crypto.js
@ -2,23 +2,37 @@ import init, * as ecies from "ecies-wasm";
|
||||
import { Key } from "./zkl-kds/key";
|
||||
|
||||
init();
|
||||
const td = new TextDecoder();
|
||||
const te = new TextEncoder();
|
||||
|
||||
const METADATA_LENGTH = 4 + 1020; // [byte length, 255 chars * 4 bytes]
|
||||
|
||||
/**
|
||||
* Encrypts given data for the recipient
|
||||
*
|
||||
* @param {Key} publicKey The public key of the recipient
|
||||
* @param {string} fileName Name of the file
|
||||
* @param {Uint8Array} data The plaintext data
|
||||
* @returns {Uint8Array} The ciphertext data
|
||||
* @throws {TypeError} If arguments are of incorrect types
|
||||
*/
|
||||
export function encryptFile(publicKey, data) {
|
||||
export function encryptFile(publicKey, fileName, data) {
|
||||
if (!(publicKey instanceof Key)) {
|
||||
throw new TypeError("publicKey must be an instance of Key");
|
||||
}
|
||||
if (!(data instanceof Uint8Array)) {
|
||||
throw new TypeError("data must be an instance of Uint8Array");
|
||||
}
|
||||
return encrypt(publicKey, data);
|
||||
if (!fileName)
|
||||
fileName = "Unknown file";
|
||||
|
||||
const _data = new Uint8Array(METADATA_LENGTH + data.length);
|
||||
const fnBytes = te.encode(fileName);
|
||||
_data.set(int2byteArray(fnBytes.length));
|
||||
_data.set(fnBytes, 4);
|
||||
_data.set(data, METADATA_LENGTH);
|
||||
|
||||
return encrypt(publicKey, _data);
|
||||
}
|
||||
|
||||
/**
|
||||
@ -26,7 +40,7 @@ export function encryptFile(publicKey, data) {
|
||||
*
|
||||
* @param {Key} privateKey The private key of the recipient
|
||||
* @param {Uint8Array} data The ciphertext data
|
||||
* @returns {Uint8Array} The plaintext data
|
||||
* @returns {{data: Uint8Array, fileName: string}} The plaintext data
|
||||
* @throws {TypeError} If arguments are of incorrect types
|
||||
*/
|
||||
export function decryptFile(privateKey, data) {
|
||||
@ -36,7 +50,17 @@ export function decryptFile(privateKey, data) {
|
||||
if (!(data instanceof Uint8Array)) {
|
||||
throw new TypeError("data must be an instance of Uint8Array");
|
||||
}
|
||||
return decrypt(privateKey, data);
|
||||
|
||||
const plaintext = decrypt(privateKey, data);
|
||||
const fnLength = byteArray2int(plaintext.slice(0, 4));
|
||||
const fnBytes = plaintext.slice(4, 4 + fnLength);
|
||||
const fdBytes = plaintext.slice(METADATA_LENGTH, plaintext.length);
|
||||
const fileName = td.decode(fnBytes);
|
||||
|
||||
return {
|
||||
data: fdBytes,
|
||||
fileName: fileName
|
||||
};
|
||||
}
|
||||
|
||||
/**
|
||||
@ -55,8 +79,7 @@ export function encryptString(publicKey, string) {
|
||||
throw new TypeError("string must be of type string");
|
||||
}
|
||||
|
||||
const encoder = new TextEncoder();
|
||||
const byteArray = encoder.encode(string);
|
||||
const byteArray = te.encode(string);
|
||||
const encryptedData = encrypt(publicKey, byteArray);
|
||||
return encryptedData.asHexString();
|
||||
}
|
||||
@ -84,8 +107,7 @@ export function decryptString(privateKey, string) {
|
||||
string.match(/.{1,2}/g).map((byte) => parseInt(byte, 16))
|
||||
);
|
||||
const decryptedData = decrypt(privateKey, byteArray);
|
||||
const decoder = new TextDecoder();
|
||||
return decoder.decode(decryptedData);
|
||||
return td.decode(decryptedData);
|
||||
}
|
||||
|
||||
function encrypt(publicKey, plaintext) {
|
||||
@ -108,6 +130,46 @@ function decrypt(privateKey, ciphertext) {
|
||||
return ecies.decrypt(privateKey.asByteArray, ciphertext);
|
||||
}
|
||||
|
||||
/**
|
||||
* Converts a 32-bit signed integer into an array of 4 bytes (little-endian).
|
||||
*
|
||||
* @param {number} int - The 32-bit signed integer to convert. Must be in the range of a signed 32-bit integer (-2^31 to 2^31-1).
|
||||
* @returns {number[]} An array of 4 bytes, where the least significant byte is the first element (little-endian).
|
||||
* @example
|
||||
* // Convert 305419896 (0x12345678) to bytes
|
||||
* int2byteArray(305419896); // [120, 86, 52, 18]
|
||||
*/
|
||||
function int2byteArray(int) {
|
||||
return [
|
||||
int & 0xff ? int & 0xff : 0,
|
||||
(int >> 8) & 0xff ? (int >> 8) & 0xff : 0,
|
||||
(int >> 16) & 0xff ? (int >> 16) & 0xff : 0,
|
||||
(int >> 24) & 0xff ? (int >> 24) & 0xff : 0
|
||||
]
|
||||
}
|
||||
|
||||
/**
|
||||
* Converts an array of 4 bytes (little-endian) into a 32-bit signed integer.
|
||||
*
|
||||
* @param {number[]} bytes - An array of 4 bytes where the least significant byte is the first element (little-endian).
|
||||
* @returns {number} The reconstructed 32-bit signed integer.
|
||||
* @example
|
||||
* // Convert [120, 86, 52, 18] back to an integer
|
||||
* byteArray2int([120, 86, 52, 18]); // 305419896 (0x12345678)
|
||||
*/
|
||||
function byteArray2int(bytes) {
|
||||
return (bytes[0]) |
|
||||
(bytes[1] << 8) |
|
||||
(bytes[2] << 16) |
|
||||
(bytes[3] << 24);
|
||||
}
|
||||
|
||||
function asByteArray() {
|
||||
return Uint8Array.from(
|
||||
this.match(/.{1,2}/g).map((byte) => parseInt(byte, 16))
|
||||
);
|
||||
}
|
||||
|
||||
function asHexString() {
|
||||
return this.reduce(
|
||||
(str, byte) => str + byte.toString(16).padStart(2, "0"),
|
||||
|
13
index.js
13
index.js
@ -7,9 +7,9 @@ import {
|
||||
} from "./crypto.js";
|
||||
|
||||
let keypair;
|
||||
const el_fileInput = document.querySelector("#fileInput");
|
||||
|
||||
document
|
||||
.getElementById("fileInput")
|
||||
el_fileInput
|
||||
.addEventListener("change", function (event) {
|
||||
const file = event.target.files[0];
|
||||
if (file) {
|
||||
@ -23,8 +23,9 @@ document
|
||||
return;
|
||||
}
|
||||
|
||||
const cipherFile = encryptFile(keypair.pkey, byteArray);
|
||||
console.log(cipherFile);
|
||||
console.log('File to be encrypted:', el_fileInput.files[0].name);
|
||||
const cipherFile = encryptFile(keypair.pkey, el_fileInput.files[0].name, byteArray);
|
||||
console.log('Ciphertext bytes:', cipherFile);
|
||||
|
||||
{
|
||||
const numPixels = cipherFile.length / 4;
|
||||
@ -47,7 +48,9 @@ document
|
||||
}
|
||||
|
||||
const plainFile = decryptFile(keypair.skey, cipherFile);
|
||||
console.log(plainFile);
|
||||
console.log("Decrypted raw data:", plainFile.data);
|
||||
console.log("Decrypted decoded:", (new TextDecoder()).decode(plainFile.data));
|
||||
console.log("Decrypted file name:", plainFile.fileName);
|
||||
};
|
||||
|
||||
reader.readAsArrayBuffer(file);
|
||||
|
70
pnpm-lock.yaml
generated
70
pnpm-lock.yaml
generated
@ -130,8 +130,8 @@ packages:
|
||||
'@types/json-schema@7.0.15':
|
||||
resolution: {integrity: sha512-5+fP8P8MFNC+AyZCDxrB2pkZFPGzqQWUzpSeuuVLvm8VMcorNYavBqoFcxK8bQz4Qsbn4oUEEem4wDLfcysGHA==}
|
||||
|
||||
'@types/node@22.5.1':
|
||||
resolution: {integrity: sha512-KkHsxej0j9IW1KKOOAA/XBA0z08UFSrRQHErzEfA3Vgq57eXIMYboIlHJuYIfd+lwCQjtKqUu3UnmKbtUc9yRw==}
|
||||
'@types/node@22.5.4':
|
||||
resolution: {integrity: sha512-FDuKUJQm/ju9fT/SeX/6+gBzoPzlVCzfzmGkwKvRHQVxi4BntVbyIwf6a4Xn62mrvndLiml6z/UBXIdEVjQLXg==}
|
||||
|
||||
'@webassemblyjs/ast@1.12.1':
|
||||
resolution: {integrity: sha512-EKfMUOPRRUTy5UII4qJDGPpqfwjOmZ5jeGFwid9mnoqIFK+e0vqoi1qH56JpmZSzEL53jKnNzScdmftJyG5xWg==}
|
||||
@ -262,8 +262,8 @@ packages:
|
||||
buffer-from@1.1.2:
|
||||
resolution: {integrity: sha512-E+XQCRwSbaaiChtv6k6Dwgc+bx+Bs6vuKJHHl5kox/BaKbhiXzqQOwK4cO22yElGp2OCmjwVhT3HmxgyPGnJfQ==}
|
||||
|
||||
caniuse-lite@1.0.30001653:
|
||||
resolution: {integrity: sha512-XGWQVB8wFQ2+9NZwZ10GxTYC5hk0Fa+q8cSkr0tgvMhYhMHP/QC+WTgrePMDBWiWc/pV+1ik82Al20XOK25Gcw==}
|
||||
caniuse-lite@1.0.30001660:
|
||||
resolution: {integrity: sha512-GacvNTTuATm26qC74pt+ad1fW15mlQ/zuTzzY1ZoIzECTP8HURDfF43kNxPgf7H1jmelCBQTTbBNxdSXOA7Bqg==}
|
||||
|
||||
chalk@2.4.2:
|
||||
resolution: {integrity: sha512-Mti+f9lpJNcwF4tWV8/OrTTtF1gZi+f8FqlyAdouralcFWFQWF2+NgCHShjkCb+IFBLq9buZwE1xckQU4peSuQ==}
|
||||
@ -303,8 +303,8 @@ packages:
|
||||
resolution: {integrity: sha512-iRDPJKUPVEND7dHPO8rkbOnPpyDygcDFtWjpeWNCgy8WP2rXcxXL8TskReQl6OrB2G7+UJrags1q15Fudc7G6w==}
|
||||
engines: {node: '>= 8'}
|
||||
|
||||
debug@4.3.6:
|
||||
resolution: {integrity: sha512-O/09Bd4Z1fBrU4VzkhFqVgpPzaGbw6Sm9FEkBT1A/YBXQFGuuSxa1dN2nxgxS34JmKXqYx8CZAwEVoJFImUXIg==}
|
||||
debug@4.3.7:
|
||||
resolution: {integrity: sha512-Er2nc/H7RrMXZBFCEim6TCmMk02Z8vLC2Rbi1KEBggpo0fS6l0S1nnapwmIi3yW/+GOJap1Krg4w0Hg80oCqgQ==}
|
||||
engines: {node: '>=6.0'}
|
||||
peerDependencies:
|
||||
supports-color: '*'
|
||||
@ -315,8 +315,8 @@ packages:
|
||||
ecies-wasm@0.2.0:
|
||||
resolution: {integrity: sha512-T0wkoz2iOu3IN0wugO3gzCn3ADAafA8FRDQUWWST31InPWlUSvH5JH5akegAZw48or1kwQsWE5jGiNqh5woxhg==}
|
||||
|
||||
electron-to-chromium@1.5.13:
|
||||
resolution: {integrity: sha512-lbBcvtIJ4J6sS4tb5TLp1b4LyfCdMkwStzXPyAgVgTRAsep4bvrAGaBOP7ZJtQMNJpSQ9SqG4brWOroNaQtm7Q==}
|
||||
electron-to-chromium@1.5.18:
|
||||
resolution: {integrity: sha512-1OfuVACu+zKlmjsNdcJuVQuVE61sZOLbNM4JAQ1Rvh6EOj0/EUKhMJjRH73InPlXSh8HIJk1cVZ8pyOV/FMdUQ==}
|
||||
|
||||
enhanced-resolve@5.17.1:
|
||||
resolution: {integrity: sha512-LMHl3dXhTcfv8gM4kEzIUeTQ+7fpdA0l2tUf34BddXPkz2A5xJ5L/Pchd5BL6rdccM9QGvu0sWZzK1Z1t4wwyg==}
|
||||
@ -330,8 +330,8 @@ packages:
|
||||
es-module-lexer@1.5.4:
|
||||
resolution: {integrity: sha512-MVNK56NiMrOwitFB7cqDwq0CQutbw+0BvLshJSse0MUNU+y1FC3bUS/AQg7oUng+/wKrrki7JfmwtVHkVfPLlw==}
|
||||
|
||||
escalade@3.1.2:
|
||||
resolution: {integrity: sha512-ErCHMCae19vR8vQGe50xIsVomy19rg6gFu3+r3jkEO46suLMWBksvVyoGgQV+jOfl84ZSOSlmv6Gxa89PmTGmA==}
|
||||
escalade@3.2.0:
|
||||
resolution: {integrity: sha512-WUj2qlxaQtO4g6Pq5c29GTcWGDyd8itL8zTlipgECz3JesAiiOKotd8JU6otB3PACgG6xkJUyVhboMS+bje/jA==}
|
||||
engines: {node: '>=6'}
|
||||
|
||||
escape-string-regexp@1.0.5:
|
||||
@ -496,8 +496,8 @@ packages:
|
||||
resolution: {integrity: sha512-ZDY+bPm5zTTF+YpCrAU9nK0UgICYPT0QtT1NZWFv4s++TNkcgVaT0g6+4R2uI4MjQjzysHB1zxuWL50hzaeXiw==}
|
||||
engines: {node: '>= 0.6'}
|
||||
|
||||
ms@2.1.2:
|
||||
resolution: {integrity: sha512-sGkPx+VjMtmA6MX27oA4FBFELFCZZ4S4XqeGOXCv68tT+jb3vk/RyaKWP0PTKyWtmLSM0b+adUTEvbs1PEaH2w==}
|
||||
ms@2.1.3:
|
||||
resolution: {integrity: sha512-6FlzubTLZG3J2a/NVCAleEhjzq5oxgHyaCU9yYXvcLsvoVaHJq/s5xXI6/XXP6tz7R9xAOtHnSO/tXtF3WRTlA==}
|
||||
|
||||
neo-async@2.6.2:
|
||||
resolution: {integrity: sha512-Yd3UES5mWCSqR+qNT93S3UoYUkqAZ9lLg8a7g9rimsWmYGK8cVToA4/sF3RrshdyV3sAGMXVUmpMYOw+dLpOuw==}
|
||||
@ -540,8 +540,8 @@ packages:
|
||||
path-parse@1.0.7:
|
||||
resolution: {integrity: sha512-LDJzPVEEEPR+y48z93A0Ed0yXb8pAByGWo/k5YYdYgpY2/2EsOsksJrq7lOHxryrVOn1ejG6oAp8ahvOIQD8sw==}
|
||||
|
||||
picocolors@1.0.1:
|
||||
resolution: {integrity: sha512-anP1Z8qwhkbmu7MFP5iTt+wQKXgwzf7zTyGlcdzabySa9vd0Xt392U0rVmz9poOaBj0uHJKyyo9/upk0HrEQew==}
|
||||
picocolors@1.1.0:
|
||||
resolution: {integrity: sha512-TQ92mBOW0l3LeMeyLV6mzy/kWr8lkd/hp3mTg7wYK7zJhuBStmGMBG0BdeDZS/dZx1IukaX6Bk11zcln25o1Aw==}
|
||||
|
||||
pkg-dir@4.2.0:
|
||||
resolution: {integrity: sha512-HRDzbaKjC+AOWVXxAU/x54COGeIv9eb+6CkDSQoNTt4XyWoIJvuPsXizxu/Fr23EiekbtZwmh1IcIG/l/a10GQ==}
|
||||
@ -647,8 +647,8 @@ packages:
|
||||
uglify-js:
|
||||
optional: true
|
||||
|
||||
terser@5.31.6:
|
||||
resolution: {integrity: sha512-PQ4DAriWzKj+qgehQ7LK5bQqCFNMmlhjR2PFFLuqGCpuCAauxemVBWwWOxo3UIwWQx8+Pr61Df++r76wDmkQBg==}
|
||||
terser@5.32.0:
|
||||
resolution: {integrity: sha512-v3Gtw3IzpBJ0ugkxEX8U0W6+TnPKRRCWGh1jC/iM/e3Ki5+qvO1L1EAZ56bZasc64aXHwRHNIQEzm6//i5cemQ==}
|
||||
engines: {node: '>=10'}
|
||||
hasBin: true
|
||||
|
||||
@ -732,7 +732,7 @@ snapshots:
|
||||
'@babel/code-frame@7.24.7':
|
||||
dependencies:
|
||||
'@babel/highlight': 7.24.7
|
||||
picocolors: 1.0.1
|
||||
picocolors: 1.1.0
|
||||
|
||||
'@babel/compat-data@7.25.4': {}
|
||||
|
||||
@ -749,7 +749,7 @@ snapshots:
|
||||
'@babel/traverse': 7.25.6
|
||||
'@babel/types': 7.25.6
|
||||
convert-source-map: 2.0.0
|
||||
debug: 4.3.6
|
||||
debug: 4.3.7
|
||||
gensync: 1.0.0-beta.2
|
||||
json5: 2.2.3
|
||||
semver: 6.3.1
|
||||
@ -811,7 +811,7 @@ snapshots:
|
||||
'@babel/helper-validator-identifier': 7.24.7
|
||||
chalk: 2.4.2
|
||||
js-tokens: 4.0.0
|
||||
picocolors: 1.0.1
|
||||
picocolors: 1.1.0
|
||||
|
||||
'@babel/parser@7.25.6':
|
||||
dependencies:
|
||||
@ -830,7 +830,7 @@ snapshots:
|
||||
'@babel/parser': 7.25.6
|
||||
'@babel/template': 7.25.0
|
||||
'@babel/types': 7.25.6
|
||||
debug: 4.3.6
|
||||
debug: 4.3.7
|
||||
globals: 11.12.0
|
||||
transitivePeerDependencies:
|
||||
- supports-color
|
||||
@ -869,7 +869,7 @@ snapshots:
|
||||
|
||||
'@types/json-schema@7.0.15': {}
|
||||
|
||||
'@types/node@22.5.1':
|
||||
'@types/node@22.5.4':
|
||||
dependencies:
|
||||
undici-types: 6.19.8
|
||||
|
||||
@ -1014,14 +1014,14 @@ snapshots:
|
||||
|
||||
browserslist@4.23.3:
|
||||
dependencies:
|
||||
caniuse-lite: 1.0.30001653
|
||||
electron-to-chromium: 1.5.13
|
||||
caniuse-lite: 1.0.30001660
|
||||
electron-to-chromium: 1.5.18
|
||||
node-releases: 2.0.18
|
||||
update-browserslist-db: 1.1.0(browserslist@4.23.3)
|
||||
|
||||
buffer-from@1.1.2: {}
|
||||
|
||||
caniuse-lite@1.0.30001653: {}
|
||||
caniuse-lite@1.0.30001660: {}
|
||||
|
||||
chalk@2.4.2:
|
||||
dependencies:
|
||||
@ -1059,13 +1059,13 @@ snapshots:
|
||||
shebang-command: 2.0.0
|
||||
which: 2.0.2
|
||||
|
||||
debug@4.3.6:
|
||||
debug@4.3.7:
|
||||
dependencies:
|
||||
ms: 2.1.2
|
||||
ms: 2.1.3
|
||||
|
||||
ecies-wasm@0.2.0: {}
|
||||
|
||||
electron-to-chromium@1.5.13: {}
|
||||
electron-to-chromium@1.5.18: {}
|
||||
|
||||
enhanced-resolve@5.17.1:
|
||||
dependencies:
|
||||
@ -1076,7 +1076,7 @@ snapshots:
|
||||
|
||||
es-module-lexer@1.5.4: {}
|
||||
|
||||
escalade@3.1.2: {}
|
||||
escalade@3.2.0: {}
|
||||
|
||||
escape-string-regexp@1.0.5: {}
|
||||
|
||||
@ -1159,7 +1159,7 @@ snapshots:
|
||||
|
||||
jest-worker@27.5.1:
|
||||
dependencies:
|
||||
'@types/node': 22.5.1
|
||||
'@types/node': 22.5.4
|
||||
merge-stream: 2.0.0
|
||||
supports-color: 8.1.1
|
||||
|
||||
@ -1199,7 +1199,7 @@ snapshots:
|
||||
dependencies:
|
||||
mime-db: 1.52.0
|
||||
|
||||
ms@2.1.2: {}
|
||||
ms@2.1.3: {}
|
||||
|
||||
neo-async@2.6.2: {}
|
||||
|
||||
@ -1231,7 +1231,7 @@ snapshots:
|
||||
|
||||
path-parse@1.0.7: {}
|
||||
|
||||
picocolors@1.0.1: {}
|
||||
picocolors@1.1.0: {}
|
||||
|
||||
pkg-dir@4.2.0:
|
||||
dependencies:
|
||||
@ -1321,10 +1321,10 @@ snapshots:
|
||||
jest-worker: 27.5.1
|
||||
schema-utils: 3.3.0
|
||||
serialize-javascript: 6.0.2
|
||||
terser: 5.31.6
|
||||
terser: 5.32.0
|
||||
webpack: 5.94.0(webpack-cli@5.1.4)
|
||||
|
||||
terser@5.31.6:
|
||||
terser@5.32.0:
|
||||
dependencies:
|
||||
'@jridgewell/source-map': 0.3.6
|
||||
acorn: 8.12.1
|
||||
@ -1338,8 +1338,8 @@ snapshots:
|
||||
update-browserslist-db@1.1.0(browserslist@4.23.3):
|
||||
dependencies:
|
||||
browserslist: 4.23.3
|
||||
escalade: 3.1.2
|
||||
picocolors: 1.0.1
|
||||
escalade: 3.2.0
|
||||
picocolors: 1.1.0
|
||||
|
||||
uri-js@4.4.1:
|
||||
dependencies:
|
||||
|
@ -7,7 +7,7 @@ module.exports = {
|
||||
path: path.resolve(__dirname, "dist"),
|
||||
},
|
||||
experiments: {
|
||||
asyncWebAssembly: true,
|
||||
syncWebAssembly: true,
|
||||
},
|
||||
mode: "development",
|
||||
};
|
||||
|
Loading…
x
Reference in New Issue
Block a user