/// <summary> /// Decrypts the inner stream and populates <paramref name="destination"/> with the resulting plaintext. /// </summary> /// <param name="destination">The buffer where the plaintext will be output to.</param> /// <param name="additionalData">Additional data to use when verify the authentication tag.</param> /// <returns>The number of bytes written to <paramref name="destination"/>.</returns> public int Read(Span <byte> destination, ReadOnlySpan <byte> additionalData) { var inputSize = CalculateCiphertextLength(destination.Length); using (var ciphertextBuffer = new RentedArray(inputSize)) { if (!this.CanRead) { throw new NotSupportedException(); } var bytesRead = this.stream.Read(ciphertextBuffer.AsSpan()); var decryptResult = crypto_secretstream_xchacha20poly1305_pull( this.state.Handle, ref MemoryMarshal.GetReference(destination), out var decryptedBlockLongLength, out var tag, in MemoryMarshal.GetReference(ciphertextBuffer.AsReadOnlySpan()), (UInt64)bytesRead, ref MemoryMarshal.GetReference(additionalData), (UInt64)additionalData.Length); if (decryptResult != 0) { throw new CryptographicException("block is invalid or corrupt"); } this.tagOfLastDecryptedBlock = tag; return((int)decryptedBlockLongLength); } }
/// <summary> /// Create a new instance with a default buffer length of 128KB. /// </summary> /// <param name="stream">When encrypting, the stream to write the ciphertext to. When decrypting, the stream to read the ciphertext from.</param> /// <param name="key">The encryption key.</param> /// <param name="encryptionMode">Whether the stream will be used for encryption or decryption.</param> /// <param name="leaveOpen">Whether to leave the <paramref name="stream"/> open.</param> public XChaChaBufferedStream( Stream stream, XChaChaKey key, EncryptionMode encryptionMode, bool leaveOpen = false) : base(stream, key, encryptionMode, leaveOpen) { this.plaintextBuffer = new RentedArray(DefaultPlaintextBufferLength); this.ciphertextBuffer = new RentedArray(CalculateCiphertextLength(DefaultPlaintextBufferLength)); }
private void EncryptBlock(ReadOnlySpan <byte> source, ReadOnlySpan <byte> additionalData, byte tag) { if (!this.CanWrite) { throw new NotSupportedException(); } if (!this.headerWritten) { this.WriteHeader(); } var count = source.Length; if (count > 0) { var outputSize = CalculateCiphertextLength(count); using (var ciphertextBuffer = new RentedArray(outputSize)) { var encryptionResult = crypto_secretstream_xchacha20poly1305_push( this.state.Handle, ref MemoryMarshal.GetReference(ciphertextBuffer.AsSpan()), out var ciphertextLength, in MemoryMarshal.GetReference(source), (ulong)count, ref MemoryMarshal.GetReference(additionalData), (UInt64)additionalData.Length, tag); if (encryptionResult != 0) { throw new CryptographicException("encryption of block failed"); } this.stream.Write(ciphertextBuffer.AsReadOnlySpan(0, (int)ciphertextLength)); } } }