public async Task Should_Throw_Decrypting_Stream_With_Wrong_FileCredentials()
        {
            FileCredentials wrongFileCredentials = new FileCredentials
            {
                FileHash = "THTjgv2FU7kff/29Vty/IcqKPmOGkL7F35fAzmkfZdI=",
                Secret   = "a+jxJoKPEaz77VCjRvDVcYHfIO3+h+oI+ruZh+KkYa0=",
            };

            IDecrypter decrypter = new Decrypter();

            Exception exception;

            using (Stream
                   encFileStream = new NonSeekableFileReadStream("Files/s_dec5.driver_license-selfie.jpg.enc"),
                   fileStream = new MemoryStream()
                   )
            {
                exception = await Assert.ThrowsAnyAsync <Exception>(() =>
                                                                    decrypter.DecryptFileAsync(
                                                                        encFileStream,
                                                                        wrongFileCredentials,
                                                                        fileStream
                                                                        )
                                                                    );
            }

            Assert.Matches(@"^Data hash mismatch at position \d+\.$", exception.Message);
            Assert.IsType <PassportDataDecryptionException>(exception);
        }
        public async Task Should_Decrypt_From_Seekable_Stream()
        {
            FileCredentials fileCredentials = new FileCredentials
            {
                FileHash = "v3q47iscI6TS94CMo7HGQUOxw28LIf82NJBkImzP57c=",
                Secret   = "vF7nut7clg/H/pEaTJigo4mQJ0s8B+HGCWKTWtOTIdo=",
            };

            IDecrypter decrypter = new Decrypter();

            Stream encContentStream = new MemoryStream();

            using (Stream encFileStream = new NonSeekableFileReadStream("Files/s_dec1.driver_license-selfie.jpg.enc"))
            {
                await encFileStream.CopyToAsync(encContentStream);
            }

            using (encContentStream)
                using (Stream contentStream = new MemoryStream())
                {
                    encContentStream.Position = 0; // Ensure method starts reading the content fro the beginning

                    await decrypter.DecryptFileAsync(
                        encContentStream,
                        fileCredentials,
                        contentStream
                        );
                }
        }
Beispiel #3
0
        public async Task Should_decrypt_utility_bill_element_translation()
        {
            Update                   update       = _classFixture.Entity;
            PassportData             passportData = update.Message.PassportData;
            RSA                      key          = EncryptionKey.ReadAsRsa();
            EncryptedPassportElement billElement  = Assert.Single(passportData.Data, el => el.Type == "utility_bill");

            PassportFile translationFile = Assert.Single(billElement.Translation);

            IDecrypter  decrypter   = new Decrypter();
            Credentials credentials = decrypter.DecryptCredentials(passportData.Credentials, key);

            FileCredentials fileCredentials = Assert.Single(credentials.SecureData.UtilityBill.Translation);

            File encryptedFileInfo;

            using (System.IO.Stream decryptedFile = new System.IO.MemoryStream())
            {
                encryptedFileInfo = await BotClient.DownloadAndDecryptPassportFileAsync(
                    translationFile,
                    fileCredentials,
                    decryptedFile
                    );

                Assert.InRange(decryptedFile.Length, translationFile.FileSize - 256, translationFile.FileSize + 256);
            }

            Assert.NotEmpty(encryptedFileInfo.FilePath);
            Assert.NotEmpty(encryptedFileInfo.FileId);
            Assert.InRange(encryptedFileInfo.FileSize, 1_000, 50_000_000);
        }
        public async Task Should_Throw_Decrypting_Unencrypted_File()
        {
            FileCredentials fileCredentials = new FileCredentials
            {
                FileHash = "v3q47iscI6TS94CMo7HGQUOxw28LIf82NJBkImzP57c=",
                Secret   = "vF7nut7clg/H/pEaTJigo4mQJ0s8B+HGCWKTWtOTIdo=",
            };

            IDecrypter decrypter = new Decrypter();

            Exception exception;

            using (Stream
                   encFileStream = new NonSeekableFileReadStream("Files/s_dec3.driver_license-selfie.jpg"),
                   fileStream = new MemoryStream()
                   )
            {
                exception = await Assert.ThrowsAnyAsync <Exception>(() =>
                                                                    decrypter.DecryptFileAsync(
                                                                        encFileStream,
                                                                        fileCredentials,
                                                                        fileStream
                                                                        )
                                                                    );
            }

            Assert.Equal("The input data is not a complete block.", exception.Message);
            Assert.IsType <CryptographicException>(exception);
        }
        public async Task Should_Throw_Decrypting_Mispositioned_Stream()
        {
            FileCredentials fileCredentials = new FileCredentials
            {
                FileHash = "v3q47iscI6TS94CMo7HGQUOxw28LIf82NJBkImzP57c=",
                Secret   = "vF7nut7clg/H/pEaTJigo4mQJ0s8B+HGCWKTWtOTIdo=",
            };

            IDecrypter decrypter = new Decrypter();

            Stream encContentStream = new MemoryStream();

            using (Stream encFileStream = new NonSeekableFileReadStream("Files/s_dec4.driver_license-selfie.jpg.enc"))
            {
                await encFileStream.CopyToAsync(encContentStream);
            }

            Exception exception;

            using (encContentStream) // Stream position is at the end and not at 0 position
                using (Stream contentStream = new MemoryStream())
                {
                    exception = await Assert.ThrowsAnyAsync <Exception>(() =>
                                                                        decrypter.DecryptFileAsync(
                                                                            encContentStream,
                                                                            fileCredentials,
                                                                            contentStream
                                                                            )
                                                                        );
                }

            Assert.Matches(@"^Data padding length is invalid: \d+\.", exception.Message);
            Assert.IsType <PassportDataDecryptionException>(exception);
        }
