This repository has been archived by the owner on Jul 31, 2018. It is now read-only.
-
Notifications
You must be signed in to change notification settings - Fork 21
/
encryption.coffee
246 lines (208 loc) · 7.9 KB
/
encryption.coffee
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
db = require('../helpers/db_connect_helper').db_connect()
nodemailer = require "nodemailer"
CryptoTools = require('./crypto_tools')
randomString = require('./random').randomString
logger = require('printit')(prefix: 'lib/encryption')
errors = require '../middlewares/errors'
timeout = null
User = require './user'
user = new User()
cryptoTools = new CryptoTools()
slaveKey = null
day = 24 * 60 * 60 * 1000
encryptionPattern = /^encrypted/
sendEmail = (mailOptions, callback) ->
transport = nodemailer.createTransport "SMTP", {}
transport.sendMail mailOptions, (error, response) ->
transport.close()
callback error, response
getBody = (domain) ->
body = """
Hello,
Your Cozy has been recently restarted.
For security reasons, a restart disables encryption and decryption.
Some features of your applications are therefore desactivated.
Don't worry, nothing is lost and they will be reactivated automatically
when you will log into your Cozy instance.
"""
if domain? and domain isnt ''
body += "Click here to login #{domain}."
body += """
Cozy Team.
P-S: If you have any question, let us know at [email protected]
or in our IRC channel #cozycloud on freenode.net.
"""
return body
resetTimeout = -> timeout = null
sendMailNow = ->
if slaveKey?
return resetTimeout()
user.getUser (err, user) ->
if err
logger.error "[sendMailToUser] an error occured while" +
" retrieving user data from database:"
logger.raw err
else
db.view 'cozyinstance/all', (err, instance) ->
if instance?[0]?.value.domain?
domain = instance[0].value.domain
else
domain = false
mailOptions =
to: user.email
from: "[email protected]"
subject: "Your Cozy has been restarted"
text: getBody(domain)
sendEmail mailOptions, (error, response) ->
logger.error error if error?
timeout = setTimeout resetTimeout, 3*day
sendMail = ->
if timeout is null
timeout = setTimeout sendMailNow, 1*day
## function updateKeys (oldKey,password, encryptedslaveKey, callback)
## @password {string} user's password
## @callback {function} Continuation to pass control back to when complete.
## Update keys, return in data new encrypted slave key and new salt
updateKeys = (password, callback) ->
salt = cryptoTools.genSalt(32 - password.length)
masterKey = cryptoTools.genHashWithSalt password, salt
encryptedSlaveKey = cryptoTools.encrypt masterKey, slaveKey
data = slaveKey: encryptedSlaveKey, salt: salt
callback data
## function encrypt (password, callback)
## @password {string} document password
## @callback {function} Continuation to pass control back to when complete.
## Return encrypted password
exports.encrypt = (password) ->
if password? and process.env.NODE_ENV isnt "development"
if slaveKey?
newPwd = cryptoTools.encrypt slaveKey, password
return newPwd
else
sendMail()
err = new Error "slave key doesn't exist"
logger.error err.message
throw err
else
return password
## function encryptNeededFields (obj, callback)
## @obj {object} object containing fields to decrypt if needed
## Analyzes an object to determine if some fields need to be encrypted, and
## proceed to encryption when needed
exports.encryptNeededFields = (obj) ->
if obj?
# Searching for fields to encrypt
try
for field in Object.keys(obj) when field.match encryptionPattern
obj[field] = @encrypt obj[field]
return obj
catch error
# Error are already logged by the encrypt function
throw error
else
err = new Error "object to encrypt doesn't exist"
logger.error "[encryptNeededFields]: #{err.message}"
throw err
## function decrypt (password, callback)
## @password {string} document password
## @callback {function} Continuation to pass control back to when complete.
## Return decrypted password if password was encrypted
exports.decrypt = (password) ->
if password? and process.env.NODE_ENV isnt "development"
if slaveKey?
newPwd = password
try
newPwd = cryptoTools.decrypt slaveKey, password
catch err
logger.error err
return newPwd
else
sendMail()
err = "master key and slave key don't exist"
logger.error "[decrypt]: #{err}"
throw err
else
return password
## function decryptNeededFields (obj, callback)
## @obj {object} object containing fields to decrypt if needed
## Analyzes an object to determine if some fields need to be decrypted, and
## proceed to decryption when needed
exports.decryptNeededFields = (obj) ->
if obj?
# Searching for fields to decrypt
try
for field in Object.keys(obj) when field.match encryptionPattern
obj[field] = @decrypt obj[field]
return obj
catch error
# Error are already logged by the decrypt function
throw error
else
err = "object to decrypt doesn't exist"
logger.error "[decryptNeededFields]: #{err}"
throw err
## function init (password, user, callback)
## @password {string} user's password
## @user {object} user
## @callback {function} Continuation to pass control back to when complete.
## Init keys at the first connection
exports.init = (password, user, callback) ->
# Generate salt and masterKey
salt = cryptoTools.genSalt(32 - password.length)
masterKey = cryptoTools.genHashWithSalt password, salt
# Generate slaveKey
slaveKey = randomString()
encryptedSlaveKey = cryptoTools.encrypt masterKey, slaveKey
# Store in database
data = salt: salt, slaveKey: encryptedSlaveKey
db.merge user._id, data, (err, res) ->
if err
logger.error "[initializeKeys] err: #{err}"
callback err
else
callback null
## function login (password, user, callback)
## @password {string} user's password
## @user {object} user
## @callback {function} Continuation to pass control back to when complete.
## Init keys when user log in
exports.logIn = (password, user, callback) ->
# Recover master and slave keys
masterKey =
cryptoTools.genHashWithSalt(password, user.salt)
encryptedSlaveKey = user.slaveKey
slaveKey =
cryptoTools.decrypt masterKey, encryptedSlaveKey
callback()
## function update (pasword, user, callback)
## @password {string} user's password
## @user {object} user
## @callback {function} Continuation to pass control back to when complete.
## Update keys when user changes his password
exports.update = (password, user, callback) ->
unless slaveKey?
err = errors.http 400, "slaveKey doesn't exist"
logger.error "[update] : #{err}"
return callback err
updateKeys password, (data) ->
db.merge user._id, data, (err, res) ->
if err
logger.error "[update] : #{err}"
return callback err
callback null
## function reset (pasword, user, callback)
## @password {string} user's password
## @user {object} user
## @callback {function} Continuation to pass control back to when complete.
## Reset keys when user resets his password
exports.reset = (user, callback) ->
data = slaveKey: null, salt: null
db.merge user._id, data, (err, res) ->
if err
callback new Error "[resetKeys] err: #{err}"
else
callback()
## function isLog ()
## Return true if slaveKey exists, which indicates if user is connected
exports.isLog = ->
return slaveKey?