diff --git a/capsule/encrypt.go b/capsule/encrypt.go index a29985b..43f20e4 100644 --- a/capsule/encrypt.go +++ b/capsule/encrypt.go @@ -102,10 +102,14 @@ type EncryptOptions struct { // makes EncryptFiles fail, once the person has signed. Set it when a // signature with certificates may be large. 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. + // TestVectors lets Encrypt write format 2, and EncryptFiles an area of + // TestAreaLen bytes, which only a generator of test vectors may write + // (spec §62.1 rules 1 and 13, §70). TestVectors bool + // TestAreaLen, with TestVectors, is the area that EncryptFiles writes + // instead of AreaLen, as the area of 512 bytes of the fixtures of v0.10: + // a multiple of AreaUnit up to MaxAreaLen. 0 for the area of this version. + TestAreaLen uint32 // Now is the clock. Required: no package of this module reads the wall // clock on its own. Now func() time.Time @@ -163,7 +167,7 @@ func Encrypt(dst io.Writer, src io.Reader, opts EncryptOptions) (*Result, error) return nil, errors.New("capsule: format 2 has no head: Comment, Author and the head extensions are for EncryptFiles") case opts.Length < 0: return nil, fmt.Errorf("capsule: EncryptOptions.Length %d is negative", opts.Length) - case opts.AuthorKey != nil || opts.CMSSigner != nil || opts.Sealer != nil || opts.LargeArea || opts.PublicNote != "": + case opts.AuthorKey != nil || opts.CMSSigner != nil || opts.Sealer != nil || opts.LargeArea || opts.PublicNote != "" || opts.TestAreaLen != 0: return nil, errors.New("capsule: format 2 has no security area or public note: AuthorKey, CMSSigner, Sealer, LargeArea and PublicNote are for EncryptFiles") } s, err := newSealer(opts, uint64(opts.Length)) diff --git a/capsule/encrypt3.go b/capsule/encrypt3.go index c83efb2..df786f2 100644 --- a/capsule/encrypt3.go +++ b/capsule/encrypt3.go @@ -65,6 +65,16 @@ func EncryptFiles(dst io.Writer, sources []Source, opts EncryptOptions) (*Result if opts.Length != 0 { return nil, errors.New("capsule: EncryptOptions.Length is for Encrypt: EncryptFiles computes L from the files") } + common := uint32(AreaLen) + switch t := opts.TestAreaLen; { + case t == 0: + case !opts.TestVectors: + return nil, errors.New("capsule: EncryptOptions.TestAreaLen is for generators of test vectors: it needs TestVectors (spec §62.1 rule 13)") + case t%AreaUnit != 0 || t > MaxAreaLen || opts.LargeArea: + return nil, fmt.Errorf("capsule: a test area of %d bytes: a multiple of %d up to %d, without LargeArea", t, AreaUnit, MaxAreaLen) + default: + common = t + } s, err := newSealer(opts, 0) if err != nil { return nil, err @@ -92,7 +102,7 @@ func EncryptFiles(dst io.Writer, sources []Source, opts EncryptOptions) (*Result // newHead bounds content by MaxPayloadLength: the sum does not overflow. // This L is the first one, with the common area: the signature decides // whether the area grows (spec §29.8), and prepare returns the final L. - area := uint32(AreaLen) + area := common length := BodyFrameSize + uint64(area) + uint64(len(measured)) + content if _, err := PaddedLength(length, s.code); err != nil { return nil, err @@ -132,10 +142,10 @@ func EncryptFiles(dst io.Writer, sources []Source, opts EncryptOptions) (*Result // The area is the common one, or the large one only when what was // signed does not fit and LargeArea allows it (§62.1 rule 13). switch { - case len(security) <= AreaLen: - area = AreaLen + case len(security) <= int(common): + area = common case !opts.LargeArea: - return 0, fmt.Errorf("capsule: SECURITY_CBOR of %d bytes does not fit in the area of %d bytes: LargeArea lets the writer widen it to %d", len(security), AreaLen, LargeAreaLen) + return 0, fmt.Errorf("capsule: SECURITY_CBOR of %d bytes does not fit in the area of %d bytes: LargeArea lets the writer widen it to %d", len(security), common, LargeAreaLen) case len(security) <= LargeAreaLen: area = LargeAreaLen default: diff --git a/capsule/signature.go b/capsule/signature.go index ad67945..174f8a1 100644 --- a/capsule/signature.go +++ b/capsule/signature.go @@ -34,6 +34,10 @@ const ( AlgEd25519 = 1 // AlgCMS is a CMS signature with X.509 certificates (§29.10). AlgCMS = 2 + // AlgTest and SealTypeTest are reserved for tests: no version defines + // them, so they are F1 and S1 in every version (§29.3). + AlgTest = 4294967295 + SealTypeTest = 4294967295 ) // AuthorKey signs AUTHOR_MESSAGE with alg 1, as *authorkey.Key does. diff --git a/internal/testkit/genfixtures/format3.go b/internal/testkit/genfixtures/format3.go index 51d5d27..81bf444 100644 --- a/internal/testkit/genfixtures/format3.go +++ b/internal/testkit/genfixtures/format3.go @@ -93,7 +93,7 @@ func randomBytes(n int) []byte { // 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(4294967295, randomBytes(32), randomBytes(64)) + return capsule.EncodeAuthorSignature(capsule.AlgTest, randomBytes(32), randomBytes(64)) } // patterned returns n bytes that look like the content of a binary file: diff --git a/internal/testkit/genfixtures/main.go b/internal/testkit/genfixtures/main.go index 7c7699a..f4c4b7b 100644 --- a/internal/testkit/genfixtures/main.go +++ b/internal/testkit/genfixtures/main.go @@ -376,6 +376,10 @@ type spec struct { // options of EncryptFiles: a signer with certificates, or a sealer. configure func(o *capsule.EncryptOptions) error body func() ([]byte, error) + // area, when not 0, is the security area of a fixture of an earlier + // version, which EncryptFiles writes only for test vectors: 512 bytes in + // the fixtures of v0.10, which are compatibility fixtures (spec §67). + area uint32 } // Extension data of the fixtures (spec §54, §72): the header carries the raw @@ -441,11 +445,11 @@ func specs() []spec { {name: "format2_time_and_key_recipients", format: f2, description: "format 2 time_and_key capsule for three known X25519 recipients and a portable .dkk, and 12 dummies", round: 1001, policy: capsule.TimeAndKey, recipients: 3, portable: true, padding: capsule.Reforzado, plaintext: []byte("DateKeys fixture for several recipients.\n")}, {name: "format2_time_and_key_sixteen", format: f2, description: "format 2 time_and_key capsule for sixteen known X25519 recipients, without dummies", round: 2000, policy: capsule.TimeAndKey, recipients: 16, padding: capsule.Reforzado, plaintext: []byte("DateKeys fixture for sixteen recipients.\n")}, - {name: "format3_single", format: f3, description: "format 3 time_only capsule with a single file, nota.txt, with its mtime", round: 1000, policy: capsule.TimeOnly, padding: capsule.Reforzado, files: note}, - {name: "format3_tree", format: f3, description: "format 3 time_only capsule with five files in three folders, one of them over two STREAM chunks and one without mtime, a comment of two lines and a declared author", round: 1001, policy: capsule.TimeOnly, padding: capsule.Reforzado, files: tree, comment: "Para abrir en familia.\nCon cariño, desde 2026.", author: "Ana López"}, - {name: "format3_comment_only", format: f3, description: "format 3 time_only capsule with a comment of two lines, the second one with a TAB, a declared author and no files", round: 1004, policy: capsule.TimeOnly, padding: capsule.Reforzado, comment: "Feliz cumpleaños.\n\tAbre esto dentro de diez años.", author: "Ana"}, - {name: "format3_bloque256", format: f3, description: "format 3 time_only capsule with padding code 1 (bloque256) and one file of 20000 bytes", round: 1000, policy: capsule.TimeOnly, padding: capsule.Bloque256, files: report}, - {name: "format3_time_and_key_portable", format: f3, description: "format 3 time_and_key capsule with one credential, a portable .dkk, and 15 dummies", round: 1000, policy: capsule.TimeAndKey, portable: true, padding: capsule.Reforzado, files: secret}, + {name: "format3_single", format: f3, area: capsule.AreaUnit, description: "format 3 time_only capsule with a single file, nota.txt, with its mtime", round: 1000, policy: capsule.TimeOnly, padding: capsule.Reforzado, files: note}, + {name: "format3_tree", format: f3, area: capsule.AreaUnit, description: "format 3 time_only capsule with five files in three folders, one of them over two STREAM chunks and one without mtime, a comment of two lines and a declared author", round: 1001, policy: capsule.TimeOnly, padding: capsule.Reforzado, files: tree, comment: "Para abrir en familia.\nCon cariño, desde 2026.", author: "Ana López"}, + {name: "format3_comment_only", format: f3, area: capsule.AreaUnit, description: "format 3 time_only capsule with a comment of two lines, the second one with a TAB, a declared author and no files", round: 1004, policy: capsule.TimeOnly, padding: capsule.Reforzado, comment: "Feliz cumpleaños.\n\tAbre esto dentro de diez años.", author: "Ana"}, + {name: "format3_bloque256", format: f3, area: capsule.AreaUnit, description: "format 3 time_only capsule with padding code 1 (bloque256) and one file of 20000 bytes", round: 1000, policy: capsule.TimeOnly, padding: capsule.Bloque256, files: report}, + {name: "format3_time_and_key_portable", format: f3, area: capsule.AreaUnit, description: "format 3 time_and_key capsule with one credential, a portable .dkk, and 15 dummies", round: 1000, policy: capsule.TimeAndKey, portable: true, padding: capsule.Reforzado, files: secret}, {name: "format3_area_1024", format: f3, description: "format 3 time_only capsule with a security area of 1024 bytes, as a later version may write it, holding the empty security", round: 1000, policy: capsule.TimeOnly, padding: capsule.Reforzado, body: func() ([]byte, error) { return body3(1024, capsule.EncodeSecurity(), "", "", note) }}, {name: "format3_security_v2", format: f3, description: "format 3 time_only capsule whose security is of version 2: verdict X", round: 1000, policy: capsule.TimeOnly, padding: capsule.Reforzado, @@ -468,13 +472,13 @@ func specs() []spec { } return body3(capsule.AreaUnit, s, "", "", note) }}, - {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, + {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 4294967295, reserved for tests, 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 { return nil, err } - seal, err := capsule.EncodeSeal(1, randomBytes(32)) + seal, err := capsule.EncodeSeal(capsule.SealTypeTest, randomBytes(32)) if err != nil { return nil, err } @@ -484,6 +488,7 @@ func specs() []spec { } return body3(capsule.AreaUnit, s, "", "", note) }}, + {name: "format3_unsigned", format: f3, description: "format 3 time_only capsule with a single file, nota.txt, as format3_signed, without a signature: the area of 32 KiB of spec v0.11 holds the empty security, and P is the one of format3_signed", round: 1000, policy: capsule.TimeOnly, padding: capsule.Reforzado, files: 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}, {name: "format3_signed_cms", format: f3, description: "format 3 time_only capsule with a single file, nota.txt, signed with alg 2 by two test certificates, an ECDSA P-256 one and an RSA 2048 one, each sealed by a test time-stamping authority before the round time: verdict F6, with the certificates, the commitments, SIGNERS and the result of each signer in the record", round: 1000, policy: capsule.TimeOnly, padding: capsule.Reforzado, files: note, configure: configureCMS}, {name: "format3_sealed", format: f3, description: "format 3 time_only capsule with a single file, nota.txt, signed with alg 1 by the test key of format3_signed and sealed with seal_type 2 by a test time-stamping authority before the round time: verdicts F4 and S4, with SEAL_SUBJECT and the token in the record", round: 1000, policy: capsule.TimeOnly, padding: capsule.Reforzado, files: note, signer: signerSeed, configure: configureSeal}, @@ -634,6 +639,9 @@ 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.area != 0 { + opts.TestVectors, opts.TestAreaLen = true, s.area + } if s.configure != nil { if err := s.configure(&opts); err != nil { return nil, err diff --git a/testdata/fixtures/format3_seal_unsupported.dkc b/testdata/fixtures/format3_seal_unsupported.dkc index fcdb590..78d93e5 100644 Binary files a/testdata/fixtures/format3_seal_unsupported.dkc and b/testdata/fixtures/format3_seal_unsupported.dkc differ diff --git a/testdata/fixtures/format3_seal_unsupported.inspect.json b/testdata/fixtures/format3_seal_unsupported.inspect.json index 36c8bef..b004709 100644 --- a/testdata/fixtures/format3_seal_unsupported.inspect.json +++ b/testdata/fixtures/format3_seal_unsupported.inspect.json @@ -1,7 +1,7 @@ { "file": "format3_seal_unsupported.dkc", "format": 3, - "capsule_id": "3517684914fad908ad9e46d7f7b811d5", + "capsule_id": "8e2f648c77bd659a89ca95f96f213780", "datekey": "dk1_eyJ2ZXJzaW9uIjoxLCJuZXR3b3JrIjoiZGF0ZWtleXM6cXVpY2tuZXQ6djEiLCJyb3VuZCI6MjAwMH0", "profile": "datekeys:quicknet:v1", "round": 2000, @@ -31,7 +31,7 @@ "step": 4, "name": "header validation", "ok": true, - "detail": "capsule_id=3517684914fad908ad9e46d7f7b811d5 datekey=dk1_eyJ2ZXJzaW9uIjoxLCJuZXR3b3JrIjoiZGF0ZWtleXM6cXVpY2tuZXQ6djEiLCJyb3VuZCI6MjAwMH0 policy=time_only profile=datekeys:quicknet:v1" + "detail": "capsule_id=8e2f648c77bd659a89ca95f96f213780 datekey=dk1_eyJ2ZXJzaW9uIjoxLCJuZXR3b3JrIjoiZGF0ZWtleXM6cXVpY2tuZXQ6djEiLCJyb3VuZCI6MjAwMH0 policy=time_only profile=datekeys:quicknet:v1" }, { "step": 5, diff --git a/testdata/fixtures/format3_seal_unsupported.json b/testdata/fixtures/format3_seal_unsupported.json index a82f20c..5eff7fa 100644 --- a/testdata/fixtures/format3_seal_unsupported.json +++ b/testdata/fixtures/format3_seal_unsupported.json @@ -1,21 +1,21 @@ { - "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", + "description": "format 3 time_only capsule with an author-signature of alg 4294967295, as in format3_signature_unsupported, and a seal of seal_type 4294967295, reserved for tests, with a random token of 32 bytes: verdicts F1 and S1", "spec": "0.11", "format": 3, "file": "format3_seal_unsupported.dkc", - "sha256": "cd3f68e430c8d41df92a364d65fe29b4aed8ec50e5129595735ede6a8d7df88b", + "sha256": "ae3219fbdbd1de4cef6fade1a3fb3f6e5d5e2e8af54d9516b05f0a48136913ad", "release": { "round": 2000, "signature": "b6cb8f482a0b15d45936a4c4ea08e98a087e71787caee3f4d07a8a9843b1bc5423c6b3c22f446488b3137eaca799c77e" }, "prelude": "444b43310300000000000079000001ca", - "public_header": "a5006a646174656b6579636170010102503517684914fad908ad9e46d7f7b811d5037853646b315f65794a325a584a7a61573975496a6f784c434a755a58523362334a72496a6f695a4746305a57746c65584d3663585670593274755a585136646a45694c434a79623356755a4349364d6a41774d48300400", + "public_header": "a5006a646174656b6579636170010102508e2f648c77bd659a89ca95f96f213780037853646b315f65794a325a584a7a61573975496a6f784c434a755a58523362334a72496a6f695a4746305a57746c65584d3663585670593274755a585136646a45694c434a79623356755a4349364d6a41774d48300400", "datekey": "dk1_eyJ2ZXJzaW9uIjoxLCJuZXR3b3JrIjoiZGF0ZWtleXM6cXVpY2tuZXQ6djEiLCJyb3VuZCI6MjAwMH0", - "capsule_id": "3517684914fad908ad9e46d7f7b811d5", + "capsule_id": "8e2f648c77bd659a89ca95f96f213780", "access_policy": "time_only", "structure": "time_only", "unlock_at": "2023-08-23T16:49:24Z", - "header_binding": "2c34950c31c80b62c31da9aa1ca72cf46d38361a60fdf37519c1f734c6646fb9", + "header_binding": "c6518ed7bd02c8f121493170776eb6c43e2c8162e97a9a6225b310a1836da612", "outer_stanzas": [ { "type": "tlock", @@ -29,21 +29,21 @@ { "type": "X25519", "args": [ - "sfu28SAWdZaPq6c3v4bLopEk8YveD80i8b59V4JMfkM" + "nhJ/XFoarftmX9LumtcoVsCBMxXMYQgJwuYYEG8lg3M" ] } ], - "control_cbor": "a60070646174656b6579732d636f6e74726f6c01030258202c34950c31c80b62c31da9aa1ca72cf46d38361a60fdf37519c1f734c6646fb9035820a0cae1dd0fb24ae1ea350ad408afa77bb040fe4553ca5a5e6283be84f1775184064800000000000002930702", - "payload_identity": "a0cae1dd0fb24ae1ea350ad408afa77bb040fe4553ca5a5e6283be84f1775184", + "control_cbor": "a60070646174656b6579732d636f6e74726f6c0103025820c6518ed7bd02c8f121493170776eb6c43e2c8162e97a9a6225b310a1836da6120358204b0e0832574c11b161d7bab4d37582fde331eabb027f7d26c23cf11113fd5cc8064800000000000002930702", + "payload_identity": "4b0e0832574c11b161d7bab4d37582fde331eabb027f7d26c23cf11113fd5cc8", "payload_length": 659, "padding": 2, "padded_length": 768, "plaintext_file": "format3_seal_unsupported.plaintext", - "plaintext_sha256": "18e5a8d45af211d036dfe64fc4065c8ada927265200f48a3094aa7ded519b94e", + "plaintext_sha256": "0f865221d26545762712271faf835cb2e9980f8fb15c9d3b94fb6747cf16c1df", "area_len": 512, - "security_cbor": "a40071646174656b6579732d7365637572697479010102586da3001affffffff0158208beec85fe1db5d53dfa1fa5b95afe208c355a1fabe0d3637c1acc09fef0f04c10258400ccc209b32e7826b70b4befbe7bbe504584631422a3b51086e9491885e8aac6d2a0c92c4c85d6c03750ddaa2d873f6d4dce20030b31669f347ee6b9b4f3abd56035826a20001015820c19dc75c9766f13383b991f54a7cfcb16701ad0299fa68d84c5ce2e82a8f18d7", - "head_cbor": "a4006d646174656b6579732d6865616401010258208eb5e2f0920209323e39c54391a74cc873690cd6d7ce94a45b12772f2a5081c10581a600686e6f74612e7478740116020003160458205d596d5f49c179f0ec337f4610155fc237e727f9dee85377a5fce67a3c853510051a6abcf9c0", - "salt": "8eb5e2f0920209323e39c54391a74cc873690cd6d7ce94a45b12772f2a5081c1", + "security_cbor": "a40071646174656b6579732d7365637572697479010102586da3001affffffff015820d956dada75e3501522321d0e57c5a06dc64e07c394e6e5666f5de65b38871732025840b1f86cea4dfa61201710331694cd3fb811eeb32f7d983d9c5679c5adc41ba25fe5b82d48a74ec76d5db3f887745d0681f2a221c71b014324e8b6ad324a4234b303582aa2001affffffff0158206721210782b8e419b97c9e620bc6247ef6775929a04eb075aaac927687283a9c", + "head_cbor": "a4006d646174656b6579732d686561640101025820f9526586bc95f1bc1a375fc7575c71081312626445cce4aae63a095e35afc84a0581a600686e6f74612e7478740116020003160458205d596d5f49c179f0ec337f4610155fc237e727f9dee85377a5fce67a3c853510051a6abcf9c0", + "salt": "f9526586bc95f1bc1a375fc7575c71081312626445cce4aae63a095e35afc84a", "content_offset": 637, "files": [ { diff --git a/testdata/fixtures/format3_seal_unsupported.plaintext b/testdata/fixtures/format3_seal_unsupported.plaintext index d9f916e..c4f27ee 100644 Binary files a/testdata/fixtures/format3_seal_unsupported.plaintext and b/testdata/fixtures/format3_seal_unsupported.plaintext differ diff --git a/testdata/fixtures/format3_unsigned.dkc b/testdata/fixtures/format3_unsigned.dkc new file mode 100644 index 0000000..624465e Binary files /dev/null and b/testdata/fixtures/format3_unsigned.dkc differ diff --git a/testdata/fixtures/format3_unsigned.inspect.json b/testdata/fixtures/format3_unsigned.inspect.json new file mode 100644 index 0000000..1e49281 --- /dev/null +++ b/testdata/fixtures/format3_unsigned.inspect.json @@ -0,0 +1,61 @@ +{ + "file": "format3_unsigned.dkc", + "format": 3, + "capsule_id": "0cced8e8b035d6dd70f39923b407a96c", + "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=0cced8e8b035d6dd70f39923b407a96c 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" + } + ] +} diff --git a/testdata/fixtures/format3_unsigned.json b/testdata/fixtures/format3_unsigned.json new file mode 100644 index 0000000..c94e6a8 --- /dev/null +++ b/testdata/fixtures/format3_unsigned.json @@ -0,0 +1,152 @@ +{ + "description": "format 3 time_only capsule with a single file, nota.txt, as format3_signed, without a signature: the area of 32 KiB of spec v0.11 holds the empty security, and P is the one of format3_signed", + "spec": "0.11", + "format": 3, + "file": "format3_unsigned.dkc", + "sha256": "317ab722ae3812a25ddd78b4c98c586363e5587c8d3634c881ce7c421af19168", + "release": { + "round": 1000, + "signature": "b44679b9a59af2ec876b1a6b1ad52ea9b1615fc3982b19576350f93447cb1125e342b73a8dd2bacbe47e4b6b63ed5e39" + }, + "prelude": "444b43310300000000000079000001ca", + "public_header": "a5006a646174656b6579636170010102500cced8e8b035d6dd70f39923b407a96c037853646b315f65794a325a584a7a61573975496a6f784c434a755a58523362334a72496a6f695a4746305a57746c65584d3663585670593274755a585136646a45694c434a79623356755a4349364d5441774d48300400", + "datekey": "dk1_eyJ2ZXJzaW9uIjoxLCJuZXR3b3JrIjoiZGF0ZWtleXM6cXVpY2tuZXQ6djEiLCJyb3VuZCI6MTAwMH0", + "capsule_id": "0cced8e8b035d6dd70f39923b407a96c", + "access_policy": "time_only", + "structure": "time_only", + "unlock_at": "2023-08-23T15:59:24Z", + "header_binding": "36fac05bdec31225c4249bbdf14381b72c28e9dc8d6eec8af2500ec83c03ea8c", + "outer_stanzas": [ + { + "type": "tlock", + "args": [ + "1000", + "52db9ba70e0cc0f6eaf7803dd07447a1f5477735fd3f661792ba94600c84e971" + ] + } + ], + "payload_stanzas": [ + { + "type": "X25519", + "args": [ + "NcekpxDZyrUYH4JypnvSI6VsJ70XDGb9dNeny2xewlQ" + ] + } + ], + "control_cbor": "a60070646174656b6579732d636f6e74726f6c010302582036fac05bdec31225c4249bbdf14381b72c28e9dc8d6eec8af2500ec83c03ea8c0358204207790feb87ae42795751d6a28fee2af78359aeef98f1dc9faef32e9c7ddba4064800000000000080930702", + "payload_identity": "4207790feb87ae42795751d6a28fee2af78359aeef98f1dc9faef32e9c7ddba4", + "payload_length": 32915, + "padding": 2, + "padded_length": 34816, + "plaintext_file": "format3_unsigned.plaintext", + "plaintext_sha256": "25527e5e2e1ce02056d4419fb89f7b0ce35e4920f93217f58dcf62a4377f8af5", + "area_len": 32768, + "security_cbor": "a20071646174656b6579732d73656375726974790101", + "head_cbor": "a4006d646174656b6579732d686561640101025820503815e579869d0498c3267596adc6555a6d9db770c834f5ba0e5ff8fc08fec90581a600686e6f74612e7478740116020003160458205d596d5f49c179f0ec337f4610155fc237e727f9dee85377a5fce67a3c853510051a6abcf9c0", + "salt": "503815e579869d0498c3267596adc6555a6d9db770c834f5ba0e5ff8fc08fec9", + "content_offset": 32893, + "files": [ + { + "path": "nota.txt", + "size": 22, + "start": 0, + "end": 22, + "sha256": "5d596d5f49c179f0ec337f4610155fc237e727f9dee85377a5fce67a3c853510", + "mtime": 1790769600 + } + ], + "verdicts": { + "signature": "F0", + "seal": "S0", + "lines": [ + "Sin firma de autor." + ] + }, + "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 + } + ] +} diff --git a/testdata/fixtures/format3_unsigned.plaintext b/testdata/fixtures/format3_unsigned.plaintext new file mode 100644 index 0000000..17f7001 Binary files /dev/null and b/testdata/fixtures/format3_unsigned.plaintext differ