| 
									
										
										
										
											2017-11-14 21:54:12 -05:00
										 |  |  | "use strict"; | 
					
						
							|  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2017-11-10 22:55:19 -05:00
										 |  |  | const utils = require('./utils'); | 
					
						
							|  |  |  | const aesjs = require('./aes'); | 
					
						
							| 
									
										
										
										
											2017-11-15 18:23:19 -05:00
										 |  |  | const crypto = require('crypto'); | 
					
						
							| 
									
										
										
										
											2017-11-10 22:55:19 -05:00
										 |  |  | 
 | 
					
						
							|  |  |  | function getProtectedSessionId(req) { | 
					
						
							|  |  |  |     return req.headers['x-protected-session-id']; | 
					
						
							|  |  |  | } | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | function getDataAes(dataKey) { | 
					
						
							|  |  |  |     return new aesjs.ModeOfOperation.ctr(dataKey, new aesjs.Counter(5)); | 
					
						
							|  |  |  | } | 
					
						
							|  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2017-11-15 22:13:45 -05:00
										 |  |  | function arraysIdentical(a, b) { | 
					
						
							|  |  |  |     let i = a.length; | 
					
						
							|  |  |  |     if (i !== b.length) return false; | 
					
						
							|  |  |  |     while (i--) { | 
					
						
							|  |  |  |         if (a[i] !== b[i]) return false; | 
					
						
							|  |  |  |     } | 
					
						
							|  |  |  |     return true; | 
					
						
							|  |  |  | } | 
					
						
							|  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2017-11-10 22:55:19 -05:00
										 |  |  | function decrypt(dataKey, encryptedBase64) { | 
					
						
							|  |  |  |     if (!dataKey) { | 
					
						
							|  |  |  |         return "[protected]"; | 
					
						
							|  |  |  |     } | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  |     const aes = getDataAes(dataKey); | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  |     const encryptedBytes = utils.fromBase64(encryptedBase64); | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  |     const decryptedBytes = aes.decrypt(encryptedBytes); | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  |     const digest = decryptedBytes.slice(0, 4); | 
					
						
							|  |  |  |     const payload = decryptedBytes.slice(4); | 
					
						
							|  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2017-11-14 21:54:12 -05:00
										 |  |  |     const hashArray = sha256Array(payload); | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  |     const computedDigest = hashArray.slice(0, 4); | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  |     if (!arraysIdentical(digest, computedDigest)) { | 
					
						
							|  |  |  |         return false; | 
					
						
							|  |  |  |     } | 
					
						
							|  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2017-11-10 22:55:19 -05:00
										 |  |  |     return aesjs.utils.utf8.fromBytes(payload); | 
					
						
							|  |  |  | } | 
					
						
							|  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2017-11-14 21:54:12 -05:00
										 |  |  | function encrypt(dataKey, plainText) { | 
					
						
							|  |  |  |     if (!dataKey) { | 
					
						
							|  |  |  |         throw new Error("No data key!"); | 
					
						
							|  |  |  |     } | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  |     const aes = getDataAes(dataKey); | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  |     const payload = Array.from(aesjs.utils.utf8.toBytes(plainText)); | 
					
						
							|  |  |  |     const digest = sha256Array(payload).slice(0, 4); | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  |     const digestWithPayload = digest.concat(payload); | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  |     const encryptedBytes = aes.encrypt(digestWithPayload); | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  |     return utils.toBase64(encryptedBytes); | 
					
						
							|  |  |  | } | 
					
						
							|  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2017-11-15 22:13:45 -05:00
										 |  |  | function shaArray(content) { | 
					
						
							|  |  |  |     // we use this as simple checksum and don't rely on its security so SHA-1 is good enough
 | 
					
						
							|  |  |  |     return crypto.createHash('sha1').update(content).digest('base64'); | 
					
						
							|  |  |  | } | 
					
						
							|  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2017-11-14 21:54:12 -05:00
										 |  |  | function sha256Array(content) { | 
					
						
							| 
									
										
										
										
											2017-11-15 18:23:19 -05:00
										 |  |  |     return crypto.createHash('sha256').update(content).digest(); | 
					
						
							|  |  |  | } | 
					
						
							|  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2017-11-15 22:13:45 -05:00
										 |  |  | function pad(data) { | 
					
						
							|  |  |  |     let padded = Array.from(data); | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  |     if (data.length >= 16) { | 
					
						
							|  |  |  |         padded = padded.slice(0, 16); | 
					
						
							| 
									
										
										
										
											2017-11-15 18:23:19 -05:00
										 |  |  |     } | 
					
						
							| 
									
										
										
										
											2017-11-15 22:13:45 -05:00
										 |  |  |     else { | 
					
						
							|  |  |  |         padded = padded.concat(Array(16 - padded.length).fill(0)); | 
					
						
							|  |  |  |     } | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  |     return Buffer.from(padded); | 
					
						
							|  |  |  | } | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | function encryptCbc(dataKey, iv, plainText) { | 
					
						
							|  |  |  |     if (!dataKey) { | 
					
						
							|  |  |  |         throw new Error("No data key!"); | 
					
						
							|  |  |  |     } | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  |     const cipher = crypto.createCipheriv('aes-128-cbc', pad(dataKey), pad(iv)); | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  |     const digest = shaArray(plainText).slice(0, 4); | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  |     const digestWithPayload = digest + plainText; | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  |     const encryptedData = cipher.update(digestWithPayload, 'utf8', 'base64') + cipher.final('base64'); | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  |     return encryptedData; | 
					
						
							|  |  |  | } | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | function decryptCbc(dataKey, iv, cipherText) { | 
					
						
							|  |  |  |     if (!dataKey) { | 
					
						
							|  |  |  |         return "[protected]"; | 
					
						
							|  |  |  |     } | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  |     const decipher = crypto.createDecipheriv('aes-128-cbc', pad(dataKey), pad(iv)); | 
					
						
							|  |  |  |     const decryptedBytes  = decipher.update(cipherText, 'base64', 'utf-8') + decipher.final('utf-8'); | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  |     const digest = decryptedBytes.slice(0, 4); | 
					
						
							|  |  |  |     const payload = decryptedBytes.slice(4); | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  |     const computedDigest = shaArray(payload).slice(0, 4); | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  |     if (!arraysIdentical(digest, computedDigest)) { | 
					
						
							|  |  |  |         return false; | 
					
						
							|  |  |  |     } | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  |     return payload; | 
					
						
							|  |  |  | } | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | function noteTitleIv(iv) { | 
					
						
							|  |  |  |     return "0" + iv; | 
					
						
							|  |  |  | } | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | function noteTextIv(iv) { | 
					
						
							|  |  |  |     return "1" + iv; | 
					
						
							| 
									
										
										
										
											2017-11-14 21:54:12 -05:00
										 |  |  | } | 
					
						
							|  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2017-11-10 22:55:19 -05:00
										 |  |  | module.exports = { | 
					
						
							|  |  |  |     getProtectedSessionId, | 
					
						
							| 
									
										
										
										
											2017-11-14 21:54:12 -05:00
										 |  |  |     decrypt, | 
					
						
							| 
									
										
										
										
											2017-11-15 22:13:45 -05:00
										 |  |  |     encrypt, | 
					
						
							|  |  |  |     encryptCbc, | 
					
						
							|  |  |  |     decryptCbc, | 
					
						
							|  |  |  |     noteTitleIv, | 
					
						
							|  |  |  |     noteTextIv | 
					
						
							| 
									
										
										
										
											2017-11-10 22:55:19 -05:00
										 |  |  | }; |