protected internal static Cipher InitCipherForBlock(Cipher cipher, int block, IEncryptionInfoBuilder builder, ISecretKey skey, int encryptMode) { EncryptionVerifier ver = builder.GetVerifier(); HashAlgorithm hashAlgo = ver.HashAlgorithm; byte[] blockKey = new byte[4]; LittleEndian.PutUInt(blockKey, 0, block); MessageDigest hashAlg = CryptoFunctions.GetMessageDigest(hashAlgo); hashAlg.Update(skey.GetEncoded()); byte[] encKey = hashAlg.Digest(blockKey); EncryptionHeader header = builder.GetHeader(); int keyBits = header.KeySize; encKey = CryptoFunctions.GetBlock0(encKey, keyBits / 8); if (keyBits == 40) { encKey = CryptoFunctions.GetBlock0(encKey, 16); } ISecretKey key = new SecretKeySpec(encKey, skey.GetAlgorithm()); if (cipher == null) { cipher = CryptoFunctions.GetCipher(key, header.CipherAlgorithm, null, null, encryptMode); } else { cipher.Init(encryptMode, key); } return(cipher); }
protected internal static Cipher InitCipherForBlock(Cipher existing, int block, bool lastChunk, IEncryptionInfoBuilder builder, ISecretKey skey, int encryptionMode) { EncryptionHeader header = builder.GetHeader(); if (existing == null || lastChunk) { String pAdding = (lastChunk ? "PKCS5PAdding" : "NoPAdding"); existing = CryptoFunctions.GetCipher(skey, header.CipherAlgorithm, header.ChainingMode, header.KeySalt, encryptionMode, pAdding); } byte[] blockKey = new byte[4]; LittleEndian.PutInt(blockKey, 0, block); byte[] iv = CryptoFunctions.GenerateIv(header.HashAlgorithm, header.KeySalt, blockKey, header.BlockSize); AlgorithmParameterSpec aps; if (header.CipherAlgorithm == CipherAlgorithm.rc2) { aps = new RC2ParameterSpec(skey.GetEncoded().Length * 8, iv); } else { aps = new IvParameterSpec(iv); } existing.Init(encryptionMode, skey, aps); return(existing); }
public EncryptionHeader(byte[] contents) { var eh = EncryptionHeader.FromBytes(contents); this.FileName = eh.FileName; this.FileSize = eh.FileSize; }
public static EncryptionHeader ReadFrom(Stream stream) { if (stream == null) { throw new ArgumentNullException("stream"); } var reader = new BinaryReader(stream); var newMagicBytes = reader.ReadBytes(HeaderMagicBytes.Length); // 8 bytes // Validate magic bytes if (false == EqualArrays(HeaderMagicBytes, newMagicBytes)) { throw new StreamCryptoException(StreamCryptoError.NotEncrypted, "Header not found at the beginning of the stream."); } var result = new EncryptionHeader(); result._version = reader.ReadInt32(); // 4 bytes result._workload = reader.ReadInt32(); // 4 bytes result._bCryptSalt = reader.ReadBytes(16); // 16 bytes result._pbkdf2Salt = reader.ReadBytes(16); // 16 bytes // Total: 48 bytes // Ensure the complete header is read if (result._pbkdf2Salt.Length != 16) { // EOS reached throw new ArgumentException("End of stream reached.", "stream"); } return(result); }
private Cipher GetCipher(ISecretKey key) { EncryptionHeader em = builder.GetHeader(); ChainingMode cm = em.ChainingMode; Debug.Assert(cm == ChainingMode.ecb); return(CryptoFunctions.GetCipher(key, em.CipherAlgorithm, cm, null, Cipher.DECRYPT_MODE)); }
/// <summary> /// Encrypts the specified stream. /// </summary> /// <param name="input">A method to call to write to the encrypted output stream.</param> /// <param name="outputStream">The output stream.</param> /// <param name="password">The password.</param> /// <param name="workload">The workload.</param> /// <exception cref="System.ArgumentNullException"> /// inputStream /// or /// outputStream /// </exception> /// <exception cref="Common.Security.Cryptography.IO.StreamCryptoException"></exception> /// <remarks> /// <para> /// This method encrypts all content written by the <see cref="output"/> delegate to the <see cref="Stream"/> /// passed to the delegate. /// </para> /// <para> /// This method uses the following algorithms: /// AES-256 - encryption /// bcrypt - key derivation /// PBKDF2 on HMACSHA1 - key derivation (deriving 256 bit key for AES from 192 bit bcrypt output) /// SHA-256 - integrity check /// </para> /// </remarks> public static void EncryptStream(Action <Stream> input, Stream outputStream, string password, int workload) { if (input == null) { throw new ArgumentNullException("input"); } if (outputStream == null) { throw new ArgumentNullException("outputStream"); } var header = new EncryptionHeader(CurrentVersion, workload); var key = DeriveKey(header, password, AesKeySize); using (var aes = CreateAes(key)) { using (var hash = SHA256.Create()) { using (var cryptor = aes.CreateEncryptor()) { // Do NOT dispose to avoid closing the output stream var cryptoStream = new CryptoStream(outputStream, cryptor, CryptoStreamMode.Write); // Do NOT dispose to avoid closing the output stream var hashStream = new CryptoStream(cryptoStream, hash, CryptoStreamMode.Write); try { header.WriteTo(outputStream); input(hashStream); // "FlushFinalBlock" can be called only on the outermost stream. It is automatically called on the inner stream // if it is a "CryptoStream". Calling it explicitly on an inner stream causes an exception. if (false == cryptoStream.HasFlushedFinalBlock) { cryptoStream.FlushFinalBlock(); } if (false == hashStream.HasFlushedFinalBlock) { hashStream.FlushFinalBlock(); } } catch (Exception exc) { throw new StreamCryptoException(StreamCryptoError.EncryptionError, exc.Message, exc); } // Write footer outputStream.Write(FooterMagicBytes, 0, FooterMagicBytes.Length); var hashBytes = hash.Hash; outputStream.Write(hashBytes, 0, hashBytes.Length); } } } }
private bool encryptionFeasible(FileModel fileModel, IImageWrapper container) { var spread = this.getByteSpread(); var numberOfChannels = Enum.GetValues(typeof(LosslessFileMuxxer.ByteOrder)).GetLength(0); var totalBytesInImage = container.Width * container.Height * numberOfChannels; var bytesAvailableForData = totalBytesInImage - EncryptionHeader.GetHeaderLength(); var bytesNeededForData = fileModel.FileContents.Length * spread; return(bytesAvailableForData >= bytesNeededForData); }
public FilePass(IStreamReader reader, RecordType id, ushort length) : base(reader, id, length) { // assert that the correct record type is instantiated Debug.Assert(this.Id == ID); this.wEncryptionType = reader.ReadUInt16(); //XOR Obfuscation if (wEncryptionType == 0) { xorObfuscationKey = reader.ReadUInt16(); xorVerificationBytes = reader.ReadUInt16(); } //RC4 Encryption or RC4 CryptoAPI else if (wEncryptionType == 1) { vMajor = reader.ReadUInt16(); vMinor = reader.ReadUInt16(); //Office Binary Document RC4 Encryption if (vMajor == 1) { rc4Salt = reader.ReadBytes(16); rc4EncryptedVerifier = reader.ReadBytes(16); rc4EncryptedVerifierHash = reader.ReadBytes(16); } //Office Binary Document RC4 CryptoAPI Encryption else if (vMajor == 2 || vMajor == 3 || vMajor == 4) { encryptionHeaderFlags = reader.ReadBytes(4); encryptionHeaderSize = reader.ReadUInt32(); encryptionHeader = new EncryptionHeader(reader); encryptionVerifier = new EncryptionVerifier(reader); } else { throw new NotImplementedException( "FilePass w/ wEncryptionType == 1 and vMajor == " + vMajor + " not supported"); } } else { throw new NotImplementedException( "FilePass w/ wEncryptionType == " + wEncryptionType + " not supported"); } // assert that the correct number of bytes has been read from the stream Debug.Assert(this.Offset + this.Length == this.Reader.BaseStream.Position); }
/// <summary> /// Derives an key from the specified password. /// </summary> /// <param name="header">A file header.</param> /// <param name="password">The password.</param> /// <param name="keySize">The size of the key to derive (bits).</param> /// <returns> /// Key. /// </returns> /// <exception cref="System.ArgumentNullException">header</exception> static byte[] DeriveKey(EncryptionHeader header, string password, int keySize) { if (header == null) { throw new ArgumentNullException("header"); } return(DeriveKey( header.Version, password, header.BCryptSalt, header.PBKDF2Salt, header.Workload, keySize )); }
public override string Serialize() { var encryption = EncryptionHeader.Serialize(); var content = Data.Serialize(); var hnvsd = $"HNVSD:999:1+@{content.Length}@{content}"; var footer = Footer.Serialize(); var list = new[] { Header.Serialize(), encryption, hnvsd, footer }; var length = list.Select(x => x.Length + 1).Sum(); Header.Size.Value = length; list[0] = Header.Serialize(); return(string.Join("'", list) + "'"); }
protected internal static Cipher InitCipherForBlock(Cipher cipher, int block, IEncryptionInfoBuilder builder, ISecretKey skey, int encryptMode) { EncryptionVerifier ver = builder.GetVerifier(); HashAlgorithm hashAlgo = ver.HashAlgorithm; byte[] blockKey = new byte[4]; LittleEndian.PutUInt(blockKey, 0, block); byte[] encKey = CryptoFunctions.GenerateKey(skey.GetEncoded(), hashAlgo, blockKey, 16); ISecretKey key = new SecretKeySpec(encKey, skey.GetAlgorithm()); if (cipher == null) { EncryptionHeader em = builder.GetHeader(); cipher = CryptoFunctions.GetCipher(key, em.CipherAlgorithm, null, null, encryptMode); } else { cipher.Init(encryptMode, key); } return(cipher); }
public static RequestHeader BuildRequestHeader(EncryptionTypes encryptionType, CompressionTypes compressionType, RequestTypes requestType) { // first create headers EncryptionHeader encryptionHeader = new EncryptionHeader() { EncryptionType = encryptionType }; MessageHeader messageHeader = new MessageHeader() { CompressionType = compressionType, EncryptionHeader = encryptionHeader }; RequestHeader requestHeader = new RequestHeader() { RequestType = requestType, MessageHeader = messageHeader }; // send response header first return(requestHeader); }
public static EncryptionHeader FromBytes(byte[] contents) { var eh = new EncryptionHeader(); if (contents == null) { throw new ArgumentNullException("contents"); // dumbass. } if (contents.Length < GetHeaderLength()) { throw new Exception("Invalid header. Cannot decrypt"); } var headerBytes = contents.Take(GetHeaderLength()).ToArray(); int index = 0; for (var i = 0; i < 4; ++index, ++i) { var part = headerBytes[i]; var size = (part << (i * 0x08)); eh.FileSize += size; } var corruptionTest = ASCIIEncoding.ASCII.GetString(headerBytes, 4, CORRUPTION_TEST_STRING.Length); if (corruptionTest != CORRUPTION_TEST_STRING) { throw new Exception("Corrupted header, cannot decrypt"); } eh.FileName = ASCIIEncoding.ASCII.GetString(headerBytes, 4 + CORRUPTION_TEST_STRING.Length, MAX_FILENAME_LENGTH).Replace("\0", string.Empty); return(eh); }
/// <summary> /// Decrypts the specified stream. /// </summary> /// <param name="inputStream">The input stream.</param> /// <param name="output">The method to call to handle the decrypted data.</param> /// <param name="password">The password.</param> /// <exception cref="System.ArgumentNullException">inputStream /// or /// outputStream</exception> /// <exception cref="StreamCryptoException"> /// Integrity check failed. /// </exception> /// <exception cref="Common.Security.Cryptography.IO.StreamCryptoException">Integrity check failed.</exception> /// <remarks> /// <para> /// This method calls the <see cref="output"/> delegate with a stream of decrypted data. /// </para> /// <para> /// This method uses the following algorithms: /// AES-256 - encryption /// bcrypt - key derivation /// PBKDF2 on HMACSHA1 - key derivation (deriving 256 bit key for AES from 192 bit bcrypt output) /// SHA-256 - integrity check /// </para> /// </remarks> public static void DecryptStream(Stream inputStream, Action <Stream> output, string password) { if (inputStream == null) { throw new ArgumentNullException("inputStream"); } if (output == null) { throw new ArgumentNullException("output"); } var header = EncryptionHeader.ReadFrom(inputStream); var key = DeriveKey(header, password, AesKeySize); using (var aes = CreateAes(key)) { using (var hash = SHA256.Create()) { using (var cryptor = aes.CreateDecryptor()) { // Do NOT dispose to avoid closing the input stream // This stream stops reading when the footer is reached var decryptionStream = new DecryptionStream(inputStream); // Do NOT dispose to avoid closing the input stream var cryptoStream = new CryptoStream(decryptionStream, cryptor, CryptoStreamMode.Read); // Do NOT dispose to avoid closing the input stream var hashStream = new CryptoStream(cryptoStream, hash, CryptoStreamMode.Read); try { output(hashStream); // "FlushFinalBlock" can be called only on the outermost stream. It is automatically called on the inner stream // if it is a "CryptoStream". Calling it explicitly on an inner stream causes an exception. if (false == cryptoStream.HasFlushedFinalBlock) { cryptoStream.FlushFinalBlock(); } if (false == hashStream.HasFlushedFinalBlock) { hashStream.FlushFinalBlock(); } } catch (Exception exc) { throw new StreamCryptoException(StreamCryptoError.DecryptionError, exc.Message, exc); } var hashBytes = hash.Hash; // Read the hash stored after the footer's magic bytes var reader = new BinaryReader(inputStream); var streamHashBytes = reader.ReadBytes(hashBytes.Length); if (false == EqualArrays(streamHashBytes, hashBytes)) { throw new StreamCryptoException(StreamCryptoError.IntegrityCheckFailed, "Integrity check failed."); } } } } }
private Pixel[,] muxxImageAndFile(FileModel fileModel, IImageWrapper container) { // warning, this is very verbose code. // i could have written this in linq much more elegantly, // however i wrote this so that others can pick it up without needing to know c# / linq / lambdas if (!this.encryptionFeasible(fileModel, container)) { throw new Exception("image is too small to hide this file in"); } int byteSpread = this.getByteSpread(); int bitShiftCount = (BITS_PER_CHANNEL - BITS_USED_PER_CHANNEL); int maxValue = (0x01 << BITS_PER_CHANNEL) - 0x01; int partMask = maxValue >> bitShiftCount; int visibleMask = (byte)(maxValue << (0x08 - bitShiftCount)); var header = new EncryptionHeader(); header.FileName = fileModel.FileName; header.FileSize = (int)fileModel.FileContents.Length; var headerBytesUnspread = header.ToBytes(); var fileBytesUnspread = new byte[fileModel.FileContents.Length]; fileModel.FileContents.Read(fileBytesUnspread, 0, (int)fileModel.FileContents.Length); var unspreadBytes = new byte[headerBytesUnspread.Length + fileBytesUnspread.Length]; var spreadBytes = new byte[unspreadBytes.Length * byteSpread]; int unspreadIndex = 0; for (var i = 0; i < headerBytesUnspread.Length; ++i) { unspreadBytes [unspreadIndex] = headerBytesUnspread [i]; ++unspreadIndex; } for (var i = 0; i < fileBytesUnspread.Length; ++i) { unspreadBytes [unspreadIndex] = fileBytesUnspread [i]; ++unspreadIndex; } int spreadIndex = 0; for (var i = 0; i < unspreadBytes.Length; ++i) { var toSpread = unspreadBytes [i]; for (var s = 0; s < byteSpread; ++s) // going least significant bit to most { var spreadShift = s * BITS_USED_PER_CHANNEL; var shifted = toSpread >> spreadShift; var masked = shifted & partMask; spreadBytes [spreadIndex] = (byte)masked; ++spreadIndex; } } // sanity check var maxSpreadValue = (0x01 << (BITS_USED_PER_CHANNEL)) - 1; for (var i = 0; i < spreadBytes.Length; ++i) { var sb = spreadBytes [i]; if (sb > maxSpreadValue) { throw new Exception("the author is a moron"); } } Pixel[,] dataSet = new Pixel[container.Width, container.Height]; var numberOfChannels = Enum.GetValues(typeof(LosslessFileMuxxer.ByteOrder)).GetLength(0); var channelIndex = 0; spreadIndex = 0; var rand = new Random(Guid.NewGuid().GetHashCode()); var randomiseNonDatasetBytes = false; for (var x = 0; x < container.Width; ++x) { for (var y = 0; y < container.Height; ++y) { Pixel p = new Pixel(); p.R = container.GetRedAtPosition(x, y); p.G = container.GetGreenAtPosition(x, y); p.B = container.GetBlueAtPosition(x, y); if (spreadIndex < spreadBytes.Length) { for (var c = 0; c < numberOfChannels; ++c) { if (spreadIndex < spreadBytes.Length) { var byteToHide = spreadBytes [spreadIndex]; var whichChannel = (ByteOrder)channelIndex; switch (whichChannel) { case ByteOrder.Red: p.R = (byte)(((int)p.R & visibleMask) + byteToHide); break; case ByteOrder.Green: p.G = (byte)(((int)p.G & visibleMask) + byteToHide); break; case ByteOrder.Blue: p.B = (byte)(((int)p.B & visibleMask) + byteToHide); break; } channelIndex = channelIndex == numberOfChannels - 1 ? 0 : channelIndex + 1; ++spreadIndex; } else { if (randomiseNonDatasetBytes) { // not a data pixel, give it a random value var newVal = rand.Next(0, maxSpreadValue); p.R = (byte)(((int)p.R & visibleMask) + newVal); newVal = rand.Next(0, maxSpreadValue); p.G = (byte)(((int)p.G & visibleMask) + newVal); newVal = rand.Next(0, maxSpreadValue); p.B = (byte)(((int)p.B & visibleMask) + newVal); } } } } else { if (randomiseNonDatasetBytes) { // not a data pixel, give it a random value var newVal = rand.Next(0, maxSpreadValue); p.R = (byte)(((int)p.R & visibleMask) + newVal); newVal = rand.Next(0, maxSpreadValue); p.G = (byte)(((int)p.G & visibleMask) + newVal); newVal = rand.Next(0, maxSpreadValue); p.B = (byte)(((int)p.B & visibleMask) + newVal); } } dataSet [x, y] = p; } } return(dataSet); }
private FileModel demuxxImage(Pixel[,] source) { FileModel fileModel = null; var numberOfChannels = Enum.GetValues(typeof(LosslessFileMuxxer.ByteOrder)).GetLength(0); var sourceWidth = source.GetLength(0); var sourceHeight = source.GetLength(1); int byteSpread = this.getByteSpread(); int bitShiftCount = (BITS_PER_CHANNEL - BITS_USED_PER_CHANNEL); int maxValue = (0x01 << BITS_PER_CHANNEL) - 0x01; int partMask = maxValue >> bitShiftCount; var spreadBytes = new byte[sourceWidth * sourceHeight * byteSpread]; var spreadIndex = 0; var channelIndex = 0; for (var x = 0; x < source.GetLength(0); ++x) { for (var y = 0; y < source.GetLength(1); ++y) { Pixel p = source [x, y]; for (var c = 0; c < numberOfChannels; ++c) { var channel = (ByteOrder)channelIndex; byte valueToUse; switch (channel) { case ByteOrder.Red: valueToUse = (byte)(p.R & partMask); break; case ByteOrder.Green: valueToUse = (byte)(p.G & partMask); break; case ByteOrder.Blue: valueToUse = (byte)(p.B & partMask); break; default: throw new NotImplementedException(); } spreadBytes [spreadIndex] = valueToUse; ++spreadIndex; channelIndex = channelIndex == numberOfChannels - 1 ? 0 : channelIndex + 1; } } } var unspreadBytes = new byte[sourceWidth * sourceHeight]; // don't yet know how many of these are useful yet var unspreadIndex = 0; spreadIndex = 0; do { byte b = 0x00; for (var i = 0; i < byteSpread; ++i) { var s = spreadBytes[spreadIndex]; var spreadShift = i * BITS_USED_PER_CHANNEL; var shifted = s << spreadShift; b += (byte)shifted; ++spreadIndex; } unspreadBytes[unspreadIndex] = b; ++unspreadIndex; }while(spreadIndex < spreadBytes.Length); var headerBytes = new byte[EncryptionHeader.GetHeaderLength()]; for (var i = 0; i < headerBytes.Length; ++i) { var h = unspreadBytes[i]; headerBytes [i] = h; } var header = new EncryptionHeader(headerBytes); var fileBytes = unspreadBytes.Skip(headerBytes.Length).Take(header.FileSize).ToArray(); var ms = new MemoryStream(fileBytes); ms.Position = 0; fileModel = new FileModel(ms, header.FileName); return(fileModel); }