Documentation
¶
Overview ¶
Package mist is an asymmetric-key audio steganography library.
A sender embeds a payload using only the recipient's X25519 public key. Only the matching private key can recover it. Phase 1 carries text inside Ogg Vorbis audio packets — not container metadata — so a digital recording of a live stream is enough to extract the message.
Hybrid encryption follows the age / NaCl box shape: an ephemeral X25519 key agreement derives a ChaCha20-Poly1305 key. Each stego frame is sealed independently so repeating a payload across a live stream does not produce a correlatable ciphertext. Unused capacity is filled with CSPRNG bytes at the same embedding density as a real message, so presence and absence have the same statistical footprint.
Codec I/O goes through libav (libavformat, libavcodec, libavutil) via cgo. Coefficient-domain embedding is intentionally behind a Codec interface so WAV/PCM and MP3 can be added later without touching crypto or framing.
Index ¶
- Constants
- Variables
- func FrameCapacity() int
- func GenerateKeyPair() (pub, priv []byte, err error)
- func GenerateSigningKeyPair() (pub, priv []byte, err error)
- type Catcher
- type CatcherOption
- type Emitter
- func (e *Emitter) Embed(ctx context.Context, source string, payload Payload) (io.ReadCloser, error)
- func (e *Emitter) EmbedFile(ctx context.Context, carrier *os.File, payload Payload) (io.ReadCloser, error)
- func (e *Emitter) EmbedReader(ctx context.Context, carrier io.Reader, payload Payload) (io.ReadCloser, error)
- type EmitterOption
- type Payload
- type PayloadType
- type Result
Constants ¶
const ( PublicKeySize = 32 PrivateKeySize = 32 )
X25519 key sizes in bytes.
const ( SigningPublicKeySize = 32 SigningPrivateKeySize = 64 SignatureSize = 64 )
Ed25519 sizes for optional sender authentication.
const ( NonceSize = 12 TagSize = 16 )
ChaCha20-Poly1305 sizes in bytes.
const EnvelopeOverhead = PublicKeySize + LengthSize + NonceSize + TagSize
EnvelopeOverhead is the number of bytes added by hybrid encryption before embedding: ephemeral public key + masked length + nonce + Poly1305 tag.
const FrameDuration = 8 * time.Second
FrameDuration is the fixed stego-frame length. Embed loops the payload across consecutive frames of this duration; Listen uses the same value to recover frame phase. It is a protocol constant, not a per-call option.
const LengthSize = 4
LengthSize is the masked ciphertext-length field that lets a receiver read exactly the right number of bytes instead of searching for the end.
const Version byte = 1
Version is the payload framing version written inside the ciphertext.
Variables ¶
var ( ErrInvalidKey = errors.New("mist: invalid key") ErrInvalidPayload = errors.New("mist: invalid payload") ErrInvalidSource = errors.New("mist: invalid source") ErrUnsupportedCodec = errors.New("mist: unsupported codec") ErrCarrier = errors.New("mist: unreadable carrier") ErrClosed = errors.New("mist: already closed") // ErrNoCapacity means no frame in the carrier had room for the // envelope, so nothing was embedded. Quiet or tonal audio yields too // few usable residues; a richer or longer carrier fixes it. ErrNoCapacity = errors.New("mist: carrier has no frame with room for the payload") )
Sentinel errors returned by the public API. Per-frame decrypt failures inside Listen are not surfaced — they are indistinguishable from frames that carry no payload and must stay silent.
Functions ¶
func FrameCapacity ¶
func FrameCapacity() int
FrameCapacity returns the maximum payload bytes embeddable per stego frame at the library's fixed embedding density, before encryption overhead. Useful for callers who need to know the maximum message size supported without multi-frame spanning (Phase 2).
func GenerateKeyPair ¶
GenerateKeyPair creates a new X25519 keypair for a recipient. pub is shared freely with senders; priv is kept secret by the recipient.
func GenerateSigningKeyPair ¶
GenerateSigningKeyPair creates an Ed25519 keypair for optional sender authentication. Pass the private key to NewEmitter via WithSenderAuth.
Types ¶
type Catcher ¶
type Catcher struct {
// contains filtered or unexported fields
}
Catcher extracts payloads with a recipient private key. Construct it once with NewCatcher and share it; methods do not mutate it.
func NewCatcher ¶
func NewCatcher(privKey []byte, opts ...CatcherOption) (*Catcher, error)
NewCatcher returns an immutable Catcher for privKey. The key and any option-held secrets are copied; later writes to the caller's slices are not observed.
func (*Catcher) Extract ¶
Extract scans source synchronously and returns every frame that decrypts and authenticates. It stops at EOF or when ctx is cancelled. source stays owned by the caller.
func (*Catcher) Listen ¶
Listen opens source (a local file path, file:// URL, or http(s):// URL), continuously scans for stego frames, and sends each successfully decrypted payload to the returned channel.
The returned channel is closed when either:
- the source reaches EOF (finite file or stream ended by the server), or
- ctx is cancelled.
The caller distinguishes the two after the channel closes:
- ctx.Err() == nil → source reached EOF naturally
- ctx.Err() != nil → context was cancelled by the caller
The error return covers only immediate setup failures (invalid source, unreadable file, malformed key). Per-frame decrypt failures are silently skipped — they are indistinguishable from frames carrying no payload and must not surface as errors.
type CatcherOption ¶
type CatcherOption func(*Catcher)
CatcherOption configures NewCatcher.
func WithBackoff ¶
func WithBackoff(backoff time.Duration) CatcherOption
WithBackoff configures the pause between read retries.
func WithLogger ¶
func WithLogger(log *slog.Logger) CatcherOption
WithLogger directs diagnostics — such as joining a stream mid-frame — to log. Failed decrypts are never logged: they are indistinguishable from frames carrying no payload. Discarded by default.
func WithMaxRetries ¶
func WithMaxRetries(retries int) CatcherOption
WithMaxRetries configures how many consecutive read failures a live source may produce before Listen gives up and closes the channel.
type Emitter ¶
type Emitter struct {
// contains filtered or unexported fields
}
Emitter encrypts a payload for a recipient public key and embeds it into an Ogg Vorbis carrier. Construct it once with NewEmitter and share it. Methods do not mutate the Emitter.
func NewEmitter ¶
func NewEmitter(recipientPubKey []byte, opts ...EmitterOption) (*Emitter, error)
NewEmitter returns an Emitter for recipientPubKey. The key and any option-held secrets are copied; later writes to the caller's slices are not observed.
func (*Emitter) Embed ¶
Embed opens source (a local file path, file:// URL, or http(s):// URL) as the carrier, encrypts payload for the recipient, and returns a new stego Ogg stream.
The payload is looped across fixed-duration stego frames, each re-encrypted with a fresh ephemeral X25519 key, for the full duration of the carrier. Suitable for both finite files and wrapping a live encoder output — the returned reader produces output as fast as the carrier does.
func (*Emitter) EmbedFile ¶
func (e *Emitter) EmbedFile(ctx context.Context, carrier *os.File, payload Payload) (io.ReadCloser, error)
EmbedFile is Embed over an *os.File carrier. The file stays owned by the caller.
func (*Emitter) EmbedReader ¶
func (e *Emitter) EmbedReader(ctx context.Context, carrier io.Reader, payload Payload) (io.ReadCloser, error)
EmbedReader is Embed over an already-open carrier (pipe, HTTP body, in-memory buffer). The caller retains ownership of carrier and must close it if it is an io.Closer.
type EmitterOption ¶
type EmitterOption func(*Emitter)
EmitterOption configures NewEmitter.
func WithSenderAuth ¶
func WithSenderAuth(senderPrivKey []byte) EmitterOption
WithSenderAuth attaches an Ed25519 signature over the plaintext, itself encrypted before embedding. Off by default — an unauthenticated ciphertext is shorter and one step closer to looking like nothing.
type Payload ¶
type Payload struct {
Type PayloadType
Data []byte
}
Payload is the unit that Emitter encrypts and Catcher emits. Phase 1 only uses PayloadText; the wire format is already type-agnostic.
type PayloadType ¶
type PayloadType byte
PayloadType identifies the kind of data inside a Payload. The type byte is encrypted with the payload, so a warden who isolates the embedded bytes still cannot read it without the private key.
const ( PayloadText PayloadType = 0x01 PayloadImage PayloadType = 0x02 PayloadAudio PayloadType = 0x03 PayloadFile PayloadType = 0x04 )
Source Files
¶
Directories
¶
| Path | Synopsis |
|---|---|
|
cmd
|
|
|
mist
command
Command mist hides a text message inside an audio file and recovers it again with the matching private key.
|
Command mist hides a text message inside an audio file and recovers it again with the matching private key. |
|
Package example contains Godoc examples for github.com/iSerganov/mist.
|
Package example contains Godoc examples for github.com/iSerganov/mist. |
|
internal
|
|
|
av
Package av is the cgo boundary to libavformat, libavcodec, and libavutil.
|
Package av is the cgo boundary to libavformat, libavcodec, and libavutil. |
|
codec
Package codec is the boundary between PCM and a compressed bitstream.
|
Package codec is the boundary between PCM and a compressed bitstream. |
|
codec/vorbis
Package vorbis is the Phase 1 codec: PCM ↔ Ogg Vorbis via package av, plus a bitstream residue parser used by stego.
|
Package vorbis is the Phase 1 codec: PCM ↔ Ogg Vorbis via package av, plus a bitstream residue parser used by stego. |
|
crypto
Package crypto is the hybrid box: X25519, HKDF, ChaCha20-Poly1305, Ed25519.
|
Package crypto is the hybrid box: X25519, HKDF, ChaCha20-Poly1305, Ed25519. |
|
frame
Package frame models fixed-duration stego frames.
|
Package frame models fixed-duration stego frames. |
|
stego
Package stego embeds and extracts bit strings in quantized Vorbis residues.
|
Package stego embeds and extracts bit strings in quantized Vorbis residues. |
|
wire
Package wire is the inner payload framing and the outer hybrid-box envelope.
|
Package wire is the inner payload framing and the outer hybrid-box envelope. |