UNPKG

secret-manager-crypto-utils

Version:

Small set of crypto utilities - ed25519 signatures, el-gamal encrpytion with AES-256-CBC, diffie-hellman and elGamal Secret sharing.

802 lines (692 loc) 27.4 kB
// Generated by CoffeeScript 2.7.0 //########################################################### var ORDER, encAlgo, hashToScalar, mod; import * as ed255 from "@noble/ed25519"; import * as tbut from "thingy-byte-utils"; import crypto from "crypto"; //########################################################### encAlgo = 'aes-256-cbc'; //########################################################### ORDER = BigInt(2) ** BigInt(252) + BigInt('27742317777372353535851937790883648493'); //########################################################### hashToScalar = function(hash) { var bigInt, relevant; relevant = hash.slice(0, 32); relevant[0] &= 248; relevant[31] &= 127; relevant[31] |= 64; bigInt = tbut.bytesToBigInt(relevant); return mod(bigInt); }; mod = function(a, b = ORDER) { var result; result = a % b; if (result >= 0n) { return result; } else { return result + b; } }; //########################################################### //region exposedStuff //########################################################### //region shas //########################################################### // Hex Version export var sha256 = function(content) { var hasher; hasher = crypto.createHash("sha256"); hasher.update(content); return hasher.digest("hex"); }; export var sha512 = function(content) { var hasher; hasher = crypto.createHash("sha512"); hasher.update(content); return hasher.digest("hex"); }; export var sha256Hex = sha256; export var sha512Hex = sha512; //########################################################### // Bytes Version export var sha256Bytes = function(content) { var hasher; hasher = crypto.createHash("sha256"); hasher.update(content); return hasher.digest(); }; export var sha512Bytes = function(content) { var hasher; hasher = crypto.createHash("sha512"); hasher.update(content); return hasher.digest(); }; //endregion //########################################################### //region keys //########################################################### // Hex Version export var createKeyPair = async function() { var publicKeyBytes, publicKeyHex, secretKeyBytes, secretKeyHex; secretKeyBytes = ed255.utils.randomPrivateKey(); publicKeyBytes = (await ed255.getPublicKeyAsync(secretKeyBytes)); secretKeyHex = tbut.bytesToHex(secretKeyBytes); publicKeyHex = tbut.bytesToHex(publicKeyBytes); return {secretKeyHex, publicKeyHex}; }; export var createSymKey = function() { var keyAndIV; keyAndIV = crypto.randomBytes(48); return tbut.bytesToHex(keyAndIV); }; export var createPublicKey = async function(secretKeyHex) { var publicKeyBytes; publicKeyBytes = (await ed255.getPublicKeyAsync(tbut.hexToBytes(secretKeyHex))); return tbut.bytesToHex(publicKeyBytes); }; export var createKeyPairHex = createKeyPair; export var createSymKeyHex = createSymKey; export var createPublicKeyHex = createPublicKey; //########################################################### // Byte Version export var createKeyPairBytes = async function() { var publicKeyBytes, secretKeyBytes; secretKeyBytes = ed255.utils.randomPrivateKey(); publicKeyBytes = (await ed255.getPublicKeyAsync(secretKeyBytes)); return {secretKeyBytes, publicKeyBytes}; }; export var createSymKeyBytes = function() { return new Uint8Array(crypto.randomBytes(48)); }; export var createPublicKeyBytes = async function(secretKeyBytes) { return (await ed255.getPublicKeyAsync(secretKeyBytes)); }; //endregion //########################################################### //region signatures //########################################################### // Hex Version export var createSignature = async function(content, signingKeyHex) { var contentBytes, signature, signingKeyBytes; contentBytes = tbut.utf8ToBytes(content); signingKeyBytes = tbut.hexToBytes(signingKeyHex); signature = (await ed255.signAsync(contentBytes, signingKeyBytes)); return tbut.bytesToHex(signature); }; export var verify = async function(sigHex, keyHex, content) { var contentBytes, keyBytes, sigBytes; sigBytes = tbut.hexToBytes(sigHex); keyBytes = tbut.hexToBytes(keyHex); contentBytes = tbut.utf8ToBytes(content); return (await ed255.verifyAsync(sigBytes, contentBytes, keyBytes)); }; export var createSignatureHex = createSignature; export var verifyHex = verify; //########################################################### // Byte Version export var createSignatureBytes = async function(content, signingKeyBytes) { var contentBytes; contentBytes = tbut.utf8ToBytes(content); return (await ed255.signAsync(contentBytes, signingKeyBytes)); }; export var verifyBytes = async function(sigBytes, keyBytes, content) { var contentBytes; contentBytes = tbut.utf8ToBytes(content); return (await ed255.verifyAsync(sigBytes, contentBytes, keyBytes)); }; //endregion //########################################################### //region symmetric encryption //########################################################### // Hex Version export var symmetricEncrypt = function(content, keyHex) { var aesKeyBuffer, aesKeyHex, cipher, gibbrish, ivBuffer, ivHex, saltedContent; ivHex = keyHex.substring(0, 32); ivBuffer = Buffer.from(ivHex, "hex"); aesKeyHex = keyHex.substring(32, 96); aesKeyBuffer = Buffer.from(aesKeyHex, "hex"); // console.log "- - ivHex: " // console.log ivHex // console.log ivHex.length // console.log "- - aesKeyHex: " // console.log aesKeyHex // console.log aesKeyHex.length saltedContent = saltContent(content); cipher = crypto.createCipheriv(encAlgo, aesKeyBuffer, ivBuffer); gibbrish = cipher.update(saltedContent, null, 'hex'); gibbrish += cipher.final('hex'); return gibbrish; }; export var symmetricDecrypt = function(gibbrishHex, keyHex) { var aesKeyBuffer, aesKeyHex, allSaltedContent, b, decipher, finalContent, i, ivBuffer, ivHex, j, k, len, len1, saltedContent; ivHex = keyHex.substring(0, 32); ivBuffer = Buffer.from(ivHex, "hex"); aesKeyHex = keyHex.substring(32, 96); aesKeyBuffer = Buffer.from(aesKeyHex, "hex"); // console.log "- - ivHex: " // console.log ivHex // console.log ivHex.length // console.log "- - aesKeyHex: " // console.log aesKeyHex // console.log aesKeyHex.length decipher = crypto.createDecipheriv(encAlgo, aesKeyBuffer, ivBuffer); saltedContent = decipher.update(gibbrishHex, 'hex'); finalContent = decipher.final(); if (finalContent.length === 0) { return unsaltContent(saltedContent); } allSaltedContent = new Uint8Array(saltedContent.length + finalContent.length); for (i = j = 0, len = saltContent.length; j < len; i = ++j) { b = saltContent[i]; allSaltedContent[i] = b; } for (i = k = 0, len1 = finalContent.length; k < len1; i = ++k) { b = finalContent[i]; allSaltedContent[saltedContent.length + i] = b; } return unsaltContent(allSaltedContent); }; export var symmetricEncryptHex = symmetricEncrypt; export var symmetricDecryptHex = symmetricDecrypt; //########################################################### // Byte Version export var symmetricEncryptBytes = function(content, keyBytes) { var aesKeyBuffer, allGibbrish, b, cipher, gibbrish, gibbrishFinal, i, ivBuffer, j, k, len, len1, saltedContent; ivBuffer = Buffer.from(keyBytes.buffer, 0, 16); aesKeyBuffer = Buffer.from(keyBytes.buffer, 16, 32); saltedContent = saltContent(content); cipher = crypto.createCipheriv(encAlgo, aesKeyBuffer, ivBuffer); gibbrish = cipher.update(saltedContent); gibbrishFinal = cipher.final(); if (gibbrishFinal.length === 0) { return new Uint8Array(gibbrish); } allGibbrish = new Uint8Array(gibbrishFinal.length + gibbrish.length); for (i = j = 0, len = gibbrish.length; j < len; i = ++j) { b = gibbrish[i]; allGibbrish[i] = b; } for (i = k = 0, len1 = gibbrishFinal.length; k < len1; i = ++k) { b = gibbrishFinal[i]; allGibbrish[gibbrish.length + i] = b; } return allGibbrish; }; export var symmetricDecryptBytes = function(gibbrishBytes, keyBytes) { var aesKeyBuffer, allSaltedContent, b, decipher, finalContent, i, ivBuffer, j, k, len, len1, saltedContent; ivBuffer = Buffer.from(keyBytes.buffer, 0, 16); aesKeyBuffer = Buffer.from(keyBytes.buffer, 16, 32); decipher = crypto.createDecipheriv(encAlgo, aesKeyBuffer, ivBuffer); saltedContent = decipher.update(gibbrishBytes); finalContent = decipher.final(); if (finalContent.length === 0) { return unsaltContent(saltedContent); } allSaltedContent = new Uint8Array(saltedContent.length + finalContent.length); for (i = j = 0, len = saltContent.length; j < len; i = ++j) { b = saltContent[i]; allSaltedContent[i] = b; } for (i = k = 0, len1 = finalContent.length; k < len1; i = ++k) { b = finalContent[i]; allSaltedContent[saltedContent.length + i] = b; } return unsaltContent(allSaltedContent); }; //endregion //########################################################### //region asymmetric encryption //########################################################### // Hex Version export var asymmetricEncrypt = async function(content, publicKeyHex) { var ABytes, B, encryptedContentHex, gibbrishHex, lB, lBigInt, nBytes, referencePointHex, symkeyHex; // a = Secret Key of target user // k = sha512(a) -> hashToScalar (scalar for multiplication) // G = basePoint // B = kG = Public Key B = ed255.ExtendedPoint.fromHex(publicKeyHex); // n = new one-time secret (generated forgotten about) // l = sha512(n) -> hashToScalar (scalar for multiplication) // A = lG = one time public key = reference point // lB = lkG = shared secret // key = sha512(lB) // X = symmetricEncrypt(content, key) // {A,X} = data for targt user // n = one-time secret -> l nBytes = ed255.utils.randomPrivateKey(); lBigInt = hashToScalar(sha512Bytes(nBytes)); // A reference Point ABytes = (await ed255.getPublicKeyAsync(nBytes)); // lB = lkG = shared Secret lB = B.multiply(lBigInt); // encrypt with symmetricEncryptHex symkeyHex = sha512Hex(lB.toRawBytes()); gibbrishHex = symmetricEncryptHex(content, symkeyHex); referencePointHex = tbut.bytesToHex(ABytes); encryptedContentHex = gibbrishHex; return {referencePointHex, encryptedContentHex}; }; export var asymmetricDecrypt = function(secrets, secretKeyHex) { var A, AHex, aBytes, content, gibbrishHex, kA, kBigInt, symkeyHex; AHex = secrets.referencePointHex || secrets.referencePoint; gibbrishHex = secrets.encryptedContentHex || secrets.encryptedContent; if ((AHex == null) || (gibbrishHex == null)) { throw new Error("Invalid secrets Object!"); } // a = Secret Key // k = sha512(a) -> hashToScalar // G = basePoint // B = kG = Public Key aBytes = tbut.hexToBytes(secretKeyHex); kBigInt = hashToScalar(sha512Bytes(aBytes)); // {A,X} = secrets // A = lG = one time public reference point // klG = lB = kA = shared secret // key = sha512(kAHex) // content = symmetricDecrypt(X, key) A = ed255.ExtendedPoint.fromHex(AHex); kA = A.multiply(kBigInt); symkeyHex = sha512Hex(kA.toRawBytes()); content = symmetricDecryptHex(gibbrishHex, symkeyHex); return content; }; export var asymmetricEncryptHex = asymmetricEncrypt; export var asymmetricDecryptHex = asymmetricDecrypt; //########################################################### // Byte Version export var asymmetricEncryptBytes = async function(content, publicKeyBytes) { var ABytes, B, encryptedContentBytes, gibbrishBytes, lB, lBigInt, nBytes, publicKeyHex, referencePointBytes, symkeyBytes; // a = Secret Key of target user // k = sha512(a) -> hashToScalar (scalar for multiplication) // G = basePoint // B = kG = Public Key publicKeyHex = tbut.bytesToHex(publicKeyBytes); B = ed255.ExtendedPoint.fromHex(publicKeyHex); // n = new one-time secret (generated forgotten about) // l = sha512(n) -> hashToScalar (scalar for multiplication) // A = lG = one time public key = reference point // lB = lkG = shared secret // key = sha512(lB) // X = symmetricEncrypt(content, key) // {A,X} = data for targt user // n = one-time secret -> l nBytes = ed255.utils.randomPrivateKey(); lBigInt = hashToScalar(sha512Bytes(nBytes)); // A reference Point ABytes = (await ed255.getPublicKeyAsync(nBytes)); // lB = lkG = shared Secret lB = B.multiply(lBigInt); symkeyBytes = sha512Bytes(lB.toRawBytes()); gibbrishBytes = symmetricEncryptBytes(content, symkeyBytes); referencePointBytes = ABytes; encryptedContentBytes = gibbrishBytes; return {referencePointBytes, encryptedContentBytes}; }; export var asymmetricDecryptBytes = function(secrets, secretKeyBytes) { var A, ABytes, AHex, content, gibbrishBytes, kA, kBigInt, symkeyBytes; ABytes = secrets.referencePointBytes || secrets.referencePoint; gibbrishBytes = secrets.encryptedContentBytes || secrets.encryptedContent; if ((ABytes == null) || (gibbrishBytes == null)) { throw new Error("Invalid secrets Object!"); } // a = Secret Key // k = sha512(a) -> hashToScalar // G = basePoint // B = kG = Public Key kBigInt = hashToScalar(sha512Bytes(secretKeyBytes)); // {A,X} = secrets // A = lG = one time public reference point // klG = lB = kA = shared secret // key = sha512(kAHex) // content = symmetricDecrypt(X, key) AHex = tbut.bytesToHex(ABytes); A = ed255.ExtendedPoint.fromHex(AHex); kA = A.multiply(kBigInt); symkeyBytes = sha512Bytes(kA.toRawBytes()); content = symmetricDecryptBytes(gibbrishBytes, symkeyBytes); return content; }; //endregion //########################################################### //region deffieHellman/ElGamal secrets //########################################################### // Hex Versions //########################################################### export var diffieHellmanSecretHash = function(secretKeyHex, publicKeyHex, contextString = "") { var B, aBytes, b, cBytes, i, j, k, kB, kBBytes, kBigInt, len, len1, seedBytes, sharedSecretHex; // a = our SecretKey // k = sha512(a) -> hashToScalar (scalar for multiplication) // B = lG = target User Public Key // kB = klG = shared Secret aBytes = tbut.hexToBytes(secretKeyHex); kBigInt = hashToScalar(sha512Bytes(aBytes)); B = ed255.ExtendedPoint.fromHex(publicKeyHex); // A reference Point kB = B.multiply(kBigInt); kBBytes = kB.toRawBytes(); cBytes = tbut.utf8ToBytes(contextString); seedBytes = new Uint8Array(kBBytes.length + cBytes.length); for (i = j = 0, len = kBBytes.length; j < len; i = ++j) { b = kBBytes[i]; seedBytes[i] = b; } for (i = k = 0, len1 = cBytes.length; k < len1; i = ++k) { b = cBytes[i]; seedBytes[kBBytes.length + i] = b; } sharedSecretHex = sha512Hex(seedBytes); return sharedSecretHex; }; export var diffieHellmanSecretHashHex = diffieHellmanSecretHash; export var createSharedSecretHash = diffieHellmanSecretHash; export var createSharedSecretHashHex = diffieHellmanSecretHash; export var diffieHellmanSecretRaw = function(secretKeyHex, publicKeyHex) { var B, aBytes, kB, kBigInt, sharedSecretBytes, sharedSecretHex; // a = our SecretKey // k = sha512(a) -> hashToScalar (scalar for multiplication) // B = lG = target User Public Key // kB = klG = shared Secret aBytes = tbut.hexToBytes(secretKeyHex); kBigInt = hashToScalar(sha512Bytes(aBytes)); B = ed255.ExtendedPoint.fromHex(publicKeyHex); kB = B.multiply(kBigInt); sharedSecretBytes = kB.toRawBytes(); sharedSecretHex = tbut.bytesToHex(sharedSecretBytes); return sharedSecretHex; }; export var diffieHellmanSecretRawHex = diffieHellmanSecretRaw; export var createSharedSecretRaw = diffieHellmanSecretRaw; export var createSharedSecretRawHex = diffieHellmanSecretRaw; //########################################################### export var elGamalSecretHash = async function(publicKeyHex, contextString = "") { var ABytes, B, b, cBytes, i, j, k, lB, lBBytes, lBigInt, len, len1, nBytes, referencePointHex, seedBytes, sharedSecretHex; // a = Secret Key of target user // k = sha512(a) -> hashToScalar (scalar for multiplication) // G = basePoint // B = kG = Public Key B = ed255.ExtendedPoint.fromHex(publicKeyHex); // n = new one-time secret (generated forgotten about) // l = sha512(n) -> hashToScalar (scalar for multiplication) // A = lG = one time public key = reference point // lB = lkG = shared secret // key = sha512(lB) // X = symmetricEncrypt(content, key) // {A,X} = data for targt user // n = one-time secret -> l nBytes = ed255.utils.randomPrivateKey(); lBigInt = hashToScalar(sha512Bytes(nBytes)); // A reference Point ABytes = (await ed255.getPublicKeyAsync(nBytes)); // lB = lkG = shared Secret lB = B.multiply(lBigInt); lBBytes = lB.toRawBytes(); cBytes = tbut.utf8ToBytes(contextString); seedBytes = new Uint8Array(lBBytes.length + cBytes.length); for (i = j = 0, len = lBBytes.length; j < len; i = ++j) { b = lBBytes[i]; seedBytes[i] = b; } for (i = k = 0, len1 = cBytes.length; k < len1; i = ++k) { b = cBytes[i]; seedBytes[lBBytes.length + i] = b; } sharedSecretHex = sha512Hex(seedBytes); referencePointHex = tbut.bytesToHex(ABytes); return {referencePointHex, sharedSecretHex}; }; export var elGamalSecretHashHex = elGamalSecretHash; export var referencedSharedSecretHash = elGamalSecretHash; export var referencedSharedSecretHashHex = elGamalSecretHash; export var referencedSecretHash = elGamalSecretHash; export var referencedSecretHashHex = elGamalSecretHash; export var elGamalSecretRaw = async function(publicKeyHex) { var ABytes, B, lB, lBBytes, lBigInt, nBytes, referencePointHex, sharedSecretHex; // a = Secret Key of target user // k = sha512(a) -> hashToScalar (scalar for multiplication) // G = basePoint // B = kG = Public Key B = ed255.ExtendedPoint.fromHex(publicKeyHex); // n = new one-time secret (generated forgotten about) // l = sha512(n) -> hashToScalar (scalar for multiplication) // A = lG = one time public key = reference point // lB = lkG = shared secret // key = sha512(lB) // X = symmetricEncrypt(content, key) // {A,X} = data for targt user // n = one-time secret -> l nBytes = ed255.utils.randomPrivateKey(); lBigInt = hashToScalar(sha512Bytes(nBytes)); // A reference Point ABytes = (await ed255.getPublicKeyAsync(nBytes)); // lB = lkG = shared Secret lB = B.multiply(lBigInt); lBBytes = lB.toRawBytes(); sharedSecretHex = tbut.bytesToHex(lBBytes); referencePointHex = tbut.bytesToHex(ABytes); return {referencePointHex, sharedSecretHex}; }; export var elGamalSecretRawHex = elGamalSecretRaw; export var referencedSharedSecretRaw = elGamalSecretRaw; export var referencedSharedSecretRawHex = elGamalSecretRaw; export var referencedSecretRaw = elGamalSecretRaw; export var referencedSecretRawHex = elGamalSecretRaw; //########################################################### // Bytes Versions //########################################################### export var diffieHellmanSecretHashBytes = function(secretKeyBytes, publicKeyBytes, contextString = "") { var B, BHex, b, cBytes, i, j, k, kB, kBBytes, kBigInt, len, len1, seedBytes, sharedSecretBytes; // a = our SecretKey // k = sha512(a) -> hashToScalar (scalar for multiplication) // B = lG = target User Public Key // kB = klG = shared Secret BHex = tbut.bytesToHex(publicKeyBytes); B = ed255.ExtendedPoint.fromHex(BHex); // k kBigInt = hashToScalar(sha512Bytes(secretKeyBytes)); // kB = klG = shared Secret kB = B.multiply(kBigInt); kBBytes = kB.toRawBytes(); cBytes = tbut.utf8ToBytes(contextString); seedBytes = new Uint8Array(kBBytes.length + cBytes.length); for (i = j = 0, len = kBBytes.length; j < len; i = ++j) { b = kBBytes[i]; seedBytes[i] = b; } for (i = k = 0, len1 = cBytes.length; k < len1; i = ++k) { b = cBytes[i]; seedBytes[kBBytes.length + i] = b; } sharedSecretBytes = sha512Bytes(seedBytes); return sharedSecretBytes; }; export var sharedSecretHashBytes = diffieHellmanSecretHashBytes; export var diffieHellmanSecretRawBytes = function(secretKeyBytes, publicKeyBytes) { var B, BHex, kB, kBBytes, kBigInt; // a = our SecretKey // k = sha512(a) -> hashToScalar (scalar for multiplication) // B = lG = target User Public Key // kB = klG = shared Secret BHex = tbut.bytesToHex(publicKeyBytes); B = ed255.ExtendedPoint.fromHex(BHex); // k kBigInt = hashToScalar(sha512Bytes(secretKeyBytes)); // kB = klG = shared Secret kB = B.multiply(kBigInt); kBBytes = kB.toRawBytes(); return kBBytes; }; export var sharedSecretRawBytes = diffieHellmanSecretRawBytes; //########################################################### export var elGamalSecretHashBytes = async function(publicKeyBytes, contextString = "") { var ABytes, B, BHex, b, cBytes, i, j, k, lB, lBBytes, lBigInt, len, len1, nBytes, referencePointBytes, seedBytes, sharedSecretBytes; // a = Secret Key of target user // k = sha512(a) -> hashToScalar (scalar for multiplication) // G = basePoint // B = kG = Public Key BHex = tbut.bytesToHex(publicKeyBytes); B = ed255.ExtendedPoint.fromHex(BHex); // n = new one-time secret (generated forgotten about) // l = sha512(n) -> hashToScalar (scalar for multiplication) // A = lG = one time public key = reference point // lB = lkG = shared secret // key = sha512(lB) // X = symmetricEncrypt(content, key) // {A,X} = data for targt user // n = one-time secret -> l nBytes = ed255.utils.randomPrivateKey(); lBigInt = hashToScalar(sha512Bytes(nBytes)); // A reference Point ABytes = (await ed255.getPublicKeyAsync(nBytes)); // lB = lkG = shared Secret lB = B.multiply(lBigInt); lBBytes = lB.toRawBytes(); cBytes = tbut.utf8ToBytes(contextString); seedBytes = new Uint8Array(lBBytes.length + cBytes.length); for (i = j = 0, len = lBBytes.length; j < len; i = ++j) { b = lBBytes[i]; seedBytes[i] = b; } for (i = k = 0, len1 = cBytes.length; k < len1; i = ++k) { b = cBytes[i]; seedBytes[lBBytes.length + i] = b; } sharedSecretBytes = sha512Bytes(seedBytes); referencePointBytes = ABytes; return {referencePointBytes, sharedSecretBytes}; }; export var referencedSharedSecretHashBytes = elGamalSecretHashBytes; export var referencedSecretHashBytes = elGamalSecretHashBytes; export var elGamalSecretRawBytes = async function(publicKeyBytes) { var ABytes, B, BHex, lB, lBBytes, lBigInt, nBytes, referencePointBytes, sharedSecretBytes; // a = Secret Key of target user // k = sha512(a) -> hashToScalar (scalar for multiplication) // G = basePoint // B = kG = Public Key BHex = tbut.bytesToHex(publicKeyBytes); B = ed255.ExtendedPoint.fromHex(BHex); // n = new one-time secret (generated forgotten about) // l = sha512(n) -> hashToScalar (scalar for multiplication) // A = lG = one time public key = reference point // lB = lkG = shared secret // key = sha512(lB) // X = symmetricEncrypt(content, key) // {A,X} = data for targt user // n = one-time secret -> l nBytes = ed255.utils.randomPrivateKey(); lBigInt = hashToScalar(sha512Bytes(nBytes)); // A reference Point ABytes = (await ed255.getPublicKeyAsync(nBytes)); // lB = lkG = shared Secret lB = B.multiply(lBigInt); lBBytes = lB.toRawBytes(); sharedSecretBytes = lBBytes; referencePointBytes = ABytes; return {referencePointBytes, sharedSecretBytes}; }; export var referencedSharedSecretRawBytes = elGamalSecretRawBytes; export var referencedSecretRawBytes = elGamalSecretRawBytes; //endregion //########################################################### //region salts export var saltContent = function(content) { var c, contentLength, end, fullLength, idx, j, len, overlap, padding, prefixLength, resultBuffer, salt, saltLength, sum, unpaddedLength; content = tbut.utf8ToBytes(content); contentLength = content.length; saltLength = 33 + (crypto.randomBytes(1)[0] & 127); salt = crypto.randomBytes(saltLength); // Prefix is salt + 3 bytes prefixLength = saltLength + 3; unpaddedLength = prefixLength + contentLength; overlap = unpaddedLength % 32; padding = 32 - overlap; fullLength = unpaddedLength + padding; resultBuffer = new Uint8Array(fullLength); // immediatly write the content to the resultBuffer for (idx = j = 0, len = content.length; j < len; idx = ++j) { c = content[idx]; resultBuffer[idx + prefixLength] = c; } // The first 32 bytes of the prefix are 1:1 from the salt. sum = 0; idx = 32; while (idx--) { sum += salt[idx]; resultBuffer[idx] = salt[idx]; } // the last byte of the prefix is the padding length resultBuffer[saltLength + 2] = padding; // the postfix padding is the first salt bytes up to padding size idx = 0; end = fullLength - 1; while (idx < padding) { resultBuffer[end - idx] = salt[idx]; idx++; } // the prefix keeps the sum of the salt values as ending identification // make sure this condition is not met before we reach the real end idx = 32; while (idx < saltLength) { // when the condition is met we add +1 to the LSB(salt[idx+1]) to destroy it // Notice! If we add +1 to the MSB(salt[idx]) then we change what we cheched for previously, which might accidentally result in the condition being met now one byte before, which we donot check for ever again // if (sum == (salt[idx]*256 + salt[idx+1])) then salt[idx+1]++ salt[idx + 1] += sum === (salt[idx] * 256 + salt[idx + 1]); sum += salt[idx]; resultBuffer[idx] = salt[idx]; idx++; } // save the sum in the right bytes resultBuffer[saltLength] = sum >> 8; resultBuffer[saltLength + 1] = sum % 256; // in this case we have the condition met when just taking the most significatn bytes of the real sum into account if (resultBuffer[saltLength] === resultBuffer[saltLength - 1] && resultBuffer[saltLength + 1] === 2 * resultBuffer[saltLength]) { resultBuffer[saltLength - 1]++; sum++; resultBuffer[saltLength] = sum >> 8; resultBuffer[saltLength + 1] = sum % 256; } return resultBuffer; }; export var unsaltContent = function(contentBytes) { var end, fullLength, idx, invalid, limit, overLimit, padding, start, sum; fullLength = contentBytes.length; if (fullLength > 160) { limit = 160; } else { limit = fullLength; } overLimit = limit + 1; sum = 0; idx = 32; while (idx--) { sum += contentBytes[idx]; } idx = 32; while (idx < overLimit) { if (sum === (contentBytes[idx] * 256 + contentBytes[idx + 1])) { start = idx + 3; padding = contentBytes[idx + 2]; break; } sum += contentBytes[idx]; idx++; } if (idx > limit) { throw new Error("Unsalt: No valid prefix ending found!"); } // Check if the padding matches the salt - so we can verify here nobody has tampered with it idx = 0; end = fullLength - 1; invalid = 0; while (idx < padding) { invalid += contentBytes[idx] !== contentBytes[end - idx]; idx++; } if (invalid) { throw new Error("Unsalt: Postfix and prefix did not match as expected!"); } end = fullLength - padding; contentBytes = contentBytes.slice(start, end); return tbut.bytesToUtf8(contentBytes); }; //endregion //endregion