public int TransformBlock(byte[] inputBuffer, int inputOffset, int inputCount, byte[] outputBuffer, int outputOffset) { if (!_writeMode) { _hmacsha1.TransformBlock(inputBuffer, inputOffset, inputCount, inputBuffer, inputOffset); } for (int i = 0; i < inputCount; i++) { if (_encrPos == 16) { int num = 0; while (++_counterNonce[num] == 0) { num++; } _encryptor.TransformBlock(_counterNonce, 0, _blockSize, _encryptBuffer, 0); _encrPos = 0; } outputBuffer[i + outputOffset] = (byte)(inputBuffer[i + inputOffset] ^ _encryptBuffer[_encrPos++]); } if (_writeMode) { _hmacsha1.TransformBlock(outputBuffer, outputOffset, inputCount, outputBuffer, outputOffset); } return(inputCount); }
// HKDF_SHA1 // libsscrypto/hkdf.c mbedtls_hkdf public static byte[] GetAeadSubkey(byte[] key, byte[] salt, byte[] info, int outputLen) { byte[] prk; using (var h = new HMACSHA1(salt)) { prk = h.ComputeHash(key); } var N = outputLen / prk.Length; if (outputLen % prk.Length != 0) { N++; } var T = new byte[0]; var c = new byte[1]; var output = new byte[outputLen]; var cur = 0; using (var h = new HMACSHA1(prk)) { for (int i = 0; i < N; i++) { h.TransformBlock(T, 0, T.Length, T, 0); h.TransformBlock(info, 0, info.Length, info, 0); c[0] = (byte)i; T = h.TransformFinalBlock(c, 0, 1); NaiveUtils.CopyBytes(T, 0, output, cur, (i < N) ? T.Length : outputLen - cur); cur += T.Length; } } return(output); }
private void WriteTransformOneBlock(byte[] buffer, int offset) { Array.Copy(BitConverter.GetBytes(_nonce++), 0, counter, 0, 4); _xform.TransformBlock(counter, 0, 16, counterOut, 0); XorInPlace(buffer, offset, 16); _mac.TransformBlock(buffer, offset, 16, null, 0); }
private void WriteTransformOneBlock(byte[] buffer, int offset) { System.Array.Copy(BitConverter.GetBytes(_nonce++), 0, counter, 0, 4); _xform.TransformBlock(counter, 0, BLOCK_SIZE_IN_BYTES, counterOut, 0); XorInPlace(buffer, offset, BLOCK_SIZE_IN_BYTES); _mac.TransformBlock(buffer, offset, BLOCK_SIZE_IN_BYTES, null, 0); }
/// <summary> /// returns the hmac of the data and the annotation chunk values (except HMAC chunk itself) /// </summary> protected byte[] hmac() { using (HMACSHA1 hmac = new HMACSHA1(Config.HMAC_KEY)) { hmac.TransformBlock(this.data, 0, this.data.Length, this.data, 0); foreach (var e in this.annotations) { if (e.Key != "HMAC") { hmac.TransformBlock(e.Value, 0, e.Value.Length, e.Value, 0); } } hmac.TransformFinalBlock(this.data, 0, 0); return(hmac.Hash); } }
/// <summary> /// returns the hmac of the data and the annotation chunk values (except HMAC chunk itself) /// </summary> public byte[] hmac(byte[] key) { using (HMACSHA1 hmac = new HMACSHA1(key)) { hmac.TransformBlock(this.data, 0, this.data.Length, this.data, 0); foreach (var e in this.annotations.OrderBy(a => a.Key)) { if (e.Key != "HMAC") { hmac.TransformBlock(e.Value, 0, e.Value.Length, e.Value, 0); } } hmac.TransformFinalBlock(this.data, 0, 0); return(hmac.Hash); } }
/// <summary> /// Decrypts using AES/CBC/PKCS7 with an input byte array and key, using the IV (comprised of random bytes and the HMAC-SHA1 of the random bytes and plaintext) prepended using AES/ECB/None /// </summary> public static byte[] SymmetricDecryptHMACIV(byte[] input, byte[] key, byte[] hmacSecret) { Debug.Assert(key.Length >= 16); var truncatedKeyForHmac = new byte[16]; Array.Copy(key, 0, truncatedKeyForHmac, 0, truncatedKeyForHmac.Length); byte[] iv; var plaintextData = SymmetricDecrypt(input, key, out iv); // validate HMAC byte[] hmacBytes; using (var hmac = new HMACSHA1(hmacSecret)) { hmac.TransformBlock(iv, iv.Length - 3, 3, null, 0); hmac.TransformFinalBlock(plaintextData, 0, plaintextData.Length); hmacBytes = hmac.Hash; } if (!hmacBytes.Take(iv.Length - 3).SequenceEqual(iv.Take(iv.Length - 3))) { throw new CryptographicException(string.Format(CultureInfo.InvariantCulture, "{0} was unable to decrypt packet: HMAC from server did not match computed HMAC.", nameof(NetFilterEncryption))); } return(plaintextData); }
/// <summary> /// Implement the ICryptoTransform method. /// </summary> public int TransformBlock(byte[] inputBuffer, int inputOffset, int inputCount, byte[] outputBuffer, int outputOffset) { // Pass the data stream to the hash algorithm for generating the Auth Code. // This does not change the inputBuffer. Do this before decryption for read mode. if (!_writeMode) { #if !OS_WINDOWS incrementalHash.AppendData(inputBuffer, inputOffset, inputCount); #else _hmacsha1.TransformBlock(inputBuffer, inputOffset, inputCount, inputBuffer, inputOffset); #endif } // Encrypt with AES in CTR mode. Regards to Dr Brian Gladman for this. int ix = 0; while (ix < inputCount) { if (_encrPos == ENCRYPT_BLOCK) { /* increment encryption nonce */ int j = 0; while (++_counterNonce[j] == 0) { ++j; } /* encrypt the nonce to form next xor buffer */ _encryptor.TransformBlock(_counterNonce, 0, _blockSize, _encryptBuffer, 0); _encrPos = 0; } outputBuffer[ix + outputOffset] = (byte)(inputBuffer[ix + inputOffset] ^ _encryptBuffer[_encrPos++]); // ix++; } if (_writeMode) { // This does not change the buffer. #if !OS_WINDOWS incrementalHash.AppendData(outputBuffer, outputOffset, inputCount); #else _hmacsha1.TransformBlock(outputBuffer, outputOffset, inputCount, outputBuffer, outputOffset); #endif } return(inputCount); }
public byte[] pyrohmac(byte[] data, IDictionary <string, byte[]> annotations) { using (HMACSHA1 hmac = new HMACSHA1(Config.HMAC_KEY)) { hmac.TransformBlock(data, 0, data.Length, data, 0); if (annotations != null) { foreach (var e in annotations) { if (e.Key != "HMAC") { hmac.TransformBlock(e.Value, 0, e.Value.Length, e.Value, 0); } } } hmac.TransformFinalBlock(data, 0, 0); return(hmac.Hash); } }
public void CheckE(string testName, byte[] key, byte[] data, byte[] result) { algo = new HMACSHA1(key); byte[] copy = new byte [data.Length]; // LAMESPEC or FIXME: TransformFinalBlock doesn't return HashValue ! for (int i = 0; i < data.Length - 1; i++) { algo.TransformBlock(data, i, 1, copy, i); } algo.TransformFinalBlock(data, data.Length - 1, 1); Assert.AreEqual(result, algo.Hash, testName + "e"); }
public void GenerateSessionKey(byte[] clientSalt, byte[] serverSalt) { var hmac = new HMACSHA1(SecureRemotePassword.SessionKey); var wow = Encoding.ASCII.GetBytes("WoW\0"); var wowSessionKey = new byte[0x28]; hmac.TransformBlock(wow, 0, wow.Length, wow, 0); hmac.TransformBlock(clientSalt, 0, clientSalt.Length, clientSalt, 0); hmac.TransformFinalBlock(serverSalt, 0, serverSalt.Length); Buffer.BlockCopy(hmac.Hash, 0, wowSessionKey, 0, hmac.Hash.Length); hmac.Initialize(); hmac.TransformBlock(wow, 0, wow.Length, wow, 0); hmac.TransformBlock(serverSalt, 0, serverSalt.Length, serverSalt, 0); hmac.TransformFinalBlock(clientSalt, 0, clientSalt.Length); Buffer.BlockCopy(hmac.Hash, 0, wowSessionKey, hmac.Hash.Length, hmac.Hash.Length); GameAccount.SessionKey = wowSessionKey.ToHexString(); // Update SessionKey in database DB.Auth.Update(GameAccount, "SessionKey"); }
public int TransformBlock(byte[] inputBuffer, int inputOffset, int inputCount, byte[] outputBuffer, int outputOffset) { if (!_writeMode) { _hmacsha1.TransformBlock(inputBuffer, inputOffset, inputCount, inputBuffer, inputOffset); } for (int i = 0; i < inputCount; i++) { if (_encrPos == ENCRYPT_BLOCK) { for (int j = 0; (_counterNonce[j] = (byte)(_counterNonce[j] + 1)) == 0; j++) { } _encryptor.TransformBlock(_counterNonce, 0, _blockSize, _encryptBuffer, 0); _encrPos = 0; } outputBuffer[i + outputOffset] = (byte)(inputBuffer[i + inputOffset] ^ _encryptBuffer[_encrPos++]); } if (_writeMode) { _hmacsha1.TransformBlock(outputBuffer, outputOffset, inputCount, outputBuffer, outputOffset); } return(inputCount); }
/// <summary> /// Performs an encryption using AES/CBC/PKCS7 with an input byte array and key, with a IV (comprised of random bytes and the HMAC-SHA1 of the random bytes and plaintext) prepended using AES/ECB/None /// </summary> public static byte[] SymmetricEncryptWithHMACIV(byte[] input, byte[] key, byte[] hmacSecret) { // IV is HMAC-SHA1(Random(3) + Plaintext) + Random(3). (Same random values for both) var iv = new byte[16]; var random = GenerateRandomBlock(3); Array.Copy(random, 0, iv, iv.Length - random.Length, random.Length); using (var hmac = new HMACSHA1(hmacSecret)) { hmac.TransformBlock(random, 0, random.Length, null, 0); hmac.TransformFinalBlock(input, 0, input.Length); Array.Copy(hmac.Hash, iv, iv.Length - random.Length); } return(SymmetricEncryptWithIV(input, key, iv)); }
private int ReadTransformOneBlock(byte[] buffer, int offset, int last) { if (_finalBlock) { throw new NotSupportedException(); } int bytesRemaining = last - offset; int bytesToRead = (bytesRemaining > BLOCK_SIZE_IN_BYTES) ? BLOCK_SIZE_IN_BYTES : bytesRemaining; // update the counter System.Array.Copy(BitConverter.GetBytes(_nonce++), 0, counter, 0, 4); // Determine if this is the final block if ((bytesToRead == bytesRemaining) && (_length > 0) && (_totalBytesXferred + last == _length)) { _mac.TransformFinalBlock(buffer, offset, bytesToRead); counterOut = _xform.TransformFinalBlock(counter, 0, BLOCK_SIZE_IN_BYTES); _finalBlock = true; } else { _mac.TransformBlock(buffer, offset, bytesToRead, null, 0); _xform.TransformBlock(counter, 0, // offset BLOCK_SIZE_IN_BYTES, counterOut, 0); // offset } XorInPlace(buffer, offset, bytesToRead); return(bytesToRead); }
// This function is defined as follow : // Func (S, i) = HMAC(S || i) | HMAC2(S || i) | ... | HMAC(iterations) (S || i) // where i is the block number. private byte[] Func() { byte[] INT_block = Int(m_block); m_hmacsha1.TransformBlock(m_salt, 0, m_salt.Length, m_salt, 0); m_hmacsha1.TransformFinalBlock(INT_block, 0, INT_block.Length); byte[] temp = m_hmacsha1.Hash; m_hmacsha1.Initialize(); byte[] ret = temp; for (int i = 2; i <= m_iterations; i++) { temp = m_hmacsha1.ComputeHash(temp); for (int j = 0; j < BlockSize; j++) { ret[j] ^= temp[j]; } } // increment the block count. m_block++; return(ret); }
private int ReadTransformOneBlock(byte[] buffer, int offset, int last) { if (isFinalBlock) { throw new InvalidOperationException(); } int bytesRemaining = last - offset; int bytesToRead = (bytesRemaining > BLOCK_SIZE_IN_BYTES) ? BLOCK_SIZE_IN_BYTES : bytesRemaining; // update the counter Array.Copy(BitConverter.GetBytes(_nonce++), 0, counter, 0, 4); // Determine if this is the final block if ((bytesToRead == bytesRemaining) && (totalBytesLeftToRead == 0)) { hmac.TransformFinalBlock(buffer, offset, bytesToRead); counterOut = transform.TransformFinalBlock(counter, 0, BLOCK_SIZE_IN_BYTES); isFinalBlock = true; } else { hmac.TransformBlock(buffer, offset, bytesToRead, null, 0); transform.TransformBlock(counter, 0, // offset BLOCK_SIZE_IN_BYTES, counterOut, 0); // offset } XorInPlace(buffer, offset, bytesToRead); return(bytesToRead); }
/// <summary> /// Returns the SHA1 HMAC of the given <paramref name="prefix"/>, the given <paramref name="request"/>'s /// body, and the given <paramref name="suffix"/> (in that order). /// </summary> /// <param name="request">The current <see cref="HttpRequest"/>.</param> /// <param name="secret">The key data used to initialize the <see cref="HMACSHA1"/>.</param> /// <param name="prefix"> /// If non-<see langword="null"/> and non-empty, additional <c>byte</c>s to include in the hashed content /// before the <paramref name="request"/>'s body. /// </param> /// <param name="suffix"> /// If non-<see langword="null"/> and non-empty, additional <c>byte</c>s to include in the hashed content /// after the <paramref name="request"/>'s body. /// </param> /// <returns> /// A <see cref="Task"/> that on completion provides a <see cref="byte"/> array containing the SHA1 HMAC of /// the <paramref name="prefix"/>, the <paramref name="request"/>'s body, and the <paramref name="suffix"/> /// (in that order). /// </returns> protected virtual async Task <byte[]> ComputeRequestBodySha1HashAsync( HttpRequest request, byte[] secret, byte[] prefix, byte[] suffix) { if (request == null) { throw new ArgumentNullException(nameof(request)); } if (secret == null) { throw new ArgumentNullException(nameof(secret)); } if (secret.Length == 0) { throw new ArgumentException(Resources.General_ArgumentCannotBeNullOrEmpty); } await WebHookHttpRequestUtilities.PrepareRequestBody(request); using (var hasher = new HMACSHA1(secret)) { try { if (prefix != null && prefix.Length > 0) { hasher.TransformBlock( prefix, inputOffset: 0, inputCount: prefix.Length, outputBuffer: null, outputOffset: 0); } // Split body into 4K chunks. var buffer = new byte[4096]; var inputStream = request.Body; int bytesRead; while ((bytesRead = await inputStream.ReadAsync(buffer, 0, buffer.Length)) > 0) { hasher.TransformBlock( buffer, inputOffset: 0, inputCount: bytesRead, outputBuffer: null, outputOffset: 0); } if (suffix != null && suffix.Length > 0) { hasher.TransformBlock( suffix, inputOffset: 0, inputCount: suffix.Length, outputBuffer: null, outputOffset: 0); } hasher.TransformFinalBlock(Array.Empty <byte>(), inputOffset: 0, inputCount: 0); return(hasher.Hash); } finally { // Reset Position because JsonInputFormatter et cetera always start from current position. request.Body.Seek(0L, SeekOrigin.Begin); } } }
private int ProcessOneBlockWriting(byte[] buffer, int offset, int last) { if (_finalBlock) { throw new Exception("The final block has already been transformed."); } int bytesRemaining = last - offset; int bytesToRead = (bytesRemaining > BLOCK_SIZE_IN_BYTES) ? BLOCK_SIZE_IN_BYTES : bytesRemaining; // update the counter System.Array.Copy(BitConverter.GetBytes(_nonce++), 0, counter, 0, 4); if (bytesToRead == (last - offset)) { // We're doing the last bytes in this batch. // // For the AES encryption stream to work properly, We must transform full // blocks of 16 bytes each, until the very last one. But, we don't know // how to recognize the "last" bytes. The approach taken here: buffer // the last full or partial block of bytes in a batch. Then at time of // Close(), we set the _NextXformWillBeFinal flag and flush that buffer. // // This works when the caller writes in odd-sized batches, for example // 5000 bytes, or in batches that are neat multiples of block-size (16). if (_NextXformWillBeFinal) { //Console.WriteLine("WinZipAesCipherStream::ProcessOneBlockWriting: _NextXformWillBeFinal = true"); counterOut = _xform.TransformFinalBlock(counter, 0, BLOCK_SIZE_IN_BYTES); _finalBlock = true; } else if (buffer == _PendingWriteBuffer && bytesToRead == BLOCK_SIZE_IN_BYTES) { // Console.WriteLine("POBW({0},{1,5},{2,5}): pc({4}) flushing {3} bytes ...", // _NextXformWillBeFinal, offset, last, bytesToRead, _pendingCount); } else { // NOT the final block, therefore buffer it. // Console.WriteLine("POBW({0},{1,5},{2,5}): pc({4}) buffering {3} more bytes ...", // _NextXformWillBeFinal, offset, last, bytesToRead, _pendingCount); Array.Copy(buffer, offset, _PendingWriteBuffer, _pendingCount, bytesToRead); _pendingCount += bytesToRead; // remember to decrement the nonce. _nonce--; return(0); } } if (!_finalBlock) { // Next, do the AES transform. According to the AES/CTR method used // by WinZip, apply the transform to the counter, and then XOR // the result with the ciphertext to get the plaintext. _xform.TransformBlock(counter, 0, // offset BLOCK_SIZE_IN_BYTES, counterOut, 0); // offset } // XOR (in place) //Console.Write("POBW({0},{1,5},{2,5}): ", _NextXformWillBeFinal, offset, last); for (int i = 0; i < bytesToRead; i++) { buffer[offset + i] = (byte)(counterOut[i] ^ buffer[offset + i]); //Console.Write("{0:X2} ", buffer[offset + i]); } //Console.WriteLine(); // when encrypting, do the MAC last if (_finalBlock) { _mac.TransformFinalBlock(buffer, offset, bytesToRead); } else { _mac.TransformBlock(buffer, offset, bytesToRead, null, 0); } return(bytesToRead); }
/// <summary> /// <exception cref="IOException"/> /// <exception cref="SpotifyAuthenticatedException"/> /// <exception cref="AccessViolationException"/> /// </summary> private void Connect() { #region ClientHello Setup ServicePointManager.SecurityProtocol = SecurityProtocolType.Tls12; var clientHello = new ClientHello { BuildInfo = new BuildInfo { Platform = Platform.Win32X86, Product = Product.Client, ProductFlags = { ProductFlags.ProductFlagNone }, Version = 112800721 } }; clientHello.CryptosuitesSupported.Add(Cryptosuite.Shannon); clientHello.LoginCryptoHello = new LoginCryptoHelloUnion { DiffieHellman = new LoginCryptoDiffieHellmanHello { Gc = ByteString.CopyFrom(keys.PublicKeyArray()), ServerKeysKnown = 1 } }; var nonce = new byte[16]; (new Random()).NextBytes(nonce); clientHello.ClientNonce = ByteString.CopyFrom(nonce); clientHello.Padding = ByteString.CopyFrom(new byte[1] { (byte)30 }); var clientHelloBytes = clientHello.ToByteArray(); var a = conn.Result.NetworkStream; a.WriteByte(0x00); a.WriteByte(0x04); a.WriteByte(0x00); a.WriteByte(0x00); a.WriteByte(0x00); a.Flush(); var length = 2 + 4 + clientHelloBytes.Length; var bytes = BitConverter.GetBytes(length); a.WriteByte(bytes[0]); a.Write(clientHelloBytes, 0, clientHelloBytes.Length); a.Flush(); var buffer = new byte[1000]; var len = int.Parse(a.Read(buffer, 0, buffer.Length).ToString()); var tmp = new byte[len]; Array.Copy(buffer, tmp, len); tmp = tmp.Skip(4).ToArray(); var accumulator = new MemoryStream(); accumulator.WriteByte(0x00); accumulator.WriteByte(0x04); var lnarr = Utils.toByteArray(length); accumulator.Write(lnarr, 0, lnarr.Length); accumulator.Write((byte[])clientHelloBytes, 0, clientHelloBytes.Length); var lenArr = Utils.toByteArray(len); accumulator.Write(lenArr, 0, lenArr.Length); accumulator.Write((byte[])tmp, 0, tmp.Length); #endregion ClientHello Setup //Read APResponseMessage #region APResponse var binaryData = accumulator.ToArray(); var apResponseMessage = APResponseMessage.Parser.ParseFrom(tmp); var sharedKey = Utils.toByteArray(keys.ComputeSharedKey(apResponseMessage .Challenge.LoginCryptoChallenge.DiffieHellman.Gs.ToByteArray())); // Check gs_signature var rsa = new RSACryptoServiceProvider(); var rsaKeyInfo = new RSAParameters { Modulus = new BigInteger(1, serverKey).ToByteArrayUnsigned(), Exponent = BigInteger.ValueOf(65537).ToByteArrayUnsigned() }; //Set to the public key values. //Import key parameters into RSA. rsa.ImportParameters(rsaKeyInfo); var gs = apResponseMessage.Challenge.LoginCryptoChallenge.DiffieHellman.Gs.ToByteArray(); var sign = apResponseMessage.Challenge.LoginCryptoChallenge.DiffieHellman.GsSignature.ToByteArray(); if (!rsa.VerifyData(gs, sign, HashAlgorithmName.SHA1, RSASignaturePadding.Pkcs1)) { throw new AccessViolationException("Failed to verify APResponse"); } // Solve challenge binaryData = accumulator.ToArray(); using var data = new MemoryStream(); var mac = new HMACSHA1(sharedKey); mac.Initialize(); for (var i = 1; i < 6; i++) { mac.TransformBlock(binaryData, 0, binaryData.Length, null, 0); var temp = new[] { (byte)i }; mac.TransformBlock(temp, 0, temp.Length, null, 0); mac.TransformFinalBlock(new byte[0], 0, 0); var final = mac.Hash; data.Write(final, 0, final.Length); mac = new HMACSHA1(sharedKey); } var dataArray = data.ToArray(); mac = new HMACSHA1(Arrays.CopyOfRange(dataArray, 0, 0x14)); mac.TransformBlock(binaryData, 0, binaryData.Length, null, 0); mac.TransformFinalBlock(new byte[0], 0, 0); var challenge = mac.Hash; var clientResponsePlaintext = new ClientResponsePlaintext { LoginCryptoResponse = new LoginCryptoResponseUnion { DiffieHellman = new LoginCryptoDiffieHellmanResponse { Hmac = ByteString.CopyFrom(challenge) } }, PowResponse = new PoWResponseUnion(), CryptoResponse = new CryptoResponseUnion() }; var clientResponsePlaintextBytes = clientResponsePlaintext.ToByteArray(); len = 4 + clientResponsePlaintextBytes.Length; a.WriteByte(0x00); a.WriteByte(0x00); a.WriteByte(0x00); var bytesb = BitConverter.GetBytes(len); a.WriteByte(bytesb[0]); a.Write(clientResponsePlaintextBytes, 0, clientResponsePlaintextBytes.Length); a.Flush(); try { var scrap = new byte[4]; conn.Result.NetworkStream.ReadTimeout = 300; var read = conn.Result.NetworkStream.Read(scrap, 0, scrap.Length); if (read == scrap.Length) { length = (scrap[0] << 24) | (scrap[1] << 16) | (scrap[2] << 8) | (scrap[3] & 0xFF); var payload = new byte[length - 4]; conn.Result.NetworkStream.ReadComplete(payload, 0, payload.Length); var failed = APResponseMessage.Parser.ParseFrom(payload)?.LoginFailed; throw new SpotifyAuthenticatedException(failed); } else if (read > 0) { throw new Exception("Read unknown data!"); } } catch (Exception x) { // ignored } finally { conn.Result.NetworkStream.ReadTimeout = Timeout.Infinite; } using (authLock.Lock()) { sendCipher = new Shannon(); sendCipher.key(Arrays.CopyOfRange(data.ToArray(), 0x14, 0x34)); recvCipher = new Shannon(); recvCipher.key(Arrays.CopyOfRange(data.ToArray(), 0x34, 0x54)); authLockEventWaitHandle.Set(); } #endregion APResponse Debug.WriteLine("Connected successfully"); }
private int ProcessOneBlockWriting(byte[] buffer, int offset, int last) { if (_finalBlock) { throw new InvalidOperationException("The final block has already been transformed."); } int bytesRemaining = last - offset; int bytesToRead = (bytesRemaining > BLOCK_SIZE_IN_BYTES) ? BLOCK_SIZE_IN_BYTES : bytesRemaining; // update the counter System.Array.Copy(BitConverter.GetBytes(_nonce++), 0, counter, 0, 4); // We're doing the last bytes in this batch. // // For the AES encryption stream to work properly, We must transform // blocks of 16 bytes, via TransformBlock, until the very last one, for // which we call TransformFinalBlock. But, we don't know how to // recognize the "last" bytes. The approach taken here: maintain a // buffer, so that one full or partial block of bytes is always // available. This is the _PendingWriteBuffer. Then at time of // Close(), we set the _NextXformWillBeFinal flag and flush that buffer. // // This works whether the caller writes in odd-sized batches, for example // 5000 bytes, or in batches that are neat multiples of the blocksize (16). // bytesToRead is always 16 or less. If it is exactly what remains, then we need to // either, if this is the final block, do the final transform, else buffer the data. if (bytesToRead == (last - offset)) { if (_NextXformWillBeFinal) { //Console.WriteLine("WinZipAesCipherStream::ProcessOneBlockWriting: _NextXformWillBeFinal = true"); counterOut = _xform.TransformFinalBlock(counter, 0, BLOCK_SIZE_IN_BYTES); _finalBlock = true; } else if (buffer == _PendingWriteBuffer && bytesToRead == BLOCK_SIZE_IN_BYTES) { // this happens with a Flush(), I think. } else { // NOT the final block, therefore buffer it. Array.Copy(buffer, offset, _PendingWriteBuffer, _pendingCount, bytesToRead); _pendingCount += bytesToRead; // remember to decrement the nonce. _nonce--; return(bytesToRead); } } if (!_finalBlock) { // Next, do the AES transform. According to the AES/CTR method used // by WinZip, apply the transform to the counter, and then XOR // the result with the ciphertext to get the plaintext. _xform.TransformBlock(counter, 0, // offset BLOCK_SIZE_IN_BYTES, counterOut, 0); // offset } // XOR (in place) for (int i = 0; i < bytesToRead; i++) { buffer[offset + i] = (byte)(counterOut[i] ^ buffer[offset + i]); } // when encrypting, do the MAC last if (_finalBlock) { _mac.TransformFinalBlock(buffer, offset, bytesToRead); } else { _mac.TransformBlock(buffer, offset, bytesToRead, null, 0); } return(bytesToRead); }