IronWeave.Blobs 0.3.0

dotnet add package IronWeave.Blobs --version 0.3.0
                    
NuGet\Install-Package IronWeave.Blobs -Version 0.3.0
                    
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="IronWeave.Blobs" Version="0.3.0" />
                    
For projects that support PackageReference, copy this XML node into the project file to reference the package.
<PackageVersion Include="IronWeave.Blobs" Version="0.3.0" />
                    
Directory.Packages.props
<PackageReference Include="IronWeave.Blobs" />
                    
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 IronWeave.Blobs --version 0.3.0
                    
#r "nuget: IronWeave.Blobs, 0.3.0"
                    
#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 IronWeave.Blobs@0.3.0
                    
#: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=IronWeave.Blobs&version=0.3.0
                    
Install as a Cake Addin
#tool nuget:?package=IronWeave.Blobs&version=0.3.0
                    
Install as a Cake Tool

IronWeave.Blobs — C# Binding

A .NET wrapper for the iwblobs native library, providing chunked AES-256-GCM streaming encryption with per-recipient ECDH key envelopes and protobuf-based signed blob envelopes. Encryption and decryption are exposed as standard System.IO.Stream subclasses — IwBlobEncryptStream (write-only) and IwBlobDecryptStream (read-only) — while utilities and envelope validation are static methods on IronWeave.Blobs.IwBlobs.

Installation

Install the NuGet package (includes native binaries for all supported platforms):

dotnet add package IronWeave.Blobs

Requirements

  • .NET 10
  • Native iwblobs library (built from the Rust crate with cargo build --release)
  • Protocol Buffers compiler (protoc) — only needed for projects using the generated BlobEnvelope proto types

Project Structure

bindings/csharp/
  IronWeave.Blobs.csproj    Class library
  IwBlobs.cs                Static API + IwBlobEncryptStream / IwBlobDecryptStream
  LICENSE                   Proprietary license (packed into the NuGet package)
  README.md                 This file (packed into the NuGet package)
  tests/
    IronWeave.Blobs.Tests.csproj    xUnit test project
    IwBlobsTests.cs                 FFI wrapper round-trip tests

Adding to Your Project

Reference the class library project:

<ProjectReference Include="path/to/bindings/csharp/IronWeave.Blobs.csproj" />

Your project must enable unsafe blocks since the wrapper uses pointer-based P/Invoke internally:

<AllowUnsafeBlocks>true</AllowUnsafeBlocks>

The native library must be resolvable at runtime. For development, register a NativeLibrary.SetDllImportResolver to point at the Cargo build output:

using System.Runtime.InteropServices;

NativeLibrary.SetDllImportResolver(
    typeof(IwBlobs).Assembly,
    (libraryName, assembly, searchPath) =>
    {
        if (libraryName == "iwblobs")
        {
            // Adjust path to your cargo build output
            return NativeLibrary.Load("/path/to/target/release/libiwblobs.dylib");
        }
        return IntPtr.Zero;
    });

For production, place the native library where .NET can find it (application directory, system library path, or bundled in a NuGet runtime package).

.NET MAUI Deployment

The same iwblobs native binaries work across all MAUI target platforms — no special MAUI-specific builds are needed. The differences are purely in packaging and linking.

iOS

MAUI uses static linking on iOS. Reference the .a file and use [LibraryImport("__Internal")] instead of [LibraryImport("iwblobs")]:


<ItemGroup Condition="$(TargetFramework.Contains('ios'))">
  <NativeReference Include="path/to/libiwblobs.a">
    <Kind>Static</Kind>
    <ForceLoad>true</ForceLoad>
  </NativeReference>
</ItemGroup>

The IwBlobs wrapper uses [LibraryImport("iwblobs")] by default. For iOS static linking, route to __Internal via a conditional DllImportResolver or a build-time substitution.

Android

MAUI uses the same .so files produced by the Android NDK (via cargo-ndk). Place them under the NuGet RID directories:

NDK ABI NuGet RID
arm64-v8a/libiwblobs.so runtimes/android-arm64/native/
armeabi-v7a/libiwblobs.so runtimes/android-arm/native/
x86_64/libiwblobs.so runtimes/android-x64/native/

Alternatively, add the .so files as AndroidNativeLibrary items:

