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>
in.pass(17,"open payload","payload authenticated; the caller refused its verdicts and nothing was published")
returnout,r.err
}
returnout,in.fail(17,"open payload",err)
returnout,in.fail(17,"open payload",err)
}
}
in.pass(17,"open payload",fmt.Sprintf("payload authenticated; BODY of %d bytes, %d files, area of %d bytes",control.PayloadLength,len(out.Head.Files),out.AreaLen))
in.pass(17,"open payload",fmt.Sprintf("payload authenticated; BODY of %d bytes, %d files, area of %d bytes",control.PayloadLength,len(out.Head.Files),out.AreaLen))
// Spec §62.1 rule 20: the key and the code of AUTHOR_MESSAGE, before the
// signature.
ifcode:=regexp.MustCompile(`AUTHOR_MESSAGE code ([0-9a-f]{4}-[0-9a-f]{4})\n`).FindStringSubmatch(stderr);code==nil||!strings.Contains(stderr,"Signing with the author key "+pub+"\n"){
t.Errorf("encrypt -sign does not show the key and the code:\n%s",stderr)
returnfmt.Errorf("the capsule was written to %s but its .dkk could not be: %w",*out,err)
returnfmt.Errorf("the capsule was written to %s but its .dkk could not be: %w",*out,err)
}
}
}
}
fmt.Fprintf(stderr,"Encrypted locally for %s (round %d)\n datekey %s\n capsule_id %x\n format %d: %d files, %d bytes of content, padded to %d (%s)\n",
fmt.Fprintf(stderr,"Encrypted locally for %s (round %d)\n datekey %s\n capsule_id %x\n format %d: %d files, a payload of %d bytes, padded to %d (%s)\n",
returnfmt.Errorf("decrypt: the capsule is not signed with the expected key %s: do not trust it as that author's",*expect)
}
returnfmt.Errorf("decrypt: the capsule is not signed with the expected key %s: its files were written to %s, but do not trust them as that author's",*expect,*out)
}
returnnil
returnnil
}
}
fmt.Fprintf(stderr," format %d, %d bytes of content\n",opened.Format,opened.PayloadLength)
fmt.Fprintf(stderr," format %d, %d bytes of content\n",opened.Format,opened.PayloadLength)
fmt.Fprintf(w," aviso: la fecha de modificación de un fichero es posterior al sello (%s): no es coherente.\n",t.UTC().Format(time.RFC3339))
writeVerdicts(w,[]string{fmt.Sprintf(" aviso: la fecha de modificación de un fichero es posterior al sello (%s): no es coherente.",t.UTC().Format(time.RFC3339))},width)
break
break
}
}
}
}
@ -123,6 +180,8 @@ func present(w io.Writer, o *capsule.Opened, dir string, width int) {
fmt.Fprintln(w,"┌ "+noteTitle)
fmt.Fprintln(w,"┌ "+noteTitle)
writeCreator(w,note,width)
writeCreator(w,note,width)
fmt.Fprintln(w,"└")
fmt.Fprintln(w,"└")
}elseifo.Inspection.Header.UnusableNote(){
writeVerdicts(w,[]string{unusableNote},width)
}
}
}
}
ifh.Author!=""{
ifh.Author!=""{
@ -143,9 +202,7 @@ func present(w io.Writer, o *capsule.Opened, dir string, width int) {
}
}
}
}
}
}
for_,line:=rangeverdicts{
writeVerdicts(w,verdicts,width)
fmt.Fprintln(w,line)
}
}
}
// The names that spec §29.7 asks a reader to warn of, compared by their key
// The names that spec §29.7 asks a reader to warn of, compared by their key
@ -210,10 +267,8 @@ func showNote(w io.Writer, in *capsule.Inspection) {
}
}
note,ok:=in.Header.PublicNote()
note,ok:=in.Header.PublicNote()
if!ok{
if!ok{
for_,e:=rangein.Header.Noncritical{
ifin.Header.UnusableNote(){
ife.ID==extension.NoteID&&e.Version==1{
fmt.Fprintln(w,unusableNote)
fmt.Fprintln(w," La cápsula lleva una nota pública que no cumple las reglas de texto: no se muestra.")
returnnil,fmt.Errorf("locator: the locator is not an age file with one tlock stanza for round %d, the one of its DateKey: %w",d.Round,datekeys.ErrExtensionDataInvalid)
}
}
return&i,nil
return&i,nil
}
}
@ -179,32 +197,77 @@ type Address struct {
Offsetuint64
Offsetuint64
}
}
// CheckURI checks an address with the rules of spec §44.1.
// CheckURI checks an address with the rules of spec §44.1: ASCII of RFC 3986,
// with its percent signs followed by two hexadecimal digits, the scheme
// https or ipfs, no "." or ".." segment in its path, and the host or the CID
// that checkHost and isCIDv1 accept.
funcCheckURI(uristring)error{
funcCheckURI(uristring)error{
ifuri==""||len(uri)>MaxURILen{
ifuri==""||len(uri)>MaxURILen{
returnfmt.Errorf("locator: an address of %d bytes, not 1 to %d",len(uri),MaxURILen)
returnfmt.Errorf("locator: an address of %d bytes, not 1 to %d",len(uri),MaxURILen)
}
}
fori:=0;i<len(uri);i++{
fori:=0;i<len(uri);i++{
ifuri[i]<=0x20||uri[i]>=0x7f{
c:=uri[i]
returnerrors.New("locator: an address with a character outside printable ASCII")