/// <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); } }
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)); } } }