Beispiel #6
0
        public void Should_Decrypt_Credentials()
        {
            Update       update       = _classFixture.Entity;
            PassportData passportData = update.Message.PassportData;

            RSA key = EncryptionKey.ReadAsRsa();

            IDecrypter decrypter = new Decrypter();

            Credentials credentials = decrypter.DecryptCredentials(
                key: key,
                encryptedCredentials: passportData.Credentials
                );

            Assert.NotNull(credentials);
            Assert.NotNull(credentials.SecureData);
            Assert.Equal("Test nonce for id card & utility bill", credentials.Nonce);

            // decryption of document data in 'identity_card' element requires accompanying DataCredentials
            Assert.NotNull(credentials.SecureData.IdentityCard);
            Assert.NotNull(credentials.SecureData.IdentityCard.Data);
            Assert.NotEmpty(credentials.SecureData.IdentityCard.Data.Secret);
            Assert.NotEmpty(credentials.SecureData.IdentityCard.Data.DataHash);

            // decryption of front side of 'identity_card' element requires accompanying FileCredentials
            Assert.NotNull(credentials.SecureData.IdentityCard.FrontSide);
            Assert.NotEmpty(credentials.SecureData.IdentityCard.FrontSide.Secret);
            Assert.NotEmpty(credentials.SecureData.IdentityCard.FrontSide.FileHash);

            // decryption of reverse side of 'identity_card' element requires accompanying FileCredentials
            Assert.NotNull(credentials.SecureData.IdentityCard.ReverseSide);
            Assert.NotEmpty(credentials.SecureData.IdentityCard.ReverseSide.Secret);
            Assert.NotEmpty(credentials.SecureData.IdentityCard.ReverseSide.FileHash);

            // decryption of selfie of 'identity_card' element requires accompanying FileCredentials
            Assert.NotNull(credentials.SecureData.IdentityCard.Selfie);
            Assert.NotEmpty(credentials.SecureData.IdentityCard.Selfie.Secret);
            Assert.NotEmpty(credentials.SecureData.IdentityCard.Selfie.FileHash);

            Assert.Null(credentials.SecureData.IdentityCard.Translation);
            Assert.Null(credentials.SecureData.IdentityCard.Files);

            // decryption of file scan in 'utility_bill' element requires accompanying FileCredentials
            Assert.NotNull(credentials.SecureData.UtilityBill.Files);
            FileCredentials billCredentials = Assert.Single(credentials.SecureData.UtilityBill.Files);

            Assert.NotEmpty(billCredentials.Secret);
            Assert.NotEmpty(billCredentials.FileHash);

            // decryption of translation file scan in 'utility_bill' element requires accompanying FileCredentials
            Assert.NotNull(credentials.SecureData.UtilityBill.Files);
            FileCredentials billTranslationFileCredentials =
                Assert.Single(credentials.SecureData.UtilityBill.Translation);

            Assert.NotEmpty(billTranslationFileCredentials.Secret);
            Assert.NotEmpty(billTranslationFileCredentials.FileHash);
        }
        public void Should_Decrypt_Credentials()
        {
            RSA          key      = EncryptionKey.RsaPrivateKey;
            PassportData passData = GetPassportData();

            IDecrypter decrypter = new Decrypter();

            Credentials credentials = decrypter.DecryptCredentials(
                encryptedCredentials: passData.Credentials,
                key: key
                );

            Assert.NotNull(credentials);
            Assert.NotNull(credentials.SecureData);
            Assert.Equal("TEST", credentials.Nonce);

            // decryption of document data in 'identity_card' element requires accompanying DataCredentials
            Assert.NotNull(credentials.SecureData.IdentityCard);
            Assert.NotNull(credentials.SecureData.IdentityCard.Data);
            Assert.NotEmpty(credentials.SecureData.IdentityCard.Data.Secret);
            Assert.NotEmpty(credentials.SecureData.IdentityCard.Data.DataHash);

            // decryption of front side of 'identity_card' element requires accompanying FileCredentials
            Assert.NotNull(credentials.SecureData.IdentityCard.FrontSide);
            Assert.NotEmpty(credentials.SecureData.IdentityCard.FrontSide.Secret);
            Assert.NotEmpty(credentials.SecureData.IdentityCard.FrontSide.FileHash);

            // decryption of reverse side of 'identity_card' element requires accompanying FileCredentials
            Assert.NotNull(credentials.SecureData.IdentityCard.ReverseSide);
            Assert.NotEmpty(credentials.SecureData.IdentityCard.ReverseSide.Secret);
            Assert.NotEmpty(credentials.SecureData.IdentityCard.ReverseSide.FileHash);

            // decryption of selfie of 'identity_card' element requires accompanying FileCredentials
            Assert.NotNull(credentials.SecureData.IdentityCard.Selfie);
            Assert.NotEmpty(credentials.SecureData.IdentityCard.Selfie.Secret);
            Assert.NotEmpty(credentials.SecureData.IdentityCard.Selfie.FileHash);

            Assert.Null(credentials.SecureData.IdentityCard.Translation);
            Assert.Null(credentials.SecureData.IdentityCard.Files);

            // decryption of file scan in 'utility_bill' element requires accompanying FileCredentials
            Assert.NotNull(credentials.SecureData.UtilityBill.Files);
            FileCredentials billFileCredentials = Assert.Single(credentials.SecureData.UtilityBill.Files);

            Assert.NotEmpty(billFileCredentials.Secret);
            Assert.NotEmpty(billFileCredentials.FileHash);

            // decryption of translation file scan in 'utility_bill' element requires accompanying FileCredentials
            Assert.NotNull(credentials.SecureData.UtilityBill.Files);
            FileCredentials billTranslationFileCredentials =
                Assert.Single(credentials.SecureData.UtilityBill.Translation);

            Assert.NotEmpty(billTranslationFileCredentials.Secret);
            Assert.NotEmpty(billTranslationFileCredentials.FileHash);
        }
        public void Should_Throw_If_Invalid_Hash(string fileHash)
        {
            FileCredentials fileCredentials = new FileCredentials {
                Secret = "", FileHash = fileHash
            };
            IDecrypter decrypter = new Decrypter();

            Assert.ThrowsAny <FormatException>(() =>
                                               decrypter.DecryptFile(new byte[16], fileCredentials)
                                               );
        }
        public async Task Should_Throw_If_Invalid_Hash(string fileHash)
        {
            FileCredentials fileCredentials = new FileCredentials {
                Secret = "", FileHash = fileHash
            };
            IDecrypter decrypter = new Decrypter();

            await Assert.ThrowsAnyAsync <FormatException>(() =>
                                                          decrypter.DecryptFileAsync(new MemoryStream(new byte[16]), fileCredentials, new MemoryStream())
                                                          );
        }
        /// <inheritdoc />
        public Task DecryptFileAsync(
            Stream encryptedContent,
            FileCredentials fileCredentials,
            Stream destination,
            CancellationToken cancellationToken = default
            )
        {
            if (encryptedContent is null)
            {
                throw new ArgumentNullException(nameof(encryptedContent));
            }
            if (fileCredentials is null)
            {
                throw new ArgumentNullException(nameof(fileCredentials));
            }
            if (fileCredentials.Secret is null)
            {
                throw new ArgumentNullException(nameof(fileCredentials.Secret));
            }
            if (fileCredentials.FileHash is null)
            {
                throw new ArgumentNullException(nameof(fileCredentials.FileHash));
            }
            if (destination is null)
            {
                throw new ArgumentNullException(nameof(destination));
            }
            if (!encryptedContent.CanRead)
            {
                throw new ArgumentException("Stream does not support reading.", nameof(encryptedContent));
            }
            if (encryptedContent.CanSeek && encryptedContent.Length == 0)
            {
                throw new ArgumentException("Stream is empty.", nameof(encryptedContent));
            }
            if (encryptedContent.CanSeek && encryptedContent.Length % 16 != 0)
            {
                throw new PassportDataDecryptionException("Data length is not divisible by 16: " +
                                                          $"{encryptedContent.Length}.");
            }
            if (!destination.CanWrite)
            {
                throw new ArgumentException("Stream does not support writing.", nameof(destination));
            }

            byte[] dataSecret = Convert.FromBase64String(fileCredentials.Secret);
            byte[] dataHash   = Convert.FromBase64String(fileCredentials.FileHash);
            if (dataHash.Length != 32)
            {
                throw new PassportDataDecryptionException($"Hash length is not 32: {dataHash.Length}.");
            }

            return(DecryptDataStreamAsync(encryptedContent, dataSecret, dataHash, destination, cancellationToken));
        }
        public void Should_Throw_If_Invalid_Secret(string secret)
        {
            FileCredentials fileCredentials = new FileCredentials {
                Secret = secret, FileHash = ""
            };
            IDecrypter decrypter = new Decrypter();

            Assert.Throws <FormatException>(() =>
                                            decrypter.DecryptFile(new byte[16], fileCredentials)
                                            );
        }
        public void Should_Throw_If_Empty_Data_Bytes_Length()
        {
            IDecrypter      decrypter       = new Decrypter();
            FileCredentials fileCredentials = new FileCredentials {
                Secret = "", FileHash = ""
            };

            Exception exception = Assert.ThrowsAny <Exception>(() =>
                                                               decrypter.DecryptFile(new byte[0], fileCredentials)
                                                               );

            Assert.Matches(@"^Data array is empty\.\s+Parameter name: encryptedContent$", exception.Message);
            Assert.IsType <ArgumentException>(exception);
        }
        public void Should_Throw_If_Null_Hash()
        {
            IDecrypter decrypter = new Decrypter();

            FileCredentials fileCredentials = new FileCredentials {
                Secret = ""
            };
            Exception exception = Assert.ThrowsAny <Exception>(() =>
                                                               decrypter.DecryptFile(new byte[0], fileCredentials)
                                                               );

            Assert.Matches(@"^Value cannot be null\.\s+Parameter name: FileHash$", exception.Message);
            Assert.IsType <ArgumentNullException>(exception);
        }
        public async Task Should_Throw_If_Invalid_Hash_Length(string fileHash)
        {
            FileCredentials fileCredentials = new FileCredentials {
                Secret = "", FileHash = fileHash
            };
            IDecrypter decrypter = new Decrypter();

            Exception exception = await Assert.ThrowsAnyAsync <Exception>(() =>
                                                                          decrypter.DecryptFileAsync(new MemoryStream(new byte[16]), fileCredentials, new MemoryStream())
                                                                          );

            Assert.Matches(@"^Hash length is not 32: \d+\.$", exception.Message);
            Assert.IsType <PassportDataDecryptionException>(exception);
        }
        public async Task Should_Throw_If_Null_Destination()
        {
            IDecrypter      decrypter       = new Decrypter();
            FileCredentials fileCredentials = new FileCredentials {
                Secret = "", FileHash = ""
            };

            Exception exception = await Assert.ThrowsAnyAsync <Exception>(() =>
                                                                          decrypter.DecryptFileAsync(new MemoryStream(), fileCredentials, null)
                                                                          );

            Assert.Matches(@"^Value cannot be null\.\s+Parameter name: destination$", exception.Message);
            Assert.IsType <ArgumentNullException>(exception);
        }
        public async Task Should_Throw_If_Empty_Data_Stream()
        {
            FileCredentials fileCredentials = new FileCredentials {
                Secret = "", FileHash = ""
            };
            IDecrypter decrypter = new Decrypter();

            Exception exception = await Assert.ThrowsAnyAsync <Exception>(() =>
                                                                          decrypter.DecryptFileAsync(new MemoryStream(), fileCredentials, new MemoryStream())
                                                                          );

            Assert.Matches(@"^Stream is empty\.\s+Parameter name: encryptedContent$", exception.Message);
            Assert.IsType <ArgumentException>(exception);
        }
        public void Should_Throw_If_Invalid_Data_Bytes_Length()
        {
            IDecrypter      decrypter       = new Decrypter();
            FileCredentials fileCredentials = new FileCredentials {
                Secret = "", FileHash = ""
            };

            Exception exception = Assert.ThrowsAny <Exception>(() =>
                                                               decrypter.DecryptFile(new byte[16 + 1], fileCredentials)
                                                               );

            Assert.Equal("Data length is not divisible by 16: 17.", exception.Message);
            Assert.IsType <PassportDataDecryptionException>(exception);
        }
        public void Should_Throw_If_Invalid_Hash_Length(string fileHash)
        {
            FileCredentials fileCredentials = new FileCredentials {
                Secret = "", FileHash = fileHash
            };
            IDecrypter decrypter = new Decrypter();

            Exception exception = Assert.ThrowsAny <Exception>(() =>
                                                               decrypter.DecryptFile(new byte[16], fileCredentials)
                                                               );

            Assert.Matches(@"^Hash length is not 32: \d+\.$", exception.Message);
            Assert.IsType <PassportDataDecryptionException>(exception);
        }
