Files
envr/zig-vendor/age-ffi/zig/README.md
2026-04-28 17:49:04 -04:00

244 lines
5.3 KiB
Markdown

# Age-FFI Zig Bindings
Idiomatic Zig bindings for the [age](https://age-encryption.org/) encryption library.
## Features
- **Complete FFI coverage** - All age-ffi functions exposed
- **Memory safety** - RAII wrappers with automatic cleanup
- **Idiomatic error handling** - Zig errors instead of C result codes
- **Type safety** - Strong typing with Zig's type system
- **Easy to use** - High-level API that feels native to Zig
## Building the C Library
First, build the Rust FFI library:
```bash
cd ..
cargo build --release
```
This creates a static library at `../target/release/libage_ffi.a`.
## Using the Bindings
### In Your Build Script
```zig
const std = @import("std");
pub fn build(b: *std.Build) void {
const exe = b.addExecutable(.{
.name = "my-app",
.root_source_file = .{ .path = "src/main.zig" },
.target = target,
.optimize = optimize,
});
// Add the age module
const age_module = b.addModule("age", .{
.root_source_file = .{ .path = "path/to/age-ffi/zig/age.zig" },
});
exe.root_module.addImport("age", age_module);
// Link the static library
exe.addLibraryPath(.{ .path = "path/to/age-ffi/target/release" });
exe.linkSystemLibrary("age_ffi");
exe.linkLibC();
b.installArtifact(exe);
}
```
### In Your Code
```zig
const age = @import("age");
// Generate a keypair
var keypair = try age.generateKeypair();
defer keypair.deinit();
// Encrypt
const plaintext = "Secret message";
var encrypted = try age.encrypt(plaintext, keypair.getPublicKey());
defer encrypted.deinit();
// Decrypt
var decrypted = try age.decrypt(encrypted.toSlice(), keypair.getPrivateKey());
defer decrypted.deinit();
```
## API Overview
### Key Generation
```zig
// Generate new keypair
var keypair = try age.generateKeypair();
defer keypair.deinit();
// Derive public key from private key
const public_key = try age.derivePublicKey(allocator, private_key);
defer allocator.free(public_key);
```
### Encryption
```zig
// Simple encryption
var encrypted = try age.encrypt(plaintext, recipient);
defer encrypted.deinit();
// With ASCII armor
var armored = try age.encryptArmor(plaintext, recipient);
defer armored.deinit();
// Multiple recipients
const recipients = [_][:0]const u8{ recipient1, recipient2 };
var multi = try age.encryptMulti(plaintext, &recipients, false);
defer multi.deinit();
// Passphrase-based
var pass_enc = try age.encryptPassphrase(plaintext, passphrase, true);
defer pass_enc.deinit();
```
### Decryption
```zig
// Simple decryption
var decrypted = try age.decrypt(ciphertext, identity);
defer decrypted.deinit();
// With multiple identities (tries each)
const identities = [_][:0]const u8{ id1, id2 };
var multi = try age.decryptMulti(ciphertext, &identities);
defer multi.deinit();
// SSH key support
var ssh_dec = try age.decryptSsh(ciphertext, ssh_private_key);
defer ssh_dec.deinit();
// Passphrase-based
var pass_dec = try age.decryptPassphrase(ciphertext, passphrase);
defer pass_dec.deinit();
```
### File Operations
```zig
// Encrypt to file
try age.encryptToFileArmor(plaintext, recipient, "/path/to/file.age");
// Decrypt from file
var decrypted = try age.decryptFileWithIdentity("/path/to/file.age", identity);
defer decrypted.deinit();
```
### Validation
```zig
// Validate keys
const is_valid = age.isValidX25519Recipient(recipient);
// Check recipient type
const recipient_type = age.getRecipientType(recipient);
// Returns: .invalid, .x25519, or .ssh
```
### ASCII Armor
```zig
// Add armor
var armored = try age.armor(binary_data);
defer armored.deinit();
// Remove armor
var binary = try age.dearmor(armored_data);
defer binary.deinit();
```
## Memory Management
The bindings use RAII wrappers that automatically free resources:
- `Buffer` - Wraps `AgeBuffer`, freed on `deinit()`
- `Keypair` - Wraps `AgeKeypair`, freed on `deinit()`
- `CString` - Wraps C strings, freed on `deinit()`
Always call `defer x.deinit()` after creating these objects.
## Error Handling
All operations return `AgeError!T` with the following error types:
- `InvalidInput`
- `EncryptionFailed`
- `DecryptionFailed`
- `KeygenFailed`
- `IoError`
- `InvalidRecipient`
- `InvalidIdentity`
- `NoRecipients`
- `NoIdentities`
- `ArmorError`
- `PassphraseRequired`
- `InvalidPassphrase`
- `SshKeyError`
- `MemoryAllocationFailed`
- `InvalidUtf8`
- `UnsupportedKey`
## Example
See `example.zig` for a comprehensive demonstration of all features.
Run the example:
```bash
# Build the example (requires build.zig in this directory)
zig build-exe example.zig -I.. -L../target/release -lage_ffi -lc
# Or manually:
zig build-exe example.zig \
-I.. \
-L../target/release \
-lage_ffi \
-lc
./example
```
## Low-Level C API
The module also exposes the raw C functions if you need direct FFI access:
```zig
const result = age.age_encrypt(
plaintext.ptr,
plaintext.len,
recipient.ptr,
&output,
);
```
## Version Information
```zig
const version = age.getVersion(); // age-ffi version
const lib_version = age.getLibVersion(); // underlying age library version
```
## Safety Notes
1. All C strings must be null-terminated (`:0` sentinel)
2. Buffers returned by the library must be freed with `deinit()`
3. Don't use buffers after calling `deinit()`
4. The `toOwnedSlice()` method transfers ownership and calls `deinit()` automatically
## License
Same as the parent age-ffi project.