<ItemGroup Condition="$(TargetFramework.Contains('android'))">
  <AndroidNativeLibrary Include="libs/arm64-v8a/libiwblobs.so" Abi="arm64-v8a" />
  <AndroidNativeLibrary Include="libs/armeabi-v7a/libiwblobs.so" Abi="armeabi-v7a" />
  <AndroidNativeLibrary Include="libs/x86_64/libiwblobs.so" Abi="x86_64" />
</ItemGroup>

Desktop (macOS / Windows / Linux)

Standard dynamic linking via [LibraryImport("iwblobs")]. Place the native library (.dylib / .dll / .so) in a NuGet runtimes/{rid}/native/ directory or alongside the application binary.

CI-Built Binaries

The release workflow stages a pre-organized runtimes/ directory by RID. The .csproj packs everything under <repo>/dist/nuget/runtimes/ into the NuGet package, so a single dotnet add package IronWeave.Blobs brings all platforms at once.

API Reference

All utilities and validation are static on IronWeave.Blobs.IwBlobs. Methods that can fail throw IwBlobsException (extends InvalidOperationException). The streaming classes are Stream subclasses and integrate with any Stream-based pipeline.

Static Utilities

using IronWeave.Blobs;

// Cryptographically secure random AES-256 key (32 bytes)
byte[] aesKey = IwBlobs.GenerateAesKey();

// 16-byte UUIDv7 (big-endian, time-ordered) — e.g. for the envelope UUID
byte[] uuid = IwBlobs.GenerateUuidV7();

// Single-shot SHA-256 (32-byte digest) — native implementation
byte[] digest = IwBlobs.Sha256(encryptedBytes);

Two constants are exposed: IwBlobs.DefaultChunkSize (1 MB) and IwBlobs.StreamHeaderSize (16 bytes).

Encrypting a Blob

IwBlobEncryptStream is a write-only stream. It writes the 16-byte stream header to the destination on construction, then emits one encrypted chunk per DefaultChunkSize (or the chunkSize you pass, 1 byte–16 MB). Disposing flushes the final chunk and finalizes the GCM state.

byte[] aesKey = IwBlobs.GenerateAesKey();

await using var output = File.Create("encrypted.bin");
await using (var enc = new IwBlobEncryptStream(output, aesKey))
{
    await using var input = File.OpenRead("input.bin");
    await input.CopyToAsync(enc);
}   // dispose here finalizes encryption and flushes the last chunk

// Custom chunk size (e.g. 4 MB):
await using var enc2 = new IwBlobEncryptStream(output, aesKey, chunkSize: 4 * 1024 * 1024);

The 16-byte header is also available via the Header property (a defensive copy) if you need to store it out-of-band rather than inline at the front of the ciphertext stream.

Note: Flush() is intentionally a no-op — partial-chunk encryption is meaningless under the chunked GCM scheme. Chunks are emitted only when full or on dispose.

Decrypting a Blob

IwBlobDecryptStream is a read-only stream. It reads the 16-byte header from the source on construction, then decrypts and verifies one chunk at a time. Disposing verifies the final chunk was seen, detecting truncation.

await using var input = File.OpenRead("encrypted.bin");
await using var dec = new IwBlobDecryptStream(input, aesKey);
await using var output = File.Create("output.bin");
await dec.CopyToAsync(output);

A wrong key, reordered/dropped chunks, or a truncated stream all surface as an IwBlobsException (GCM authentication failure or unexpected end-of-stream).

SHA-256 Hashing

For the encrypted_hash field in a BlobEnvelope, you can use the native hash or — preferably — the .NET built-ins:

using System.Security.Cryptography;

// Recommended: .NET built-in
byte[] hash = SHA256.HashData(encryptedBytes);

// Or streaming over chunks:
using var hasher = IncrementalHash.CreateHash(HashAlgorithmName.SHA256);
hasher.AppendData(chunk1);
hasher.AppendData(chunk2);
byte[] streamingHash = hasher.GetHashAndReset();

// Or the native FFI implementation (byte-identical):
byte[] nativeHash = IwBlobs.Sha256(encryptedBytes);

The algorithm is SHA-256 in all cases; the envelope's Hash proto carries HASH_TYPE_SHA2_256 to make the wire representation self-describing (see Working with Proto Types).

Envelope Validation

// Structural validation only — for envelope authors, pre-sign
IwBlobs.ValidateBlobEnvelope(envelopeBytes);

