Evan Feenstra
5 years ago
12 changed files with 805 additions and 318 deletions
@ -1,18 +1,71 @@ |
|||
import * as path from 'path' |
|||
import RNCryptor from '../utils/rncryptor' |
|||
import * as fetch from 'node-fetch' |
|||
import {parseLDAT} from '../utils/ldat' |
|||
import * as rsa from '../crypto/rsa' |
|||
import * as crypto from 'crypto' |
|||
import * as Blob from 'fetch-blob' |
|||
|
|||
const constants = require(path.join(__dirname,'../../config/constants.json')) |
|||
const msgtypes = constants.message_types |
|||
|
|||
export function modifyPayload(payload) { |
|||
export async function modifyPayload(payload, chat) { |
|||
if(payload.type===msgtypes.attachment) { |
|||
console.log("MODIFY, ", payload) |
|||
// download image from mediaToken
|
|||
// decrypt key
|
|||
// decrypt image
|
|||
// new key, re-encrypt, re-upload
|
|||
// new payload
|
|||
|
|||
const mt = payload.message && payload.message.mediaToken |
|||
const key = payload.message && payload.message.mediaKey |
|||
const typ = payload.message && payload.message.mediaType |
|||
if(!mt || !key) return payload |
|||
|
|||
const terms = parseLDAT(mt) |
|||
if(!terms.host) return payload |
|||
|
|||
try { |
|||
const r = await fetch(`https://${terms.host}/file/${mt}`) |
|||
const buf = await r.buffer() |
|||
|
|||
const decMediaKey = rsa.decrypt(chat.groupPrivateKey, key) |
|||
|
|||
const imgUTF8 = RNCryptor.Decrypt(decMediaKey, buf.toString()) |
|||
|
|||
const newKey = crypto.randomBytes(20).toString('hex') |
|||
|
|||
const encImg = RNCryptor.Encrypt(newKey, imgUTF8) |
|||
|
|||
const resp = await fetch(`https://${terms.host}/file`, { |
|||
method: 'POST', |
|||
body: new Blob([encImg], { type: typ||'image/jpg', name:'file', filename:'Image.jpg' }) |
|||
}) |
|||
|
|||
let json = resp.json() |
|||
if(!json.muid) return payload |
|||
|
|||
// PUT NEW TERMS, to finish in personalizeMessage
|
|||
|
|||
const amt = terms.meta&&terms.meta.amt |
|||
const ttl = terms.meta&&terms.meta.ttl |
|||
const mediaTerms: {[k:string]:any} = { |
|||
muid:json.muid, ttl:ttl||31536000, |
|||
meta:{...amt && {amt}}, |
|||
skipSigning: amt ? true : false // only sign if its free
|
|||
} |
|||
|
|||
const encKey = rsa.encrypt(chat.groupKey, newKey) |
|||
return fillmsg(payload, {mediaTerms,mediaKey:encKey}) // key is re-encrypted later
|
|||
} catch(e) { |
|||
return payload |
|||
} |
|||
// how to link w og msg? ogMediaToken?
|
|||
} |
|||
return payload |
|||
} |
|||
|
|||
function fillmsg(full, props){ |
|||
return { |
|||
...full, message: { |
|||
...full.message, |
|||
...props, |
|||
} |
|||
} |
|||
} |
@ -0,0 +1,5 @@ |
|||
|
|||
|
|||
import {RNCryptor} from './rncryptor' |
|||
|
|||
export default RNCryptor |
@ -0,0 +1,100 @@ |
|||
var sjcl = require('sjcl') |
|||
|
|||
var RNCryptor = {}; |
|||
|
|||
/* |
|||
Takes password string and salt WordArray |
|||
Returns key bitArray |
|||
*/ |
|||
|
|||
RNCryptor.KeyForPassword = function(password, salt) { |
|||
var hmacSHA1 = function (key) { |
|||
var hasher = new sjcl.misc.hmac(key, sjcl.hash.sha1); |
|||
this.encrypt = function () { |
|||
return hasher.encrypt.apply(hasher, arguments); |
|||
}; |
|||
}; |
|||
return sjcl.misc.pbkdf2(password, salt, 10000, 32 * 8, hmacSHA1); |
|||
} |
|||
|
|||
/* |
|||
Takes password string and plaintext bitArray |
|||
options: |
|||
iv |
|||
encryption_salt |
|||
html_salt |
|||
Returns ciphertext bitArray |
|||
*/ |
|||
RNCryptor.Encrypt = function(password, plaintext, options) { |
|||
options = options || {} |
|||
var encryption_salt = options["encryption_salt"] || sjcl.random.randomWords(8 / 4); // FIXME: Need to seed PRNG
|
|||
var encryption_key = RNCryptor.KeyForPassword(password, encryption_salt); |
|||
|
|||
var hmac_salt = options["hmac_salt"] || sjcl.random.randomWords(8 / 4); |
|||
var hmac_key = RNCryptor.KeyForPassword(password, hmac_salt); |
|||
|
|||
var iv = options["iv"] || sjcl.random.randomWords(16 / 4); |
|||
|
|||
var version = sjcl.codec.hex.toBits("03"); |
|||
var options = sjcl.codec.hex.toBits("01"); |
|||
|
|||
var message = sjcl.bitArray.concat(version, options); |
|||
message = sjcl.bitArray.concat(message, encryption_salt); |
|||
message = sjcl.bitArray.concat(message, hmac_salt); |
|||
message = sjcl.bitArray.concat(message, iv); |
|||
|
|||
var aes = new sjcl.cipher.aes(encryption_key); |
|||
// sjcl.beware["CBC mode is dangerous because it doesn't protect message integrity."]();
|
|||
var encrypted = sjcl.mode.cbc.encrypt(aes, plaintext, iv); |
|||
|
|||
message = sjcl.bitArray.concat(message, encrypted); |
|||
|
|||
var hmac = new sjcl.misc.hmac(hmac_key).encrypt(message); |
|||
message = sjcl.bitArray.concat(message, hmac); |
|||
|
|||
return sjcl.codec.utf8String.fromBits(message); |
|||
} |
|||
|
|||
/* |
|||
Takes password string and message (ciphertext) bitArray |
|||
options: |
|||
iv |
|||
encryption_salt |
|||
html_salt |
|||
Returns plaintext bitArray |
|||
*/ |
|||
RNCryptor.Decrypt = function(password, message, options) { |
|||
options = options || {} |
|||
|
|||
var version = sjcl.bitArray.extract(message, 0 * 8, 8); |
|||
var options = sjcl.bitArray.extract(message, 1 * 8, 8); |
|||
|
|||
var encryption_salt = sjcl.bitArray.bitSlice(message, 2 * 8, 10 * 8); |
|||
var encryption_key = RNCryptor.KeyForPassword(password, encryption_salt); |
|||
|
|||
var hmac_salt = sjcl.bitArray.bitSlice(message, 10 * 8, 18 * 8); |
|||
var hmac_key = RNCryptor.KeyForPassword(password, hmac_salt); |
|||
|
|||
var iv = sjcl.bitArray.bitSlice(message, 18 * 8, 34 * 8); |
|||
|
|||
var ciphertext_end = sjcl.bitArray.bitLength(message) - (32 * 8); |
|||
|
|||
var ciphertext = sjcl.bitArray.bitSlice(message, 34 * 8, ciphertext_end); |
|||
|
|||
var hmac = sjcl.bitArray.bitSlice(message, ciphertext_end); |
|||
|
|||
var expected_hmac = new sjcl.misc.hmac(hmac_key).encrypt(sjcl.bitArray.bitSlice(message, 0, ciphertext_end)); |
|||
|
|||
// .equal is of consistent time
|
|||
if (! sjcl.bitArray.equal(hmac, expected_hmac)) { |
|||
throw new sjcl.exception.corrupt("HMAC mismatch or bad password."); |
|||
} |
|||
|
|||
var aes = new sjcl.cipher.aes(encryption_key); |
|||
// sjcl.beware["CBC mode is dangerous because it doesn't protect message integrity."]();
|
|||
var decrypted = sjcl.mode.cbc.decrypt(aes, ciphertext, iv); |
|||
|
|||
return sjcl.codec.utf8String.fromBits(decrypted); |
|||
} |
|||
|
|||
export {RNCryptor} |
@ -1 +1 @@ |
|||
{"version":3,"file":"modify.js","sourceRoot":"","sources":["../../../api/network/modify.ts"],"names":[],"mappings":";;AAAA,6BAA4B;AAE5B,MAAM,SAAS,GAAG,OAAO,CAAC,IAAI,CAAC,IAAI,CAAC,SAAS,EAAC,6BAA6B,CAAC,CAAC,CAAA;AAC7E,MAAM,QAAQ,GAAG,SAAS,CAAC,aAAa,CAAA;AAExC,SAAgB,aAAa,CAAC,OAAO;IACnC,IAAG,OAAO,CAAC,IAAI,KAAG,QAAQ,CAAC,UAAU,EAAE;QACrC,OAAO,CAAC,GAAG,CAAC,UAAU,EAAE,OAAO,CAAC,CAAA;QAChC,iCAAiC;QACjC,cAAc;QACd,gBAAgB;QAChB,iCAAiC;QACjC,cAAc;QAEd,sCAAsC;KACvC;IACD,OAAO,OAAO,CAAA;AAChB,CAAC;AAZD,sCAYC"} |
|||
{"version":3,"file":"modify.js","sourceRoot":"","sources":["../../../api/network/modify.ts"],"names":[],"mappings":";;AAAA,6BAA4B;AAC5B,kDAA0C;AAE1C,OAAO,CAAC,GAAG,CAAC,WAAW,EAAC,mBAAS,CAAC,CAAA;AAElC,MAAM,SAAS,GAAG,OAAO,CAAC,IAAI,CAAC,IAAI,CAAC,SAAS,EAAC,6BAA6B,CAAC,CAAC,CAAA;AAC7E,MAAM,QAAQ,GAAG,SAAS,CAAC,aAAa,CAAA;AAGxC,6CAA6C;AAG7C,SAAgB,aAAa,CAAC,OAAO;IACnC,IAAG,OAAO,CAAC,IAAI,KAAG,QAAQ,CAAC,UAAU,EAAE;QACrC,OAAO,CAAC,GAAG,CAAC,UAAU,EAAE,OAAO,CAAC,CAAA;QAEhC,iCAAiC;QACjC,cAAc;QACd,gBAAgB;QAChB,iCAAiC;QACjC,cAAc;QAEd,sCAAsC;KACvC;IACD,OAAO,OAAO,CAAA;AAChB,CAAC;AAbD,sCAaC"} |
@ -0,0 +1,5 @@ |
|||
"use strict"; |
|||
Object.defineProperty(exports, "__esModule", { value: true }); |
|||
const rncryptor_1 = require("./rncryptor"); |
|||
exports.default = rncryptor_1.RNCryptor; |
|||
//# sourceMappingURL=index.js.map
|
@ -0,0 +1 @@ |
|||
{"version":3,"file":"index.js","sourceRoot":"","sources":["../../../../api/utils/rncryptor/index.js"],"names":[],"mappings":";;AAEA,2CAAqC;AAErC,kBAAe,qBAAS,CAAA"} |
@ -0,0 +1,78 @@ |
|||
"use strict"; |
|||
Object.defineProperty(exports, "__esModule", { value: true }); |
|||
var sjcl = require('sjcl'); |
|||
var RNCryptor = {}; |
|||
exports.RNCryptor = RNCryptor; |
|||
/* |
|||
Takes password string and salt WordArray |
|||
Returns key bitArray |
|||
*/ |
|||
RNCryptor.KeyForPassword = function (password, salt) { |
|||
var hmacSHA1 = function (key) { |
|||
var hasher = new sjcl.misc.hmac(key, sjcl.hash.sha1); |
|||
this.encrypt = function () { |
|||
return hasher.encrypt.apply(hasher, arguments); |
|||
}; |
|||
}; |
|||
return sjcl.misc.pbkdf2(password, salt, 10000, 32 * 8, hmacSHA1); |
|||
}; |
|||
/* |
|||
Takes password string and plaintext bitArray |
|||
options: |
|||
iv |
|||
encryption_salt |
|||
html_salt |
|||
Returns ciphertext bitArray |
|||
*/ |
|||
RNCryptor.Encrypt = function (password, plaintext, options) { |
|||
options = options || {}; |
|||
var encryption_salt = options["encryption_salt"] || sjcl.random.randomWords(8 / 4); // FIXME: Need to seed PRNG
|
|||
var encryption_key = RNCryptor.KeyForPassword(password, encryption_salt); |
|||
var hmac_salt = options["hmac_salt"] || sjcl.random.randomWords(8 / 4); |
|||
var hmac_key = RNCryptor.KeyForPassword(password, hmac_salt); |
|||
var iv = options["iv"] || sjcl.random.randomWords(16 / 4); |
|||
var version = sjcl.codec.hex.toBits("03"); |
|||
var options = sjcl.codec.hex.toBits("01"); |
|||
var message = sjcl.bitArray.concat(version, options); |
|||
message = sjcl.bitArray.concat(message, encryption_salt); |
|||
message = sjcl.bitArray.concat(message, hmac_salt); |
|||
message = sjcl.bitArray.concat(message, iv); |
|||
var aes = new sjcl.cipher.aes(encryption_key); |
|||
sjcl.beware["CBC mode is dangerous because it doesn't protect message integrity."](); |
|||
var encrypted = sjcl.mode.cbc.encrypt(aes, plaintext, iv); |
|||
message = sjcl.bitArray.concat(message, encrypted); |
|||
var hmac = new sjcl.misc.hmac(hmac_key).encrypt(message); |
|||
message = sjcl.bitArray.concat(message, hmac); |
|||
return message; |
|||
}; |
|||
/* |
|||
Takes password string and message (ciphertext) bitArray |
|||
options: |
|||
iv |
|||
encryption_salt |
|||
html_salt |
|||
Returns plaintext bitArray |
|||
*/ |
|||
RNCryptor.Decrypt = function (password, message, options) { |
|||
options = options || {}; |
|||
var version = sjcl.bitArray.extract(message, 0 * 8, 8); |
|||
var options = sjcl.bitArray.extract(message, 1 * 8, 8); |
|||
var encryption_salt = sjcl.bitArray.bitSlice(message, 2 * 8, 10 * 8); |
|||
var encryption_key = RNCryptor.KeyForPassword(password, encryption_salt); |
|||
var hmac_salt = sjcl.bitArray.bitSlice(message, 10 * 8, 18 * 8); |
|||
var hmac_key = RNCryptor.KeyForPassword(password, hmac_salt); |
|||
var iv = sjcl.bitArray.bitSlice(message, 18 * 8, 34 * 8); |
|||
var ciphertext_end = sjcl.bitArray.bitLength(message) - (32 * 8); |
|||
var ciphertext = sjcl.bitArray.bitSlice(message, 34 * 8, ciphertext_end); |
|||
var hmac = sjcl.bitArray.bitSlice(message, ciphertext_end); |
|||
var expected_hmac = new sjcl.misc.hmac(hmac_key).encrypt(sjcl.bitArray.bitSlice(message, 0, ciphertext_end)); |
|||
// .equal is of consistent time
|
|||
if (!sjcl.bitArray.equal(hmac, expected_hmac)) { |
|||
throw new sjcl.exception.corrupt("HMAC mismatch or bad password."); |
|||
} |
|||
var aes = new sjcl.cipher.aes(encryption_key); |
|||
sjcl.beware["CBC mode is dangerous because it doesn't protect message integrity."](); |
|||
var decrypted = sjcl.mode.cbc.decrypt(aes, ciphertext, iv); |
|||
return decrypted; |
|||
}; |
|||
//# sourceMappingURL=rncryptor.js.map
|
@ -0,0 +1 @@ |
|||
{"version":3,"file":"rncryptor.js","sourceRoot":"","sources":["../../../../api/utils/rncryptor/rncryptor.js"],"names":[],"mappings":"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"} |
File diff suppressed because it is too large
Loading…
Reference in new issue