diff options
author | Marius David Wieschollek <passwords.public@mdns.eu> | 2020-12-27 20:43:01 +0300 |
---|---|---|
committer | Marius David Wieschollek <passwords.public@mdns.eu> | 2020-12-27 20:43:01 +0300 |
commit | a9311b7569fe54008bbdb376a7a5845608d250b9 (patch) | |
tree | 83767c833517c7dfafc70ab8e19fe1b31e186994 /src | |
parent | 39f832d056b3e386d8934ca2e2840abde83f377d (diff) |
Fix issues with encrypted object creation
Signed-off-by: Marius David Wieschollek <passwords.public@mdns.eu>
Diffstat (limited to 'src')
-rw-r--r-- | src/ClassLoader/DefaultClassLoader.js | 2 | ||||
-rw-r--r-- | src/Collection/AbstractCollection.js | 4 | ||||
-rw-r--r-- | src/Converter/AbstractConverter.js | 2 | ||||
-rw-r--r-- | src/Converter/PasswordConverter.js | 17 | ||||
-rw-r--r-- | src/Encryption/CSEv1Encryption.js | 12 | ||||
-rw-r--r-- | src/Services/HashService.js | 100 | ||||
-rw-r--r-- | src/Services/ModelService.js | 2 |
7 files changed, 127 insertions, 12 deletions
diff --git a/src/ClassLoader/DefaultClassLoader.js b/src/ClassLoader/DefaultClassLoader.js index 5cc8bf0..18c5dbd 100644 --- a/src/ClassLoader/DefaultClassLoader.js +++ b/src/ClassLoader/DefaultClassLoader.js @@ -63,6 +63,7 @@ import ChallengeTypeNotSupported from "../Exception/ChallengeTypeNotSupported"; import ConfigurationError from "../Exception/ConfigruationError"; import MissingEncryptionKeyError from "../Exception/Encryption/MissingEncryptionKeyError"; import InvalidEncryptedTextLength from "../Exception/Encryption/InvalidEncryptedTextLength"; +import HashService from "../Services/HashService"; export default class DefaultClassLoader extends BasicClassLoader { @@ -119,6 +120,7 @@ export default class DefaultClassLoader extends BasicClassLoader { 'keychain.csev1': (k, p) => { return new CSEv1Keychain(this.getInstance('classes'), k, p); }, + 'service.hash' : () => { return new HashService(this.getInstance('classes')); }, 'service.model' : () => { return new ModelService(this.getInstance('classes')); }, 'service.password': () => { return new PasswordService(this.getInstance('client')); }, diff --git a/src/Collection/AbstractCollection.js b/src/Collection/AbstractCollection.js index 41acbd5..d7de25c 100644 --- a/src/Collection/AbstractCollection.js +++ b/src/Collection/AbstractCollection.js @@ -138,7 +138,7 @@ export default class AbstractCollection { } /** - * @return {String[]} + * @return {Object} */ toJSON() { let json = []; @@ -147,7 +147,7 @@ export default class AbstractCollection { json.push(this._converter.toObject(element)); } - return JSON.stringify(json); + return json; } [Symbol.iterator]() { diff --git a/src/Converter/AbstractConverter.js b/src/Converter/AbstractConverter.js index 0622aa7..3b471bf 100644 --- a/src/Converter/AbstractConverter.js +++ b/src/Converter/AbstractConverter.js @@ -89,7 +89,7 @@ export default class AbstractConverter { * @api */ async toEncryptedData(model) { - let data = this.toObject(model); + let data = await this.toObject(model); if(data.cseType === 'none') { return await this._api.getInstance('encryption.none').encrypt(data, this._type); diff --git a/src/Converter/PasswordConverter.js b/src/Converter/PasswordConverter.js index 2874ebf..13e16e5 100644 --- a/src/Converter/PasswordConverter.js +++ b/src/Converter/PasswordConverter.js @@ -8,8 +8,9 @@ export default class PasswordConverter extends AbstractConverter { */ constructor(api) { super(api, 'password'); - /** @type CustomFieldConverter **/ + /** @type {CustomFieldConverter} **/ this._customFieldConverter = this._api.getInstance('converter.field'); + this._hashService = /** @type {HashService} **/ this._api.getInstance('service.hash'); } /** @@ -37,4 +38,18 @@ export default class PasswordConverter extends AbstractConverter { return this._api.getClass(`model.${this._type}`, clone, this._api); } + + /** + * + * @param {(Password|AbstractRevisionModel)} model + * @returns {Promise<void>} + */ + async toObject(model) { + let data = super.toObject(model); + + data.customFields = this._customFieldConverter.toJSON(model.getCustomFields()); + data.hash = await this._hashService.getHash(model.getPassword(), this._hashService.HASH_SHA_1); + + return data; + } }
\ No newline at end of file diff --git a/src/Encryption/CSEv1Encryption.js b/src/Encryption/CSEv1Encryption.js index 06e5891..10e29a9 100644 --- a/src/Encryption/CSEv1Encryption.js +++ b/src/Encryption/CSEv1Encryption.js @@ -49,11 +49,10 @@ export default class CSEv1Encryption { let fields = this.fields[type], key = this._keychain.getCurrentKey(); - for(let i = 0; i < fields.length; i++) { - let field = fields[i], - data = object[field]; + for(let field of fields) { + let data = object[field]; - if(data === null || data.length === 0) continue; + if(data === null || data === undefined || data.length === 0) continue; object[field] = this._encryptString(data, key); } @@ -78,9 +77,8 @@ export default class CSEv1Encryption { let fields = this.fields[type], key = this._keychain.getKey(object.cseKey); - for(let i = 0; i < fields.length; i++) { - let field = fields[i], - data = object[field]; + for(let field of fields) { + let data = object[field]; if(data === null || data.length === 0) continue; object[field] = this._decryptString(data, key); diff --git a/src/Services/HashService.js b/src/Services/HashService.js new file mode 100644 index 0000000..7942ae7 --- /dev/null +++ b/src/Services/HashService.js @@ -0,0 +1,100 @@ +import sodium from "libsodium-wrappers"; + +export default class HashService { + + get HASH_SHA_1() { + return 'SHA-1'; + } + + get HASH_SHA_256() { + return 'SHA-256'; + } + + get HASH_SHA_384() { + return 'SHA-384'; + } + + get HASH_SHA_512() { + return 'SHA-512'; + } + + get HASH_BLAKE2B() { + return 'BLAKE2b'; + } + + get HASH_BLAKE2B_224() { + return 'BLAKE2b-224'; + } + + get HASH_BLAKE2B_256() { + return 'BLAKE2b-256'; + } + + get HASH_BLAKE2B_384() { + return 'BLAKE2b-384'; + } + + get HASH_BLAKE2B_512() { + return 'BLAKE2b-512'; + } + + get HASH_ARGON2() { + return 'Argon2'; + } + + constructor(classLoader) { + this._ready = classLoader.getClass('state.boolean', false); + + sodium.ready.then(() => {this._ready.set(true);}); + } + + /** + * Generate a hash of the given value with the given algorithm + * + * @param {String} value + * @param {String} [algorithm=SHA-1] + * @returns {Promise<string>} + */ + async getHash(value, algorithm = 'SHA-1') { + await this._ready.awaitTrue(); + + if([this.HASH_SHA_1, this.HASH_SHA_256, this.HASH_SHA_384, this.HASH_SHA_512].indexOf(algorithm) !== -1) { + return await this._makeShaHash(value, algorithm); + } else if(algorithm.substr(0, 7) === this.HASH_BLAKE2B) { + return this._makeBlake2bHash(algorithm, value); + } else if(algorithm === this.HASH_ARGON2) { + return sodium.crypto_pwhash_str(value, sodium.crypto_pwhash_OPSLIMIT_MIN, sodium.crypto_pwhash_MEMLIMIT_MIN); + } + } + + /** + * + * @param {String} value + * @param {String} algorithm + * @returns {Promise<String>} + * @private + */ + async _makeShaHash(value, algorithm) { + let msgBuffer = new TextEncoder('utf-8').encode(value), + hashBuffer = await crypto.subtle.digest(algorithm, msgBuffer); + + return sodium.to_hex(new Uint8Array(hashBuffer)); + } + + /** + * @param {String} algorithm + * @param {String} value + * @returns {String} + * @private + */ + _makeBlake2bHash(algorithm, value) { + let bytes = sodium.crypto_generichash_BYTES_MAX; + if(algorithm.indexOf('-') !== -1) { + bytes = algorithm.split('-')[1]; + if(sodium.crypto_generichash_BYTES_MAX < bytes) bytes = sodium.crypto_generichash_BYTES_MAX; + if(sodium.crypto_generichash_BYTES_MIN > bytes) bytes = sodium.crypto_generichash_BYTES_MIN; + } + + return sodium.to_hex(sodium.crypto_generichash(bytes, sodium.from_string(value))); + } +}
\ No newline at end of file diff --git a/src/Services/ModelService.js b/src/Services/ModelService.js index 395255e..211b44d 100644 --- a/src/Services/ModelService.js +++ b/src/Services/ModelService.js @@ -22,6 +22,7 @@ export default class ModelService { addModel(type, model) { this._cache.set(`${type}.${model.getId()}`, model); + this._cache.set(`${type}.${model.getId()}.${model.getRevision()}`, model); // @TODO update related models } @@ -254,7 +255,6 @@ export default class ModelService { _mergeStandardProperties(model, newModel, excludeProperties) { if(model.getRevision() === newModel.getRevision()) { model.setUpdated(newModel.getUpdated()); - return; } excludeProperties.push('id, revisions') |