Signature plan, step 6: the fixture format3_signed and the vectors of its signature

format3_signed is written by EncryptFiles with a test key. Its record gives
the seed of the key, control_commit, head_digest, signers_digest,
AUTHOR_MESSAGE with its code, the signature and the content of key 2, and
verdicts carries the dkauthor1 key. The conformance test recomputes all of
it from the control, the head and the security area, and signs again from
the seed. A second test changes the context, a bit of the signature or of
the key, and the key itself, and removes the signature.

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

@ -18,6 +18,7 @@ import (
"g.activething.com/go/DateKeys/accesskey"
"g.activething.com/go/DateKeys/agewrap"
"g.activething.com/go/DateKeys/authorkey"
"g.activething.com/go/DateKeys/capsule"
"g.activething.com/go/DateKeys/extension"
"g.activething.com/go/DateKeys/internal/testkit"
@ -35,6 +36,7 @@ var fixtureNames = []string{
"format2_time_and_key_portable", "format2_time_and_key_recipients", "format2_time_and_key_sixteen",
"format3_single", "format3_tree", "format3_comment_only", "format3_bloque256", "format3_time_and_key_portable",
"format3_area_1024", "format3_security_v2", "format3_signature_unsupported", "format3_seal_unsupported",
"format3_signed",
}
type fixture struct {
@ -301,7 +303,8 @@ func TestConformanceFixtures(t *testing.T) {
}
v := opened.Verdicts
if string(v.Signature) != f.Verdicts.Signature || string(v.Seal) != f.Verdicts.Seal || opened.AreaLen != f.AreaLen ||
opened.Head.Comment != f.Comment || opened.Head.Author != f.Author || hex.EncodeToString(opened.Head.Salt[:]) != f.Salt {
opened.Head.Comment != f.Comment || opened.Head.Author != f.Author || hex.EncodeToString(opened.Head.Salt[:]) != f.Salt ||
(f.Verdicts.AuthorKey != "") != (v.AuthorKey != [32]byte{}) {
t.Fatalf("Open reports another head or verdicts: %+v", v)
}
} else if !bytes.Equal(out.Bytes(), f.plaintext) {
@ -325,6 +328,55 @@ func TestConformanceFixtures(t *testing.T) {
}
}
// checkSignature3 checks the record of the signature of a format 3 fixture
// against what this implementation computes from the control, the head and
// the security of the fixture (spec v0.11, §29.8, §29.9): the commitments,
// AUTHOR_MESSAGE and its code, the signature, which Ed25519 makes again
// from the seed of the record, and the key. It returns the verdicts in the
// context of the capsule.
func checkSignature3(t *testing.T, f *fixture, security, head []byte) capsule.Verdicts {
t.Helper()
s := f.Signature
cb, err := hex.DecodeString(f.ControlCBOR)
if err != nil {
t.Fatal(err)
}
c, err := capsule.DecodeControl(cb, f.format())
if err != nil {
t.Fatal(err)
}
cc, err := capsule.ControlCommit(c, f.format())
if err != nil {
t.Fatal(err)
}
hd, sd := capsule.HeadDigest(head), capsule.SignersDigest(capsule.AlgEd25519, nil)
msg := capsule.AuthorMessage(cc, hd, sd)
content, value, err := capsule.SecurityKey2(security)
if err != nil {
t.Fatal(err)
}
seed, err := hex.DecodeString(s.SecretSeed)
if err != nil {
t.Fatal(err)
}
key, err := authorkey.NewFromSeed(seed)
if err != nil {
t.Fatal(err)
}
pub, _ := authorkey.PublicString(key.Public())
if s.Alg != capsule.AlgEd25519 || s.AuthorKey != pub || s.ControlCommit != hex.EncodeToString(cc[:]) || s.HeadDigest != hex.EncodeToString(hd[:]) ||
s.SignersDigest != hex.EncodeToString(sd[:]) || s.AuthorMessage != string(msg) || s.AuthorCode != capsule.AuthorCode(msg) ||
s.SignatureValue != hex.EncodeToString(value) || s.SecurityKey2 != hex.EncodeToString(content) ||
hex.EncodeToString(key.Sign(msg)) != s.SignatureValue {
t.Fatalf("the record of the signature differs from what is computed: %+v", s)
}
unlock, err := time.Parse(time.RFC3339, f.UnlockAt)
if err != nil {
t.Fatal(err)
}
return capsule.EvaluateSecurityIn(security, &capsule.SecurityContext{ControlCommit: cc, HeadDigest: hd, RoundTime: unlock})
}
// checkBody3 checks BODY, the plaintext file of a format 3 fixture, against
// its record: the frame, the area, security and its verdicts, the head and
// each file with its SHA-256 (spec §29.2 to §29.7).
@ -347,6 +399,9 @@ func checkBody3(t *testing.T, f *fixture) {
t.Fatal("the area, security, the head or the offset of CONTENT differ")
}
v := capsule.EvaluateSecurity(security)
if f.Signature != nil {
v = checkSignature3(t, f, security, hb)
}
if f.Verdicts == nil || string(v.Signature) != f.Verdicts.Signature || string(v.Seal) != f.Verdicts.Seal || !reflect.DeepEqual(v.Lines(), f.Verdicts.Lines) {
t.Fatalf("verdicts %+v, recorded %+v", v, f.Verdicts)
}

@ -4,6 +4,7 @@ import (
"crypto/sha256"
"encoding/binary"
"encoding/hex"
"errors"
"time"
"g.activething.com/go/DateKeys/codec/bech32"
@ -123,6 +124,22 @@ func SealSubject(controlCommit, headDigest [32]byte, sigPart []byte) [32]byte {
return domainHash(sealSubjectPrefix, controlCommit[:], headDigest[:], sigPart)
}
// SecurityKey2 returns the exact content of key 2 of SECURITY_CBOR and, when
// it is an author-signature that decodes, the signature value it holds: what
// a generator of test vectors records about a signature. It fails when
// security has no key 2 or its content does not decode.
func SecurityKey2(security []byte) (content, value []byte, err error) {
w, ok := decodeSecurity(security)
if !ok || w.signature == nil {
return nil, nil, errors.New("capsule: SECURITY_CBOR holds no author-signature")
}
a, err := decodeAuthorSignature(w.signature)
if err != nil {
return nil, nil, err
}
return w.signature, a.value, nil
}
// SecurityContext is what the verdicts of a signature or a seal need besides
// SECURITY_CBOR: the commitments of the capsule, the time of its round, and
// the author keys that the person saved, by their dkauthor1… string, with the

@ -3,6 +3,7 @@ package capsule_test
import (
"bytes"
"context"
"encoding/hex"
"strings"
"testing"
@ -96,3 +97,85 @@ func TestEncryptFilesSignatureChecked(t *testing.T) {
}
}
}
// Spec v0.11 §29.7, §29.8: a signature of the fixture format3_signed holds
// in the context of its capsule and in no other: a bit of the signature, of
// a commitment or of the message changes the verdict to F2; the same message
// signed by another key is another key's F4; and a security area without it
// is F0.
func TestSignedFixtureVerdicts(t *testing.T) {
f := loadFixture(t, "format3_signed")
body := f.plaintext
frame, err := capsule.ParseBodyFrame(body[:capsule.BodyFrameSize], uint64(len(body)))
if err != nil {
t.Fatal(err)
}
security := body[capsule.BodyFrameSize : capsule.BodyFrameSize+frame.SecurityLen]
cb, _ := hex.DecodeString(f.ControlCBOR)
c, err := capsule.DecodeControl(cb, f.format())
if err != nil {
t.Fatal(err)
}
cc, err := capsule.ControlCommit(c, f.format())
if err != nil {
t.Fatal(err)
}
hb := body[capsule.BodyFrameSize+frame.AreaLen : capsule.BodyFrameSize+frame.AreaLen+frame.HeadLen]
ctx := func() *capsule.SecurityContext {
return &capsule.SecurityContext{ControlCommit: cc, HeadDigest: capsule.HeadDigest(hb)}
}
if v := capsule.EvaluateSecurityIn(security, ctx()); v.Signature != capsule.VerdictSignedOther || v.Seal != capsule.VerdictNoSeal {
t.Fatalf("the signature of the fixture: %+v", v)
}
// Another capsule: another control commitment, or another head.
other := ctx()
other.ControlCommit[0] ^= 1
if v := capsule.EvaluateSecurityIn(security, other); v.Signature != capsule.VerdictSignatureInvalid {
t.Errorf("another control: %+v", v)
}
other = ctx()
other.HeadDigest[31] ^= 1
if v := capsule.EvaluateSecurityIn(security, other); v.Signature != capsule.VerdictSignatureInvalid {
t.Errorf("another head: %+v", v)
}
// A bit of the signature, or of the key.
_, value, err := capsule.SecurityKey2(security)
if err != nil {
t.Fatal(err)
}
pub, _ := authorkey.ParsePublic(f.Signature.AuthorKey)
for name, mutate := range map[string]func(sig, key []byte){
"signature": func(sig, key []byte) { sig[63] ^= 1 },
"key": func(sig, key []byte) { key[0] ^= 1 },
} {
sig, key := bytes.Clone(value), bytes.Clone(pub)
mutate(sig, key)
x, err := capsule.EncodeAuthorSignature(capsule.AlgEd25519, key, sig)
if err != nil {
t.Fatal(err)
}
s, err := capsule.EncodeSecurityWith(x, nil)
if err != nil {
t.Fatal(err)
}
if v := capsule.EvaluateSecurityIn(s, ctx()); v.Signature != capsule.VerdictSignatureInvalid {
t.Errorf("a bit of the %s: %+v", name, v)
}
}
// The same message signed by another key: F4 with that key.
msg := capsule.AuthorMessage(cc, capsule.HeadDigest(hb), capsule.SignersDigest(capsule.AlgEd25519, nil))
k, _ := authorkey.Generate()
x, _ := capsule.EncodeAuthorSignature(capsule.AlgEd25519, k.Public(), k.Sign(msg))
s, _ := capsule.EncodeSecurityWith(x, nil)
if v := capsule.EvaluateSecurityIn(s, ctx()); v.Signature != capsule.VerdictSignedOther || v.AuthorKey != [32]byte(k.Public()) {
t.Errorf("another key: %+v", v)
}
// Removed: F0.
if v := capsule.EvaluateSecurityIn(capsule.EncodeSecurity(), ctx()); v.Signature != capsule.VerdictNoSignature {
t.Errorf("removed: %+v", v)
}
}

@ -133,6 +133,7 @@ func TestDecryptFormat3Fixtures(t *testing.T) {
{"format3_comment_only", ""},
{"format3_time_and_key_portable", "format3_time_and_key_portable.dkk"},
{"format3_seal_unsupported", ""},
{"format3_signed", ""},
} {
t.Run(tc.name, func(t *testing.T) {
var f testkit.DKCFixture
@ -475,8 +476,8 @@ func TestInspectJSONGoldens(t *testing.T) {
if err != nil {
t.Fatal(err)
}
if len(names) != len(dkcs) || len(dkcs) != 21 {
t.Fatalf("%d frozen inspect outputs, want one per official .dkc (%d, 21)", len(names), len(dkcs))
if len(names) != len(dkcs) || len(dkcs) != 22 {
t.Fatalf("%d frozen inspect outputs, want one per official .dkc (%d, 22)", len(names), len(dkcs))
}
t.Chdir(fixtures)
for _, path := range names {

@ -84,6 +84,10 @@ type DKCFixture struct {
ContentOffset uint64 `json:"content_offset,omitempty"`
Files []FixtureFile `json:"files,omitempty"`
Verdicts *FixtureVerdicts `json:"verdicts,omitempty"`
// Signature is, in a fixture signed with alg 1, what a second
// implementation needs to check the signature and to make it again
// (spec v0.11, §29.8, §29.9).
Signature *FixtureSignature `json:"signature,omitempty"`
Stages []FixtureStage `json:"stages"`
}
@ -104,6 +108,28 @@ type FixtureVerdicts struct {
Signature string `json:"signature"`
Seal string `json:"seal"`
Lines []string `json:"lines"`
// AuthorKey is the dkauthor1… key of a valid signature (F3, F4).
AuthorKey string `json:"author_key,omitempty"`
}
// FixtureSignature describes the signature of alg 1 of a format 3 fixture.
// SecretSeed is the 32-byte seed of a test key made for it: Ed25519 is
// deterministic, so signing AuthorMessage with it gives SignatureValue
// again. ControlCommit, HeadDigest and SignersDigest are the commitments of
// spec §29.8, in hexadecimal; AuthorMessage is the ASCII text that is
// signed, and AuthorCode its code. SecurityKey2 is the exact content of key
// 2 of SECURITY_CBOR, in hexadecimal.
type FixtureSignature struct {
Alg int `json:"alg"`
SecretSeed string `json:"secret_seed"`
AuthorKey string `json:"author_key"`
ControlCommit string `json:"control_commit"`
HeadDigest string `json:"head_digest"`
SignersDigest string `json:"signers_digest"`
AuthorMessage string `json:"author_message"`
AuthorCode string `json:"author_code"`
SignatureValue string `json:"signature"`
SecurityKey2 string `json:"security_key_2"`
}
// FixtureExt is an extension in a fixture.

@ -15,6 +15,7 @@ import (
"g.activething.com/go/DateKeys/capsule"
"g.activething.com/go/DateKeys/codec"
"g.activething.com/go/DateKeys/codec/bech32"
"g.activething.com/go/DateKeys/internal/testkit"
)
@ -107,7 +108,7 @@ func patterned(seed string, n int) []byte {
// record3 fills the fields of format 3 of f from BODY, the first L bytes of
// the plaintext of PAYLOAD_AGE, checking it with the rules of the reader.
func record3(f *testkit.DKCFixture, body []byte) error {
func record3(f *testkit.DKCFixture, body []byte, verdicts *capsule.Verdicts) error {
l := uint64(len(body))
if l < capsule.BodyFrameSize {
return errors.New("BODY shorter than its frame")
@ -131,6 +132,9 @@ func record3(f *testkit.DKCFixture, body []byte) error {
return err
}
v := capsule.EvaluateSecurity(security)
if verdicts != nil {
v = *verdicts
}
f.AreaLen = frame.AreaLen
f.Security = hex.EncodeToString(security)
f.Head = hex.EncodeToString(hb)
@ -152,9 +156,60 @@ func record3(f *testkit.DKCFixture, body []byte) error {
f.Files = append(f.Files, ff)
}
f.Verdicts = &testkit.FixtureVerdicts{Signature: string(v.Signature), Seal: string(v.Seal), Lines: v.Lines()}
f.Signature, err = recordSignature(f, security, hb, v)
if err != nil {
return err
}
if f.Signature != nil {
f.Verdicts.AuthorKey = f.Signature.AuthorKey
}
return nil
}
// recordSignature returns what the record of a fixture says about its
// signature of alg 1: the seed of its test key, which the generator wrote and
// this keeps, and the commitments and the message, which it computes from the
// control of the fixture, its head and its security (spec v0.11, §29.8). Nil
// when the fixture has no valid signature.
func recordSignature(f *testkit.DKCFixture, security, head []byte, v capsule.Verdicts) (*testkit.FixtureSignature, error) {
if v.Signature != capsule.VerdictSignedOther && v.Signature != capsule.VerdictSignedSaved {
return nil, nil
}
if f.Signature == nil {
return nil, errors.New("the fixture has a valid signature and its record no seed of the key")
}
cb, err := hex.DecodeString(f.ControlCBOR)
if err != nil {
return nil, err
}
c, err := capsule.DecodeControl(cb, capsule.Format(f.Format))
if err != nil {
return nil, err
}
defer clear(c.PayloadIdentity[:])
cc, err := capsule.ControlCommit(c, capsule.Format(f.Format))
if err != nil {
return nil, err
}
hd := capsule.HeadDigest(head)
sd := capsule.SignersDigest(capsule.AlgEd25519, nil)
msg := capsule.AuthorMessage(cc, hd, sd)
content, value, err := capsule.SecurityKey2(security)
if err != nil {
return nil, err
}
key, err := bech32.Encode("dkauthor", v.AuthorKey[:])
if err != nil {
return nil, err
}
return &testkit.FixtureSignature{
Alg: capsule.AlgEd25519, SecretSeed: f.Signature.SecretSeed, AuthorKey: key,
ControlCommit: hex.EncodeToString(cc[:]), HeadDigest: hex.EncodeToString(hd[:]), SignersDigest: hex.EncodeToString(sd[:]),
AuthorMessage: string(msg), AuthorCode: capsule.AuthorCode(msg),
SignatureValue: hex.EncodeToString(value), SecurityKey2: hex.EncodeToString(content),
}, nil
}
// check3 checks what Open delivered for the format 3 fixture f, whose BODY
// is body: the files in its sink, the head and the verdicts.
func check3(f *testkit.DKCFixture, body []byte, opened *capsule.Opened, sink *testkit.MemorySink) error {
@ -168,6 +223,9 @@ func check3(f *testkit.DKCFixture, body []byte, opened *capsule.Opened, sink *te
}
}
want := &testkit.FixtureVerdicts{Signature: string(opened.Verdicts.Signature), Seal: string(opened.Verdicts.Seal), Lines: opened.Verdicts.Lines()}
if f.Verdicts != nil && f.Verdicts.AuthorKey != "" {
want.AuthorKey, _ = bech32.Encode("dkauthor", opened.Verdicts.AuthorKey[:])
}
if !reflect.DeepEqual(want, f.Verdicts) || opened.AreaLen != f.AreaLen || opened.Head.Comment != f.Comment || opened.Head.Author != f.Author {
return errors.New("Open reports another head or other verdicts")
}

@ -50,6 +50,7 @@ import (
"g.activething.com/go/DateKeys/accesskey"
"g.activething.com/go/DateKeys/agewrap"
"g.activething.com/go/DateKeys/authorkey"
"g.activething.com/go/DateKeys/capsule"
"g.activething.com/go/DateKeys/datekey"
"g.activething.com/go/DateKeys/extension"
@ -315,7 +316,7 @@ func refreshRecord(dir, name string) error {
}
if opened.Format == capsule.Format3 {
// The plaintext file holds BODY.
if err := record3(&f, plaintext); err != nil {
if err := record3(&f, plaintext, &opened.Verdicts); err != nil {
return err
}
if err := check3(&f, plaintext, opened, sink); err != nil {
@ -368,6 +369,9 @@ type spec struct {
// vectors writes (spec §62.1 rule 13), and testkit.Build writes it.
files []file3
comment, author string
// signer, when not nil, is the 32-byte seed of the author key that signs
// a format 3 fixture written by EncryptFiles (spec v0.11, §29.9).
signer []byte
body func() ([]byte, error)
}
@ -379,6 +383,9 @@ var (
headerExtData = []byte("public label")
controlExtData = mustHex("a2000701667365616c6564")
dkkExtData = mustHex("a1006468616e64")
// signerSeed is the seed of the test key of format3_signed: the SHA-256
// of a text. It is not a secret: anyone may sign with it.
signerSeed = func() []byte { h := sha256.Sum256([]byte("DateKeys fixture author key 1")); return h[:] }()
)
func mustHex(s string) []byte {
@ -474,6 +481,7 @@ func specs() []spec {
}
return body3(capsule.AreaUnit, s, "", "", note)
}},
{name: "format3_signed", format: f3, description: "format 3 time_only capsule with a single file, nota.txt, signed with alg 1 by a test key whose seed the record gives: verdict F4, and the commitments and the message of the signature", round: 1000, policy: capsule.TimeOnly, padding: capsule.Reforzado, files: note, signer: signerSeed},
}
}
@ -621,6 +629,14 @@ func write(s spec) (*written, error) {
var res *capsule.Result
if s.format == capsule.Format3 {
opts.Comment, opts.Author = s.comment, s.author
if s.signer != nil {
k, err := authorkey.NewFromSeed(s.signer)
if err != nil {
return nil, err
}
defer k.Clear()
opts.AuthorKey = k
}
res, err = capsule.EncryptFiles(&dkc, sources(s.files), opts)
} else {
opts.Length, opts.TestVectors = int64(len(s.plaintext)), true
@ -749,7 +765,9 @@ func generate(dir string, s spec) error {
return errors.New("fixture plaintext mismatch")
}
case capsule.Format3:
if err := record3(&f, plaintext); err != nil {
// Without the verdicts of Open, which come later: the derived record
// that refreshRecord writes takes them from the opening.
if err := record3(&f, plaintext, nil); err != nil {
return err
}
}
@ -770,6 +788,10 @@ func generate(dir string, s spec) error {
return fmt.Errorf("fixture does not open: %w", err)
}
if format == capsule.Format3 {
// A signature is checked in the context of its capsule, which the
// record above does not have yet: the verdicts are those of Open.
v := opened.Verdicts
f.Verdicts = &testkit.FixtureVerdicts{Signature: string(v.Signature), Seal: string(v.Seal), Lines: v.Lines()}
if err := check3(&f, plaintext, opened, sink); err != nil {
return err
}
@ -800,6 +822,9 @@ func generate(dir string, s spec) error {
f.PlaintextSHA256 = hex.EncodeToString(psum[:])
f.HeaderExtensions = exts(false, s.headerExt)
f.ControlExt = exts(false, s.controlExt)
if s.signer != nil {
f.Signature = &testkit.FixtureSignature{SecretSeed: hex.EncodeToString(s.signer)}
}
for _, c := range opened.Inspection.Checks {
f.Stages = append(f.Stages, testkit.FixtureStage{Step: c.Step, Name: c.Name, OK: c.OK, Error: c.Error})
}

Binary file not shown.

@ -0,0 +1,61 @@
{
"file": "format3_signed.dkc",
"format": 3,
"capsule_id": "681476c1bfa81dd12fec810ee44fe7f7",
"datekey": "dk1_eyJ2ZXJzaW9uIjoxLCJuZXR3b3JrIjoiZGF0ZWtleXM6cXVpY2tuZXQ6djEiLCJyb3VuZCI6MTAwMH0",
"profile": "datekeys:quicknet:v1",
"round": 1000,
"unlock_at": "2023-08-23T15:59:24Z",
"access_policy": "time_only",
"valid": true,
"checks": [
{
"step": 1,
"name": "parse DKC1",
"ok": true,
"detail": "magic DKC1"
},
{
"step": 2,
"name": "prelude",
"ok": true,
"detail": "DKC1 v3, PUBLIC_HEADER_LEN=121, SEALED_CONTROL_LEN=458"
},
{
"step": 3,
"name": "public header",
"ok": true,
"detail": "121 bytes"
},
{
"step": 4,
"name": "header validation",
"ok": true,
"detail": "capsule_id=681476c1bfa81dd12fec810ee44fe7f7 datekey=dk1_eyJ2ZXJzaW9uIjoxLCJuZXR3b3JrIjoiZGF0ZWtleXM6cXVpY2tuZXQ6djEiLCJyb3VuZCI6MTAwMH0 policy=time_only profile=datekeys:quicknet:v1"
},
{
"step": 5,
"name": "sealed control structure",
"ok": true,
"detail": "one tlock stanza"
},
{
"step": 6,
"name": "payload structure",
"ok": true,
"detail": "one X25519 stanza"
},
{
"step": 7,
"name": "condition",
"ok": true,
"detail": "round 1000, unlock at 2023-08-23T15:59:24Z"
},
{
"step": 8,
"name": "tlock stanza",
"ok": true,
"detail": "round 1000, chain 52db9ba70e0cc0f6eaf7803dd07447a1f5477735fd3f661792ba94600c84e971"
}
]
}

@ -0,0 +1,165 @@
{
"description": "format 3 time_only capsule with a single file, nota.txt, signed with alg 1 by a test key whose seed the record gives: verdict F4, and the commitments and the message of the signature",
"spec": "0.10",
"format": 3,
"file": "format3_signed.dkc",
"sha256": "3c7d3c9e24c02853a0c7761b93bea1120b27fce396468d8d0f68e53aeb668c5e",
"release": {
"round": 1000,
"signature": "b44679b9a59af2ec876b1a6b1ad52ea9b1615fc3982b19576350f93447cb1125e342b73a8dd2bacbe47e4b6b63ed5e39"
},
"prelude": "444b43310300000000000079000001ca",
"public_header": "a5006a646174656b657963617001010250681476c1bfa81dd12fec810ee44fe7f7037853646b315f65794a325a584a7a61573975496a6f784c434a755a58523362334a72496a6f695a4746305a57746c65584d3663585670593274755a585136646a45694c434a79623356755a4349364d5441774d48300400",
"datekey": "dk1_eyJ2ZXJzaW9uIjoxLCJuZXR3b3JrIjoiZGF0ZWtleXM6cXVpY2tuZXQ6djEiLCJyb3VuZCI6MTAwMH0",
"capsule_id": "681476c1bfa81dd12fec810ee44fe7f7",
"access_policy": "time_only",
"structure": "time_only",
"unlock_at": "2023-08-23T15:59:24Z",
"header_binding": "10c269d7fcc8ae1f92c9854b8d01b23781fd6c615474c018b5e75c8620d476eb",
"outer_stanzas": [
{
"type": "tlock",
"args": [
"1000",
"52db9ba70e0cc0f6eaf7803dd07447a1f5477735fd3f661792ba94600c84e971"
]
}
],
"payload_stanzas": [
{
"type": "X25519",
"args": [
"TO8ryXIRUX0S1odrqs9IZJ43q7yYotzn/fMycGebKGQ"
]
}
],
"control_cbor": "a60070646174656b6579732d636f6e74726f6c010302582010c269d7fcc8ae1f92c9854b8d01b23781fd6c615474c018b5e75c8620d476eb0358203ffe484f443014980f2b2ee8bb66095f6ea418d4c2fce2ae6ad74c550bbefc21064800000000000080930702",
"payload_identity": "3ffe484f443014980f2b2ee8bb66095f6ea418d4c2fce2ae6ad74c550bbefc21",
"payload_length": 32915,
"padding": 2,
"padded_length": 34816,
"plaintext_file": "format3_signed.plaintext",
"plaintext_sha256": "3de3ccab0ac74f95a76aa45c0f85e1749d4b4a051d87e81828eff6bf24372000",
"area_len": 32768,
"security_cbor": "a30071646174656b6579732d73656375726974790101025869a3000101582092d3a82b1e2387a860d57cae4cc55091d43904fdd625e4d2b285e3a1b4ba67400258406f8efe0dfadaf89bf71295167f8d8cc4890de0441a37a26d1f7d0b1f56b79c43c4010861e11742a5431b072b21490d4a976dc9ff385d36242047ad4cb996270b",
"head_cbor": "a4006d646174656b6579732d68656164010102582092a0fe390ff1063d9e93bbafe3ca75be219d950128e2e5aca6a9c12d5c1f1c9b0581a600686e6f74612e7478740116020003160458205d596d5f49c179f0ec337f4610155fc237e727f9dee85377a5fce67a3c853510051a6abcf9c0",
"salt": "92a0fe390ff1063d9e93bbafe3ca75be219d950128e2e5aca6a9c12d5c1f1c9b",
"content_offset": 32893,
"files": [
{
"path": "nota.txt",
"size": 22,
"start": 0,
"end": 22,
"sha256": "5d596d5f49c179f0ec337f4610155fc237e727f9dee85377a5fce67a3c853510",
"mtime": 1790769600
}
],
"verdicts": {
"signature": "F4",
"seal": "S0",
"lines": [
"Firmado con la clave dkauthor1jtf6s2c7ywr6scx40jhye32sj82rjp8a6cj7f54jsh36rd96vaqqcg54xg. No prueba quién la tiene."
],
"author_key": "dkauthor1jtf6s2c7ywr6scx40jhye32sj82rjp8a6cj7f54jsh36rd96vaqqcg54xg"
},
"signature": {
"alg": 1,
"secret_seed": "294b60d256e79fc4c18b4bcc0a156810b44144619969dadedcbf01d3b37c1054",
"author_key": "dkauthor1jtf6s2c7ywr6scx40jhye32sj82rjp8a6cj7f54jsh36rd96vaqqcg54xg",
"control_commit": "9f296b40049889c944be75c61827b694a0104eb5c5424e17217ff1fd32529efe",
"head_digest": "0111835cdde5ef15710cea96fc600757e1695ddc8f44b2b20e57eead31278b92",
"signers_digest": "1665c6f3f1afb37b1a87d87e4265f156d7108c3c1762972d2a4241e6e5ab824e",
"author_message": "datekeys:dkc3:author-signature:v1\n58aedcded250af5f798eeace817f8a5bbd0c245d05627949970b5eb998bb0413\n",
"author_code": "58ae-dcde",
"signature": "6f8efe0dfadaf89bf71295167f8d8cc4890de0441a37a26d1f7d0b1f56b79c43c4010861e11742a5431b072b21490d4a976dc9ff385d36242047ad4cb996270b",
"security_key_2": "a3000101582092d3a82b1e2387a860d57cae4cc55091d43904fdd625e4d2b285e3a1b4ba67400258406f8efe0dfadaf89bf71295167f8d8cc4890de0441a37a26d1f7d0b1f56b79c43c4010861e11742a5431b072b21490d4a976dc9ff385d36242047ad4cb996270b"
},
"stages": [
{
"step": 1,
"name": "parse DKC1",
"ok": true
},
{
"step": 2,
"name": "prelude",
"ok": true
},
{
"step": 3,
"name": "public header",
"ok": true
},
{
"step": 4,
"name": "header validation",
"ok": true
},
{
"step": 5,
"name": "sealed control structure",
"ok": true
},
{
"step": 6,
"name": "payload structure",
"ok": true
},
{
"step": 7,
"name": "condition",
"ok": true
},
{
"step": 8,
"name": "tlock stanza",
"ok": true
},
{
"step": 9,
"name": "release",
"ok": true
},
{
"step": 10,
"name": "release verification",
"ok": true
},
{
"step": 11,
"name": "open sealed control",
"ok": true
},
{
"step": 12,
"name": "policy structure",
"ok": true
},
{
"step": 14,
"name": "control",
"ok": true
},
{
"step": 15,
"name": "header binding",
"ok": true
},
{
"step": 16,
"name": "payload identity",
"ok": true
},
{
"step": 17,
"name": "open payload",
"ok": true
},
{
"step": 18,
"name": "commit",
"ok": true
}
]
}

Binary file not shown.
Loading…
Cancel
Save

Powered by TurnKey Linux.