TinkStreamingAead 1.0.1

dotnet add package TinkStreamingAead --version 1.0.1
                    
NuGet\Install-Package TinkStreamingAead -Version 1.0.1
                    
This command is intended to be used within the Package Manager Console in Visual Studio, as it uses the NuGet module's version of Install-Package.
<PackageReference Include="TinkStreamingAead" Version="1.0.1" />
                    
For projects that support PackageReference, copy this XML node into the project file to reference the package.
<PackageVersion Include="TinkStreamingAead" Version="1.0.1" />
                    
Directory.Packages.props
<PackageReference Include="TinkStreamingAead" />
                    
Project file
For projects that support Central Package Management (CPM), copy this XML node into the solution Directory.Packages.props file to version the package.
paket add TinkStreamingAead --version 1.0.1
                    
#r "nuget: TinkStreamingAead, 1.0.1"
                    
#r directive can be used in F# Interactive and Polyglot Notebooks. Copy this into the interactive tool or source code of the script to reference the package.
#:package TinkStreamingAead@1.0.1
                    
#:package directive can be used in C# file-based apps starting in .NET 10 preview 4. Copy this into a .cs file before any lines of code to reference the package.
#addin nuget:?package=TinkStreamingAead&version=1.0.1
                    
Install as a Cake Addin
#tool nuget:?package=TinkStreamingAead&version=1.0.1
                    
Install as a Cake Tool

TinkStreamingAead

Open-source .NET port of Google Tink's AES256-GCM-HKDF Streaming AEAD.

CI NuGet License: MIT Target

Fully interoperable with Google Tink (Java, C++, Go, Python) — encrypt in .NET, decrypt in any Tink language, and vice versa.


Features

  • Tink wire-format compatible — Uses the exact AES256-GCM-HKDF-1MB streaming format.
  • Streaming — Encrypt/decrypt multi-gigabyte files without loading them into memory.
  • Three API styles:
    • Static convenience (Encrypt / Decrypt) — straightforward sync/async calls
    • Lazy streams (CreateEncryptStream / CreateDecryptStream) — compose with GZipStream, CryptoStream, etc.
    • Simplified facade (TinkCrypto) — set the key once, one-liner file operations
  • Associated Data (AAD) — Authenticate metadata alongside the ciphertext.
  • Cross-framework — Targets netstandard2.0 (.NET Framework 4.8+) and net6.0.
  • Zero dependencies on modern .NET — Uses Portable.BouncyCastle on netstandard2.0 where AesGcm isn't available; native AesGcm on net6.0+.

Installation

dotnet add package TinkStreamingAead

Or via Package Manager:

Install-Package TinkStreamingAead

Quick Start

Encrypt and decrypt a file (one-liner with TinkCrypto)

using TinkStreamingAead;

// Generate a key (save this securely!)
byte[] key = TinkCrypto.GenerateKey();

// Create a crypto instance
var crypto = new TinkCrypto(key);

// Encrypt a file
crypto.EncryptFile("document.pdf", "document.pdf.enc");

// Decrypt it back
crypto.DecryptFile("document.pdf.enc", "document_decrypted.pdf");

// For multi-threaded scenarios, create one instance per thread
var workerCrypto = new TinkCrypto(key);

Async with progress reporting

var progress = new Progress<long>(bytes => Console.WriteLine($"Processed: {bytes} bytes"));

await Aes256GcmHkdf1MB.EncryptAsync(
    key,
    inputStream,
    outputStream,
    aad: null,
    progress: progress,
    cancellationToken: ct);

Lazy streaming (compose with other streams)

using var encryptor = Aes256GcmHkdf1MB.CreateEncryptStream(key, fileStream);
using var gzip = new System.IO.Compression.GZipStream(encryptor, CompressionMode.Compress);
// Write to gzip → automatically encrypted to fileStream

In-memory encryption

var crypto = new TinkCrypto(key);
byte[] ciphertext = crypto.EncryptBytes(Encoding.UTF8.GetBytes("Hello, World!"));
byte[] plaintext = crypto.DecryptBytes(ciphertext);

With associated data (AAD)

byte[] aad = Encoding.UTF8.GetBytes("session-12345");

// Encrypt with AAD
Aes256GcmHkdf1MB.Encrypt(key, plaintextStream, ciphertextStream, aad);

