1 | Crypto = require './crypto'
|
2 | Opdata = require './opdata'
|
3 |
|
4 | ###*
|
5 | * @class An item stores data such as usernames and passwords.
|
6 | ###
|
7 |
|
8 | class Item
|
9 |
|
10 |
|
11 | ###*
|
12 | * Create a new Item.
|
13 | * @param {Object} data The data to add to the Item.
|
14 | * @param {Object} master The master encryption keys.
|
15 | * @param {Object} overview The overview encryption keys.
|
16 | * @return {Item} The item.
|
17 | ###
|
18 | @create: (data, master, overview) =>
|
19 |
|
20 | timeNow = Math.floor Date.now() / 1000
|
21 |
|
22 | item = new Item
|
23 | uuid: Crypto.generateUuid()
|
24 | created: timeNow
|
25 | updated: timeNow
|
26 | category: '001'
|
27 |
|
28 | item.overview =
|
29 | title: data.title
|
30 | ainfo: data.username
|
31 | url: data.url
|
32 | URLS: [
|
33 | l: 'website'
|
34 | u: data.url
|
35 | ]
|
36 |
|
37 | item.details =
|
38 | fields: [
|
39 | type: 'T'
|
40 | name: 'username'
|
41 | value: data.username
|
42 | designation: 'username'
|
43 | ,
|
44 | type: 'P'
|
45 | name: 'password'
|
46 | value: data.password
|
47 | designation: 'password'
|
48 | ]
|
49 | notesPlain: data.notes or ''
|
50 |
|
51 | item.keys =
|
52 | encryption: Crypto.randomBytes(32)
|
53 | hmac: Crypto.randomBytes(32)
|
54 |
|
55 | ###*
|
56 | *
|
57 | * TODO: Move into seperate encryption functions
|
58 | *
|
59 |
|
60 | keys.both = Crypto.concat([encryptionKey, hmacKey])
|
61 |
|
62 | detailsBuffer = Crypto.toBuffer(JSON.stringify(item.details), 'utf8')
|
63 | overviewBuffer = Crypto.toBuffer(JSON.stringify(item.overview), 'utf8')
|
64 |
|
65 | masterKey = new Opdata(master.encryption, master.hmac)
|
66 | overviewKey = new Opdata(overview.encryption, overview.hmac)
|
67 | itemKey = new Opdata(encryptionKey, hmacKey)
|
68 |
|
69 | item.k = masterKey.encrypt('itemKey', encryptionAndHmacKey)
|
70 | item.d = itemKey.encrypt('item', detailsBuffer)
|
71 | item.o = overviewKey.encrypt('item', overviewBuffer)
|
72 |
|
73 | ###
|
74 |
|
75 | return item
|
76 |
|
77 |
|
78 | ###*
|
79 | * Create a new Item instance.
|
80 | * @constructor
|
81 | * @param {Object} [attrs] Any attributes to load into the item
|
82 | ###
|
83 | constructor: (attrs) ->
|
84 | if attrs?
|
85 | for key, attr of attrs
|
86 | @[key] = attr
|
87 |
|
88 |
|
89 | ###*
|
90 | * Load attributes from the exported format
|
91 | * @param {Object} data Data to load
|
92 | * @return {this}
|
93 | ###
|
94 | load: (data) ->
|
95 | for key in ['category', 'created', 'folder', 'tx', 'updated', 'uuid']
|
96 | if data[key]? then @[key] = data[key]
|
97 |
|
98 | for key in ['d', 'hmac', 'k', 'o']
|
99 | continue unless data[key]?
|
100 | @[key] = Crypto.fromBase64(data[key])
|
101 |
|
102 | return this
|
103 |
|
104 |
|
105 | ###*
|
106 | * Decrypt the overview data of an item.
|
107 | * @param {Opdata} overviewKey An Opdata profile key made with the
|
108 | * keychain's overview keys. Used to decrypt
|
109 | * the overview data.
|
110 | * @return {Object} The overview data.
|
111 | ###
|
112 | decryptOverview: (overviewKey) ->
|
113 | json = overviewKey.decrypt('item', @o)
|
114 | @overview = JSON.parse(json)
|
115 |
|
116 |
|
117 | encryptOverview: (overviewKey) ->
|
118 | json = JSON.stringify(@overview)
|
119 | buffer = Crypto.toBuffer(json)
|
120 | @o = overviewKey.encrypt('item', buffer)
|
121 | return @o
|
122 |
|
123 |
|
124 | ###*
|
125 | * Calculate the hmac of the item
|
126 | * TODO: Find out why it doesn't work...
|
127 | * @param {Buffer} key The master hmac key
|
128 | * @return {String} The hmac of the item encoded in hex
|
129 | ###
|
130 | calculateHmac: (key) ->
|
131 | dataToHmac = ""
|
132 | for element, data of @toJSON()
|
133 | continue if element is "hmac"
|
134 | dataToHmac += element + data
|
135 |
|
136 | dataToHmac = new Buffer(dataToHmac, 'utf8')
|
137 | hmac = Crypto.hmac(dataToHmac, key, 256)
|
138 |
|
139 | console.log hmac
|
140 | console.log @hmac.toString('hex')
|
141 |
|
142 |
|
143 |
|
144 | ###*
|
145 | * Decrypt the item details.
|
146 | * @param {Object} master The keychain's master keys. Used to decrypt the encryption keys.
|
147 | * @return {Object} The item details.
|
148 | ###
|
149 | decryptDetails: (masterKey) ->
|
150 |
|
151 | # Decrypt item keys
|
152 | keys = masterKey.decrypt('itemKey', @k)
|
153 | itemKey = new Opdata(keys[0], keys[1])
|
154 |
|
155 | # Decrypt item details
|
156 | details = itemKey.decrypt('item', @d)
|
157 | return JSON.parse(details)
|
158 |
|
159 |
|
160 | encryptDetails: (masterKey, details) ->
|
161 |
|
162 | keys = masterKey.decrypt('itemKey', @k)
|
163 | itemKey = new Opdata(keys[0], keys[1])
|
164 |
|
165 | json = JSON.stringify(details)
|
166 | buffer = Crypto.toBuffer(json)
|
167 | @d = itemKey.encrypt('item', buffer)
|
168 | return @d
|
169 |
|
170 |
|
171 | ###*
|
172 | * Turn an item into a JSON object.
|
173 | * @return {Object} The JSON object.
|
174 | ###
|
175 | toJSON: ->
|
176 | category: @category
|
177 | created: @created
|
178 | d: @d?.toString('base64')
|
179 | # folder: ""
|
180 | hmac: @hmac?.toString('base64')
|
181 | k: @k?.toString('base64')
|
182 | o: @o?.toString('base64')
|
183 | tx: @tx
|
184 | updated: @updated
|
185 | uuid: @uuid
|
186 |
|
187 |
|
188 | ###*
|
189 | * Check to see if an item matches a query. Used for filtering items.
|
190 | * @param {String} query The search query.
|
191 | * @return {Boolean} Whether or not the item matches the query.
|
192 | ###
|
193 | match: (query) =>
|
194 | query = query.toLowerCase()
|
195 | @overview.title.toLowerCase().match(query)
|
196 |
|
197 |
|
198 | class Note extends Item
|
199 | category: "003"
|
200 |
|
201 | set: (data) ->
|
202 | @details.notesPlain = data
|
203 | @overview.notesPlain = data[0..79]
|
204 |
|
205 |
|
206 |
|
207 | module.exports = Item
|