Signature plan, steps 3 and 4: EncryptFiles signs and Open checks, alg 1

AreaLen is 32 KiB, and LargeArea asks for 64 KiB. EncryptOptions.AuthorKey
signs inside sealer.write, through a prepare hook that gets the final
control: SECURITY_CBOR and the frame are built and evaluated with the rules
of the reader before anything is written. OpenOptions.AuthorKeys feeds
EvaluateSecurityIn from openBody with control_commit, head_digest and the
round time: F4, F3 with a saved key, F2 when it does not verify.

The fixtures of v0.10 keep the area of 512 (AreaUnit). The two
"unsupported" fixtures use alg 4294967295, since a random alg 1 is now F2.

Co-Authored-By: Claude Sonnet 5.5 <noreply@anthropic.com>
v0.11
dev 6 days ago
parent 3d85a0b857
commit c3175a150a

@ -70,6 +70,17 @@ type EncryptOptions struct {
// HeadCritical and HeadNoncritical are the extensions of the head that
// EncryptFiles writes, sealed in PAYLOAD_AGE (spec §29.4, §54).
HeadCritical, HeadNoncritical []extension.Extension
// AuthorKey signs the capsule with alg 1 (spec v0.11, §29.9): EncryptFiles
// signs AUTHOR_MESSAGE with it, checks the signature with the strict
// profile before writing anything, and puts it in the security area.
// Nil for no signature. *authorkey.Key is an AuthorKey. Encrypt, which
// writes format 2, takes none.
AuthorKey AuthorKey
// LargeArea asks EncryptFiles for the security area of 64 KiB instead of
// the common one of 32 KiB, for signatures that do not fit in it (spec
// §29.2, §62.1 rule 13). It never widens on its own: a signature that
// does not fit in the area makes EncryptFiles fail.
LargeArea bool
// TestVectors lets Encrypt write format 2, which only a generator of
// test vectors may write (spec §62.1 rule 1, §70). EncryptFiles, which
// writes format 3, ignores it.
@ -136,7 +147,7 @@ func Encrypt(dst io.Writer, src io.Reader, opts EncryptOptions) (*Result, error)
if err != nil {
return nil, err
}
return s.write(dst, Format2, uint64(opts.Length), func(w io.Writer) error {
return s.write(dst, Format2, uint64(opts.Length), nil, func(w io.Writer) error {
return copyExactly(w, src, opts.Length)
})
}
@ -195,8 +206,11 @@ func newSealer(opts EncryptOptions, length uint64) (*sealer, error) {
// write writes a capsule of format f whose content, of length bytes, body
// writes into the plaintext of PAYLOAD_AGE; write adds the zeros of the
// padding up to P (spec §29.1).
func (s *sealer) write(dst io.Writer, f Format, length uint64, body func(w io.Writer) error) (*Result, error) {
// padding up to P (spec §29.1). prepare, when not nil, receives the final
// control, with I_PAYLOAD and the binding, before anything is written to dst:
// it is where a writer of format 3 signs, with the commitments that the
// control gives (spec v0.11, §29.8). Its error stops the writing.
func (s *sealer) write(dst io.Writer, f Format, length uint64, prepare func(c *Control) error, body func(w io.Writer) error) (*Result, error) {
opts := s.opts
padded, err := PaddedLength(length, s.code)
if err != nil {
@ -315,6 +329,12 @@ func (s *sealer) write(dst io.Writer, f Format, length uint64, body func(w io.Wr
return nil, err
}
if prepare != nil {
if err := prepare(ctrl); err != nil {
return nil, err
}
}
// SEALED_CONTROL = OUTER_TIME_AGE.
sealed, err := seal(controlBytes)
if err != nil {

@ -53,8 +53,11 @@ type Source struct {
//
// The head, the control and the security area are decoded with the rules of
// the reader before anything is written (spec §62.1 rule 17), and the
// self-checks of Encrypt apply too. The security area is the empty one of
// this version, in an area of 512 bytes, whatever the options (rule 13).
// self-checks of Encrypt apply too. The security area is in an area of
// AreaLen bytes, or LargeAreaLen with opts.LargeArea (rule 13): empty, or
// with the signature of opts.AuthorKey, made before anything is written
// with the commitments of the final control and the head (spec v0.11, §29.8)
// and checked with the strict profile (rule 19).
//
// opts is as for Encrypt, with the head in Comment, Author and the head
// extensions, and with Length 0: L is the length of BODY.
@ -87,7 +90,11 @@ func EncryptFiles(dst io.Writer, sources []Source, opts EncryptOptions) (*Result
content = h.Files[n-1].End
}
// newHead bounds content by MaxPayloadLength: the sum does not overflow.
length := BodyFrameSize + AreaLen + uint64(len(measured)) + content
area := uint32(AreaLen)
if opts.LargeArea {
area = LargeAreaLen
}
length := BodyFrameSize + uint64(area) + uint64(len(measured)) + content
if _, err := PaddedLength(length, s.code); err != nil {
return nil, err
}
@ -112,22 +119,31 @@ func EncryptFiles(dst io.Writer, sources []Source, opts EncryptOptions) (*Result
if err := selfCheckHead(head); err != nil {
return nil, err
}
security := EncodeSecurity()
if v := EvaluateSecurity(security); v != (Verdicts{Signature: VerdictNoSignature, Seal: VerdictNoSeal}) {
return nil, fmt.Errorf("capsule: self-check: the reader finds the verdicts %s and %s in this security area", v.Signature, v.Seal)
}
frame := BodyFrame{AreaLen: AreaLen, SecurityLen: uint32(len(security)), HeadLen: uint32(len(head))}
fb := frame.Bytes()
if _, err := ParseBodyFrame(fb[:], length); err != nil {
return nil, fmt.Errorf("capsule: self-check: %w", err)
}
if err := CheckHeadEnd(h, frame.ContentLength(length)); err != nil {
return nil, fmt.Errorf("capsule: self-check: %w", err)
// SECURITY_CBOR and the frame are final once the control is: the
// signature commits to it (spec v0.11, §29.8). prepare builds them
// before anything is written, and write decodes CONTROL_CBOR.
var security []byte
var fb [BodyFrameSize]byte
prepare := func(c *Control) error {
var err error
if security, err = s.security(c, head, area); err != nil {
return err
}
frame := BodyFrame{AreaLen: area, SecurityLen: uint32(len(security)), HeadLen: uint32(len(head))}
fb = frame.Bytes()
if _, err := ParseBodyFrame(fb[:], length); err != nil {
return fmt.Errorf("capsule: self-check: %w", err)
}
if err := CheckHeadEnd(h, frame.ContentLength(length)); err != nil {
return fmt.Errorf("capsule: self-check: %w", err)
}
return nil
}
// Step 16: BODY, and the second reading of each file.
res, err := s.write(dst, Format3, length, func(w io.Writer) error {
for _, b := range [][]byte{fb[:], security, make([]byte, AreaLen-len(security)), head} {
res, err := s.write(dst, Format3, length, prepare, func(w io.Writer) error {
for _, b := range [][]byte{fb[:], security, make([]byte, int(area)-len(security)), head} {
if _, err := w.Write(b); err != nil {
return err
}
@ -151,6 +167,45 @@ func EncryptFiles(dst io.Writer, sources []Source, opts EncryptOptions) (*Result
return res, nil
}
// security returns SECURITY_CBOR for the capsule whose final control is c and
// whose head is head: empty, or with the signature of opts.AuthorKey (spec
// v0.11, §29.3, §29.8, §29.9). It decodes and evaluates what it returns with
// the rules of the reader, in the context of this capsule, and checks that
// it fits in an area of area bytes (§62.1 rules 13, 17 and 19).
func (s *sealer) security(c *Control, head []byte, area uint32) ([]byte, error) {
sc := &SecurityContext{HeadDigest: HeadDigest(head), RoundTime: s.unlock}
want := Verdicts{Signature: VerdictNoSignature, Seal: VerdictNoSeal}
security := EncodeSecurity()
if k := s.opts.AuthorKey; k != nil {
var err error
if sc.ControlCommit, err = ControlCommit(c, Format3); err != nil {
return nil, err
}
pub := k.Public()
if len(pub) != 32 {
return nil, fmt.Errorf("capsule: the author key is %d bytes, not 32", len(pub))
}
msg := AuthorMessage(sc.ControlCommit, sc.HeadDigest, SignersDigest(AlgEd25519, nil))
sig := k.Sign(msg)
content, err := EncodeAuthorSignature(AlgEd25519, pub, sig)
if err != nil {
return nil, err
}
if security, err = EncodeSecurityWith(content, nil); err != nil {
return nil, err
}
want.Signature = VerdictSignedOther
copy(want.AuthorKey[:], pub)
}
if len(security) > int(area) {
return nil, fmt.Errorf("capsule: SECURITY_CBOR of %d bytes does not fit in the area of %d bytes", len(security), area)
}
if v := EvaluateSecurityIn(security, sc); v != want {
return nil, fmt.Errorf("capsule: self-check: the reader finds the verdicts %s and %s in this security area, not %s and %s", v.Signature, v.Seal, want.Signature, want.Seal)
}
return security, nil
}
// newHead checks the files and the texts of opts with the rules of spec
// §29.4 to §29.6, in the words of a writer (spec §62.1 rule 15), and returns
// the head with the files in the byte order of their paths, their layout and

@ -122,7 +122,8 @@ func TestEncryptFilesRoundTrip(t *testing.T) {
}
}
// Spec §29.2: the lengths of its examples, written by EncryptFiles.
// Spec §29.2: the lengths of its examples, written by EncryptFiles, with the
// area of 32 KiB of v0.11: 32256 bytes more than with that of 512 of v0.10.
func TestEncryptFilesLengths(t *testing.T) {
note := source("nota.txt", strings.Repeat("n", 1000))
note.ModTime = time.Date(2026, 9, 30, 0, 0, 0, 0, time.UTC)
@ -132,8 +133,8 @@ func TestEncryptFilesLengths(t *testing.T) {
comment string
l, p uint64
}{
{"a comment of one byte", nil, "a", 580, 768},
{"nota.txt of 1000 bytes, with mtime", []capsule.Source{note}, "", 1641, 1792},
{"a comment of one byte", nil, "a", 32836, 34816},
{"nota.txt of 1000 bytes, with mtime", []capsule.Source{note}, "", 33897, 34816},
} {
opts := files3(t)
opts.Comment = tc.comment

@ -22,8 +22,12 @@ const (
AreaUnit = 512
MaxAreaLen = 128 * AreaUnit
// AreaLen is the size of the security area that writers of this version
// write, always, whatever the capsule holds (spec §29.2, §62.1 rule 13).
AreaLen = 512
// write, always, whatever the capsule holds (spec v0.11, §29.2, §62.1
// rule 13): 32 KiB. Writers of v0.10 wrote AreaUnit, 512 bytes.
AreaLen = 64 * AreaUnit
// LargeAreaLen is the area of a capsule whose creator expressly asked for
// a larger one because the signatures do not fit in AreaLen: 64 KiB.
LargeAreaLen = MaxAreaLen
// MaxHeadLen is the maximum of HEAD_LEN, 16 MiB.
MaxHeadLen = 16 << 20

@ -59,6 +59,10 @@ type OpenOptions struct {
// content of formats 1 and 2. Open fails right after step 2 with
// ErrSinkRequired when a format 3 capsule has no Sink.
Sink Sink
// AuthorKeys are the author keys that the person saved, by their
// dkauthor1… string, with the label she gave each: a valid signature of
// one of them is F3 and not F4 (spec v0.11, §29.7). Nil for none.
AuthorKeys map[string]string
}
// Opened describes a capsule that Open decrypted completely.
@ -306,7 +310,7 @@ func Open(ctx context.Context, dst io.Writer, r io.Reader, opts OpenOptions) (*O
}
if format == Format3 {
out.PayloadLength = control.PayloadLength
if err := openBody(pr, control.PayloadLength, out.PaddedLength, opts.Sink, opts.Extensions, out); err != nil {
if err := openBody(pr, control.PayloadLength, out.PaddedLength, opts.Sink, opts.Extensions, newSecurityContext(control, format, in.UnlockAt, opts.AuthorKeys), out); err != nil {
return out, 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))

@ -6,6 +6,7 @@ import (
"errors"
"fmt"
"io"
"time"
datekeys "g.activething.com/go/DateKeys"
"g.activething.com/go/DateKeys/extension"
@ -138,6 +139,19 @@ 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
}
// 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:
@ -156,7 +170,7 @@ func sinkFailure(what string, err error) error {
// 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.
func openBody(pr io.Reader, l, p uint64, sink Sink, reg extension.Registry, out *Opened) (err error) {
func openBody(pr io.Reader, l, p uint64, sink Sink, reg extension.Registry, sc *SecurityContext, out *Opened) (err error) {
r := &plainReader{r: pr}
begun := false
defer func() {
@ -185,7 +199,7 @@ func openBody(pr io.Reader, l, p uint64, sink Sink, reg extension.Registry, out
// 17.3 and 17.6: security never fails; its verdicts are shown after
// step 18 only.
verdicts := EvaluateSecurity(area[:frame.SecurityLen])
security := area[:frame.SecurityLen]
// 17.4: the head. Its codes other than ErrIntegrity are reported only
// after reading to EOF.
@ -193,6 +207,10 @@ func openBody(pr io.Reader, l, p uint64, sink Sink, reg extension.Registry, out
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)
h, err := DecodeHead(hb, reg)
if err != nil {
if derr := drain(r, p); derr != nil {

@ -55,7 +55,7 @@ func (c capsule3) build(t *testing.T) ([]byte, []byte) {
if sec == nil {
sec = capsule.EncodeSecurity()
}
body := testkit.Body3(capsule.AreaLen, sec, hb, c.contents...)
body := testkit.Body3(capsule.AreaUnit, sec, hb, c.contents...)
if c.body != nil {
body = c.body(body)
}
@ -135,7 +135,7 @@ func TestOpen3(t *testing.T) {
}
p, _ := capsule.PaddedLength(uint64(len(body)), capsule.Reforzado)
switch {
case o.Format != capsule.Format3 || o.AreaLen != capsule.AreaLen:
case o.Format != capsule.Format3 || o.AreaLen != capsule.AreaUnit:
t.Errorf("format %d, area %d", o.Format, o.AreaLen)
case o.PayloadLength != uint64(len(body)) || o.PaddedLength != p || o.Padding != capsule.Reforzado:
t.Errorf("L = %d, P = %d, padding %s; want %d, %d", o.PayloadLength, o.PaddedLength, o.Padding, len(body), p)
@ -192,7 +192,7 @@ func TestOpen3Substeps(t *testing.T) {
lastPadding := func(p []byte) []byte { p[len(p)-1] = 1; return p }
short := func(p []byte) []byte { return p[:len(p)-1] }
long := func(p []byte) []byte { return append(p, make([]byte, 256)...) }
areaByte := func(b []byte) []byte { b[capsule.BodyFrameSize+capsule.AreaLen-1] = 1; return b }
areaByte := func(b []byte) []byte { b[capsule.BodyFrameSize+capsule.AreaUnit-1] = 1; return b }
// HEAD_CBOR starts with the map, key 0, the text header and the 13 bytes
// of "datekeys-head", then key 1 and the version.
const headTag, headVersion = 3, 17

@ -0,0 +1,98 @@
package capsule_test
import (
"bytes"
"context"
"strings"
"testing"
"g.activething.com/go/DateKeys/authorkey"
"g.activething.com/go/DateKeys/capsule"
"g.activething.com/go/DateKeys/internal/testkit"
)
// openSigned opens dkc with the author keys that the person saved.
func openSigned(t *testing.T, dkc []byte, saved map[string]string) *capsule.Opened {
t.Helper()
o := defaultOpen(1000)
o.Sink, o.AuthorKeys = &testkit.MemorySink{}, saved
opened, err := capsule.Open(context.Background(), nil, bytes.NewReader(dkc), o)
if err != nil {
t.Fatal(err)
}
return opened
}
// Spec v0.11 §29.7, §29.8, §62.1 rule 19: EncryptFiles signs with the key of
// opts.AuthorKey and Open gives F4, or F3 with the key saved.
func TestEncryptFilesSigned(t *testing.T) {
key, err := authorkey.Generate()
if err != nil {
t.Fatal(err)
}
pub, _ := authorkey.PublicString(key.Public())
opts := files3(t)
opts.AuthorKey = key
var dkc bytes.Buffer
if _, err := capsule.EncryptFiles(&dkc, []capsule.Source{source("nota.txt", "Hola.\n")}, opts); err != nil {
t.Fatal(err)
}
o := openSigned(t, dkc.Bytes(), nil)
if o.Verdicts.Signature != capsule.VerdictSignedOther || o.Verdicts.AuthorKey != [32]byte(key.Public()) || o.Verdicts.Seal != capsule.VerdictNoSeal || o.AreaLen != capsule.AreaLen {
t.Errorf("verdicts %+v, area %d", o.Verdicts, o.AreaLen)
}
o = openSigned(t, dkc.Bytes(), map[string]string{pub: "Ana"})
if o.Verdicts.Signature != capsule.VerdictSignedSaved || o.Verdicts.AuthorLabel != "Ana" {
t.Errorf("saved key: verdicts %+v", o.Verdicts)
}
other, _ := authorkey.Generate()
otherPub, _ := authorkey.PublicString(other.Public())
if o = openSigned(t, dkc.Bytes(), map[string]string{otherPub: "Luis"}); o.Verdicts.Signature != capsule.VerdictSignedOther {
t.Errorf("another saved key: verdicts %+v", o.Verdicts)
}
// The area grows only when asked, and the signature still verifies.
opts.LargeArea = true
dkc.Reset()
if _, err := capsule.EncryptFiles(&dkc, []capsule.Source{source("nota.txt", "Hola.\n")}, opts); err != nil {
t.Fatal(err)
}
if o = openSigned(t, dkc.Bytes(), nil); o.AreaLen != capsule.LargeAreaLen || o.Verdicts.Signature != capsule.VerdictSignedOther {
t.Errorf("large area: verdicts %+v, area %d", o.Verdicts, o.AreaLen)
}
}
// badKey is an AuthorKey that signs wrongly or has a public key of the wrong
// length.
type badKey struct {
pub, sig []byte
}
func (k badKey) Public() []byte { return k.pub }
func (k badKey) Sign([]byte) []byte { return k.sig }
// Spec v0.11 §62.1 rule 19: a signature that does not verify, or a key of
// another length, fails before anything is written.
func TestEncryptFilesSignatureChecked(t *testing.T) {
key, _ := authorkey.Generate()
for _, tc := range []struct {
name string
key capsule.AuthorKey
want string
}{
{"wrong signature", badKey{key.Public(), make([]byte, 64)}, "self-check"},
{"short key", badKey{key.Public()[:31], make([]byte, 64)}, "not 32"},
} {
opts := files3(t)
opts.AuthorKey = tc.key
var dkc bytes.Buffer
_, err := capsule.EncryptFiles(&dkc, []capsule.Source{source("nota.txt", "x")}, opts)
if err == nil || !strings.Contains(err.Error(), tc.want) {
t.Errorf("%s: %v", tc.name, err)
}
if dkc.Len() != 0 {
t.Errorf("%s: %d bytes written", tc.name, dkc.Len())
}
}
}

@ -87,10 +87,11 @@ func randomBytes(n int) []byte {
}
// unsupportedSignature is the content of key 2 of security: an
// author-signature of alg 1, with a random key of 32 bytes and a random
// signature of 64 (verdict F1: this version implements no alg).
// author-signature of an alg that no version defines, 4294967295, with a
// random key of 32 bytes and a random signature of 64 (verdict F1: this
// reader does not implement that alg; spec v0.11, §29.7).
func unsupportedSignature() ([]byte, error) {
return capsule.EncodeAuthorSignature(1, randomBytes(32), randomBytes(64))
return capsule.EncodeAuthorSignature(4294967295, randomBytes(32), randomBytes(64))
}
// patterned returns n bytes that look like the content of a binary file:

@ -444,9 +444,9 @@ func specs() []spec {
if err != nil {
return nil, err
}
return body3(capsule.AreaLen, s, "", "", note)
return body3(capsule.AreaUnit, s, "", "", note)
}},
{name: "format3_signature_unsupported", format: f3, description: "format 3 time_only capsule with an author-signature of alg 1, a random key of 32 bytes and a random signature of 64: verdicts F1 and S0", round: 1001, policy: capsule.TimeOnly, padding: capsule.Reforzado,
{name: "format3_signature_unsupported", format: f3, description: "format 3 time_only capsule with an author-signature of alg 4294967295, a random key of 32 bytes and a random signature of 64: verdicts F1 and S0", round: 1001, policy: capsule.TimeOnly, padding: capsule.Reforzado,
body: func() ([]byte, error) {
sig, err := unsupportedSignature()
if err != nil {
@ -456,9 +456,9 @@ func specs() []spec {
if err != nil {
return nil, err
}
return body3(capsule.AreaLen, s, "", "", note)
return body3(capsule.AreaUnit, s, "", "", note)
}},
{name: "format3_seal_unsupported", format: f3, description: "format 3 time_only capsule with an author-signature of alg 1, as in format3_signature_unsupported, and a seal of seal_type 1 with a random token of 32 bytes: verdicts F1 and S1", round: 2000, policy: capsule.TimeOnly, padding: capsule.Reforzado,
{name: "format3_seal_unsupported", format: f3, description: "format 3 time_only capsule with an author-signature of alg 4294967295, as in format3_signature_unsupported, and a seal of seal_type 1 with a random token of 32 bytes: verdicts F1 and S1", round: 2000, policy: capsule.TimeOnly, padding: capsule.Reforzado,
body: func() ([]byte, error) {
sig, err := unsupportedSignature()
if err != nil {
@ -472,7 +472,7 @@ func specs() []spec {
if err != nil {
return nil, err
}
return body3(capsule.AreaLen, s, "", "", note)
return body3(capsule.AreaUnit, s, "", "", note)
}},
}
}

@ -546,7 +546,7 @@ func specMutations(f capsule.Format) []Mutation {
if err != nil {
return nil, err
}
b.Plaintext = Body3(capsule.AreaLen, capsule.EncodeSecurity(), hb, []byte(content))
b.Plaintext = Body3(capsule.AreaUnit, capsule.EncodeSecurity(), hb, []byte(content))
}
return built(b)
}

@ -261,8 +261,10 @@ func format3Mutations() []Mutation {
return h + streamNonceSize + 64<<10 + 16, err
}
sigAlg1 := mustMarshal(map[uint64]any{0: uint64(1), 1: fill(0x11, 32), 2: fill(0x22, 64)})
sigAlgMax := mustMarshal(map[uint64]any{0: uint64(4294967295), 1: fill(0x11, 32), 2: fill(0x22, 64)})
sealType1 := mustMarshal(map[uint64]any{0: uint64(1), 1: fill(0x33, 32)})
named := func(m Mutation, name string) Mutation { m.Name = name; return m }
notSpec := func(m Mutation) Mutation { m.Spec = false; return m }
withIdentity := func(f *LoadedFixture, in *MutationInput) (*MutationInput, error) { return f.withIdentity(in) }
relabelTK := func(e *MutationEnv) (*MutationInput, error) {
@ -376,7 +378,8 @@ func format3Mutations() []Mutation {
// Security never decides the opening: these open, without a code,
// with their verdicts (spec §29.3, §29.7).
named(security(securityV2(), capsule.Verdicts{Signature: capsule.VerdictUnreadable, Seal: capsule.VerdictUnreadable}), "security of version 2 opens with the verdict X"),
named(security(mustSecurity(sigAlg1, nil), capsule.Verdicts{Signature: capsule.VerdictSignatureUnchecked, Seal: capsule.VerdictNoSeal}), "a signature of alg 1 opens with the verdict F1"),
named(security(mustSecurity(sigAlgMax, nil), capsule.Verdicts{Signature: capsule.VerdictSignatureUnchecked, Seal: capsule.VerdictNoSeal}), "a signature of alg 4294967295 opens with the verdict F1"),
notSpec(named(security(mustSecurity(sigAlg1, nil), capsule.Verdicts{Signature: capsule.VerdictSignatureInvalid, Seal: capsule.VerdictNoSeal}), "a signature of alg 1 that does not verify opens with the verdict F2")),
named(security(mustSecurity(nil, sealType1), capsule.Verdicts{Signature: capsule.VerdictNoSignature, Seal: capsule.VerdictSealUnsupported}), "a seal of seal_type 1 opens with the verdict S1"),
// Further cases: the other relabelings of a format 3 capsule.

Binary file not shown.

@ -1,7 +1,7 @@
{
"file": "format3_seal_unsupported.dkc",
"format": 3,
"capsule_id": "48da6a985c7bf92b0fbf2042c36b1039",
"capsule_id": "3517684914fad908ad9e46d7f7b811d5",
"datekey": "dk1_eyJ2ZXJzaW9uIjoxLCJuZXR3b3JrIjoiZGF0ZWtleXM6cXVpY2tuZXQ6djEiLCJyb3VuZCI6MjAwMH0",
"profile": "datekeys:quicknet:v1",
"round": 2000,
@ -31,7 +31,7 @@
"step": 4,
"name": "header validation",
"ok": true,
"detail": "capsule_id=48da6a985c7bf92b0fbf2042c36b1039 datekey=dk1_eyJ2ZXJzaW9uIjoxLCJuZXR3b3JrIjoiZGF0ZWtleXM6cXVpY2tuZXQ6djEiLCJyb3VuZCI6MjAwMH0 policy=time_only profile=datekeys:quicknet:v1"
"detail": "capsule_id=3517684914fad908ad9e46d7f7b811d5 datekey=dk1_eyJ2ZXJzaW9uIjoxLCJuZXR3b3JrIjoiZGF0ZWtleXM6cXVpY2tuZXQ6djEiLCJyb3VuZCI6MjAwMH0 policy=time_only profile=datekeys:quicknet:v1"
},
{
"step": 5,

@ -1,21 +1,21 @@
{
"description": "format 3 time_only capsule with an author-signature of alg 1, as in format3_signature_unsupported, and a seal of seal_type 1 with a random token of 32 bytes: verdicts F1 and S1",
"description": "format 3 time_only capsule with an author-signature of alg 4294967295, as in format3_signature_unsupported, and a seal of seal_type 1 with a random token of 32 bytes: verdicts F1 and S1",
"spec": "0.10",
"format": 3,
"file": "format3_seal_unsupported.dkc",
"sha256": "9e729c4d523aa8235c1f94b6c4366500a442a7ce69a5f92b9780662daca070e3",
"sha256": "cd3f68e430c8d41df92a364d65fe29b4aed8ec50e5129595735ede6a8d7df88b",
"release": {
"round": 2000,
"signature": "b6cb8f482a0b15d45936a4c4ea08e98a087e71787caee3f4d07a8a9843b1bc5423c6b3c22f446488b3137eaca799c77e"
},
"prelude": "444b43310300000000000079000001ca",
"public_header": "a5006a646174656b65796361700101025048da6a985c7bf92b0fbf2042c36b1039037853646b315f65794a325a584a7a61573975496a6f784c434a755a58523362334a72496a6f695a4746305a57746c65584d3663585670593274755a585136646a45694c434a79623356755a4349364d6a41774d48300400",
"public_header": "a5006a646174656b6579636170010102503517684914fad908ad9e46d7f7b811d5037853646b315f65794a325a584a7a61573975496a6f784c434a755a58523362334a72496a6f695a4746305a57746c65584d3663585670593274755a585136646a45694c434a79623356755a4349364d6a41774d48300400",
"datekey": "dk1_eyJ2ZXJzaW9uIjoxLCJuZXR3b3JrIjoiZGF0ZWtleXM6cXVpY2tuZXQ6djEiLCJyb3VuZCI6MjAwMH0",
"capsule_id": "48da6a985c7bf92b0fbf2042c36b1039",
"capsule_id": "3517684914fad908ad9e46d7f7b811d5",
"access_policy": "time_only",
"structure": "time_only",
"unlock_at": "2023-08-23T16:49:24Z",
"header_binding": "bc80975e44978cb51465890b4499f591ad0b7af61337bb35b25ad2d1d500b6b3",
"header_binding": "2c34950c31c80b62c31da9aa1ca72cf46d38361a60fdf37519c1f734c6646fb9",
"outer_stanzas": [
{
"type": "tlock",
@ -29,21 +29,21 @@
{
"type": "X25519",
"args": [
"irINIJ7PYPkx0Asd7r+Nw3GfeeEnQc6M1u2QWGBtIyc"
"sfu28SAWdZaPq6c3v4bLopEk8YveD80i8b59V4JMfkM"
]
}
],
"control_cbor": "a60070646174656b6579732d636f6e74726f6c0103025820bc80975e44978cb51465890b4499f591ad0b7af61337bb35b25ad2d1d500b6b303582075b43a62e103df83789f741e0b2cc4b22a15e948d9105902cae0c735bbf9fa86064800000000000002930702",
"payload_identity": "75b43a62e103df83789f741e0b2cc4b22a15e948d9105902cae0c735bbf9fa86",
"control_cbor": "a60070646174656b6579732d636f6e74726f6c01030258202c34950c31c80b62c31da9aa1ca72cf46d38361a60fdf37519c1f734c6646fb9035820a0cae1dd0fb24ae1ea350ad408afa77bb040fe4553ca5a5e6283be84f1775184064800000000000002930702",
"payload_identity": "a0cae1dd0fb24ae1ea350ad408afa77bb040fe4553ca5a5e6283be84f1775184",
"payload_length": 659,
"padding": 2,
"padded_length": 768,
"plaintext_file": "format3_seal_unsupported.plaintext",
"plaintext_sha256": "b7e84ded53528372d35ef5aef5c7935eff5827824832f8556e6ed06eb2951ef5",
"plaintext_sha256": "18e5a8d45af211d036dfe64fc4065c8ada927265200f48a3094aa7ded519b94e",
"area_len": 512,
"security_cbor": "a40071646174656b6579732d73656375726974790101025869a30001015820296414f1ab39ebe5d7f366597d677ee979e2071266a1a9d59fd4688810795bde025840eb7530c2c468fd7d86dd2238551c436a713a978219baec42f7b4763ef2a9f3977d52353e8783875a4ee8d03e504883ff1390be2dde82ac86c182b5a79ad4d624035826a2000101582029bdc2748984e5be979d7c79e6c382cb29cfd8924a645fd8c2446f686700f343",
"head_cbor": "a4006d646174656b6579732d686561640101025820ac66fed2ff62fd1bb417ee393b4476a5694b859e7feceb37aaf6cb49401895400581a600686e6f74612e7478740116020003160458205d596d5f49c179f0ec337f4610155fc237e727f9dee85377a5fce67a3c853510051a6abcf9c0",
"salt": "ac66fed2ff62fd1bb417ee393b4476a5694b859e7feceb37aaf6cb4940189540",
"security_cbor": "a40071646174656b6579732d7365637572697479010102586da3001affffffff0158208beec85fe1db5d53dfa1fa5b95afe208c355a1fabe0d3637c1acc09fef0f04c10258400ccc209b32e7826b70b4befbe7bbe504584631422a3b51086e9491885e8aac6d2a0c92c4c85d6c03750ddaa2d873f6d4dce20030b31669f347ee6b9b4f3abd56035826a20001015820c19dc75c9766f13383b991f54a7cfcb16701ad0299fa68d84c5ce2e82a8f18d7",
"head_cbor": "a4006d646174656b6579732d6865616401010258208eb5e2f0920209323e39c54391a74cc873690cd6d7ce94a45b12772f2a5081c10581a600686e6f74612e7478740116020003160458205d596d5f49c179f0ec337f4610155fc237e727f9dee85377a5fce67a3c853510051a6abcf9c0",
"salt": "8eb5e2f0920209323e39c54391a74cc873690cd6d7ce94a45b12772f2a5081c1",
"content_offset": 637,
"files": [
{

Binary file not shown.

Before

Width:  |  Height:  |  Size: 659 B

After

Width:  |  Height:  |  Size: 659 B

@ -1,7 +1,7 @@
{
"file": "format3_signature_unsupported.dkc",
"format": 3,
"capsule_id": "d2296b10c37ba96cd7cf2af7f1b95717",
"capsule_id": "a6bf56d5084eb0054779492620b8af34",
"datekey": "dk1_eyJ2ZXJzaW9uIjoxLCJuZXR3b3JrIjoiZGF0ZWtleXM6cXVpY2tuZXQ6djEiLCJyb3VuZCI6MTAwMX0",
"profile": "datekeys:quicknet:v1",
"round": 1001,
@ -31,7 +31,7 @@
"step": 4,
"name": "header validation",
"ok": true,
"detail": "capsule_id=d2296b10c37ba96cd7cf2af7f1b95717 datekey=dk1_eyJ2ZXJzaW9uIjoxLCJuZXR3b3JrIjoiZGF0ZWtleXM6cXVpY2tuZXQ6djEiLCJyb3VuZCI6MTAwMX0 policy=time_only profile=datekeys:quicknet:v1"
"detail": "capsule_id=a6bf56d5084eb0054779492620b8af34 datekey=dk1_eyJ2ZXJzaW9uIjoxLCJuZXR3b3JrIjoiZGF0ZWtleXM6cXVpY2tuZXQ6djEiLCJyb3VuZCI6MTAwMX0 policy=time_only profile=datekeys:quicknet:v1"
},
{
"step": 5,

@ -1,21 +1,21 @@
{
"description": "format 3 time_only capsule with an author-signature of alg 1, a random key of 32 bytes and a random signature of 64: verdicts F1 and S0",
"description": "format 3 time_only capsule with an author-signature of alg 4294967295, a random key of 32 bytes and a random signature of 64: verdicts F1 and S0",
"spec": "0.10",
"format": 3,
"file": "format3_signature_unsupported.dkc",
"sha256": "83dc0f3b71cd57f2c06b5ea5860c1fd709627c2ab11c86ca1fc3b5a16d89c497",
"sha256": "e8e3106d8d73bb7b845062e0fe42af21df7d7cd8f63c335cab8dedb3e690df31",
"release": {
"round": 1001,
"signature": "b33bf3667cbd5a82de3a24b4e0e9fe5513cc1a0e840368c6e31f5fcfa79bea03f73896b25883abf2853d10337fb8fa41"
},
"prelude": "444b43310300000000000079000001ca",
"public_header": "a5006a646174656b657963617001010250d2296b10c37ba96cd7cf2af7f1b95717037853646b315f65794a325a584a7a61573975496a6f784c434a755a58523362334a72496a6f695a4746305a57746c65584d3663585670593274755a585136646a45694c434a79623356755a4349364d5441774d58300400",
"public_header": "a5006a646174656b657963617001010250a6bf56d5084eb0054779492620b8af34037853646b315f65794a325a584a7a61573975496a6f784c434a755a58523362334a72496a6f695a4746305a57746c65584d3663585670593274755a585136646a45694c434a79623356755a4349364d5441774d58300400",
"datekey": "dk1_eyJ2ZXJzaW9uIjoxLCJuZXR3b3JrIjoiZGF0ZWtleXM6cXVpY2tuZXQ6djEiLCJyb3VuZCI6MTAwMX0",
"capsule_id": "d2296b10c37ba96cd7cf2af7f1b95717",
"capsule_id": "a6bf56d5084eb0054779492620b8af34",
"access_policy": "time_only",
"structure": "time_only",
"unlock_at": "2023-08-23T15:59:27Z",
"header_binding": "fdb39427d6f10d3ad89a81161fa1f65293c73291a45d18ca139e6d12a44f919e",
"header_binding": "bc6877680d44462fd92546d3f82eeaea2234505721c3b588dd7676a2eb7fc7a7",
"outer_stanzas": [
{
"type": "tlock",
@ -29,21 +29,21 @@
{
"type": "X25519",
"args": [
"6nefFx+947eX7B7qEcU6R9btIgMg69ldm+3OS6hOPns"
"qxmJwwotwDawKgsGMI+wJv5XlldCGOUP4qutXZJbQ20"
]
}
],
"control_cbor": "a60070646174656b6579732d636f6e74726f6c0103025820fdb39427d6f10d3ad89a81161fa1f65293c73291a45d18ca139e6d12a44f919e035820ba8f5da6c9464c35f9680be7a7948bc6d61522ccba72123cc5a187b248673132064800000000000002930702",
"payload_identity": "ba8f5da6c9464c35f9680be7a7948bc6d61522ccba72123cc5a187b248673132",
"control_cbor": "a60070646174656b6579732d636f6e74726f6c0103025820bc6877680d44462fd92546d3f82eeaea2234505721c3b588dd7676a2eb7fc7a70358200d50b7c8f4aa63f49c590f417b410dc3c71941e6827f407ee7921c99aba3ccdd064800000000000002930702",
"payload_identity": "0d50b7c8f4aa63f49c590f417b410dc3c71941e6827f407ee7921c99aba3ccdd",
"payload_length": 659,
"padding": 2,
"padded_length": 768,
"plaintext_file": "format3_signature_unsupported.plaintext",
"plaintext_sha256": "414deeb8dc9f45fcf5f3883154e9f0e5a872ee9438a01b997d2f513b3d5a1eb7",
"plaintext_sha256": "9fe05e6b3a463371b33fc6a81b81d538e572789a8d03ace9f752a931f4ca4728",
"area_len": 512,
"security_cbor": "a30071646174656b6579732d73656375726974790101025869a30001015820dd7f33121df820b64504dcb4f63133fbf7fefc26b9a79cdc83000540d8b3c35d025840a9717fe754465361c6d1a9a781628d8c11eda5a46b4a80f574328fa2a9d566a9923a4a796e3c52fdefa05daea4f135a4a4b73206f52643396aee6bfd29558f97",
"head_cbor": "a4006d646174656b6579732d6865616401010258206dd3b33fb1a390aa6d63404a29b677aec03285f97da6625a3a3ecae612f9d94e0581a600686e6f74612e7478740116020003160458205d596d5f49c179f0ec337f4610155fc237e727f9dee85377a5fce67a3c853510051a6abcf9c0",
"salt": "6dd3b33fb1a390aa6d63404a29b677aec03285f97da6625a3a3ecae612f9d94e",
"security_cbor": "a30071646174656b6579732d7365637572697479010102586da3001affffffff015820da2a88c336770633057e3b53daed5bc074fc8dc1679bc43eb6a173e3ef10a93102584053c88f03105b77470a999d92a268c7964be74ae856ba87a6cf6756ccae09631d0e971d3356bf39862540dd11476fc98e4fc75686dcd43742f0f6ea0105f4e47e",
"head_cbor": "a4006d646174656b6579732d686561640101025820e5e946ad436f63f659a1660ab7022355f4724d593e55766ec571776d73e3a6520581a600686e6f74612e7478740116020003160458205d596d5f49c179f0ec337f4610155fc237e727f9dee85377a5fce67a3c853510051a6abcf9c0",
"salt": "e5e946ad436f63f659a1660ab7022355f4724d593e55766ec571776d73e3a652",
"content_offset": 637,
"files": [
{

Binary file not shown.

Before

Width:  |  Height:  |  Size: 659 B

After

Width:  |  Height:  |  Size: 659 B

File diff suppressed because one or more lines are too long
Loading…
Cancel
Save

Powered by TurnKey Linux.