Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
168 changes: 166 additions & 2 deletions Cargo.lock

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

3 changes: 3 additions & 0 deletions Cargo.toml
Original file line number Diff line number Diff line change
Expand Up @@ -19,6 +19,7 @@
]

[dependencies]
aes-gcm = { version = "0.11.1", default-features = false, features = ["aes", "alloc"] }
apl-associated-token-account = { version = "=0.10.0", features = ["no-entrypoint"] }
apl-token = { version = "=0.10.0", features = ["no-entrypoint"] }
arch_sdk = "=0.10.0"
Expand All @@ -26,14 +27,16 @@
base64 = "0.22"
bitcoin = "=0.32.7"
bs58 = "0.5"
clap = { version = "4.5", features = ["derive", "env"] }

Check warning on line 30 in Cargo.toml

View workflow job for this annotation

GitHub Actions / Initialize and Deploy

unused build dependency `indexmap`
flate2 = "1.1"

Check warning on line 31 in Cargo.toml

View workflow job for this annotation

GitHub Actions / Initialize and Deploy

unused build dependency `proc-macro-crate`
hex = "0.4"

Check warning on line 32 in Cargo.toml

View workflow job for this annotation

GitHub Actions / Initialize and Deploy

unused build dependency `unicode-segmentation`
hkdf = "0.12"
include_dir = "0.7"
minijinja = "2"
rand = "0.8"
reqwest = { version = "0.12", default-features = false, features = ["blocking", "json", "rustls-tls"] }
serde_json = "1.0"
sha2 = "0.10"
thiserror = "2.0"

[dev-dependencies]
Expand Down
29 changes: 29 additions & 0 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -111,6 +111,8 @@ Install it with `rustup toolchain install nightly`; Satellite's
| --- | --- | --- |
| [`keygen`](#generate-keys) | `arch-kit keygen [OPTIONS] <PATH>...` | Generate one or more secp256k1 key files, with optional public key prefixes (vanity). |
| [`pubkey`](#derive-a-public-key) | `arch-kit pubkey <PATH>` | Derive a Base58 Arch public key from a secret key file. |
| [`encrypt`](#encrypt-and-decrypt-messages) | `arch-kit encrypt --key <PATH> [TEXT]` | Encrypt a UTF-8 message with AES-256-GCM. |
| [`decrypt`](#encrypt-and-decrypt-messages) | `arch-kit decrypt --key <PATH> [TEXT]` | Decrypt a message using the same secret key file. |

### Token program

Expand Down Expand Up @@ -245,6 +247,33 @@ arch-kit pubkey ./keys/authority.key
The command reads either supported secret-key file format and writes only the
derived Base58 Arch public key to standard output.

## Encrypt and decrypt messages

```bash
arch-kit encrypt --key ./keys/authority.key "Hello"
arch-kit decrypt --key ./keys/authority.key "<encrypted-payload>"

# Omit TEXT to read from stdin:
printf 'Hello' | arch-kit encrypt --key ./keys/authority.key > message.enc
arch-kit decrypt --key ./keys/authority.key < message.enc
```

Both commands run locally and accept existing hex or SDK JSON secret-key files.
Encryption and decryption require the same private key. Messages must be UTF-8;
empty messages, Unicode, and multiline text are supported. Encryption prints a
Base64 payload with a trailing newline. Decryption preserves the original text
exactly without adding a newline. Add `--json` for `{"ciphertext":"..."}` or
`{"message":"..."}` output. Authentication failures exit unsuccessfully without
printing plaintext.

Encryption uses [RustCrypto AES-GCM](https://docs.rs/aes-gcm/), whose documentation
reports an NCC Group audit. A dedicated 32-byte AES key is derived from the raw
32-byte private key using HKDF-SHA256, no salt, and the context
`arch-kit/message-encryption/v1`. Each message uses a fresh OS-random 12-byte
nonce and a full 16-byte authentication tag. The format is standard padded Base64
of `0x01 || nonce || ciphertext || tag`; the version byte is authenticated as
associated data. Surrounding whitespace in an encrypted payload is ignored.

## Inspect tokens

Derive an ATA locally without contacting an RPC node:
Expand Down
12 changes: 9 additions & 3 deletions src/cli.rs
Original file line number Diff line number Diff line change
Expand Up @@ -2,9 +2,9 @@ use clap::{Parser, Subcommand};

use crate::{
commands::{
arch_balance, ata, build_idl, create_mint, deploy, faucet, init, keygen, mint_info,
mint_tokens, pubkey, token_account, token_accounts, token_balance, token_transfer,
transfer_arch,
arch_balance, ata, build_idl, create_mint, deploy, encryption, faucet, init, keygen,
mint_info, mint_tokens, pubkey, token_account, token_accounts, token_balance,
token_transfer, transfer_arch,
},
network::{BitcoinNetwork, DEFAULT_RPC_URL},
};
Expand Down Expand Up @@ -58,6 +58,12 @@ pub(crate) enum Command {
/// Derive an Arch public key from a secret key file.
Pubkey(pubkey::Args),

/// Encrypt a UTF-8 message using a secret key file and AES-256-GCM.
Encrypt(encryption::Args),

/// Decrypt an AES-256-GCM message using the same secret key file.
Decrypt(encryption::Args),

/// Derive an associated token account address for an owner and mint.
Ata(ata::Args),

Expand Down
Loading
Loading