Beispiel #19
0
        /// <summary>
        /// Downloads an encrypted Passport file, decrypts it, and writes the content to
        /// <paramref name="destination"/> stream
        /// </summary>
        /// <param name="botClient">Instance of bot client</param>
        /// <param name="passportFile"></param>
        /// <param name="fileCredentials"></param>
        /// <param name="destination"></param>
        /// <param name="cancellationToken">The cancellation token to cancel operation.</param>
        /// <returns>File information of the encrypted Passport file on Telegram servers.</returns>
        /// <exception cref="ArgumentNullException"></exception>
        public static async Task <File> DownloadAndDecryptPassportFileAsync(
            this ITelegramBotClient botClient,
            PassportFile passportFile,
            FileCredentials fileCredentials,
            System.IO.Stream destination,
            CancellationToken cancellationToken = default
            )
        {
            if (passportFile == null)
            {
                throw new ArgumentNullException(nameof(passportFile));
            }
            if (fileCredentials == null)
            {
                throw new ArgumentNullException(nameof(fileCredentials));
            }
            if (destination == null)
            {
                throw new ArgumentNullException(nameof(destination));
            }

            File fileInfo;

            var encryptedContentStream = passportFile.FileSize > 0
                ? new System.IO.MemoryStream(passportFile.FileSize)
                : new System.IO.MemoryStream();

            using (encryptedContentStream)
            {
                fileInfo = await botClient.GetInfoAndDownloadFileAsync(
                    passportFile.FileId,
                    encryptedContentStream,
                    cancellationToken
                    ).ConfigureAwait(false);

                encryptedContentStream.Position = 0;

                await new Decrypter().DecryptFileAsync(
                    encryptedContentStream,
                    fileCredentials,
                    destination,
                    cancellationToken
                    ).ConfigureAwait(false);
            }

            return(fileInfo);
        }
        public async Task Should_Decrypt_Translation_File()
        {
            Update                   update       = _classFixture.Entity;
            PassportData             passportData = update.Message.PassportData;
            RSA                      key          = EncryptionKey.ReadAsRsa();
            EncryptedPassportElement element      = passportData.Data.Single();

            IDecrypter  decrypter   = new Decrypter();
            Credentials credentials = decrypter.DecryptCredentials(passportData.Credentials, key);

            for (int i = 0; i < element.Translation.Length; i++)
            {
                PassportFile    passportFile    = element.Translation[i];
                FileCredentials fileCredentials = credentials.SecureData.DriverLicense.Translation[i];

                byte[] encryptedContent;
                {
                    File encryptedFileInfo = await BotClient.GetFileAsync(passportFile.FileId);

                    Assert.NotEmpty(encryptedFileInfo.FilePath);
                    Assert.NotEmpty(encryptedFileInfo.FileId);
                    Assert.InRange(encryptedFileInfo.FileSize, 1_000, 50_000_000);

                    using (System.IO.MemoryStream stream = new System.IO.MemoryStream(encryptedFileInfo.FileSize))
                    {
                        await BotClient.DownloadFileAsync(encryptedFileInfo.FilePath, stream);

                        encryptedContent = stream.ToArray();
                    }
                }

                byte[] translationContent = decrypter.DecryptFile(
                    encryptedContent,
                    fileCredentials
                    );

                Assert.NotEmpty(translationContent);

                string decryptedFilePath = System.IO.Path.GetTempFileName();
                await System.IO.File.WriteAllBytesAsync(decryptedFilePath, translationContent);

                _output.WriteLine("Translation JPEG file is written to \"{0}\".", decryptedFilePath);
            }
        }
        public async Task Should_Decrypt_Utility_Bill_Element_Translation()
        {
            PassportData             passportData = GetPassportData();
            EncryptedPassportElement billElement  = Assert.Single(passportData.Data, el => el.Type == "utility_bill");

            Assert.NotNull(billElement.Translation);
            PassportFile translationFile = Assert.Single(billElement.Translation);

            Assert.Equal("DgADAQADOwADGV9BRP4b7RLGAtUKAg", translationFile.FileId);
            Assert.InRange(translationFile.FileDate, new DateTime(2018, 8, 30), new DateTime(2018, 8, 31));
            Assert.Equal(0, translationFile.FileSize);

            IDecrypter  decrypter   = new Decrypter();
            Credentials credentials =
                decrypter.DecryptCredentials(passportData.Credentials, EncryptionKey.RsaPrivateKey);

            FileCredentials translationFileCredentials = Assert.Single(credentials.SecureData.UtilityBill.Translation);

            byte[] encryptedContent = await System.IO.File.ReadAllBytesAsync("Files/utility_bill-translation.jpg.enc");

            byte[] content = decrypter.DecryptFile(
                encryptedContent,
                translationFileCredentials
                );

            Assert.NotEmpty(content);

            await System.IO.File.WriteAllBytesAsync("Files/utility_bill-translation.jpg", content);

            using (System.IO.MemoryStream
                   encryptedFileStream = new System.IO.MemoryStream(encryptedContent),
                   decryptedFileStream = new System.IO.MemoryStream()
                   )
            {
                await decrypter.DecryptFileAsync(
                    encryptedFileStream,
                    translationFileCredentials,
                    decryptedFileStream
                    );

                Assert.Equal(content, decryptedFileStream.ToArray());
            }
        }
        public async Task Should_Throw_If_Invalid_Seekable_Data_Stream_Length()
        {
            IDecrypter      decrypter       = new Decrypter();
            FileCredentials fileCredentials = new FileCredentials {
                Secret = "", FileHash = ""
            };

            Exception exception;

            using (Stream encStream = new MemoryStream(new byte[16 - 1]))
            {
                exception = await Assert.ThrowsAnyAsync <Exception>(() =>
                                                                    decrypter.DecryptFileAsync(encStream, fileCredentials, new MemoryStream())
                                                                    );
            }

            Assert.Equal("Data length is not divisible by 16: 15.", exception.Message);
            Assert.IsType <PassportDataDecryptionException>(exception);
        }
        public async Task Should_Throw_If_NonWritable_Destination()
        {
            IDecrypter      decrypter       = new Decrypter();
            FileCredentials fileCredentials = new FileCredentials {
                Secret = "", FileHash = ""
            };

            Exception exception;

            using (Stream destStream = File.OpenRead("Files/s_dec7.driver_license-selfie.jpg"))
            {
                exception = await Assert.ThrowsAnyAsync <Exception>(() =>
                                                                    decrypter.DecryptFileAsync(new MemoryStream(new byte[16]), fileCredentials, destStream)
                                                                    );
            }

            Assert.Matches(@"^Stream does not support writing\.\s+Parameter name: destination$", exception.Message);
            Assert.IsType <ArgumentException>(exception);
        }
        public async Task Should_Decrypt_From_Bytes()
        {
            FileCredentials fileCredentials = new FileCredentials
            {
                FileHash = "v3q47iscI6TS94CMo7HGQUOxw28LIf82NJBkImzP57c=",
                Secret   = "vF7nut7clg/H/pEaTJigo4mQJ0s8B+HGCWKTWtOTIdo=",
            };

            IDecrypter decrypter = new Decrypter();

            byte[] encContent = await File.ReadAllBytesAsync("Files/bytes_dec1.driver_license-selfie.jpg.enc");

            byte[] content = decrypter.DecryptFile(
                encContent,
                fileCredentials
                );

            Assert.NotEmpty(content);
            Assert.InRange(content.Length, encContent.Length - 256, encContent.Length - 33);
        }
