You can not select more than 25 topics Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.
DateKeys/capsule/open3.go

284 lines
9.3 KiB

Format 3, step 3: the reader Open reads format 3 (spec 29.2 to 29.7, 63 steps 17 and 18): the PRELUDE accepts VERSION 3, and the files go to a Sink. - Sink: Begin with the validated head, Create for each file in the order of the head, and Commit only after every check of step 17; after any failure that follows a successful Begin, Abort, once. A format 3 capsule without a Sink fails right after step 2 with ErrSinkRequired, a caller error with no code, no failed step and no request; a capsule of format 1 or 2 without dst fails there too. - Step 17 in its substeps: the frame and the area, security and its verdicts, which never fail, the head, the files filling CONTENT, the SHA-256 of each file and the padding. A failure of age or a plaintext whose length is not P prevails; otherwise the first substep that fails decides, and a code other than ERR_INTEGRITY is reported only after reading PAYLOAD_AGE to its end. - The reads of BODY grow with the bytes received, never with AREA_LEN, HEAD_LEN or a declared size (spec 57); a test measures it. - A failure of the Sink is the caller's own error with ERR_INTEGRITY, as one of dst is in formats 1 and 2. - Opened gains Head, Verdicts, AreaLen and UnusableHeadExtensions. - Test data: "version changed" sets VERSION 4, and the format 2 list gains "format 2 time_only relabeled format 3", which fails at step 14, as section 64 of spec v0.10 lists: 126 cases, 89 of the spec. The randomly built capsules keep their recorded bytes. - testkit: Build writes format 3 and can edit the padded plaintext; Head3, Body3, DiscardSink and MemorySink build and open BODY. Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
1 week ago
package capsule
import (
"crypto/hmac"
"crypto/sha256"
"errors"
"fmt"
"io"
"time"
Format 3, step 3: the reader Open reads format 3 (spec 29.2 to 29.7, 63 steps 17 and 18): the PRELUDE accepts VERSION 3, and the files go to a Sink. - Sink: Begin with the validated head, Create for each file in the order of the head, and Commit only after every check of step 17; after any failure that follows a successful Begin, Abort, once. A format 3 capsule without a Sink fails right after step 2 with ErrSinkRequired, a caller error with no code, no failed step and no request; a capsule of format 1 or 2 without dst fails there too. - Step 17 in its substeps: the frame and the area, security and its verdicts, which never fail, the head, the files filling CONTENT, the SHA-256 of each file and the padding. A failure of age or a plaintext whose length is not P prevails; otherwise the first substep that fails decides, and a code other than ERR_INTEGRITY is reported only after reading PAYLOAD_AGE to its end. - The reads of BODY grow with the bytes received, never with AREA_LEN, HEAD_LEN or a declared size (spec 57); a test measures it. - A failure of the Sink is the caller's own error with ERR_INTEGRITY, as one of dst is in formats 1 and 2. - Opened gains Head, Verdicts, AreaLen and UnusableHeadExtensions. - Test data: "version changed" sets VERSION 4, and the format 2 list gains "format 2 time_only relabeled format 3", which fails at step 14, as section 64 of spec v0.10 lists: 126 cases, 89 of the spec. The randomly built capsules keep their recorded bytes. - testkit: Build writes format 3 and can edit the padded plaintext; Head3, Body3, DiscardSink and MemorySink build and open BODY. Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
1 week ago
datekeys "g.activething.com/go/DateKeys"
"g.activething.com/go/DateKeys/extension"
)
// Sink receives the files of a format 3 capsule (spec §56, §63 steps 17 and
// 18). Open calls Begin once the head is validated, then Create for each
// file in the order of the head, and then Commit, only after every check of
// step 17 has passed. After any failure that follows a successful Begin, a
// failure of Commit included, it calls Abort, once; a Begin that fails
// cleans up after itself. Nothing a Sink receives before Commit may be
// presented as valid: write to a temporary place and publish it in Commit.
type Sink interface {
// Begin receives the validated head. Its files are h.Files.
Begin(h *Head) error
// Create returns the writer of file i of the head. Open closes it after
// writing its Size bytes, before checking its SHA-256. Size, Start and
// End are declared, not received: a Sink must not reserve memory or disk
// by them (spec §57).
Create(i int) (io.WriteCloser, error)
// Commit publishes the files: step 18.
Commit() error
// Abort discards everything the Sink received.
Abort()
}
// ErrSinkRequired is the error of Open for a format 3 capsule without
// OpenOptions.Sink: its content is several files, which one io.Writer cannot
// receive. It is an error of the caller, with no normative code: Open returns
// it right after step 2, before any request, and never reports the capsule
// as ErrUnsupportedVersion, because it is valid (spec §70).
var ErrSinkRequired = errors.New("capsule: a format 3 capsule holds files: OpenOptions.Sink is required")
// errWriterRequired is the error of Open for a capsule of format 1 or 2 with
// a nil dst.
var errWriterRequired = errors.New("capsule: a capsule of format 1 or 2 holds one content: dst is required")
// plainReader reads the plaintext of PAYLOAD_AGE and counts its bytes.
type plainReader struct {
r io.Reader
n uint64
}
func (p *plainReader) Read(b []byte) (int, error) {
n, err := p.r.Read(b)
p.n += uint64(n)
return n, err
}
// plaintextFailure is the error of a read of the plaintext that stopped
// before it wanted: a failure of age, or a plaintext that ends before P.
func plaintextFailure(err error, n, p uint64) error {
if err == io.EOF {
return fmt.Errorf("capsule: PAYLOAD_AGE: the plaintext is %d bytes, shorter than P = %d: %w", n, p, datekeys.ErrIntegrity)
}
return classify("PAYLOAD_AGE", ageStreamFailure, err)
}
// readN reads exactly n bytes of the plaintext. Its memory grows with the
// bytes received, not with n, a length that BODY declares (spec §57). Not
// io.ReadFull: it would turn the io.ErrUnexpectedEOF of a truncated STREAM
// into a short read.
func readN(r *plainReader, n int, p uint64) ([]byte, error) {
var buf []byte
part := make([]byte, min(n, 32<<10))
for len(buf) < n {
m, err := r.Read(part[:min(n-len(buf), len(part))])
buf = append(buf, part[:m]...)
if len(buf) == n {
break
}
if err != nil {
return nil, plaintextFailure(err, r.n, p)
}
}
return buf, nil
}
// drain reads the rest of the plaintext to EOF. A failure of age, or a
// plaintext whose length is not P, prevails over the failure of any substep
// of step 17 (spec §63).
func drain(r *plainReader, p uint64) error {
buf := make([]byte, 32<<10)
for {
_, err := r.Read(buf)
switch {
case err == io.EOF && r.n != p:
return fmt.Errorf("capsule: PAYLOAD_AGE: the plaintext is %d bytes, not P = %d: %w", r.n, p, datekeys.ErrIntegrity)
case err == io.EOF:
return nil
case err != nil:
return classify("PAYLOAD_AGE", ageStreamFailure, err)
}
}
}
// copyFile writes the next size bytes of the plaintext to w, hashing them.
func copyFile(w io.Writer, r *plainReader, size, p uint64) ([]byte, error) {
sum := sha256.New()
buf := make([]byte, 32<<10)
for left := size; left > 0; {
m := uint64(len(buf))
if m > left {
m = left
}
n, err := r.Read(buf[:m])
if n > 0 {
sum.Write(buf[:n])
if _, werr := w.Write(buf[:n]); werr != nil {
return nil, &sinkError{werr}
}
left -= uint64(n)
}
if left > 0 && err != nil {
return nil, plaintextFailure(err, r.n, p)
}
}
return sum.Sum(nil), nil
}
Review fixes: author keys, the writer, the CLI, extensions and the locator Fixes of the review of the session of 1 and 2 October that the text of spec v0.11 already asks for: - authorkey: String and GoString hide the secret key, which only Secret returns; ParsePublic refuses a key that is not a point of the curve (ed25519strict.OnCurve, checked against the square root of testkit). - capsule: a typed nil in AuthorKey, CMSSigner or Sealer is an error, never a capsule without the signature or the seal that was asked for. A panic while evaluating the signature or the seal fails only that part, F1 or S2, not both. OpenOptions.Accept sees the verdicts before step 18 and can refuse to publish the files. - extension.CheckWrite, the rule of encoders of spec 72: the writers of capsules and .dkk files refuse datekeys.note and datekeys.capsule outside the arrays where they are registered, or with invalid data. - CLI: encrypt -sign shows the author key and the code of AUTHOR_MESSAGE before it signs (rule 20); decrypt -expect-author compares the key of an F4 and writes nothing unless it matches; decrypt notifies a public note that it does not show; the lines of the verdicts break at the last space that fits, each row after the first behind a mark, so that the terminal never breaks them; L is the payload, not the content. - locator: a reader rejects an address that breaks 44.1 and keeps the others; addresses refuse the special-purpose blocks of IANA, IPv6 outside 2000::/3, localhost and local names, characters outside RFC 3986, dot segments, and a CID that does not decode to version 1 and a multihash; ParseInfo checks that the locator is an age file with one tlock stanza for the round of its DateKey; Info.Extension reads what it writes; its errors carry no normative code. Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
1 week ago
// refused is the error of OpenOptions.Accept: every check of step 17 passed,
// and the caller refused to publish the files.
type refused struct{ err error }
func (e *refused) Error() string { return e.err.Error() }
func (e *refused) Unwrap() error { return e.err }
Format 3, step 3: the reader Open reads format 3 (spec 29.2 to 29.7, 63 steps 17 and 18): the PRELUDE accepts VERSION 3, and the files go to a Sink. - Sink: Begin with the validated head, Create for each file in the order of the head, and Commit only after every check of step 17; after any failure that follows a successful Begin, Abort, once. A format 3 capsule without a Sink fails right after step 2 with ErrSinkRequired, a caller error with no code, no failed step and no request; a capsule of format 1 or 2 without dst fails there too. - Step 17 in its substeps: the frame and the area, security and its verdicts, which never fail, the head, the files filling CONTENT, the SHA-256 of each file and the padding. A failure of age or a plaintext whose length is not P prevails; otherwise the first substep that fails decides, and a code other than ERR_INTEGRITY is reported only after reading PAYLOAD_AGE to its end. - The reads of BODY grow with the bytes received, never with AREA_LEN, HEAD_LEN or a declared size (spec 57); a test measures it. - A failure of the Sink is the caller's own error with ERR_INTEGRITY, as one of dst is in formats 1 and 2. - Opened gains Head, Verdicts, AreaLen and UnusableHeadExtensions. - Test data: "version changed" sets VERSION 4, and the format 2 list gains "format 2 time_only relabeled format 3", which fails at step 14, as section 64 of spec v0.10 lists: 126 cases, 89 of the spec. The randomly built capsules keep their recorded bytes. - testkit: Build writes format 3 and can edit the padded plaintext; Head3, Body3, DiscardSink and MemorySink build and open BODY. Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
1 week ago
// sinkError is a failure of the caller's Sink, not of the capsule.
type sinkError struct{ err error }
func (e *sinkError) Error() string { return e.err.Error() }
func (e *sinkError) Unwrap() error { return e.err }
// sinkFailure reports a failure of the Sink as the caller's own error, with
// ErrIntegrity as its code, as a failure of dst is in formats 1 and 2.
func sinkFailure(what string, err error) error {
return fmt.Errorf("capsule: PAYLOAD_AGE: %s: %w: %w", what, err, datekeys.ErrIntegrity)
}
// newSecurityContext is the context of the verdicts of a capsule of format f
// whose control is c, opened at the round time unlock (spec v0.11, §29.7).
// The head digest is added when the head is read. A control that cannot be
// encoded leaves control_commit at zero: no signature verifies then, and F2
// is the verdict, though DecodeControl has already decoded it.
func newSecurityContext(c *Control, f Format, unlock time.Time, keys map[string]string) *SecurityContext {
sc := &SecurityContext{RoundTime: unlock, AuthorKeys: keys}
if cc, err := ControlCommit(c, f); err == nil {
sc.ControlCommit = cc
}
return sc
}
Format 3, step 3: the reader Open reads format 3 (spec 29.2 to 29.7, 63 steps 17 and 18): the PRELUDE accepts VERSION 3, and the files go to a Sink. - Sink: Begin with the validated head, Create for each file in the order of the head, and Commit only after every check of step 17; after any failure that follows a successful Begin, Abort, once. A format 3 capsule without a Sink fails right after step 2 with ErrSinkRequired, a caller error with no code, no failed step and no request; a capsule of format 1 or 2 without dst fails there too. - Step 17 in its substeps: the frame and the area, security and its verdicts, which never fail, the head, the files filling CONTENT, the SHA-256 of each file and the padding. A failure of age or a plaintext whose length is not P prevails; otherwise the first substep that fails decides, and a code other than ERR_INTEGRITY is reported only after reading PAYLOAD_AGE to its end. - The reads of BODY grow with the bytes received, never with AREA_LEN, HEAD_LEN or a declared size (spec 57); a test measures it. - A failure of the Sink is the caller's own error with ERR_INTEGRITY, as one of dst is in formats 1 and 2. - Opened gains Head, Verdicts, AreaLen and UnusableHeadExtensions. - Test data: "version changed" sets VERSION 4, and the format 2 list gains "format 2 time_only relabeled format 3", which fails at step 14, as section 64 of spec v0.10 lists: 126 cases, 89 of the spec. The randomly built capsules keep their recorded bytes. - testkit: Build writes format 3 and can edit the padded plaintext; Head3, Body3, DiscardSink and MemorySink build and open BODY. Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
1 week ago
// openBody runs step 17 of a format 3 capsule and step 18 (spec §63): pr is
// the plaintext of PAYLOAD_AGE, whose BODY is l bytes long and whose
// padding goes up to p. The substeps, in order:
//
// 17.2 the frame of BODY and the area (§29.2), ErrIntegrity;
// 17.3 security, layers 2 and 3 (§29.3), without a code;
// 17.4 the head, layers 2 to 4 (§29.4 to §29.6);
// 17.5 the files fill CONTENT, ErrIntegrity;
// 17.6 the verdicts of security (§29.7), without a code;
// 17.7 the SHA-256 of each file, ErrIntegrity;
// 17.8 the padding, ErrIntegrity.
//
// A failure of age after its header, or a plaintext whose length is not P,
// is ErrIntegrity and prevails; otherwise the first substep that fails
// decides. openBody stops early only with ErrIntegrity when that is already
// the final code: on a failure of age, or of a substep of ErrIntegrity with
// no earlier failure of another code. After a failure of another code, at
// 17.4, it reads PAYLOAD_AGE to EOF before reporting it.
Review fixes: author keys, the writer, the CLI, extensions and the locator Fixes of the review of the session of 1 and 2 October that the text of spec v0.11 already asks for: - authorkey: String and GoString hide the secret key, which only Secret returns; ParsePublic refuses a key that is not a point of the curve (ed25519strict.OnCurve, checked against the square root of testkit). - capsule: a typed nil in AuthorKey, CMSSigner or Sealer is an error, never a capsule without the signature or the seal that was asked for. A panic while evaluating the signature or the seal fails only that part, F1 or S2, not both. OpenOptions.Accept sees the verdicts before step 18 and can refuse to publish the files. - extension.CheckWrite, the rule of encoders of spec 72: the writers of capsules and .dkk files refuse datekeys.note and datekeys.capsule outside the arrays where they are registered, or with invalid data. - CLI: encrypt -sign shows the author key and the code of AUTHOR_MESSAGE before it signs (rule 20); decrypt -expect-author compares the key of an F4 and writes nothing unless it matches; decrypt notifies a public note that it does not show; the lines of the verdicts break at the last space that fits, each row after the first behind a mark, so that the terminal never breaks them; L is the payload, not the content. - locator: a reader rejects an address that breaks 44.1 and keeps the others; addresses refuse the special-purpose blocks of IANA, IPv6 outside 2000::/3, localhost and local names, characters outside RFC 3986, dot segments, and a CID that does not decode to version 1 and a multihash; ParseInfo checks that the locator is an age file with one tlock stanza for the round of its DateKey; Info.Extension reads what it writes; its errors carry no normative code. Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
1 week ago
func openBody(pr io.Reader, l, p uint64, sink Sink, reg extension.Registry, sc *SecurityContext, accept func(Verdicts) error, out *Opened) (err error) {
Format 3, step 3: the reader Open reads format 3 (spec 29.2 to 29.7, 63 steps 17 and 18): the PRELUDE accepts VERSION 3, and the files go to a Sink. - Sink: Begin with the validated head, Create for each file in the order of the head, and Commit only after every check of step 17; after any failure that follows a successful Begin, Abort, once. A format 3 capsule without a Sink fails right after step 2 with ErrSinkRequired, a caller error with no code, no failed step and no request; a capsule of format 1 or 2 without dst fails there too. - Step 17 in its substeps: the frame and the area, security and its verdicts, which never fail, the head, the files filling CONTENT, the SHA-256 of each file and the padding. A failure of age or a plaintext whose length is not P prevails; otherwise the first substep that fails decides, and a code other than ERR_INTEGRITY is reported only after reading PAYLOAD_AGE to its end. - The reads of BODY grow with the bytes received, never with AREA_LEN, HEAD_LEN or a declared size (spec 57); a test measures it. - A failure of the Sink is the caller's own error with ERR_INTEGRITY, as one of dst is in formats 1 and 2. - Opened gains Head, Verdicts, AreaLen and UnusableHeadExtensions. - Test data: "version changed" sets VERSION 4, and the format 2 list gains "format 2 time_only relabeled format 3", which fails at step 14, as section 64 of spec v0.10 lists: 126 cases, 89 of the spec. The randomly built capsules keep their recorded bytes. - testkit: Build writes format 3 and can edit the padded plaintext; Head3, Body3, DiscardSink and MemorySink build and open BODY. Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
1 week ago
r := &plainReader{r: pr}
begun := false
defer func() {
if err != nil && begun {
sink.Abort()
}
}()
// 17.2: the frame of BODY and the area.
b, err := readN(r, BodyFrameSize, p)
if err != nil {
return err
}
frame, err := ParseBodyFrame(b, l)
if err != nil {
return err
}
area, err := readN(r, int(frame.AreaLen), p)
if err != nil {
return err
}
if err := CheckArea(area, frame.SecurityLen); err != nil {
return err
}
out.AreaLen = frame.AreaLen
// 17.3 and 17.6: security never fails; its verdicts are shown after
// step 18 only.
security := area[:frame.SecurityLen]
Format 3, step 3: the reader Open reads format 3 (spec 29.2 to 29.7, 63 steps 17 and 18): the PRELUDE accepts VERSION 3, and the files go to a Sink. - Sink: Begin with the validated head, Create for each file in the order of the head, and Commit only after every check of step 17; after any failure that follows a successful Begin, Abort, once. A format 3 capsule without a Sink fails right after step 2 with ErrSinkRequired, a caller error with no code, no failed step and no request; a capsule of format 1 or 2 without dst fails there too. - Step 17 in its substeps: the frame and the area, security and its verdicts, which never fail, the head, the files filling CONTENT, the SHA-256 of each file and the padding. A failure of age or a plaintext whose length is not P prevails; otherwise the first substep that fails decides, and a code other than ERR_INTEGRITY is reported only after reading PAYLOAD_AGE to its end. - The reads of BODY grow with the bytes received, never with AREA_LEN, HEAD_LEN or a declared size (spec 57); a test measures it. - A failure of the Sink is the caller's own error with ERR_INTEGRITY, as one of dst is in formats 1 and 2. - Opened gains Head, Verdicts, AreaLen and UnusableHeadExtensions. - Test data: "version changed" sets VERSION 4, and the format 2 list gains "format 2 time_only relabeled format 3", which fails at step 14, as section 64 of spec v0.10 lists: 126 cases, 89 of the spec. The randomly built capsules keep their recorded bytes. - testkit: Build writes format 3 and can edit the padded plaintext; Head3, Body3, DiscardSink and MemorySink build and open BODY. Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
1 week ago
// 17.4: the head. Its codes other than ErrIntegrity are reported only
// after reading to EOF.
hb, err := readN(r, int(frame.HeadLen), p)
if err != nil {
return err
}
// The verdicts need the head digest, so they are evaluated once the head
// bytes are read; they never fail, and no decoding of the head changes them.
sc.HeadDigest = HeadDigest(hb)
verdicts := EvaluateSecurityIn(security, sc)
Format 3, step 3: the reader Open reads format 3 (spec 29.2 to 29.7, 63 steps 17 and 18): the PRELUDE accepts VERSION 3, and the files go to a Sink. - Sink: Begin with the validated head, Create for each file in the order of the head, and Commit only after every check of step 17; after any failure that follows a successful Begin, Abort, once. A format 3 capsule without a Sink fails right after step 2 with ErrSinkRequired, a caller error with no code, no failed step and no request; a capsule of format 1 or 2 without dst fails there too. - Step 17 in its substeps: the frame and the area, security and its verdicts, which never fail, the head, the files filling CONTENT, the SHA-256 of each file and the padding. A failure of age or a plaintext whose length is not P prevails; otherwise the first substep that fails decides, and a code other than ERR_INTEGRITY is reported only after reading PAYLOAD_AGE to its end. - The reads of BODY grow with the bytes received, never with AREA_LEN, HEAD_LEN or a declared size (spec 57); a test measures it. - A failure of the Sink is the caller's own error with ERR_INTEGRITY, as one of dst is in formats 1 and 2. - Opened gains Head, Verdicts, AreaLen and UnusableHeadExtensions. - Test data: "version changed" sets VERSION 4, and the format 2 list gains "format 2 time_only relabeled format 3", which fails at step 14, as section 64 of spec v0.10 lists: 126 cases, 89 of the spec. The randomly built capsules keep their recorded bytes. - testkit: Build writes format 3 and can edit the padded plaintext; Head3, Body3, DiscardSink and MemorySink build and open BODY. Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
1 week ago
h, err := DecodeHead(hb, reg)
if err != nil {
if derr := drain(r, p); derr != nil {
return derr
}
return err
}
// 17.5: the files fill CONTENT.
if err := CheckHeadEnd(h, frame.ContentLength(l)); err != nil {
return err
}
// 17.7: each file, to the Sink, with its SHA-256.
if err := sink.Begin(h); err != nil {
return sinkFailure("beginning the files", err)
}
begun = true
for i := range h.Files {
f := &h.Files[i]
w, err := sink.Create(i)
if err != nil {
return sinkFailure(fmt.Sprintf("creating file %d", i+1), err)
}
sum, err := copyFile(w, r, f.Size, p)
if cerr := w.Close(); err == nil && cerr != nil {
err = &sinkError{cerr}
}
var se *sinkError
switch {
case errors.As(err, &se):
return sinkFailure(fmt.Sprintf("writing file %d", i+1), se.err)
case err != nil:
return err
case !hmac.Equal(sum, f.SHA256[:]):
return fmt.Errorf("capsule: PAYLOAD_AGE: file %d: its SHA-256 is not the one of the head: %w", i+1, datekeys.ErrIntegrity)
}
}
// 17.8: the padding, up to P, and nothing after it.
if err := checkPadding(r, r.n, p); err != nil {
if datekeys.Code(err) == "" {
err = classify("PAYLOAD_AGE", ageStreamFailure, err)
}
return err
}
Review fixes: author keys, the writer, the CLI, extensions and the locator Fixes of the review of the session of 1 and 2 October that the text of spec v0.11 already asks for: - authorkey: String and GoString hide the secret key, which only Secret returns; ParsePublic refuses a key that is not a point of the curve (ed25519strict.OnCurve, checked against the square root of testkit). - capsule: a typed nil in AuthorKey, CMSSigner or Sealer is an error, never a capsule without the signature or the seal that was asked for. A panic while evaluating the signature or the seal fails only that part, F1 or S2, not both. OpenOptions.Accept sees the verdicts before step 18 and can refuse to publish the files. - extension.CheckWrite, the rule of encoders of spec 72: the writers of capsules and .dkk files refuse datekeys.note and datekeys.capsule outside the arrays where they are registered, or with invalid data. - CLI: encrypt -sign shows the author key and the code of AUTHOR_MESSAGE before it signs (rule 20); decrypt -expect-author compares the key of an F4 and writes nothing unless it matches; decrypt notifies a public note that it does not show; the lines of the verdicts break at the last space that fits, each row after the first behind a mark, so that the terminal never breaks them; L is the payload, not the content. - locator: a reader rejects an address that breaks 44.1 and keeps the others; addresses refuse the special-purpose blocks of IANA, IPv6 outside 2000::/3, localhost and local names, characters outside RFC 3986, dot segments, and a CID that does not decode to version 1 and a multihash; ParseInfo checks that the locator is an age file with one tlock stanza for the round of its DateKey; Info.Extension reads what it writes; its errors carry no normative code. Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
1 week ago
// The caller sees the verdicts before anything is published
// (OpenOptions.Accept).
if accept != nil {
if err := accept(verdicts); err != nil {
return &refused{err}
}
}
Format 3, step 3: the reader Open reads format 3 (spec 29.2 to 29.7, 63 steps 17 and 18): the PRELUDE accepts VERSION 3, and the files go to a Sink. - Sink: Begin with the validated head, Create for each file in the order of the head, and Commit only after every check of step 17; after any failure that follows a successful Begin, Abort, once. A format 3 capsule without a Sink fails right after step 2 with ErrSinkRequired, a caller error with no code, no failed step and no request; a capsule of format 1 or 2 without dst fails there too. - Step 17 in its substeps: the frame and the area, security and its verdicts, which never fail, the head, the files filling CONTENT, the SHA-256 of each file and the padding. A failure of age or a plaintext whose length is not P prevails; otherwise the first substep that fails decides, and a code other than ERR_INTEGRITY is reported only after reading PAYLOAD_AGE to its end. - The reads of BODY grow with the bytes received, never with AREA_LEN, HEAD_LEN or a declared size (spec 57); a test measures it. - A failure of the Sink is the caller's own error with ERR_INTEGRITY, as one of dst is in formats 1 and 2. - Opened gains Head, Verdicts, AreaLen and UnusableHeadExtensions. - Test data: "version changed" sets VERSION 4, and the format 2 list gains "format 2 time_only relabeled format 3", which fails at step 14, as section 64 of spec v0.10 lists: 126 cases, 89 of the spec. The randomly built capsules keep their recorded bytes. - testkit: Build writes format 3 and can edit the padded plaintext; Head3, Body3, DiscardSink and MemorySink build and open BODY. Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
1 week ago
// Step 18.
if err := sink.Commit(); err != nil {
return sinkFailure("committing the files", err)
}
out.Head, out.Verdicts = h, verdicts
out.UnusableHeadExtensions = extension.CheckNoncriticalIn(extension.Head, h.Noncritical, reg)
return nil
}

Powered by TurnKey Linux.