Merge remote-tracking branch 'yury/owner-password' into fixpassword

This commit is contained in:
Jakob Miland 2012-05-14 17:57:28 +02:00
commit 8f9b086d71
2 changed files with 130 additions and 17 deletions

View File

@ -419,13 +419,14 @@ var CipherTransform = (function CipherTransformClosure() {
})();
var CipherTransformFactory = (function CipherTransformFactoryClosure() {
var defaultPasswordBytes = new Uint8Array([
0x28, 0xBF, 0x4E, 0x5E, 0x4E, 0x75, 0x8A, 0x41,
0x64, 0x00, 0x4E, 0x56, 0xFF, 0xFA, 0x01, 0x08,
0x2E, 0x2E, 0x00, 0xB6, 0xD0, 0x68, 0x3E, 0x80,
0x2F, 0x0C, 0xA9, 0xFE, 0x64, 0x53, 0x69, 0x7A]);
function prepareKeyData(fileId, password, ownerPassword, userPassword,
flags, revision, keyLength, encryptMetadata) {
var defaultPasswordBytes = new Uint8Array([
0x28, 0xBF, 0x4E, 0x5E, 0x4E, 0x75, 0x8A, 0x41,
0x64, 0x00, 0x4E, 0x56, 0xFF, 0xFA, 0x01, 0x08,
0x2E, 0x2E, 0x00, 0xB6, 0xD0, 0x68, 0x3E, 0x80,
0x2F, 0x0C, 0xA9, 0xFE, 0x64, 0x53, 0x69, 0x7A]);
var hashData = new Uint8Array(100), i = 0, j, n;
if (password) {
n = Math.min(32, password.length);
@ -462,9 +463,8 @@ var CipherTransformFactory = (function CipherTransformFactoryClosure() {
var cipher, checkData;
if (revision >= 3) {
// padded password in hashData, we can use this array for user
// password check
i = 32;
for (i = 0; i < 32; ++i)
hashData[i] = defaultPasswordBytes[i];
for (j = 0, n = fileId.length; j < n; ++j)
hashData[i++] = fileId[j];
cipher = new ARCFourCipher(encryptionKey);
@ -477,16 +477,53 @@ var CipherTransformFactory = (function CipherTransformFactoryClosure() {
cipher = new ARCFourCipher(derivedKey);
checkData = cipher.encryptBlock(checkData);
}
for (j = 0, n = checkData.length; j < n; ++j) {
if (userPassword[j] != checkData[j])
return null;
}
} else {
cipher = new ARCFourCipher(encryptionKey);
checkData = cipher.encryptBlock(hashData.subarray(0, 32));
}
for (j = 0, n = checkData.length; j < n; ++j) {
if (userPassword[j] != checkData[j])
error('incorrect password');
checkData = cipher.encryptBlock(defaultPasswordBytes);
for (j = 0, n = checkData.length; j < n; ++j) {
if (userPassword[j] != checkData[j])
return null;
}
}
return encryptionKey;
}
function decodeUserPassword(password, ownerPassword, revision, keyLength) {
var hashData = new Uint8Array(32), i = 0, j, n;
n = Math.min(32, password.length);
for (; i < n; ++i)
hashData[i] = password[i];
j = 0;
while (i < 32) {
hashData[i++] = defaultPasswordBytes[j++];
}
var hash = calculateMD5(hashData, 0, i);
var keyLengthInBytes = keyLength >> 3;
if (revision >= 3) {
for (j = 0; j < 50; ++j) {
hash = calculateMD5(hash, 0, hash.length);
}
}
var cipher, userPassword;
if (revision >= 3) {
userPassword = ownerPassword;
var derivedKey = new Uint8Array(keyLengthInBytes), k;
for (j = 19; j >= 0; j--) {
for (k = 0; k < keyLengthInBytes; ++k)
derivedKey[k] = hash[k] ^ j;
cipher = new ARCFourCipher(derivedKey);
userPassword = cipher.encryptBlock(userPassword);
}
} else {
cipher = new ARCFourCipher(hash.subarray(0, keyLengthInBytes));
userPassword = cipher.encryptBlock(ownerPassword);
}
return userPassword;
}
var identityName = new Name('Identity');
@ -516,10 +553,23 @@ var CipherTransformFactory = (function CipherTransformFactoryClosure() {
if (password)
passwordBytes = stringToBytes(password);
this.encryptionKey = prepareKeyData(fileIdBytes, passwordBytes,
ownerPassword, userPassword,
flags, revision,
keyLength, encryptMetadata);
var encryptionKey = prepareKeyData(fileIdBytes, passwordBytes,
ownerPassword, userPassword, flags,
revision, keyLength, encryptMetadata);
if (!encryptionKey && password) {
// Attempting use the password as an owner password
var decodedPassword = decodeUserPassword(passwordBytes, ownerPassword,
revision, keyLength);
encryptionKey = prepareKeyData(fileIdBytes, decodedPassword,
ownerPassword, userPassword, flags,
revision, keyLength, encryptMetadata);
}
if (!encryptionKey)
error('incorrect password or encryption data');
this.encryptionKey = encryptionKey;
if (algorithm == 4) {
this.cf = dict.get('CF');
this.stmf = dict.get('StmF') || identityName;

View File

@ -185,3 +185,66 @@ describe('crypto', function() {
});
});
describe('CipherTransformFactory', function() {
function DictMock(map) {
this.map = map;
}
DictMock.prototype = {
get: function(key) {
return this.map[key];
}
};
var map1 = {
Filter: new Name('Standard'),
V: 2,
Length: 128,
O: unescape('%80%C3%04%96%91o%20sl%3A%E6%1B%13T%91%F2%0DV%12%E3%FF%5E%BB%' +
'E9VO%D8k%9A%CA%7C%5D'),
U: unescape('j%0C%8D%3EY%19%00%BCjd%7D%91%BD%AA%00%18%00%00%00%00%00%00%0' +
'0%00%00%00%00%00%00%00%00%00'),
P: -1028,
R: 3
};
var fileID1 = unescape('%F6%C6%AF%17%F3rR%8DRM%9A%80%D1%EF%DF%18');
var map2 = {
Filter: new Name('Standard'),
V: 4,
Length: 128,
O: unescape('sF%14v.y5%27%DB%97%0A5%22%B3%E1%D4%AD%BD%9B%3C%B4%A5%89u%15%' +
'B2Y%F1h%D9%E9%F4'),
U: unescape('%93%04%89%A9%BF%8AE%A6%88%A2%DB%C2%A0%A8gn%00%00%00%00%00%00' +
'%00%00%00%00%00%00%00%00%00%00'),
P: -1084,
R: 4
};
var fileID2 = unescape('%3CL_%3AD%96%AF@%9A%9D%B3%3Cx%1Cv%AC');
describe('#ctor', function() {
it('should accept user password', function() {
var factory = new CipherTransformFactory(new DictMock(map1), fileID1,
'123456');
});
it('should accept owner password', function() {
var factory = new CipherTransformFactory(new DictMock(map1), fileID1,
'654321');
});
it('should not accept wrong password', function() {
var thrown = false;
try {
var factory = new CipherTransformFactory(new DictMock(map1), fileID1,
'wrong');
} catch (e) {
thrown = true;
}
expect(thrown).toEqual(true);
});
it('should accept no password', function() {
var factory = new CipherTransformFactory(new DictMock(map2), fileID2);
});
});
});