field references decoding

This commit is contained in:
antelle 2016-08-14 21:53:55 +03:00
parent f52578f6de
commit ae96fca478
4 changed files with 95 additions and 8 deletions

View File

@ -2,6 +2,9 @@
var kdbxweb = require('kdbxweb');
const ExpectedFieldRefChars = '{REF:0@I:00000000000000000000000000000000}'.split('');
const ExpectedFieldRefByteLength = ExpectedFieldRefChars.length;
kdbxweb.ProtectedValue.prototype.isProtected = true;
kdbxweb.ProtectedValue.prototype.forEachChar = function(fn) {
@ -10,30 +13,42 @@ kdbxweb.ProtectedValue.prototype.forEachChar = function(fn) {
for (var i = 0, len = value.length; i < len; i++) {
b = value[i] ^ salt[i];
if (b < 128) {
fn(b);
if (fn(b) === false) {
return;
}
continue;
}
i++; b1 = value[i] ^ salt[i];
if (i === len) { break; }
if (b >= 192 && b < 224) {
fn(((b & 0x1f) << 6) | (b1 & 0x3f));
if (fn(((b & 0x1f) << 6) | (b1 & 0x3f)) === false) {
return;
}
continue;
}
i++; b2 = value[i] ^ salt[i];
if (i === len) { break; }
if (b >= 224 && b < 240) {
fn(((b & 0xf) << 12) | ((b1 & 0x3f) << 6) | (b2 & 0x3f));
if (fn(((b & 0xf) << 12) | ((b1 & 0x3f) << 6) | (b2 & 0x3f)) === false) {
return;
}
}
i++; b3 = value[i] ^ salt[i];
if (i === len) { break; }
if (b >= 240 && b < 248) {
var c = ((b & 7) << 18) | ((b1 & 0x3f) << 12) | ((b2 & 0x3f) << 6) | (b3 & 0x3f);
if (c <= 0xffff) {
fn(c);
if (fn(c) === false) {
return;
}
} else {
c ^= 0x10000;
fn(0xd800 | (c >> 10));
fn(0xdc00 | (c & 0x3ff));
if (fn(0xd800 | (c >> 10)) === false) {
return;
}
if (fn(0xdc00 | (c & 0x3ff)) === false) {
return;
}
}
}
// skip error
@ -98,4 +113,18 @@ kdbxweb.ProtectedValue.prototype.equals = function(other) {
return true;
};
kdbxweb.ProtectedValue.prototype.isFieldReference = function() {
if (this.byteLength !== ExpectedFieldRefByteLength) {
return false;
}
let ix = 0;
this.forEachChar(ch => {
let expected = ExpectedFieldRefChars[ix++];
if (expected !== '0' && ch !== expected) {
return false;
}
});
return true;
};
module.exports = kdbxweb.ProtectedValue;

View File

@ -10,9 +10,13 @@ var Backbone = require('backbone'),
var EntryModel = Backbone.Model.extend({
defaults: {},
urlRegex: /^https?:\/\//i,
builtInFields: ['Title', 'Password', 'Notes', 'URL', 'UserName', 'TOTP Seed', 'TOTP Settings'],
urlRegex: /^https?:\/\//i,
fieldRefRegex: /^\{REF:([TNPAU])@I:(\w{32})}$/,
builtInFields: ['Title', 'Password', 'UserName', 'URL', 'Notes', 'TOTP Seed', 'TOTP Settings'],
fieldRefFields: ['title', 'password', 'user', 'url', 'notes'],
fieldRefIds: { T: 'Title', U: 'UserName', P: 'Password', A: 'URL', N: 'Notes' },
initialize: function() {
},
@ -24,7 +28,10 @@ var EntryModel = Backbone.Model.extend({
if (this.get('uuid') === entry.uuid.id) {
this._checkUpdatedEntry();
}
// we cannot calculate field references now because database index has not yet been built
this.hasFieldRefs = false;
this._fillByEntry();
this.hasFieldRefs = true;
},
_fillByEntry: function() {
@ -54,6 +61,9 @@ var EntryModel = Backbone.Model.extend({
this._buildSearchTags();
this._buildSearchColor();
this._buildAutoType();
if (this.hasFieldRefs) {
this.resolveFieldReferences();
}
},
_checkUpdatedEntry: function() {
@ -261,6 +271,45 @@ var EntryModel = Backbone.Model.extend({
return val ? compare(val, search) : false;
},
resolveFieldReferences: function() {
this.hasFieldRefs = false;
this.fieldRefFields.forEach(field => {
let fieldValue = this[field];
if (!fieldValue) {
return;
}
if (fieldValue.isProtected && fieldValue.isFieldReference()) {
fieldValue = fieldValue.getText();
}
if (typeof fieldValue !== 'string') {
return;
}
let match = fieldValue.match(this.fieldRefRegex);
if (!match) {
return;
}
this.hasFieldRefs = true;
let value = this._getReferenceValue(match[1], match[2]);
if (!value) {
return;
}
this[field] = value;
});
},
_getReferenceValue: function(fieldRefId, idStr) {
let id = new Uint8Array(16);
for (let i = 0; i < 16; i++) {
id[i] = parseInt(idStr.substr(i * 2, 2), 16);
}
let uuid = new kdbxweb.KdbxUuid(id);
let entry = this.file.getEntry(this.file.subId(uuid.id));
if (!entry) {
return undefined;
}
return entry.entry.fields[this.fieldRefIds[fieldRefId]];
},
setColor: function(color) {
this._entryModified();
this.entry.bgColor = Color.getKnownBgColor(color);

View File

@ -156,6 +156,7 @@ var FileModel = Backbone.Model.extend({
groups.add(groupModel);
}, this);
this.buildObjectMap();
this.resolveFieldReferences();
},
subId: function(id) {
@ -175,6 +176,13 @@ var FileModel = Backbone.Model.extend({
this.groupMap = groupMap;
},
resolveFieldReferences: function() {
let entryMap = this.entryMap;
Object.keys(entryMap).forEach(e => {
entryMap[e].resolveFieldReferences();
});
},
reload: function() {
this.buildObjectMap();
this.readModel();

View File

@ -13,6 +13,7 @@ Audit, generator presets, auto-type and ui improvements
`+` save displayed table columns
`+` confirmation in password change dialog
`+` inline generator keyboard management
`+` field references decoding
`-` fix app redraw in background
`-` fix idle timer on computer sleep