Beispiel #25
0
        public void Should_Decrypt_Credentials()
        {
            PassportData passportData = GetPassportData();

            IDecrypter decrypter = new Decrypter();

            Credentials credentials =
                decrypter.DecryptCredentials(passportData.Credentials, EncryptionKey.RsaPrivateKey);

            Assert.NotNull(credentials);
            Assert.NotNull(credentials.SecureData);
            Assert.NotEmpty(credentials.Nonce);
            Assert.Equal("TEST", credentials.Nonce);

            // decryption of document data in 'driver_license' element requires accompanying DataCredentials
            Assert.NotNull(credentials.SecureData.DriverLicense);
            Assert.NotNull(credentials.SecureData.DriverLicense.Data);
            Assert.NotEmpty(credentials.SecureData.DriverLicense.Data.Secret);
            Assert.NotEmpty(credentials.SecureData.DriverLicense.Data.DataHash);

            // decryption of front side file in 'driver_license' element requires accompanying FileCredentials
            Assert.NotNull(credentials.SecureData.DriverLicense.FrontSide);
            Assert.NotEmpty(credentials.SecureData.DriverLicense.FrontSide.Secret);
            Assert.NotEmpty(credentials.SecureData.DriverLicense.FrontSide.FileHash);

            // decryption of selfie file in 'driver_license' element requires accompanying FileCredentials
            Assert.NotNull(credentials.SecureData.DriverLicense.Selfie);
            Assert.NotEmpty(credentials.SecureData.DriverLicense.Selfie.Secret);
            Assert.NotEmpty(credentials.SecureData.DriverLicense.Selfie.FileHash);

            // decryption of translation file in 'driver_license' element requires accompanying FileCredentials
            Assert.NotEmpty(credentials.SecureData.DriverLicense.Translation);
            FileCredentials translationFileCredentials = Assert.Single(
                credentials.SecureData.DriverLicense.Translation
                );

            Assert.NotNull(translationFileCredentials);
            Assert.NotEmpty(translationFileCredentials.Secret);
            Assert.NotEmpty(translationFileCredentials.FileHash);
        }
        /// <inheritdoc />
        public byte[] DecryptFile(
            byte[] encryptedContent,
            FileCredentials fileCredentials
            )
        {
            if (encryptedContent is null)
            {
                throw new ArgumentNullException(nameof(encryptedContent));
            }
            if (fileCredentials is null)
            {
                throw new ArgumentNullException(nameof(fileCredentials));
            }
            if (fileCredentials.Secret is null)
            {
                throw new ArgumentNullException(nameof(fileCredentials.Secret));
            }
            if (fileCredentials.FileHash is null)
            {
                throw new ArgumentNullException(nameof(fileCredentials.FileHash));
            }
            if (encryptedContent.Length == 0)
            {
                throw new ArgumentException("Data array is empty.", nameof(encryptedContent));
            }
            if (encryptedContent.Length % 16 != 0)
            {
                throw new PassportDataDecryptionException
                          ($"Data length is not divisible by 16: {encryptedContent.Length}.");
            }

            byte[] dataSecret = Convert.FromBase64String(fileCredentials.Secret);
            byte[] dataHash   = Convert.FromBase64String(fileCredentials.FileHash);
            if (dataHash.Length != 32)
            {
                throw new PassportDataDecryptionException($"Hash length is not 32: {dataHash.Length}.");
            }

            return(DecryptDataBytes(encryptedContent, dataSecret, dataHash));
        }
        public async Task Should_Throw_Decrypting_Bytes_With_Wrong_FileCredentials()
        {
            FileCredentials wrongFileCredentials = new FileCredentials
            {
                FileHash = "THTjgv2FU7kff/29Vty/IcqKPmOGkL7F35fAzmkfZdI=",
                Secret   = "a+jxJoKPEaz77VCjRvDVcYHfIO3+h+oI+ruZh+KkYa0=",
            };

            IDecrypter decrypter = new Decrypter();

            byte[] encContent = await File.ReadAllBytesAsync("Files/bytes_dec3.driver_license-selfie.jpg.enc");

            Exception exception = Assert.ThrowsAny <Exception>(() =>
                                                               decrypter.DecryptFile(
                                                                   encContent,
                                                                   wrongFileCredentials
                                                                   )
                                                               );

            Assert.Matches(@"^Data hash mismatch at position \d+\.$", exception.Message);
            Assert.IsType <PassportDataDecryptionException>(exception);
        }
        public void Should_Throw_Decrypting_Unencrypted_File_Valid_Length()
        {
            FileCredentials fileCredentials = new FileCredentials
            {
                FileHash = "v3q47iscI6TS94CMo7HGQUOxw28LIf82NJBkImzP57c=",
                Secret   = "vF7nut7clg/H/pEaTJigo4mQJ0s8B+HGCWKTWtOTIdo=",
            };

            IDecrypter decrypter = new Decrypter();

            byte[] encContent = new byte[2048]; // data length is divisible by 16

            Exception exception = Assert.ThrowsAny <Exception>(() =>
                                                               decrypter.DecryptFile(
                                                                   encContent,
                                                                   fileCredentials
                                                                   )
                                                               );

            Assert.Matches(@"^Data hash mismatch at position \d+\.$", exception.Message);
            Assert.IsType <PassportDataDecryptionException>(exception);
        }
        public async Task Should_Throw_Decrypting_Unencrypted_File_Invalid_Length()
        {
            FileCredentials fileCredentials = new FileCredentials
            {
                FileHash = "v3q47iscI6TS94CMo7HGQUOxw28LIf82NJBkImzP57c=",
                Secret   = "vF7nut7clg/H/pEaTJigo4mQJ0s8B+HGCWKTWtOTIdo=",
            };

            IDecrypter decrypter = new Decrypter();

            byte[] encContent = await File.ReadAllBytesAsync("Files/bytes_dec2.driver_license-selfie.jpg");

            Exception exception = Assert.ThrowsAny <Exception>(() =>
                                                               decrypter.DecryptFile(
                                                                   encContent,
                                                                   fileCredentials
                                                                   )
                                                               );

            Assert.Matches(@"^Data length is not divisible by 16: \d+\.$", exception.Message);
            Assert.IsType <PassportDataDecryptionException>(exception);
        }
        public async Task Should_Decrypt_From_NonSeekable_Stream()
        {
            FileCredentials fileCredentials = new FileCredentials
            {
                FileHash = "v3q47iscI6TS94CMo7HGQUOxw28LIf82NJBkImzP57c=",
                Secret   = "vF7nut7clg/H/pEaTJigo4mQJ0s8B+HGCWKTWtOTIdo=",
            };

            IDecrypter decrypter = new Decrypter();

            using (Stream
                   encFileStream = new NonSeekableFileReadStream("Files/s_dec2.driver_license-selfie.jpg.enc"),
                   fileStream = new MemoryStream()
                   )
            {
                await decrypter.DecryptFileAsync(
                    encFileStream,
                    fileCredentials,
                    fileStream
                    );
            }
        }