verify password against user password or throw

This commit is contained in:
Eliot Jones
2019-05-07 18:53:51 +01:00
parent bad57763a1
commit 53811a7d97
2 changed files with 70 additions and 2 deletions

View File

@@ -19,6 +19,10 @@
public string UserPasswordCheck { get; }
public byte[] OwnerBytes { get; }
public byte[] UserBytes { get; }
public UserAccessPermissions UserAccessPermissions { get; }
public bool IsStandardFilter => string.Equals(Filter, "Standard", StringComparison.OrdinalIgnoreCase);
@@ -45,6 +49,9 @@
UserAccessPermissions = userAccessPermissions;
Dictionary = dictionary;
EncryptMetadata = encryptMetadata;
OwnerBytes = OtherEncodings.StringAsLatin1Bytes(ownerPasswordCheck);
UserBytes = OtherEncodings.StringAsLatin1Bytes(userPasswordCheck);
}
}

View File

@@ -46,6 +46,7 @@
documentIdBytes = trailerDictionary.Identifier != null && trailerDictionary.Identifier.Count == 2 ?
OtherEncodings.StringAsLatin1Bytes(trailerDictionary.Identifier[0])
: EmptyArray<byte>.Instance;
this.password = password ?? string.Empty;
if (encryptionDictionary == null)
@@ -70,12 +71,72 @@
? 5
: encryptionDictionary.KeyLength.GetValueOrDefault() / 8;
if (IsUserPassword(passwordBytes, length))
{
encryptionKey = CalculateKeyRevisions2To4(passwordBytes, ownerKey, (int) encryptionDictionary.UserAccessPermissions, encryptionDictionary.StandardSecurityHandlerRevision,
length, documentIdBytes, encryptionDictionary.EncryptMetadata);
}
else
{
throw new NotImplementedException("TODO: check owner password.");
}
useAes = false;
}
private bool IsUserPassword(byte[] passwordBytes, int length)
{
// 1. Create an encryption key based on the user password string.
var password = CalculateKeyRevisions2To4(passwordBytes, encryptionDictionary.OwnerBytes, (int) encryptionDictionary.UserAccessPermissions,
encryptionDictionary.StandardSecurityHandlerRevision, length, documentIdBytes, encryptionDictionary.EncryptMetadata);
byte[] output;
if (encryptionDictionary.StandardSecurityHandlerRevision >= 3)
{
using (var md5 = MD5.Create())
{
// 2. Initialize the MD5 hash function and pass the 32-byte padding string.
UpdateMd5(md5, PaddingBytes);
// 3. Pass the first element of the file identifier array to the hash function and finish the hash.
UpdateMd5(md5, documentIdBytes);
md5.TransformFinalBlock(EmptyArray<byte>.Instance, 0, 0);
var result = md5.Hash;
// 4. Encrypt the 16-byte result of the hash, using an RC4 encryption function with the encryption key from step 1.
var temp = RC4.Encrypt(password, result);
// 5. Do the following 19 times:
for (byte i = 1; i <= 19; i++)
{
// Take the output from the previous invocation of the RC4 function
// and pass it as input to a new invocation of the function
// Use an encryption key generated by taking each byte of the original encryption key (from step 1)
// and performing an XOR operation between that byte and the single-byte value of the iteration counter.
var key = password.Select(x => (byte)(x ^ i)).ToArray();
temp = RC4.Encrypt(key, temp);
}
output = temp;
}
}
else
{
// 2. Encrypt the 32-byte padding string using an RC4 encryption function with the encryption key from the preceding step.
output = RC4.Encrypt(password, PaddingBytes);
}
if (encryptionDictionary.StandardSecurityHandlerRevision >= 3)
{
return encryptionDictionary.UserBytes.Take(16).SequenceEqual(output.Take(16));
}
return encryptionDictionary.UserBytes.SequenceEqual(output);
}
public IToken Decrypt(IndirectReference reference, IToken token)
{
if (token == null)