// Ed25519 signature + structural validation — for verifiers
IwBlobs.ValidateSignedBlobEnvelope(signedEnvelopeBytes);

Both methods throw IwBlobsException on failure and return normally on success. Structural validation enforces, among other things, that every Address carries a known AddressType of the correct length and that encrypted_hash is exactly HASH_TYPE_SHA2_256 + 32 bytes. The signed validator additionally checks the Ed25519 signature and the (AddressType, SignatureType) allowlist, failing closed on unknown pairs.

Working with Proto Types

BlobEnvelope / SignedBlobEnvelope are protobuf messages. Add proto codegen to your project to construct and parse them:

<PackageReference Include="Google.Protobuf" Version="3.*" />
<PackageReference Include="Grpc.Tools" Version="2.*" PrivateAssets="all" />

<Protobuf Include="path/to/proto/basic_types.proto"   ProtoRoot="path/to/proto" GrpcServices="None" />
<Protobuf Include="path/to/proto/blob_envelope.proto" ProtoRoot="path/to/proto" GrpcServices="None" />

blob_envelope.proto imports basic_types.proto — both must be compiled with the same ProtoRoot. Generated C# types land in the Weave.Blobs namespace (envelope types) and Weave.Common namespace (shared Address, UUID, SignatureType, Hash, HashType).

Key points when assembling an envelope by hand:

using Google.Protobuf;
using Weave.Common;
using Weave.Blobs;

// Every Address carries an AddressType discriminator (Ed25519 = 0 today).
var creator = new Address
{
    AddressType = AddressType.Ed25519,
    Value = ByteString.CopyFrom(creatorPublicKey),  // 32 bytes for Ed25519
};

// encrypted_hash is a typed Hash, NOT bare bytes.
// You MUST set hash_type explicitly: the proto3 default 0 = HASH_TYPE_SHA3_256
// (the chain's hash), which iwblobs does not produce and which validation rejects.
var attachment = new BlobAttachment
{
    EncryptedHash = new Hash
    {
        HashType = HashType.Sha2256,
        Value = ByteString.CopyFrom(SHA256.HashData(encryptedBytes)),
    },
    // ... AES-encrypted key shares, storage locations, etc.
};

An envelope can be standalone (SignedBlobEnvelope) or attached to a CryptoOperation from IronWeave.Crypto by referencing the envelope UUID in the operation's context.

Chunked Wire Format

Region Layout
Stream header (16 bytes) 12-byte random base nonce + 4-byte LE u32 version (currently 1)
Per chunk 4-byte LE u32 payload length + ciphertext + 16-byte GCM tag

Each chunk's nonce is base_nonce XOR chunk_index; a 33-byte AAD binds a domain separator, the chunk index, an is_last_chunk flag, and the base nonce — defending against truncation, reordering, and cross-blob chunk swaps. The stream classes handle all of this internally; you only ever see plaintext on one side and the opaque ciphertext stream on the other.

Error Handling

API Failure behavior
GenerateAesKey, GenerateUuidV7, Sha256 Throws IwBlobsException
ValidateBlobEnvelope, ValidateSignedBlobEnvelope Throws IwBlobsException (returns normally on success)
IwBlobEncryptStream / IwBlobDecryptStream ctor & I/O Throws IwBlobsException (FFI errors), ArgumentException (bad key length), ArgumentOutOfRangeException (bad chunk size)

IwBlobsException exposes structured fields for programmatic handling:

Property Type Description
Function string The FFI function that failed (e.g. "iwblob_decrypt_stream_write")
Code int Return code: 1 = input/format, 2 = internal panic, 3 = verification failed, 6 = signature invalid, 7 = encryption/decryption failed
Detail string? Human-readable detail from iwblob_last_error()

Running Tests

Build the native library first, then run the C# tests:

# Build the native library
cargo build --release

# Run all C# tests (10 tests)
cd iwblobs/bindings/csharp/tests && dotnet test

IwBlobsTests covers FFI wrapper round-trips: AES key / UUID generation, single-shot and streaming SHA-256, full chunked encrypt → decrypt cycles (including empty and multi-chunk payloads), and envelope structural/signature validation.

License

Proprietary. All rights reserved. See LICENSE.

Product Compatible and additional computed target framework versions.
.NET net10.0 is compatible.  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. 
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
0.3.0 104 5/28/2026
0.2.0 99 5/18/2026