Diffstat (limited to 'frontend/delta/js/Clipperz/PM/Crypto.js') (more/less context) (ignore whitespace changes)
-rw-r--r-- | frontend/delta/js/Clipperz/PM/Crypto.js | 546 |
1 files changed, 546 insertions, 0 deletions
diff --git a/frontend/delta/js/Clipperz/PM/Crypto.js b/frontend/delta/js/Clipperz/PM/Crypto.js new file mode 100644 index 0000000..7edf17f --- a/dev/null +++ b/frontend/delta/js/Clipperz/PM/Crypto.js | |||
@@ -0,0 +1,546 @@ | |||
1 | /* | ||
2 | |||
3 | Copyright 2008-2013 Clipperz Srl | ||
4 | |||
5 | This file is part of Clipperz, the online password manager. | ||
6 | For further information about its features and functionalities please | ||
7 | refer to http://www.clipperz.com. | ||
8 | |||
9 | * Clipperz is free software: you can redistribute it and/or modify it | ||
10 | under the terms of the GNU Affero General Public License as published | ||
11 | by the Free Software Foundation, either version 3 of the License, or | ||
12 | (at your option) any later version. | ||
13 | |||
14 | * Clipperz is distributed in the hope that it will be useful, but | ||
15 | WITHOUT ANY WARRANTY; without even the implied warranty of | ||
16 | MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. | ||
17 | See the GNU Affero General Public License for more details. | ||
18 | |||
19 | * You should have received a copy of the GNU Affero General Public | ||
20 | License along with Clipperz. If not, see http://www.gnu.org/licenses/. | ||
21 | |||
22 | */ | ||
23 | |||
24 | if (typeof(Clipperz) == 'undefined') { Clipperz = {}; } | ||
25 | if (typeof(Clipperz.PM) == 'undefined') { Clipperz.PM = {}; } | ||
26 | if (typeof(Clipperz.PM.Crypto) == 'undefined') { Clipperz.PM.Crypto = {}; } | ||
27 | |||
28 | Clipperz.PM.Crypto.VERSION = "0.2"; | ||
29 | Clipperz.PM.Crypto.NAME = "Clipperz.PM.Crypto"; | ||
30 | |||
31 | Clipperz.PM.Crypto.encryptingFunctions = {}; | ||
32 | |||
33 | MochiKit.Base.update(Clipperz.PM.Crypto, { | ||
34 | |||
35 | '__repr__': function () { | ||
36 | return "[" + this.NAME + " " + this.VERSION + "]"; | ||
37 | }, | ||
38 | |||
39 | //------------------------------------------------------------------------- | ||
40 | |||
41 | 'toString': function () { | ||
42 | return this.__repr__(); | ||
43 | }, | ||
44 | |||
45 | //------------------------------------------------------------------------- | ||
46 | /* | ||
47 | 'communicationProtocol': { | ||
48 | 'currentVersion': '0.2', | ||
49 | 'versions': { | ||
50 | '0.1': Clipperz.PM.Connection.SRP['1.0'],//Clipperz.Crypto.SRP.versions['1.0'].Connection, | ||
51 | '0.2': Clipperz.PM.Connection.SRP['1.1']//Clipperz.Crypto.SRP.versions['1.1'].Connection | ||
52 | }, | ||
53 | 'fallbackVersions': { | ||
54 | 'current':'0.1', | ||
55 | '0.2': '0.1', | ||
56 | '0.1': null | ||
57 | } | ||
58 | }, | ||
59 | */ | ||
60 | //------------------------------------------------------------------------- | ||
61 | |||
62 | 'encryptingFunctions': { | ||
63 | 'currentVersion': '0.4', | ||
64 | 'versions': { | ||
65 | |||
66 | //##################################################################### | ||
67 | |||
68 | '0.1': { | ||
69 | 'encrypt': function(aKey, aValue) { | ||
70 | return Clipperz.Crypto.Base.encryptUsingSecretKey(aKey, Clipperz.Base.serializeJSON(aValue)); | ||
71 | }, | ||
72 | |||
73 | 'deferredEncrypt': function(aKey, aValue) { | ||
74 | var deferredResult; | ||
75 | |||
76 | deferredResult = new Clipperz.Async.Deferred("Crypto[0.1].deferredEncrypt"); | ||
77 | deferredResult.addCallback(Clipperz.PM.Crypto.encryptingFunctions.versions['0.1'].encrypt, aKey, aValue); | ||
78 | deferredResult.callback(); | ||
79 | |||
80 | return deferredResult; | ||
81 | }, | ||
82 | |||
83 | 'decrypt': function(aKey, aValue) { | ||
84 | var result; | ||
85 | |||
86 | if (aValue != null) { | ||
87 | result = Clipperz.Base.evalJSON(Clipperz.Crypto.Base.decryptUsingSecretKey(aKey, aValue)); | ||
88 | } else { | ||
89 | result = null; | ||
90 | } | ||
91 | |||
92 | return result; | ||
93 | }, | ||
94 | |||
95 | 'deferredDecrypt': function(aKey, aValue) { | ||
96 | var deferredResult; | ||
97 | |||
98 | deferredResult = new Clipperz.Async.Deferred("Crypto.[0.1].deferredDecrypt"); | ||
99 | deferredResult.addCallback(Clipperz.PM.Crypto.encryptingFunctions.versions['0.1'].decrypt, aKey, aValue); | ||
100 | deferredResult.callback(); | ||
101 | |||
102 | return deferredResult; | ||
103 | }, | ||
104 | |||
105 | 'hash': function(aValue) { | ||
106 | var result; | ||
107 | var strngResult; | ||
108 | |||
109 | stringResult = Clipperz.Crypto.Base.computeHashValue(aValue.asString()); //!!!!!!! | ||
110 | result = new Clipperz.ByteArray("0x" + stringResult); | ||
111 | |||
112 | return result; | ||
113 | }, | ||
114 | |||
115 | 'deriveKey': function(aStringValue) { | ||
116 | return Clipperz.Crypto.Base.computeHashValue(aStringValue); | ||
117 | } | ||
118 | }, | ||
119 | |||
120 | //##################################################################### | ||
121 | |||
122 | '0.2': { | ||
123 | 'encrypt': function(aKey, aValue, aNonce) { | ||
124 | var result; | ||
125 | varkey, value; | ||
126 | var dataToEncrypt; | ||
127 | var encryptedData; | ||
128 | |||
129 | key = Clipperz.Crypto.SHA.sha_d256(new Clipperz.ByteArray(aKey)); | ||
130 | value = new Clipperz.ByteArray(Clipperz.Base.serializeJSON(aValue)); | ||
131 | dataToEncrypt = Clipperz.Crypto.SHA.sha_d256(value).appendBlock(value); | ||
132 | encryptedData = Clipperz.Crypto.AES.encrypt(key, dataToEncrypt, aNonce); | ||
133 | result = encryptedData.toBase64String(); | ||
134 | |||
135 | return result; | ||
136 | }, | ||
137 | |||
138 | 'deferredEncrypt': function(aKey, aValue, aNonce) { | ||
139 | var deferredResult; | ||
140 | varkey, value; | ||
141 | var dataToEncrypt; | ||
142 | // var encryptedData; | ||
143 | |||
144 | key = Clipperz.Crypto.SHA.sha_d256(new Clipperz.ByteArray(aKey)); | ||
145 | value = new Clipperz.ByteArray(Clipperz.Base.serializeJSON(aValue)); | ||
146 | dataToEncrypt = Clipperz.Crypto.SHA.sha_d256(value).appendBlock(value); | ||
147 | |||
148 | deferredResult = new Clipperz.Async.Deferred("Crypto[0.2].deferredEncrypt") | ||
149 | deferredResult.addCallback(Clipperz.Crypto.AES.deferredEncrypt, key, dataToEncrypt, aNonce); | ||
150 | deferredResult.addCallback(function(aResult) { | ||
151 | return aResult.toBase64String(); | ||
152 | }) | ||
153 | deferredResult.callback(); | ||
154 | |||
155 | return deferredResult; | ||
156 | }, | ||
157 | |||
158 | 'decrypt': function(aKey, aValue) { | ||
159 | var result; | ||
160 | |||
161 | if (aValue != null) { | ||
162 | var key, value; | ||
163 | var decryptedData; | ||
164 | var decryptedValue; | ||
165 | |||
166 | key = Clipperz.Crypto.SHA.sha_d256(new Clipperz.ByteArray(aKey)); | ||
167 | value = new Clipperz.ByteArray().appendBase64String(aValue); | ||
168 | |||
169 | decryptedData = Clipperz.Crypto.AES.decrypt(key, value); | ||
170 | decryptedValue = decryptedData.split((256/8)); | ||
171 | |||
172 | try { | ||
173 | result = Clipperz.Base.evalJSON(decryptedValue.asString()); | ||
174 | } catch (exception) { | ||
175 | Clipperz.logError("Error while decrypting data [1]"); | ||
176 | throw Clipperz.Crypto.Base.exception.CorruptedMessage; | ||
177 | } | ||
178 | } else { | ||
179 | result = null; | ||
180 | } | ||
181 | |||
182 | return result; | ||
183 | }, | ||
184 | |||
185 | 'deferredDecrypt': function(aKey, aValue) { | ||
186 | var result; | ||
187 | |||
188 | if (aValue != null) { | ||
189 | var deferredResult; | ||
190 | var key, value; | ||
191 | // var decryptedData; | ||
192 | |||
193 | key = Clipperz.Crypto.SHA.sha_d256(new Clipperz.ByteArray(aKey)); | ||
194 | value = new Clipperz.ByteArray().appendBase64String(aValue); | ||
195 | |||
196 | deferredResult = new Clipperz.Async.Deferred("Crypto.[0.2].deferredDecrypt"); | ||
197 | deferredResult.addCallback(Clipperz.Crypto.AES.deferredDecrypt, key, value); | ||
198 | deferredResult.addCallback(function(aResult) { | ||
199 | var result; | ||
200 | var decryptedData; | ||
201 | |||
202 | decryptedData = aResult.split((256/8)); | ||
203 | |||
204 | try { | ||
205 | result = Clipperz.Base.evalJSON(decryptedData.asString()); | ||
206 | } catch (exception) { | ||
207 | Clipperz.logError("Error while decrypting data [2]"); | ||
208 | throw Clipperz.Crypto.Base.exception.CorruptedMessage; | ||
209 | } | ||
210 | |||
211 | return result; | ||
212 | }) | ||
213 | deferredResult.callback(); | ||
214 | |||
215 | result = deferredResult; | ||
216 | } else { | ||
217 | result = MochiKit.Async.succeed(null); | ||
218 | } | ||
219 | |||
220 | return result; | ||
221 | }, | ||
222 | |||
223 | 'hash': Clipperz.Crypto.SHA.sha_d256, | ||
224 | |||
225 | 'deriveKey': function(aStringValue) { | ||
226 | varbyteData; | ||
227 | var result; | ||
228 | |||
229 | byteData = new Clipperz.ByteArray(aStringValue); | ||
230 | result = Clipperz.Crypto.SHA.sha_d256(byteData); | ||
231 | |||
232 | return result; | ||
233 | } | ||
234 | }, | ||
235 | |||
236 | //##################################################################### | ||
237 | |||
238 | '0.3': { | ||
239 | 'encrypt': function(aKey, aValue, aNonce) { | ||
240 | var result; | ||
241 | varkey, value; | ||
242 | var data; | ||
243 | var dataToEncrypt; | ||
244 | var encryptedData; | ||
245 | |||
246 | key = Clipperz.Crypto.SHA.sha_d256(new Clipperz.ByteArray(aKey)); | ||
247 | value = Clipperz.Base.serializeJSON(aValue); | ||
248 | data = new Clipperz.ByteArray(value); | ||
249 | encryptedData = Clipperz.Crypto.AES.encrypt(key, data, aNonce); | ||
250 | result = encryptedData.toBase64String(); | ||
251 | |||
252 | return result; | ||
253 | }, | ||
254 | |||
255 | 'deferredEncrypt': function(aKey, aValue, aNonce) { | ||
256 | var deferredResult; | ||
257 | varkey, value; | ||
258 | var data; | ||
259 | var dataToEncrypt; | ||
260 | var encryptedData; | ||
261 | |||
262 | key = Clipperz.Crypto.SHA.sha_d256(new Clipperz.ByteArray(aKey)); | ||
263 | value = Clipperz.Base.serializeJSON(aValue); | ||
264 | data = new Clipperz.ByteArray(value); | ||
265 | |||
266 | deferredResult = new Clipperz.Async.Deferred("Crypto[0.3].deferredEncrypt") | ||
267 | deferredResult.addCallback(Clipperz.Crypto.AES.deferredEncrypt, key, data, aNonce); | ||
268 | deferredResult.addCallback(function(aResult) { | ||
269 | return aResult.toBase64String(); | ||
270 | }) | ||
271 | deferredResult.callback(); | ||
272 | |||
273 | return deferredResult; | ||
274 | }, | ||
275 | |||
276 | 'decrypt': function(aKey, aValue) { | ||
277 | var result; | ||
278 | |||
279 | if (aValue != null) { | ||
280 | var key, value; | ||
281 | var decryptedData; | ||
282 | |||
283 | key = Clipperz.Crypto.SHA.sha_d256(new Clipperz.ByteArray(aKey)); | ||
284 | value = new Clipperz.ByteArray().appendBase64String(aValue); | ||
285 | |||
286 | decryptedData = Clipperz.Crypto.AES.decrypt(key, value); | ||
287 | |||
288 | value = decryptedData.asString(); | ||
289 | try { | ||
290 | result = Clipperz.Base.evalJSON(value); | ||
291 | } catch (exception) { | ||
292 | Clipperz.logError("Error while decrypting data [3]"); | ||
293 | throw Clipperz.Crypto.Base.exception.CorruptedMessage; | ||
294 | } | ||
295 | } else { | ||
296 | result = null; | ||
297 | } | ||
298 | |||
299 | return result; | ||
300 | }, | ||
301 | |||
302 | 'deferredDecrypt': function(aKey, aValue) { | ||
303 | var deferredResult; | ||
304 | |||
305 | deferredResult = new Clipperz.Async.Deferred("Crypto[0.3].deferredDecrypt", {trace: false}); | ||
306 | // now = new Date; | ||
307 | |||
308 | if (aValue != null) { | ||
309 | var key, value; | ||
310 | // var decryptedData; | ||
311 | |||
312 | key = Clipperz.Crypto.SHA.sha_d256(new Clipperz.ByteArray(aKey)); | ||
313 | value = new Clipperz.ByteArray().appendBase64String(aValue); | ||
314 | |||
315 | deferredResult.addCallback(Clipperz.Crypto.AES.deferredDecrypt, key, value); | ||
316 | deferredResult.addCallback(MochiKit.Async.wait, 0.1); | ||
317 | deferredResult.addCallback(function(aResult) { | ||
318 | return aResult.asString(); | ||
319 | }); | ||
320 | deferredResult.addCallback(MochiKit.Async.wait, 0.1); | ||
321 | deferredResult.addCallback(Clipperz.Base.evalJSON); | ||
322 | deferredResult.addErrback(function(anError) { | ||
323 | console.log("PIPPO_1", anError) | ||
324 | Clipperz.logError("Error while decrypting data [4]"); | ||
325 | throw Clipperz.Crypto.Base.exception.CorruptedMessage; | ||
326 | }) | ||
327 | } else { | ||
328 | deferredResult.addCallback(function() { | ||
329 | return null; | ||
330 | }); | ||
331 | } | ||
332 | deferredResult.callback(); | ||
333 | |||
334 | return deferredResult; | ||
335 | }, | ||
336 | |||
337 | 'hash': Clipperz.Crypto.SHA.sha_d256, | ||
338 | |||
339 | 'deriveKey': function(aStringValue) { | ||
340 | varbyteData; | ||
341 | var result; | ||
342 | |||
343 | byteData = new Clipperz.ByteArray(aStringValue); | ||
344 | result = Clipperz.Crypto.SHA.sha_d256(byteData); | ||
345 | |||
346 | return result; | ||
347 | } | ||
348 | }, | ||
349 | |||
350 | //##################################################################### | ||
351 | |||
352 | '0.4': { | ||
353 | 'encrypt': function(aKey, aValue, aNonce) { | ||
354 | var result; | ||
355 | varkey, value; | ||
356 | var data; | ||
357 | var dataToEncrypt; | ||
358 | var encryptedData; | ||
359 | |||
360 | key = Clipperz.Crypto.SHA.sha_d256(new Clipperz.ByteArray(aKey)); | ||
361 | value = Clipperz.Base.serializeJSON(aValue); | ||
362 | data = new Clipperz.ByteArray(value); | ||
363 | encryptedData = Clipperz.Crypto.AES_2.encrypt(key, data, aNonce); | ||
364 | result = encryptedData.toBase64String(); | ||
365 | |||
366 | return result; | ||
367 | }, | ||
368 | |||
369 | 'deferredEncrypt': function(aKey, aValue, aNonce) { | ||
370 | var deferredResult; | ||
371 | varkey, value; | ||
372 | var data; | ||
373 | var dataToEncrypt; | ||
374 | var encryptedData; | ||
375 | |||
376 | key = Clipperz.Crypto.SHA.sha_d256(new Clipperz.ByteArray(aKey)); | ||
377 | value = Clipperz.Base.serializeJSON(aValue); | ||
378 | data = new Clipperz.ByteArray(value); | ||
379 | |||
380 | deferredResult = new Clipperz.Async.Deferred("Crypto[0.4].deferredEncrypt") | ||
381 | deferredResult.addCallback(Clipperz.Crypto.AES_2.deferredEncrypt, key, data, aNonce); | ||
382 | deferredResult.addCallback(function(aResult) { | ||
383 | return aResult.toBase64String(); | ||
384 | }) | ||
385 | deferredResult.callback(); | ||
386 | |||
387 | return deferredResult; | ||
388 | }, | ||
389 | |||
390 | 'decrypt': function(aKey, aValue) { | ||
391 | var result; | ||
392 | |||
393 | if (aValue != null) { | ||
394 | var key, value; | ||
395 | var decryptedData; | ||
396 | |||
397 | key = Clipperz.Crypto.SHA.sha_d256(new Clipperz.ByteArray(aKey)); | ||
398 | value = new Clipperz.ByteArray().appendBase64String(aValue); | ||
399 | |||
400 | decryptedData = Clipperz.Crypto.AES_2.decrypt(key, value); | ||
401 | |||
402 | value = decryptedData.asString(); | ||
403 | try { | ||
404 | result = Clipperz.Base.evalJSON(value); | ||
405 | } catch (exception) { | ||
406 | console.log("PIPPO_2", anError) | ||
407 | Clipperz.logError("Error while decrypting data [4]"); | ||
408 | throw Clipperz.Crypto.Base.exception.CorruptedMessage; | ||
409 | } | ||
410 | } else { | ||
411 | result = null; | ||
412 | } | ||
413 | |||
414 | return result; | ||
415 | }, | ||
416 | |||
417 | 'deferredDecrypt': function(aKey, aValue) { | ||
418 | var deferredResult; | ||
419 | |||
420 | deferredResult = new Clipperz.Async.Deferred("Crypto[0.4].deferredDecrypt", {trace: false}); | ||
421 | |||
422 | if (aValue != null) { | ||
423 | var key, value; | ||
424 | |||
425 | key = Clipperz.Crypto.SHA.sha_d256(new Clipperz.ByteArray(aKey)); | ||
426 | value = new Clipperz.ByteArray().appendBase64String(aValue); | ||
427 | |||
428 | deferredResult.addCallback(Clipperz.Crypto.AES_2.deferredDecrypt, key, value); | ||
429 | deferredResult.addCallback(MochiKit.Async.wait, 0.1); | ||
430 | deferredResult.addCallback(function(aResult) { | ||
431 | return aResult.asString(); | ||
432 | }); | ||
433 | deferredResult.addCallback(MochiKit.Async.wait, 0.1); | ||
434 | deferredResult.addCallback(Clipperz.Base.evalJSON); | ||
435 | deferredResult.addErrback(function(anError) { | ||
436 | Clipperz.logError("Error while decrypting data [4]"); | ||
437 | throw Clipperz.Crypto.Base.exception.CorruptedMessage; | ||
438 | }) | ||
439 | } else { | ||
440 | deferredResult.addCallback(function() { | ||
441 | return null; | ||
442 | }); | ||
443 | } | ||
444 | deferredResult.callback(); | ||
445 | |||
446 | return deferredResult; | ||
447 | }, | ||
448 | |||
449 | 'hash': Clipperz.Crypto.SHA.sha_d256, | ||
450 | |||
451 | 'deriveKey': function(aStringValue) { | ||
452 | varbyteData; | ||
453 | var result; | ||
454 | |||
455 | byteData = new Clipperz.ByteArray(aStringValue); | ||
456 | result = Clipperz.Crypto.SHA.sha_d256(byteData); | ||
457 | |||
458 | return result; | ||
459 | } | ||
460 | }, | ||
461 | |||
462 | //##################################################################### | ||
463 | __syntaxFix__: "syntax fix" | ||
464 | } | ||
465 | }, | ||
466 | |||
467 | //------------------------------------------------------------------------- | ||
468 | |||
469 | 'encrypt': function(aKey, aValue, aVersion) { | ||
470 | return Clipperz.PM.Crypto.encryptingFunctions.versions[aVersion].encrypt(aKey, aValue); | ||
471 | }, | ||
472 | |||
473 | 'deferredEncrypt': function(someParameters) { | ||
474 | return Clipperz.PM.Crypto.encryptingFunctions.versions[someParameters['version']].deferredEncrypt(someParameters['key'], someParameters['value']); | ||
475 | }, | ||
476 | |||
477 | //......................................................................... | ||
478 | |||
479 | 'decrypt': function(aKey, aValue, aVersion) { | ||
480 | return Clipperz.PM.Crypto.encryptingFunctions.versions[aVersion].decrypt(aKey, aValue); | ||
481 | }, | ||
482 | |||
483 | 'deferredDecrypt': function(someParameters) { | ||
484 | return Clipperz.PM.Crypto.encryptingFunctions.versions[someParameters['version']].deferredDecrypt(someParameters['key'], someParameters['value']); | ||
485 | }, | ||
486 | |||
487 | //------------------------------------------------------------------------- | ||
488 | |||
489 | 'hash': function(aValue) { | ||
490 | return Clipperz.PM.Crypto.encryptingFunctions.versions[Clipperz.PM.Crypto.encryptingFunctions.currentVersion]['hash'](aValue); | ||
491 | }, | ||
492 | |||
493 | //------------------------------------------------------------------------- | ||
494 | |||
495 | 'randomKey': function() { | ||
496 | return Clipperz.Crypto.PRNG.defaultRandomGenerator().getRandomBytes(32).toHexString().substring(2); | ||
497 | }, | ||
498 | |||
499 | //------------------------------------------------------------------------- | ||
500 | |||
501 | 'deriveKey': function(aValue) { | ||
502 | return Clipperz.PM.Crypto.encryptingFunctions.versions[Clipperz.PM.Crypto.encryptingFunctions.currentVersion].deriveKey(aValue); | ||
503 | }, | ||
504 | |||
505 | //------------------------------------------------------------------------- | ||
506 | |||
507 | 'passwordEntropy': function(aValue) { | ||
508 | var result; | ||
509 | varbitPerChar; | ||
510 | |||
511 | bitPerChar = 4; | ||
512 | if (/[a-z]/.test(aValue)) { | ||
513 | bitPerChar ++; | ||
514 | } | ||
515 | if (/[A-Z]/.test(aValue)) { | ||
516 | bitPerChar ++; | ||
517 | } | ||
518 | if (/[^a-zA-Z0-9]/.test(aValue)) { | ||
519 | bitPerChar ++; | ||
520 | } | ||
521 | |||
522 | result = aValue.length * bitPerChar; | ||
523 | |||
524 | return result; | ||
525 | }, | ||
526 | |||
527 | //------------------------------------------------------------------------- | ||
528 | |||
529 | 'nullValue': '####', | ||
530 | |||
531 | //------------------------------------------------------------------------- | ||
532 | __syntaxFix__: "syntax fix" | ||
533 | |||
534 | }); | ||
535 | |||
536 | //***************************************************************************** | ||
537 | |||
538 | //MochiKit.Base.update(Clipperz.PM.Connection.communicationProtocol.versions, { | ||
539 | //'current': Clipperz.PM.Connection.communicationProtocol.versions[Clipperz.PM.Connection.communicationProtocol.currentVersion] | ||
540 | //}); | ||
541 | |||
542 | MochiKit.Base.update(Clipperz.PM.Crypto.encryptingFunctions.versions, { | ||
543 | 'current': Clipperz.PM.Crypto.encryptingFunctions.versions[Clipperz.PM.Crypto.encryptingFunctions.currentVersion] | ||
544 | }); | ||
545 | |||
546 | //***************************************************************************** | ||