2022-06-17 16:19:00 +00:00
|
|
|
|
// (c) Copyright Ascensio System SIA 2010-2022
|
|
|
|
|
//
|
|
|
|
|
// This program is a free software product.
|
|
|
|
|
// You can redistribute it and/or modify it under the terms
|
|
|
|
|
// of the GNU Affero General Public License (AGPL) version 3 as published by the Free Software
|
|
|
|
|
// Foundation. In accordance with Section 7(a) of the GNU AGPL its Section 15 shall be amended
|
|
|
|
|
// to the effect that Ascensio System SIA expressly excludes the warranty of non-infringement of
|
|
|
|
|
// any third-party rights.
|
|
|
|
|
//
|
|
|
|
|
// This program is distributed WITHOUT ANY WARRANTY, without even the implied warranty
|
|
|
|
|
// of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. For details, see
|
|
|
|
|
// the GNU AGPL at: http://www.gnu.org/licenses/agpl-3.0.html
|
|
|
|
|
//
|
|
|
|
|
// You can contact Ascensio System SIA at Lubanas st. 125a-25, Riga, Latvia, EU, LV-1021.
|
|
|
|
|
//
|
|
|
|
|
// The interactive user interfaces in modified source and object code versions of the Program must
|
|
|
|
|
// display Appropriate Legal Notices, as required under Section 5 of the GNU AGPL version 3.
|
|
|
|
|
//
|
|
|
|
|
// Pursuant to Section 7(b) of the License you must retain the original Product logo when
|
|
|
|
|
// distributing the program. Pursuant to Section 7(e) we decline to grant you any rights under
|
|
|
|
|
// trademark law for use of our trademarks.
|
|
|
|
|
//
|
|
|
|
|
// All the Product's GUI elements, including illustrations and icon sets, as well as technical writing
|
|
|
|
|
// content are licensed under the terms of the Creative Commons Attribution-ShareAlike 4.0
|
|
|
|
|
// International. See the License terms at http://creativecommons.org/licenses/by-sa/4.0/legalcode
|
|
|
|
|
|
|
|
|
|
/*
|
2020-09-14 10:32:20 +00:00
|
|
|
|
*
|
|
|
|
|
* (c) Copyright Ascensio System Limited 2010-2020
|
2020-09-16 07:54:52 +00:00
|
|
|
|
*
|
|
|
|
|
* This program is freeware. You can redistribute it and/or modify it under the terms of the GNU
|
|
|
|
|
* General Public License (GPL) version 3 as published by the Free Software Foundation (https://www.gnu.org/copyleft/gpl.html).
|
|
|
|
|
* In accordance with Section 7(a) of the GNU GPL its Section 15 shall be amended to the effect that
|
|
|
|
|
* Ascensio System SIA expressly excludes the warranty of non-infringement of any third-party rights.
|
|
|
|
|
*
|
|
|
|
|
* THIS PROGRAM IS DISTRIBUTED WITHOUT ANY WARRANTY; WITHOUT EVEN THE IMPLIED WARRANTY OF MERCHANTABILITY OR
|
|
|
|
|
* FITNESS FOR A PARTICULAR PURPOSE. For more details, see GNU GPL at https://www.gnu.org/copyleft/gpl.html
|
|
|
|
|
*
|
|
|
|
|
* You can contact Ascensio System SIA by email at sales@onlyoffice.com
|
|
|
|
|
*
|
|
|
|
|
* The interactive user interfaces in modified source and object code versions of ONLYOFFICE must display
|
|
|
|
|
* Appropriate Legal Notices, as required under Section 5 of the GNU GPL version 3.
|
|
|
|
|
*
|
|
|
|
|
* Pursuant to Section 7 § 3(b) of the GNU GPL you must retain the original ONLYOFFICE logo which contains
|
|
|
|
|
* relevant author attributions when distributing the software. If the display of the logo in its graphic
|
|
|
|
|
* form is not reasonably feasible for technical reasons, you must include the words "Powered by ONLYOFFICE"
|
|
|
|
|
* in every copy of the program you distribute.
|
|
|
|
|
* Pursuant to Section 7 § 3(e) we decline to grant you any rights under trademark law for use of our trademarks.
|
2020-09-14 10:32:20 +00:00
|
|
|
|
*
|
|
|
|
|
*/
|
|
|
|
|
|
|
|
|
|
|
2022-06-17 16:19:00 +00:00
|
|
|
|
namespace ASC.Data.Encryption;
|
2020-10-19 15:53:15 +00:00
|
|
|
|
|
2022-06-17 16:19:00 +00:00
|
|
|
|
[Transient]
|
|
|
|
|
public class Metadata
|
|
|
|
|
{
|
|
|
|
|
private const string prefixString = "AscEncrypted";
|
2020-09-14 10:32:20 +00:00
|
|
|
|
|
2022-06-17 16:19:00 +00:00
|
|
|
|
private const int prefixLength = 12; // prefixString length
|
|
|
|
|
private const int versionLength = 1; // byte
|
|
|
|
|
private const int sizeLength = 8; // long (int64)
|
2020-09-14 10:32:20 +00:00
|
|
|
|
|
2022-06-17 16:19:00 +00:00
|
|
|
|
private const int saltLength = 32; // The salt size must be 8 bytes or larger
|
2020-09-14 10:32:20 +00:00
|
|
|
|
|
2022-06-17 16:19:00 +00:00
|
|
|
|
private const int keySize = 256; // key size, in bits, of the secret key used for the symmetric algorithm. AES-256
|
|
|
|
|
private const int blockSize = 128; // block size, in bits, of the cryptographic operation. default is 128 bits
|
2020-09-14 10:32:20 +00:00
|
|
|
|
|
2022-06-17 16:19:00 +00:00
|
|
|
|
private const int keyLength = keySize / 8; // secret key used for the symmetric algorithm. 32 bytes
|
|
|
|
|
private const int ivLength = blockSize / 8; // The initialization vector (IV) to use for the symmetric algorithm. 16 bytes
|
2020-09-14 10:32:20 +00:00
|
|
|
|
|
2022-06-17 16:19:00 +00:00
|
|
|
|
private const int hmacKeyLength = 64; // HMACSHA256 64-byte private key is recommended.
|
|
|
|
|
private const int hmacHashLength = 32; // HMACSHA256 The output hash is 256 bits (32 bytes) in length
|
2020-09-14 10:32:20 +00:00
|
|
|
|
|
2022-06-17 16:19:00 +00:00
|
|
|
|
private const int metadataLength = prefixLength + versionLength + sizeLength + saltLength + hmacHashLength + ivLength;
|
2020-09-14 10:32:20 +00:00
|
|
|
|
|
2022-06-17 16:19:00 +00:00
|
|
|
|
private static int? iterations; // Rfc2898DeriveBytes: The minimum recommended number of iterations is 1000.
|
2020-09-14 10:32:20 +00:00
|
|
|
|
|
2022-06-17 16:19:00 +00:00
|
|
|
|
private IConfiguration Configuration { get; set; }
|
2020-09-14 10:32:20 +00:00
|
|
|
|
|
2022-06-17 16:19:00 +00:00
|
|
|
|
public Metadata(IConfiguration configuration)
|
|
|
|
|
{
|
|
|
|
|
Configuration = configuration;
|
|
|
|
|
}
|
2020-09-14 10:32:20 +00:00
|
|
|
|
|
2022-06-17 16:19:00 +00:00
|
|
|
|
private int Iterations
|
|
|
|
|
{
|
|
|
|
|
get
|
2020-09-14 10:32:20 +00:00
|
|
|
|
{
|
2022-06-17 16:19:00 +00:00
|
|
|
|
if (iterations.HasValue)
|
2020-09-14 10:32:20 +00:00
|
|
|
|
{
|
2022-06-17 16:19:00 +00:00
|
|
|
|
return iterations.Value;
|
|
|
|
|
}
|
2020-09-14 10:32:20 +00:00
|
|
|
|
|
2022-06-17 16:19:00 +00:00
|
|
|
|
if (!int.TryParse(Configuration["storage:encryption:iterations"], out var iterationsCount))
|
|
|
|
|
{
|
|
|
|
|
iterationsCount = 4096;
|
|
|
|
|
}
|
2020-09-14 10:32:20 +00:00
|
|
|
|
|
2022-06-17 16:19:00 +00:00
|
|
|
|
iterations = iterationsCount;
|
2020-09-14 10:32:20 +00:00
|
|
|
|
|
2022-06-17 16:19:00 +00:00
|
|
|
|
return iterations.Value;
|
2020-09-14 10:32:20 +00:00
|
|
|
|
}
|
2022-06-17 16:19:00 +00:00
|
|
|
|
}
|
2020-09-14 10:32:20 +00:00
|
|
|
|
|
2022-06-17 16:19:00 +00:00
|
|
|
|
private string Password;
|
2020-09-14 10:32:20 +00:00
|
|
|
|
|
2022-06-17 16:19:00 +00:00
|
|
|
|
private byte[] Prefix;
|
|
|
|
|
private byte[] Version;
|
|
|
|
|
private byte[] Size;
|
2020-09-14 10:32:20 +00:00
|
|
|
|
|
2022-06-17 16:19:00 +00:00
|
|
|
|
private byte[] Salt;
|
2020-09-14 10:32:20 +00:00
|
|
|
|
|
2022-06-17 16:19:00 +00:00
|
|
|
|
private byte[] Key;
|
|
|
|
|
private byte[] IV;
|
2020-09-14 10:32:20 +00:00
|
|
|
|
|
2022-06-17 16:19:00 +00:00
|
|
|
|
private byte[] HmacKey;
|
|
|
|
|
private byte[] HmacHash;
|
2020-09-14 10:32:20 +00:00
|
|
|
|
|
|
|
|
|
|
2022-06-17 16:19:00 +00:00
|
|
|
|
public void Initialize(string password)
|
|
|
|
|
{
|
|
|
|
|
Password = password;
|
2020-09-14 10:32:20 +00:00
|
|
|
|
|
2022-06-17 16:19:00 +00:00
|
|
|
|
Prefix = Encoding.UTF8.GetBytes(prefixString);
|
|
|
|
|
Version = new byte[versionLength];
|
|
|
|
|
Size = new byte[sizeLength];
|
2020-09-14 10:32:20 +00:00
|
|
|
|
|
2022-06-17 16:19:00 +00:00
|
|
|
|
Salt = new byte[saltLength];
|
2020-09-14 10:32:20 +00:00
|
|
|
|
|
2022-06-17 16:19:00 +00:00
|
|
|
|
Key = new byte[keyLength];
|
|
|
|
|
IV = new byte[ivLength];
|
2020-09-14 10:32:20 +00:00
|
|
|
|
|
2022-06-17 16:19:00 +00:00
|
|
|
|
HmacKey = new byte[hmacKeyLength];
|
|
|
|
|
HmacHash = new byte[hmacHashLength];
|
|
|
|
|
}
|
2020-09-14 10:32:20 +00:00
|
|
|
|
|
2022-06-17 16:19:00 +00:00
|
|
|
|
public void Initialize(byte version, string password, long fileSize)
|
|
|
|
|
{
|
|
|
|
|
Password = password;
|
2020-09-14 10:32:20 +00:00
|
|
|
|
|
2022-06-17 16:19:00 +00:00
|
|
|
|
Prefix = Encoding.UTF8.GetBytes(prefixString);
|
|
|
|
|
Version = new byte[versionLength] { version };
|
|
|
|
|
Size = LongToByteArray(fileSize);
|
2020-09-14 10:32:20 +00:00
|
|
|
|
|
2022-06-17 16:19:00 +00:00
|
|
|
|
Salt = GenerateRandom(saltLength);
|
2020-09-14 10:32:20 +00:00
|
|
|
|
|
2022-06-17 16:19:00 +00:00
|
|
|
|
Key = GenerateKey();
|
|
|
|
|
IV = GenerateRandom(ivLength);
|
2020-09-14 10:32:20 +00:00
|
|
|
|
|
2022-06-17 16:19:00 +00:00
|
|
|
|
HmacKey = GenerateHmacKey();
|
|
|
|
|
HmacHash = new byte[hmacHashLength]; // Empty byte array. The real hmac will be computed after encryption
|
|
|
|
|
}
|
2020-09-14 10:32:20 +00:00
|
|
|
|
|
|
|
|
|
|
2022-06-17 16:19:00 +00:00
|
|
|
|
public bool TryReadFromStream(Stream stream, byte cryptVersion)
|
|
|
|
|
{
|
|
|
|
|
try
|
2020-09-14 10:32:20 +00:00
|
|
|
|
{
|
2022-06-17 16:19:00 +00:00
|
|
|
|
var readed = stream.Read(Prefix, 0, prefixLength);
|
|
|
|
|
if (readed < prefixLength) return false;
|
2020-09-14 10:32:20 +00:00
|
|
|
|
|
2022-06-17 16:19:00 +00:00
|
|
|
|
if (Encoding.UTF8.GetString(Prefix) != prefixString) return false;
|
2020-09-14 10:32:20 +00:00
|
|
|
|
|
2022-06-17 16:19:00 +00:00
|
|
|
|
readed = stream.Read(Version, 0, versionLength);
|
|
|
|
|
if (readed < versionLength) return false;
|
2020-09-14 10:32:20 +00:00
|
|
|
|
|
2022-06-17 16:19:00 +00:00
|
|
|
|
if (Version[0] != cryptVersion) return false;
|
2020-09-14 10:32:20 +00:00
|
|
|
|
|
2022-06-17 16:19:00 +00:00
|
|
|
|
readed = stream.Read(Size, 0, sizeLength);
|
|
|
|
|
if (readed < sizeLength) return false;
|
2020-09-14 10:32:20 +00:00
|
|
|
|
|
2022-06-17 16:19:00 +00:00
|
|
|
|
if (ByteArrayToLong(Size) < 0) return false;
|
2020-09-14 10:32:20 +00:00
|
|
|
|
|
2022-06-17 16:19:00 +00:00
|
|
|
|
readed = stream.Read(Salt, 0, saltLength);
|
|
|
|
|
if (readed < saltLength) return false;
|
2020-09-14 10:32:20 +00:00
|
|
|
|
|
2022-06-17 16:19:00 +00:00
|
|
|
|
readed = stream.Read(HmacHash, 0, hmacHashLength);
|
|
|
|
|
if (readed < hmacHashLength) return false;
|
2020-09-14 10:32:20 +00:00
|
|
|
|
|
2022-06-17 16:19:00 +00:00
|
|
|
|
readed = stream.Read(IV, 0, ivLength);
|
|
|
|
|
if (readed < ivLength) return false;
|
2020-09-14 10:32:20 +00:00
|
|
|
|
|
2022-06-17 16:19:00 +00:00
|
|
|
|
return true;
|
2020-09-14 10:32:20 +00:00
|
|
|
|
}
|
2022-06-17 16:19:00 +00:00
|
|
|
|
catch (Exception)
|
2020-09-14 10:32:20 +00:00
|
|
|
|
{
|
2022-06-17 16:19:00 +00:00
|
|
|
|
return false;
|
2020-09-14 10:32:20 +00:00
|
|
|
|
}
|
2022-06-17 16:19:00 +00:00
|
|
|
|
}
|
2020-09-14 10:32:20 +00:00
|
|
|
|
|
2022-06-17 16:19:00 +00:00
|
|
|
|
public void WriteToStream(Stream stream)
|
|
|
|
|
{
|
|
|
|
|
stream.Seek(0, SeekOrigin.Begin);
|
|
|
|
|
|
|
|
|
|
stream.Write(Prefix, 0, prefixLength);
|
|
|
|
|
stream.Write(Version, 0, versionLength);
|
|
|
|
|
stream.Write(Size, 0, sizeLength);
|
|
|
|
|
stream.Write(Salt, 0, saltLength);
|
|
|
|
|
stream.Write(HmacHash, 0, hmacHashLength);
|
|
|
|
|
stream.Write(IV, 0, ivLength);
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
public SymmetricAlgorithm GetCryptographyAlgorithm()
|
|
|
|
|
{
|
|
|
|
|
var aes = Aes.Create();
|
|
|
|
|
aes.KeySize = keySize;
|
|
|
|
|
aes.BlockSize = blockSize;
|
|
|
|
|
aes.Key = Key;
|
|
|
|
|
aes.IV = IV;
|
|
|
|
|
aes.Padding = PaddingMode.PKCS7;
|
|
|
|
|
aes.Mode = CipherMode.CBC;
|
|
|
|
|
return aes;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
public void ComputeAndWriteHmacHash(Stream stream)
|
|
|
|
|
{
|
|
|
|
|
HmacHash = ComputeHmacHash(stream);
|
2020-09-14 10:32:20 +00:00
|
|
|
|
|
2022-06-17 16:19:00 +00:00
|
|
|
|
stream.Seek(metadataLength - ivLength - hmacHashLength, SeekOrigin.Begin); // Move position to hmac
|
2020-09-14 10:32:20 +00:00
|
|
|
|
|
2022-06-17 16:19:00 +00:00
|
|
|
|
stream.Write(HmacHash, 0, hmacHashLength); // Replace empty hmac with computed
|
|
|
|
|
}
|
2020-09-14 10:32:20 +00:00
|
|
|
|
|
2022-06-17 16:19:00 +00:00
|
|
|
|
public void ComputeAndValidateHmacHash(Stream stream)
|
|
|
|
|
{
|
|
|
|
|
Key = GenerateKey();
|
|
|
|
|
|
|
|
|
|
HmacKey = GenerateHmacKey();
|
2020-09-14 10:32:20 +00:00
|
|
|
|
|
2022-06-17 16:19:00 +00:00
|
|
|
|
var computedHash = ComputeHmacHash(stream);
|
|
|
|
|
|
|
|
|
|
if (!HmacHash.SequenceEqual(computedHash))
|
2020-09-14 10:32:20 +00:00
|
|
|
|
{
|
2022-06-17 16:19:00 +00:00
|
|
|
|
stream.Close();
|
2020-09-14 10:32:20 +00:00
|
|
|
|
|
2022-06-17 16:19:00 +00:00
|
|
|
|
throw new IntegrityProtectionException("Invalid signature");
|
|
|
|
|
}
|
2020-09-14 10:32:20 +00:00
|
|
|
|
|
2022-06-17 16:19:00 +00:00
|
|
|
|
stream.Seek(metadataLength, SeekOrigin.Begin); // Move position to encrypted data
|
|
|
|
|
}
|
2020-09-14 10:32:20 +00:00
|
|
|
|
|
2022-06-17 16:19:00 +00:00
|
|
|
|
public byte GetCryptoVersion()
|
|
|
|
|
{
|
|
|
|
|
return Version[0];
|
|
|
|
|
}
|
2020-09-14 10:32:20 +00:00
|
|
|
|
|
2022-06-17 16:19:00 +00:00
|
|
|
|
public long GetFileSize()
|
|
|
|
|
{
|
|
|
|
|
return ByteArrayToLong(Size);
|
|
|
|
|
}
|
2020-09-14 10:32:20 +00:00
|
|
|
|
|
2022-06-17 16:19:00 +00:00
|
|
|
|
public int GetMetadataLength()
|
|
|
|
|
{
|
|
|
|
|
return metadataLength;
|
|
|
|
|
}
|
2020-09-14 10:32:20 +00:00
|
|
|
|
|
|
|
|
|
|
2022-06-17 16:19:00 +00:00
|
|
|
|
private byte[] GenerateRandom(int length)
|
|
|
|
|
{
|
|
|
|
|
var random = RandomNumberGenerator.GetBytes(length);
|
2020-09-14 10:32:20 +00:00
|
|
|
|
|
2022-06-17 16:19:00 +00:00
|
|
|
|
return random;
|
|
|
|
|
}
|
2020-09-14 10:32:20 +00:00
|
|
|
|
|
2022-06-17 16:19:00 +00:00
|
|
|
|
private byte[] GenerateKey()
|
|
|
|
|
{
|
|
|
|
|
byte[] key;
|
2020-09-14 10:32:20 +00:00
|
|
|
|
|
2022-06-17 16:19:00 +00:00
|
|
|
|
using (var deriveBytes = new Rfc2898DeriveBytes(Password, Salt, Iterations, HashAlgorithmName.SHA256))
|
2020-09-14 10:32:20 +00:00
|
|
|
|
{
|
2022-06-17 16:19:00 +00:00
|
|
|
|
key = deriveBytes.GetBytes(keyLength);
|
2020-09-14 10:32:20 +00:00
|
|
|
|
}
|
|
|
|
|
|
2022-06-17 16:19:00 +00:00
|
|
|
|
return key;
|
|
|
|
|
}
|
2020-09-14 10:32:20 +00:00
|
|
|
|
|
2022-06-17 16:19:00 +00:00
|
|
|
|
private byte[] GenerateHmacKey()
|
|
|
|
|
{
|
|
|
|
|
byte[] hmacKey;
|
2020-09-14 10:32:20 +00:00
|
|
|
|
|
2022-06-17 16:19:00 +00:00
|
|
|
|
using (var sha512 = SHA512.Create())
|
|
|
|
|
{
|
|
|
|
|
hmacKey = sha512.ComputeHash(Key);
|
2020-09-14 10:32:20 +00:00
|
|
|
|
}
|
|
|
|
|
|
2022-06-17 16:19:00 +00:00
|
|
|
|
return hmacKey;
|
|
|
|
|
}
|
2020-09-14 10:32:20 +00:00
|
|
|
|
|
2022-06-17 16:19:00 +00:00
|
|
|
|
private byte[] ComputeHmacHash(Stream stream)
|
|
|
|
|
{
|
|
|
|
|
byte[] hmacHash;
|
2020-09-14 10:32:20 +00:00
|
|
|
|
|
2022-06-17 16:19:00 +00:00
|
|
|
|
stream.Seek(metadataLength - ivLength, SeekOrigin.Begin); // Move position to (IV + encrypted data)
|
2020-09-14 10:32:20 +00:00
|
|
|
|
|
2022-06-17 16:19:00 +00:00
|
|
|
|
using (var hmac = new HMACSHA256(HmacKey))
|
|
|
|
|
{
|
|
|
|
|
hmacHash = hmac.ComputeHash(stream); // IV needs to be part of the MAC calculation
|
|
|
|
|
}
|
2020-09-14 10:32:20 +00:00
|
|
|
|
|
2022-06-17 16:19:00 +00:00
|
|
|
|
return hmacHash;
|
|
|
|
|
}
|
2020-09-14 10:32:20 +00:00
|
|
|
|
|
2022-06-17 16:19:00 +00:00
|
|
|
|
private byte[] LongToByteArray(long value)
|
|
|
|
|
{
|
|
|
|
|
var result = BitConverter.GetBytes(value);
|
2020-09-14 10:32:20 +00:00
|
|
|
|
|
2022-06-17 16:19:00 +00:00
|
|
|
|
if (!BitConverter.IsLittleEndian)
|
|
|
|
|
Array.Reverse(result);
|
2020-09-14 10:32:20 +00:00
|
|
|
|
|
2022-06-17 16:19:00 +00:00
|
|
|
|
return result;
|
|
|
|
|
}
|
2020-09-14 10:32:20 +00:00
|
|
|
|
|
2022-06-17 16:19:00 +00:00
|
|
|
|
private long ByteArrayToLong(byte[] value)
|
|
|
|
|
{
|
|
|
|
|
if (!BitConverter.IsLittleEndian)
|
|
|
|
|
Array.Reverse(value);
|
2020-09-14 10:32:20 +00:00
|
|
|
|
|
2022-06-17 16:19:00 +00:00
|
|
|
|
try
|
|
|
|
|
{
|
|
|
|
|
return BitConverter.ToInt64(value, 0);
|
2020-09-14 10:32:20 +00:00
|
|
|
|
}
|
2022-06-17 16:19:00 +00:00
|
|
|
|
catch (Exception)
|
2020-09-14 10:32:20 +00:00
|
|
|
|
{
|
2022-06-17 16:19:00 +00:00
|
|
|
|
return -1;
|
2020-09-14 10:32:20 +00:00
|
|
|
|
}
|
2022-06-17 16:19:00 +00:00
|
|
|
|
}
|
|
|
|
|
}
|
2020-10-12 16:23:15 +00:00
|
|
|
|
|