// Decrypt must use the same AAD, or it fails
Aes256GcmHkdf1MB.Decrypt(key, ciphertextStream, plaintextStream, aad);

Predict ciphertext size

long plainLen = new FileInfo("large_file.dat").Length;
long cipherLen = Aes256GcmHkdf1MB.GetCiphertextLength(plainLen);
Console.WriteLine($"Ciphertext will be {cipherLen} bytes");

Extract key from Tink JSON keyset

string keysetJson = File.ReadAllText("tink-keyset.json");
byte[] key = TinkCrypto.ExtractKeyFromJsonKeyset(keysetJson);
var crypto = new TinkCrypto(key);
crypto.DecryptFile("encrypted.bin", "decrypted.bin");

Lazy file streaming

var crypto = new TinkCrypto(key);

// Encrypt directly to a file without buffering everything
using var encryptor = crypto.CreateEncryptFileStream("output.enc");
encryptor.Write(plaintextBytes, 0, plaintextBytes.Length);
// File is finalized on Dispose

// Decrypt directly from a file
using var decryptor = crypto.CreateDecryptFileStream("output.enc");
using var ms = new MemoryStream();
decryptor.CopyTo(ms);
byte[] result = ms.ToArray();

API Reference

Aes256GcmHkdf1MB (core static class)

Method Description
Encrypt(key, plaintext, ciphertext, aad?) Synchronous all-in-one encryption
Decrypt(key, ciphertext, plaintext, aad?) Synchronous all-in-one decryption
EncryptAsync(key, plaintext, ciphertext, aad?, progress?, ct) Async encryption with progress & cancellation
DecryptAsync(key, ciphertext, plaintext, aad?, progress?, ct) Async decryption with progress & cancellation
CreateEncryptStream(key, ciphertext, aad?) Returns a writable Stream — write plaintext, get ciphertext
CreateDecryptStream(key, ciphertext, aad?) Returns a readable Stream — read plaintext from ciphertext
GetCiphertextLength(plaintextLength) Predicts total ciphertext size for a given plaintext length

TinkCrypto (instance-based wrapper, implements IDisposable)

Constructor: new TinkCrypto(key, associatedData?) — creates an instance with a 32-byte key.

Method Description
Key / AssociatedData The key (read-only) and optional AAD (read-write)
EncryptFile(inPath, outPath) / DecryptFile(inPath, outPath) File-to-file operations
EncryptFileAsync(...) / DecryptFileAsync(...) Async file operations with progress & cancellation
EncryptBytes(data) / DecryptBytes(data) In-memory byte array operations
EncryptStream(src, dst) / DecryptStream(src, dst) Stream-to-stream operations
EncryptStreamAsync(...) / DecryptStreamAsync(...) Async stream operations with progress & cancellation
CreateEncryptStream(dst) / CreateDecryptStream(src) Lazy streaming wrappers (disposing them disposes the underlying stream)
CreateEncryptFileStream(path) / CreateDecryptFileStream(path) Lazy file-to-file streaming wrappers
Dispose() Zeroes the key and marks the instance as disposed

Static utilities:

Method Description
TinkCrypto.GenerateKey() Generates a random 32-byte AES-256 key
TinkCrypto.ExtractKeyFromJsonKeyset(json) Extracts the raw key from a Google Tink JSON keyset

Note: TinkCrypto instances are not thread-safe — use one instance per thread. For stateless multi-threaded use, call Aes256GcmHkdf1MB methods directly with per-call keys.


Cryptographic Details

Property Value
Algorithm AES-256-GCM
Key size 32 bytes (256 bits)
Key derivation HKDF-SHA256 (Extract + Expand)
Segment size 1,048,576 bytes (1 MiB) ciphertext
GCM tag 16 bytes per segment
Nonce 12 bytes: 7B prefix + 4B counter (BE) + 1B last-flag
Header 40 bytes: 0x28 + 32B salt + 7B nonce prefix

The implementation follows the Google Tink Streaming AEAD specification exactly.


Interoperability

Ciphertexts produced by this library can be decrypted by:

  • Google Tink (Java, C++, Go, Python)
  • Tinkey CLI tool
  • The test suite verifies interoperability against ciphertexts generated by Google Tink (Python).

Testing

The test project (tests/TinkStreamingAead.Tests/) targets net8.0 and net48 and includes:

# Run all tests
dotnet test

# Run a specific category
dotnet test --filter "FullyQualifiedName~GoldenVectorTests"
dotnet test --filter "FullyQualifiedName~TinkInteropTests"

