Skip to content

v0.0.26

Choose a tag to compare

@pilinux pilinux released this 05 Sep 17:00
· 25 commits to main since this release
51e1492

Padded streams get their own domain

Padded streams are now domain-separated from plain ones, and the padded sealer
enforces the declared size before it writes any padding. Padded blobs sealed
by v0.0.24 or v0.0.25 no longer open.
Everything else is untouched.

Breaking

Both padded helpers prepend a fixed domain tag to the caller's AAD, so a padded
stream and a plain one can no longer be opened as each other. A padded blob from
v0.0.24 or v0.0.25 fails as ErrStreamAuth.

Unaffected: SealStream/SealFile blobs, single-shot tokens
(SealString, SealBytes, SealInt64), and wrapped master keys. Only
SealPaddedStream/SealPaddedFile output changed.

Migration: open with v0.0.25 and re-seal with v0.0.26. No signature changed,
so the only edit is the version in go.mod.

The bytes on disk are still identically shaped, so a file continues not to
advertise whether it is padded. The price is diagnosis: a plain stream opened as
padded now fails as ErrStreamAuth, indistinguishable from a wrong key. Retry
with OpenStream over a fresh reader, which succeeds only in the unpadded case.

Changed

  • A wrong size now costs a header instead of a Padmé bucket. The padded
    sealer wraps src and enforces the declared length at the payload boundary,
    before any padding is generated. A caller declaring 1 TiB and sending one byte
    previously had ~16 GiB of zeros written before the end-of-stream check
    noticed; it now costs the 37-byte header. That is what makes size safe to
    take from an untrusted peer, such as an upload that states its own length.
  • The opener's drain is bounded. The expected padding is computed from
    PaddedSize up front and drained with io.CopyN plus a one-byte end probe,
    so a frame declaring a small payload inside a huge stream is rejected after a
    couple of chunks rather than after the whole file.
  • ErrNotPadded and ErrSourceSize are now umbrellas and carry no cause of
    their own; their messages changed accordingly. errors.Is against either
    still matches.

Added

  • New errors, each wrapping the umbrella it belongs to:
    ErrNoPaddingFrame and ErrPaddingMalformed under ErrNotPadded;
    ErrSourceIrregular, ErrSourceShort and ErrSourceLong under
    ErrSourceSize. A caller that must tell "retry the upload" from "reject it"
    can now branch on the direction.

  • Deferred padding, demonstrated. An HTML multipart upload states no length,
    so SealPaddedStream cannot run on the request path. Section 11 of the
    envelope example seals the upload unpadded, then recovers the length from the
    sealed size and re-seals it padded in a second pass.

  • An HTTP upload server in the example, behind -serve, for pushing a real
    file through the streaming and padding APIs by hand:

    go run ./_example/envelope -serve 127.0.0.1:8080
    go run ./_example/envelope -serve 127.0.0.1:8080 -max 0 -dir /tmp/enc
    
    It caps uploads at 1 GiB and stores ciphertext in a temp dir; -max 0 -dir
    lifts both, which is what a multi-gigabyte test needs.
    

Compatibility

  • Requires Go 1.25+. The only external dependency is golang.org/x/crypto.
  • No exported signature changed.
  • The wire formats are unchanged: single-shot 0x01, stream 0x81, padding
    frame 0x01 inside the sealed plaintext. What changed is the AAD a padded
    stream authenticates, which is never stored.

Full Changelog: v0.0.25...v0.0.26