at_chops 3.3.0
at_chops: ^3.3.0 copied to clipboard
Package for at_protocol cryptographic and hashing operations
Package for Cryptographic and Hashing Operations (CHOPS) such as encryption, decryption, data signing, key agreement, and hashing that can be leveraged by client applications using the Atsign Protocol.
Features #
- Asymmetric encryption/decryption using RSA-2048 and RSA-4096
- Symmetric encryption/decryption using AES-128, AES-192, and AES-256 (CTR and GCM modes)
- Digest signing and verification for PKAM authentication (RSA, ECC secp256r1, Ed25519)
- Data signing and verification for public data in the Atsign Protocol
- Post-quantum digital signatures: ML-DSA-65 (FIPS 204) — pure-Dart and OpenSSL FFI backends
- Post-quantum key encapsulation: ML-KEM-768 (FIPS 203) — pure-Dart and OpenSSL FFI backends
- Hybrid PQ/classical KEM: X-Wing (X25519 + ML-KEM-768, draft-connolly-cfrg-xwing-kem-10)
- Elliptic-curve key agreement: X25519 — pure-Dart and OpenSSL FFI backends
- Serializable key-pair generation helpers for RSA, X25519, ML-KEM-768, ML-DSA-65, and X-Wing
- Hashing: SHA-256, SHA-512, MD5, Argon2id
- HKDF key derivation
Getting started #
Developers should have a basic understanding of asymmetric and symmetric encryption, as well as key encapsulation mechanisms (KEMs) for the PQC APIs.
Use the algorithm classes directly. Generate or load key material first, then pass it to the relevant encryption, signing, key agreement, KEM, or hashing class.
Usage #
Examples assume package:at_chops/at_chops.dart is imported. Snippets using
utf8 or Uint8List also require dart:convert or dart:typed_data.
Serializable key generation #
Use these helpers when the key material needs to fit the SDK's string-backed key
types (AtPublicKey, AtPrivateKey, and SymmetricKey). Byte-oriented key
pairs are base64-encoded by the wrapper classes.
final aes128 = AESKey.generate(16);
final aes192 = AESKey.generate(24);
final aes256 = AESKey.generate(32);
final rsa2048 = RsaKeyPair.generate();
final rsa4096 = RsaKeyPair.generate(keySize: 4096);
final x25519 = await X25519KeyPair.generate();
final mlKem768 = await MlKem768KeyPair.generate();
final mlDsa65 = await MlDsa65KeyPair.generate();
final xWing = await XWingKeyPair.generate();
RSA encryption #
final keyPair = RsaKeyPair.generate();
final rsa = RsaEncryptionAlgo.fromKeyPair(keyPair);
final message = Uint8List.fromList(utf8.encode('Hello World'));
final encrypted = rsa.encrypt(message);
final decrypted = rsa.decrypt(encrypted);
AES encryption #
final aesKey = AESKey.generate(32);
final iv = InitialisationVector.random(16);
final aes = AESEncryptionAlgo(aesKey);
final message = Uint8List.fromList(utf8.encode('Hello World'));
final encrypted = await aes.encrypt(message, iv: iv);
final decrypted = await aes.decrypt(encrypted, iv: iv);
Signing and verification #
final keyPair = RsaKeyPair.generate();
final signing = RsaSigningAlgo(keyPair, HashingAlgoType.sha256);
final message = Uint8List.fromList(utf8.encode('data to sign'));
final signature = signing.sign(message);
final valid = signing.verify(message, signature);
ML-DSA-65 (post-quantum signing, pure-Dart) #
final kp = await MlDsa65PureDartAlgo.generateKeyPair();
// kp.publicKey — 1952 bytes; kp.secretKey — 4032 bytes
final signature = await MlDsa65PureDartAlgo.signBytes(message, kp.secretKey);
final valid = await MlDsa65PureDartAlgo.verifyBytes(message, signature, kp.publicKey);
ML-KEM-768 (post-quantum KEM, pure-Dart) #
final kem = MlKem768PureDartAlgo.instance;
final kp = await kem.generateKeyPair();
// kp.publicKey — 1184 bytes; kp.secretKey — 2400 bytes
// Sender
final (ciphertext: ct, sharedSecret: ss1) = await kem.encapsulate(kp.publicKey);
// Receiver
final ss2 = await kem.decapsulate(kp.secretKey, ct);
// ss1 == ss2
X-Wing (hybrid PQ/classical KEM) #
final xwing = XWingPureDartAlgo.instance;
final kp = await xwing.generateKeyPair();
// kp.publicKey — 1216 bytes; kp.secretKey — 32 bytes (seed)
// Sender
final (ciphertext: ct, sharedSecret: ss1) = await xwing.encapsulate(kp.publicKey);
// Receiver
final ss2 = await xwing.decapsulate(kp.secretKey, ct);
// ss1 == ss2
X25519 (Diffie–Hellman, pure-Dart) #
final x25519 = X25519PureDartAlgo.instance;
final kpA = await x25519.generateKeyPair();
final kpB = await x25519.generateKeyPair();
final ssA = await x25519.dh(kpA.privateKey, kpB.publicKey);
final ssB = await x25519.dh(kpB.privateKey, kpA.publicKey);
// ssA == ssB
Hashing #
final hash = SHA512HashingAlgo().hash('some-data'.codeUnits);
FFI backends #
ML-DSA-65, ML-KEM-768, and X25519 each have an OpenSSL FFI backend (MlDsa65FfiAlgo, MlKem768FfiAlgo, X25519FfiAlgo) that requires libcrypto to be installed. The pure-Dart backends (*PureDartAlgo) work on all platforms without native dependencies.
X-Wing (XWingFfiAlgo) composes the FFI backends for maximum performance when libcrypto is available.
Running Tests #
Some tests require libcrypto.so to be installed. Running dart test without it will fail those tests.
To run all tests EXCEPT the FFI tests:
dart test --exclude-tags ffi
To run ONLY the FFI tests:
dart test --tags ffi