Test Categories

Test Suite What it covers
GoldenVectorTests Deterministic byte-level verification against known ciphertext
TinkInteropTests Decrypts ciphertexts produced by Google Tink (Python)
RoundTripTests Encrypt-then-decrypt for 0 B → 50 MiB
AsyncRoundTripTests Async encrypt/decrypt with progress reporting & cancellation
LazyStreamTests Incremental reads/writes, GZipStream composition
EdgeCaseTests Null/wrong-key/malformed/truncated/corrupted inputs
HkdfTests RFC 5869 test vectors
LargeFileRoundTripTests Powers of ten up to 4 GB, boundary cases, GetCiphertextLength, keyset extraction, lazy file streams, IDisposable semantics

Building from Source

# Clone the repository
git clone https://github.com/salehalakhras/TinkStreamingAead.git
cd TinkStreamingAead

# Build
dotnet build

# Run tests
dotnet test

# Create NuGet package
dotnet pack src/TinkStreamingAead/TinkStreamingAead.csproj -c Release

License

MIT License

Copyright (c) 2026 Saleh Alakhras

Permission is hereby granted, free of charge, to any person obtaining a copy of this software and associated documentation files (the "Software"), to deal in the Software without restriction, including without limitation the rights to use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies of the Software, and to permit persons to whom the Software is furnished to do so, subject to the following conditions:

The above copyright notice and this permission notice shall be included in all copies or substantial portions of the Software.

THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.

Product Compatible and additional computed target framework versions.
.NET net5.0 was computed.  net5.0-windows was computed.  net6.0 is compatible.  net6.0-android was computed.  net6.0-ios was computed.  net6.0-maccatalyst was computed.  net6.0-macos was computed.  net6.0-tvos was computed.  net6.0-windows was computed.  net7.0 was computed.  net7.0-android was computed.  net7.0-ios was computed.  net7.0-maccatalyst was computed.  net7.0-macos was computed.  net7.0-tvos was computed.  net7.0-windows was computed.  net8.0 was computed.  net8.0-android was computed.  net8.0-browser was computed.  net8.0-ios was computed.  net8.0-maccatalyst was computed.  net8.0-macos was computed.  net8.0-tvos was computed.  net8.0-windows was computed.  net9.0 was computed.  net9.0-android was computed.  net9.0-browser was computed.  net9.0-ios was computed.  net9.0-maccatalyst was computed.  net9.0-macos was computed.  net9.0-tvos was computed.  net9.0-windows was computed.  net10.0 was computed.  net10.0-android was computed.  net10.0-browser was computed.  net10.0-ios was computed.  net10.0-maccatalyst was computed.  net10.0-macos was computed.  net10.0-tvos was computed.  net10.0-windows was computed. 
.NET Core netcoreapp2.0 was computed.  netcoreapp2.1 was computed.  netcoreapp2.2 was computed.  netcoreapp3.0 was computed.  netcoreapp3.1 was computed. 
.NET Standard netstandard2.0 is compatible.  netstandard2.1 was computed. 
.NET Framework net461 was computed.  net462 was computed.  net463 was computed.  net47 was computed.  net471 was computed.  net472 was computed.  net48 was computed.  net481 was computed. 
MonoAndroid monoandroid was computed. 
MonoMac monomac was computed. 
MonoTouch monotouch was computed. 
Tizen tizen40 was computed.  tizen60 was computed. 
Xamarin.iOS xamarinios was computed. 
Xamarin.Mac xamarinmac was computed. 
Xamarin.TVOS xamarintvos was computed. 
Xamarin.WatchOS xamarinwatchos was computed. 
Compatible target framework(s)
Included target framework(s) (in package)
Learn more about Target Frameworks and .NET Standard.

NuGet packages

This package is not used by any NuGet packages.

GitHub repositories

This package is not used by any popular GitHub repositories.

Version Downloads Last Updated
1.0.1 124 6/16/2026
1.0.0 103 6/16/2026

1.0.1:
     - Hardened: constant-time key zeroing, CryptographicException on tag mismatch across all
       targets, canonical full-final-segment encoding for exact-multiple inputs (lazy streams),
       and a 2^32-segment nonce-overflow guard.
     - Verified against Google Tink (Python) ciphertexts and RFC 5869 vectors on net8.0 and net48.