package capsule import ( "encoding/binary" "fmt" "strings" "time" datekeys "g.activething.com/go/DateKeys" "g.activething.com/go/DateKeys/codec" "g.activething.com/go/DateKeys/codec/bech32" "g.activething.com/go/DateKeys/extension" "g.activething.com/go/DateKeys/internal/cms" "g.activething.com/go/DateKeys/internal/pathrule" ) // The content of a format 3 capsule (spec §29.2): the plaintext of // PAYLOAD_AGE is BODY followed by its padding, and BODY is a frame of 12 // bytes, the security area, the head and the files. const ( // BodyFrameSize is the size of the frame of BODY: AREA_LEN, SECURITY_LEN // and HEAD_LEN, three unsigned 32-bit big-endian integers. BodyFrameSize = 12 // AreaUnit is the unit of AREA_LEN, and MaxAreaLen its maximum. 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 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 SecurityTypeTag = "datekeys-security" SecurityVersion = 1 HeadTypeTag = "datekeys-head" HeadVersion = 1 // SaltSize is the size of the salt of the head (spec §29.4). SaltSize = 32 // Limits of the head, fixed with format 3 (spec §29.4). MaxCommentLen = pathrule.MaxCommentLen MaxAuthorLen = pathrule.MaxAuthorLen MaxFiles = 65535 MaxPathLen = pathrule.MaxPathLen // MaxMTime is 9999-12-31T23:59:59Z in seconds since 1970-01-01 UTC. MaxMTime = 253402300799 // maxSecurityItem bounds the byte strings of keys 2 and 3 of security. maxSecurityItem = 65536 // maxAlg bounds alg and seal_type. maxAlg = 1<<32 - 1 ) // BodyFrame is the frame of BODY (spec §29.2). type BodyFrame struct { AreaLen, SecurityLen, HeadLen uint32 } // Bytes returns the 12 bytes of the frame. func (f BodyFrame) Bytes() [BodyFrameSize]byte { var b [BodyFrameSize]byte binary.BigEndian.PutUint32(b[0:], f.AreaLen) binary.BigEndian.PutUint32(b[4:], f.SecurityLen) binary.BigEndian.PutUint32(b[8:], f.HeadLen) return b } // ContentLength is C, the length of CONTENT in a BODY of length l whose frame // is f. ParseBodyFrame has checked that it does not underflow. func (f BodyFrame) ContentLength(l uint64) uint64 { return l - BodyFrameSize - uint64(f.AreaLen) - uint64(f.HeadLen) } // ParseBodyFrame decodes the frame of BODY from its first 12 bytes and checks // it against L, the length of BODY (spec §29.2, §63 step 17.2). Every // violation is ErrIntegrity. The plaintext of PAYLOAD_AGE is at least 256 // bytes, so the 12 bytes exist even when L is shorter than the frame. func ParseBodyFrame(b []byte, l uint64) (BodyFrame, error) { if len(b) != BodyFrameSize { return BodyFrame{}, fmt.Errorf("capsule: BODY: frame of %d bytes, want %d", len(b), BodyFrameSize) } if l < BodyFrameSize { return BodyFrame{}, fmt.Errorf("capsule: BODY: L = %d is shorter than the frame of %d bytes: %w", l, BodyFrameSize, datekeys.ErrIntegrity) } f := BodyFrame{ AreaLen: binary.BigEndian.Uint32(b[0:]), SecurityLen: binary.BigEndian.Uint32(b[4:]), HeadLen: binary.BigEndian.Uint32(b[8:]), } switch { case f.AreaLen < AreaUnit || f.AreaLen > MaxAreaLen || f.AreaLen%AreaUnit != 0: return f, fmt.Errorf("capsule: BODY: AREA_LEN %d is not a multiple of %d from %d to %d: %w", f.AreaLen, AreaUnit, AreaUnit, MaxAreaLen, datekeys.ErrIntegrity) case f.SecurityLen < 1 || f.SecurityLen > f.AreaLen: return f, fmt.Errorf("capsule: BODY: SECURITY_LEN %d is not from 1 to AREA_LEN = %d: %w", f.SecurityLen, f.AreaLen, datekeys.ErrIntegrity) case f.HeadLen < 1 || f.HeadLen > MaxHeadLen: return f, fmt.Errorf("capsule: BODY: HEAD_LEN %d is not from 1 to %d: %w", f.HeadLen, MaxHeadLen, datekeys.ErrIntegrity) case BodyFrameSize+uint64(f.AreaLen)+uint64(f.HeadLen) > l: return f, fmt.Errorf("capsule: BODY: the frame, the area of %d bytes and the head of %d bytes exceed L = %d: %w", f.AreaLen, f.HeadLen, l, datekeys.ErrIntegrity) } return f, nil } // CheckArea checks that the bytes of the security area after SECURITY_CBOR, // its first securityLen bytes, are zero (spec §29.2): ErrIntegrity if not. func CheckArea(area []byte, securityLen uint32) error { for i, c := range area[securityLen:] { if c != 0 { return fmt.Errorf("capsule: BODY: byte %d of the security area is not zero: %w", int(securityLen)+i, datekeys.ErrIntegrity) } } return nil } // Verdict is the result of evaluating the signature or the seal of the // security area (spec §29.7). A verdict never prevents opening. type Verdict string // The verdicts of a reader (spec v0.11, §29.7). A reader of v0.10 reaches // X, F0, F1, S0, S1 and S2 only. const ( // VerdictUnreadable (X): security fails its layer 2 or 3; it stands for // both the signature and the seal. VerdictUnreadable Verdict = "X" // VerdictNoSignature (F0): no key 2. VerdictNoSignature Verdict = "F0" // VerdictSignatureUnchecked (F1): a signature that does not decode, breaks // its schema or has an alg this reader does not implement. VerdictSignatureUnchecked Verdict = "F1" // VerdictSignatureInvalid (F2): a signature present that does not verify. VerdictSignatureInvalid Verdict = "F2" // VerdictSignedSaved (F3): a valid signature of alg 1 with a key the // person saved, whose label Verdicts.AuthorLabel holds. VerdictSignedSaved Verdict = "F3" // VerdictSignedOther (F4): a valid signature of alg 1 with another key, // which Verdicts.AuthorKey holds. It does not prove who holds it. VerdictSignedOther Verdict = "F4" // VerdictNoSeal (S0): no key 3; nothing is shown about the date. VerdictNoSeal Verdict = "S0" // VerdictSealUnsupported (S1): a seal_type this reader does not implement. VerdictSealUnsupported Verdict = "S1" // VerdictSealUnreadable (S2): a seal that does not decode or breaks its // schema. VerdictSealUnreadable Verdict = "S2" // VerdictSignedIncomplete (F5): a signature of alg 2 with a required // signer absent, not verifiable, without a seal or with an invalid one or // out of validity, or with a key 3 (spec v0.11, §29.10). VerdictSignedIncomplete Verdict = "F5" // VerdictSignedComplete (F6): a signature of alg 2 with every required // signer valid and sealed. Verdicts.Detail names them. VerdictSignedComplete Verdict = "F6" // VerdictSealInvalid (S3): a seal that does not verify. VerdictSealInvalid Verdict = "S3" // VerdictSealed (S4): a valid seal with accuracy and t + accuracy < // round_time (spec v0.16, §29.11). VerdictSealed Verdict = "S4" // VerdictSealedLate (S5): a valid seal that does not prove that it came // before round_time. Detail.SealReason says why. VerdictSealedLate Verdict = "S5" ) // SealReason is why a valid seal does not prove that it came before the // opening date (spec v0.16, §29.7): the reason of S5, and of the line of a // signer of F6 that does not say «antes de la fecha de apertura». type SealReason string const ( // ReasonNone: the seal proves it (S4, or the line of a signer that says // so). ReasonNone SealReason = "" // ReasonLate: t plus the accuracy, 0 without one, is not before // round_time. ReasonLate SealReason = "late" // ReasonNoAccuracyBTSP: the token carries no accuracy, and its policy is // the BTSP of ETSI EN 319 421, which requires it. ReasonNoAccuracyBTSP SealReason = "no accuracy, BTSP" // ReasonNoAccuracy: the token carries no accuracy. ReasonNoAccuracy SealReason = "no accuracy" ) // Text is the reason as the texts of §29.7 write it, "" for ReasonNone. func (r SealReason) Text() string { switch r { case ReasonLate: return "se selló después de esa fecha o demasiado cerca de ella" case ReasonNoAccuracyBTSP: return "el sello no dice la precisión que exige su política" case ReasonNoAccuracy: return "el sello no dice su precisión" } return "" } // sealReason is the reason of a token that verifies, the first that holds // (spec v0.16, §29.7): late, then without accuracy under BTSP, then without // accuracy; ReasonNone when it proves that it came before roundTime. func sealReason(tok *cms.Token, roundTime time.Time) SealReason { switch { case roundTime.IsZero() || !tok.GenTime.Add(tok.Accuracy).Before(roundTime): return ReasonLate case !tok.HasAccuracy && tok.BTSP(): return ReasonNoAccuracyBTSP case !tok.HasAccuracy: return ReasonNoAccuracy } return ReasonNone } // Text returns the text of the verdict that the official SDK shows, in // Spanish (spec §29.7), and "" for S0, which shows nothing, and for the // verdicts whose text names a key or a reason, which Verdicts.Lines writes. func (v Verdict) Text() string { switch v { case VerdictUnreadable: return "No se han podido comprobar la firma ni el sello: trátala como no firmada y sin fecha probada." case VerdictNoSignature: return "Sin firma de autor." case VerdictSignatureUnchecked: return "No se ha comprobado ninguna firma: trátala como no firmada." case VerdictSignatureInvalid: return "La firma no corresponde a este contenido." case VerdictSealUnsupported: return "Lleva un sello de tiempo que esta versión no sabe comprobar: aquí no prueba nada." case VerdictSealUnreadable: return "El sello de tiempo es ilegible: no prueba nada." case VerdictSignedIncomplete: return "Faltan firmas o sellos que la propia cápsula exige: trátala como no firmada." case VerdictSealInvalid: return "El sello no corresponde a este contenido." } return "" } // Verdicts are the verdicts of the security area of a format 3 capsule. type Verdicts struct { Signature, Seal Verdict // AuthorKey is the public key of a valid signature of alg 1 (F3, F4). AuthorKey [32]byte // AuthorLabel is the label of the saved key that signed (F3). AuthorLabel string // Detail names the signers of an alg 2 signature and the authority of a // valid seal; nil otherwise. A pointer, so that Verdicts stays comparable. Detail *Detail } // Detail is what the texts of F6, S4 and S5 name (spec v0.11, §29.7, §29.10). type Detail struct { // Signers are the required signers, in the order of SIGNERS, and Foreign // the SignerInfo of other certificates, which never count. Signers, Foreign []SignerLine // SealHolder and SealTime are the holder of the certificate of the // authority of a valid seal, as §29.7 writes it, and t. SealReason is // why it does not prove that it came before round_time (S5). SealHolder string SealTime time.Time SealReason SealReason } // SignerLine is a signer of an alg 2 signature. type SignerLine struct { // Holder is the name of the certificate as §29.7 shows it: the subject, // or the SHA-256 of the certificate in hexadecimal when it does not meet // the rules of a name of a certificate. Holder string // Issuer is the issuer that the certificate says, with the same rules. Issuer string // Result is "valid", "invalid", "absent", "not verifiable", "without // seal", "invalid seal" or "out of validity". Result string // SealHolder is the holder of the certificate of the authority of its // seal, and SealTime t, both zero without a seal that verifies. Before is // true when the seal proves that it came before round_time: it carries // accuracy and t plus the accuracy is before round_time (spec v0.16, // §29.11); Reason says why not, for a valid signer. SealHolder string SealTime time.Time Before bool Reason SealReason } // resultText is the result of a signer in the texts of §29.7. var resultText = map[string]string{ "valid": "válida", "invalid": "inválida", "absent": "ausente", "not verifiable": "no verificable", "without seal": "sin sello", "invalid seal": "con el sello inválido", "out of validity": "con el certificado fuera de validez", } // quoted puts a name of a certificate between « and », as the texts of §29.7 // write it, so that where it starts and where it ends is in view. func quoted(name string) string { return "«" + name + "»" } // instant writes t in UTC as §29.7 shows it: RFC 3339, with the fraction of // the seal when it has one. func instant(t time.Time) string { return t.UTC().Format(time.RFC3339Nano) } // SealedAt returns the earliest instant that a valid seal gives, the seal of // key 3 or that of a required signer of an alg 2 signature, and false when // there is none (spec v0.11, §29.7). A reader shows an mtime later than it as // an inconsistency: whoever made the capsule claims a file that is newer than // the proof that it existed. func (v Verdicts) SealedAt() (time.Time, bool) { var best time.Time take := func(t time.Time) { if !t.IsZero() && (best.IsZero() || t.Before(best)) { best = t } } if v.Detail != nil { if v.Seal == VerdictSealed || v.Seal == VerdictSealedLate { take(v.Detail.SealTime) } for _, s := range v.Detail.Signers { if s.Result == "valid" { take(s.SealTime) } } } return best, !best.IsZero() } // Lines are the verdicts as the official SDK shows them, in order: X alone, // or the signature and then the seal, when it shows something. func (v Verdicts) Lines() []string { if v.Signature == VerdictUnreadable { return []string{VerdictUnreadable.Text()} } lines := []string{v.Signature.Text()} switch v.Signature { case VerdictSignedSaved: lines[0] = "Firmado con la clave que guardaste como " + v.AuthorLabel + "." case VerdictSignedOther: key, _ := bech32.Encode("dkauthor", v.AuthorKey[:]) lines[0] = "Firmado con la clave " + key + ". No prueba quién la tiene." } if v.Signature == VerdictSignedComplete && v.Detail != nil { names := make([]string, len(v.Detail.Signers)) for i, s := range v.Detail.Signers { names[i] = quoted(s.Holder) } lines[0] = "Firmado con un certificado a nombre de " + strings.Join(names, ", ") + ". DateKeys no comprueba quién lo emitió: para eso, exporta la firma a un validador oficial." before := false for _, s := range v.Detail.Signers { when := "sin acreditar que fuera antes de la fecha de apertura: " + s.Reason.Text() if s.Before { when, before = "antes de la fecha de apertura", true } lines = append(lines, fmt.Sprintf(" %s (emisor según su certificado: %s), sellado por %s el %s, %s.", quoted(s.Holder), quoted(s.Issuer), quoted(s.SealHolder), instant(s.SealTime), when)) } // §29.7: whoever says that a capsule was signed before the date says // that it does not check who issued the seal. if before { lines = append(lines, " DateKeys no comprueba quién emitió los sellos.") } } if v.Detail != nil { for _, s := range v.Detail.Foreign { lines = append(lines, fmt.Sprintf(" Otro firmante, %s: %s. No cuenta.", quoted(s.Holder), resultText[s.Result])) } } switch t := v.Seal.Text(); { case v.Seal == VerdictSealed && v.Detail != nil: lines = append(lines, "Según un sello a nombre de "+quoted(v.Detail.SealHolder)+", existía el "+instant(v.Detail.SealTime)+ ", antes de que la cápsula pudiera abrirse. DateKeys no comprueba quién emitió el sello.") case v.Seal == VerdictSealedLate && v.Detail != nil: lines = append(lines, "No acredita que se sellara antes de la fecha de apertura: "+v.Detail.SealReason.Text()+".") case t != "": lines = append(lines, t) } return lines } // securityWire is the outer map of SECURITY_CBOR: keys 2 and 3 hold // separately encoded CBOR (spec §29.3). type securityWire struct { signature, seal []byte // nil when absent } func (w *securityWire) encode(e *codec.Encoder) { n := 2 if w.signature != nil { n++ } if w.seal != nil { n++ } e.Map(n) e.Uint(0) e.Text(SecurityTypeTag) e.Uint(1) e.Uint(SecurityVersion) if w.signature != nil { e.Uint(2) e.Bstr(w.signature) } if w.seal != nil { e.Uint(3) e.Bstr(w.seal) } } func (w *securityWire) decode(d *codec.Decoder) error { pairs, err := d.Map(4) if err != nil { return err } var seen uint for range pairs { k, err := d.Key() if err != nil { return err } switch k { case 0: _, err = d.Text(len(SecurityTypeTag)) case 1: _, err = d.Uint(SecurityVersion) case 2: w.signature, err = d.Bstr(1, maxSecurityItem) case 3: w.seal, err = d.Bstr(1, maxSecurityItem) default: return fmt.Errorf("key %d is not defined: %w", k, datekeys.ErrNonCanonicalCBOR) } if err != nil { return fmt.Errorf("key %d: %w", k, err) } seen |= 1 << k } if err := required(seen, 2); err != nil { return err } return d.EndMap() } // EncodeSecurity returns SECURITY_CBOR as a writer of this version writes it: // empty, {0: "datekeys-security", 1: 1}, 22 bytes (spec §29.3). func EncodeSecurity() []byte { var e codec.Encoder (&securityWire{}).encode(&e) b, _ := e.Out() return b } // EncodeSecurityWith returns SECURITY_CBOR with the given contents of keys 2 // and 3, nil when absent (spec §29.3). EncryptFiles writes it with what this // version defines, a signature of alg 1 or 2 and a seal of seal_type 2; any // other content only a generator of test vectors writes (spec §62.1 rule 13). func EncodeSecurityWith(signature, seal []byte) ([]byte, error) { var e codec.Encoder (&securityWire{signature: signature, seal: seal}).encode(&e) return e.Out() } // EvaluateSecurity reads SECURITY_CBOR and returns its verdicts without the // capsule around it (spec §29.3, §29.7), as a reader of v0.10 does: it checks // the structure, and any signature is F1. EvaluateSecurityIn checks the // signature too. It never fails: security never decides the opening. For the // signature and for the seal apart, the first row of the table of §29.7 that // holds decides: alg and seal_type are read only from content that decodes // and meets its schema. func EvaluateSecurity(b []byte) Verdicts { return EvaluateSecurityIn(b, nil) } // decodeSecurity decodes the outer map of SECURITY_CBOR, layers 2 and 3. func decodeSecurity(b []byte) (*securityWire, bool) { if tag, version, err := codec.Peek(b); err != nil || tag != SecurityTypeTag || version != SecurityVersion { return nil, false } var w securityWire if err := codec.Unmarshal(b, w.decode, w.encode); err != nil { return nil, false } return &w, true } // authorSignature is the content of key 2 of security: {0: alg, 1: public // key, 2: signature} (spec §29.3). type authorSignature struct { alg uint64 key, value []byte } func (a *authorSignature) encode(e *codec.Encoder) { e.Map(3) e.Uint(0) e.Uint(a.alg) e.Uint(1) e.Bstr(a.key) e.Uint(2) e.Bstr(a.value) } func (a *authorSignature) decode(d *codec.Decoder) error { return decodeItem(d, 3, func(k uint64) (err error) { switch k { case 0: a.alg, err = decodeAlg(d) case 1: a.key, err = d.Bstr(0, maxSecurityItem) case 2: a.value, err = d.Bstr(0, maxSecurityItem) default: err = fmt.Errorf("key %d is not defined: %w", k, datekeys.ErrNonCanonicalCBOR) } return err }) } // decodeAuthorSignature decodes the content of key 2 of security. func decodeAuthorSignature(b []byte) (*authorSignature, error) { var a authorSignature if err := codec.Unmarshal(b, a.decode, a.encode); err != nil { return nil, err } return &a, nil } // EncodeAuthorSignature returns the content of key 2 of security, {0: alg, // 1: key, 2: signature} (spec §29.3). This version defines alg 1, AlgEd25519, // and alg 2, AlgCMS, whose key 1 holds SIGNERS: EncryptFiles writes them, and // only a generator of test vectors writes another alg, such as AlgTest. func EncodeAuthorSignature(alg uint64, key, signature []byte) ([]byte, error) { var e codec.Encoder (&authorSignature{alg, key, signature}).encode(&e) return e.Out() } // seal is the content of key 3 of security: {0: seal_type, 1: token}. type seal struct { sealType uint64 token []byte } func (s *seal) encode(e *codec.Encoder) { e.Map(2) e.Uint(0) e.Uint(s.sealType) e.Uint(1) e.Bstr(s.token) } func (s *seal) decode(d *codec.Decoder) error { return decodeItem(d, 2, func(k uint64) (err error) { switch k { case 0: s.sealType, err = decodeAlg(d) case 1: s.token, err = d.Bstr(0, maxSecurityItem) default: err = fmt.Errorf("key %d is not defined: %w", k, datekeys.ErrNonCanonicalCBOR) } return err }) } func decodeSeal(b []byte) (*seal, error) { var s seal if err := codec.Unmarshal(b, s.decode, s.encode); err != nil { return nil, err } return &s, nil } // EncodeSeal returns the content of key 3 of security, {0: seal_type, 1: // token} (spec §29.3). This version defines seal_type 2, SealTypeRFC3161, // which EncryptFiles writes; only a generator of test vectors writes another // seal_type, such as SealTypeTest. func EncodeSeal(sealType uint64, token []byte) ([]byte, error) { var e codec.Encoder (&seal{sealType, token}).encode(&e) return e.Out() } // decodeAlg reads alg or seal_type, from 1 to 2^32 - 1. func decodeAlg(d *codec.Decoder) (uint64, error) { v, err := d.Uint(maxAlg) if err == nil && v == 0 { err = fmt.Errorf("0 is not defined: %w", datekeys.ErrNonCanonicalCBOR) } return v, err } // decodeItem reads a map of exactly n required keys, 0 to n-1, with field. func decodeItem(d *codec.Decoder, n int, field func(k uint64) error) error { pairs, err := d.Map(n) if err != nil { return err } var seen uint for range pairs { k, err := d.Key() if err != nil { return err } if err := field(k); err != nil { return fmt.Errorf("key %d: %w", k, err) } seen |= 1 << k } if err := required(seen, n); err != nil { return err } return d.EndMap() } // Head is the head of a format 3 capsule (spec §29.4). type Head struct { Salt [SaltSize]byte // Comment and Author are the comment and the declared author, "" when // absent. The declared author is text of the creator and proves nothing. Comment, Author string // Files are the entries, in strictly ascending byte order of their paths. Files []File // Critical and Noncritical are the extensions of the head (keys 6 and 7). Critical, Noncritical []extension.Extension } // File is an entry of the head: a file of CONTENT. type File struct { Path string Size, Start, End uint64 SHA256 [32]byte // MTime is the modification time of the file at its source, in seconds // since 1970-01-01 UTC, when HasMTime. It is informative. MTime uint64 HasMTime bool } // headWire is HEAD_CBOR as it is encoded. type headWire struct { h *Head } func (w *headWire) encode(e *codec.Encoder) { h := w.h n := 3 + nonEmpty(h.Critical) + nonEmpty(h.Noncritical) if h.Comment != "" { n++ } if h.Author != "" { n++ } if len(h.Files) > 0 { n++ } e.Map(n) e.Uint(0) e.Text(HeadTypeTag) e.Uint(1) e.Uint(HeadVersion) e.Uint(2) e.Bstr(h.Salt[:]) if h.Comment != "" { e.Uint(3) e.Text(h.Comment) } if h.Author != "" { e.Uint(4) e.Text(h.Author) } if len(h.Files) > 0 { e.Uint(5) e.Array(len(h.Files)) for i := range h.Files { encodeFile(e, &h.Files[i]) } } encodeExtensions(e, 6, h.Critical, h.Noncritical) } func encodeFile(e *codec.Encoder, f *File) { n := 5 if f.HasMTime { n++ } e.Map(n) e.Uint(0) e.Text(f.Path) e.Uint(1) e.Uint(f.Size) e.Uint(2) e.Uint(f.Start) e.Uint(3) e.Uint(f.End) e.Uint(4) e.Bstr(f.SHA256[:]) if f.HasMTime { e.Uint(5) e.Uint(f.MTime) } } // decode reads HEAD_CBOR with the rules of the third layer: the CDDL, R1 and // R8 (spec §29.4, §29.5). The fourth layer comes after, in DecodeHead. func (w *headWire) decode(d *codec.Decoder) error { h := w.h pairs, err := d.Map(8) if err != nil { return err } var seen uint for range pairs { k, err := d.Key() if err != nil { return err } switch k { case 0: _, err = d.Text(len(HeadTypeTag)) case 1: _, err = d.Uint(HeadVersion) case 2: var salt []byte if salt, err = d.Bstr(SaltSize, SaltSize); err == nil { copy(h.Salt[:], salt) } case 3: h.Comment, err = decodeText(d, MaxCommentLen, "comment") case 4: h.Author, err = decodeText(d, MaxAuthorLen, "declared author") case 5: h.Files, err = decodeFiles(d) case 6: h.Critical, err = extension.DecodeArray(d) case 7: h.Noncritical, err = extension.DecodeArray(d) default: return fmt.Errorf("key %d is not defined: %w", k, datekeys.ErrNonCanonicalCBOR) } if err != nil { return fmt.Errorf("key %d: %w", k, err) } seen |= 1 << k } if err := required(seen, 3); err != nil { return err } return d.EndMap() } // decodeText reads a text of 1 to max bytes. func decodeText(d *codec.Decoder, max int, what string) (string, error) { s, err := d.Text(max) if err == nil && s == "" { err = fmt.Errorf("empty %s: %w", what, datekeys.ErrNonCanonicalCBOR) } return s, err } // decodeFiles reads the array of entries: 1 to MaxFiles, each path of 1 to // MaxPathLen bytes (R1), in strictly ascending byte order (R8). func decodeFiles(d *codec.Decoder) ([]File, error) { n, err := d.Array(MaxFiles) if err != nil { return nil, err } if n == 0 { return nil, fmt.Errorf("empty array of files: %w", datekeys.ErrNonCanonicalCBOR) } files := make([]File, 0, min(n, 1024)) for i := range n { var f File if err := decodeItemOptional(d, 5, 6, func(k uint64) (err error) { switch k { case 0: f.Path, err = d.Text(MaxPathLen) if err == nil && f.Path == "" { err = fmt.Errorf("R1: the path is empty: %w", datekeys.ErrNonCanonicalCBOR) } case 1: f.Size, err = d.Uint(MaxPayloadLength) case 2: f.Start, err = d.Uint(MaxPayloadLength) case 3: f.End, err = d.Uint(MaxPayloadLength) case 4: var sum []byte if sum, err = d.Bstr(32, 32); err == nil { copy(f.SHA256[:], sum) } case 5: f.MTime, err = d.Uint(MaxMTime) f.HasMTime = err == nil default: err = fmt.Errorf("key %d is not defined: %w", k, datekeys.ErrNonCanonicalCBOR) } return err }); err != nil { return nil, fmt.Errorf("file %d: %w", i+1, err) } if i > 0 && f.Path <= files[i-1].Path { return nil, fmt.Errorf("file %d: R8: the path is not after the path of file %d in byte order: %w", i+1, i, datekeys.ErrNonCanonicalCBOR) } files = append(files, f) } return files, nil } // decodeItemOptional reads a map whose keys 0 to required-1 are required and // whose keys up to max-1 are optional. func decodeItemOptional(d *codec.Decoder, need, max int, field func(k uint64) error) error { pairs, err := d.Map(max) if err != nil { return err } var seen uint for range pairs { k, err := d.Key() if err != nil { return err } if err := field(k); err != nil { return fmt.Errorf("key %d: %w", k, err) } seen |= 1 << k } if err := required(seen, need); err != nil { return err } return d.EndMap() } // EncodeHead returns HEAD_CBOR for h. The files must already be in byte // order of their paths. The writer checks the result with DecodeHead, the // rules of the reader (spec §62.1 rule 17). func EncodeHead(h *Head) ([]byte, error) { w := headWire{h: &Head{Salt: h.Salt, Comment: h.Comment, Author: h.Author, Files: h.Files}} var err error if w.h.Critical, err = extension.Canonical(h.Critical); err != nil { return nil, err } if w.h.Noncritical, err = extension.Canonical(h.Noncritical); err != nil { return nil, err } if err := extension.CheckDisjoint(w.h.Critical, w.h.Noncritical); err != nil { return nil, err } if len(h.Files) > MaxFiles { return nil, fmt.Errorf("capsule: head: %d files, more than %d", len(h.Files), MaxFiles) } var e codec.Encoder w.encode(&e) return e.Out() } // DecodeHead validates and decodes HEAD_CBOR with the layers of spec §69.1 // (spec §29.4, §63 step 17.4): the type tag and the version (layer 2), the // CDDL with R1 and R8 (layer 3), and then, in key order, the comment and the // declared author (§29.6), the files with R2 to R6c, R10 and their layout, // R7 and R9 over the tree (§29.5), all ErrHeadInvalid, and the critical // extensions with reg (layer 4). func DecodeHead(b []byte, reg extension.Registry) (*Head, error) { h, err := decodeHead(b) if err != nil { return nil, err } if err := extension.CheckCriticalIn(extension.Head, h.Critical, reg); err != nil { return nil, fmt.Errorf("capsule: head: %w", err) } return h, nil } // decodeHead is DecodeHead but for the critical extensions, whose knowledge // depends on the reader: the self-check of a writer decodes with it, as it // decodes the control with DecodeControl (spec §62.1 rule 17). func decodeHead(b []byte) (*Head, error) { if len(b) > MaxHeadLen { return nil, fmt.Errorf("capsule: head: %d bytes, more than %d: %w", len(b), MaxHeadLen, datekeys.ErrIntegrity) } if err := codec.CheckSchema(b, HeadTypeTag, HeadVersion); err != nil { return nil, fmt.Errorf("capsule: head: %w", err) } h := &Head{} w := headWire{h: h} if err := codec.Unmarshal(b, w.decode, w.encode); err != nil { return nil, fmt.Errorf("capsule: head: %w", err) } if err := extension.CheckDisjoint(h.Critical, h.Noncritical); err != nil { return nil, fmt.Errorf("capsule: head: %w", err) } if err := checkHeadFields(h); err != nil { return nil, err } return h, nil } // checkHeadFields applies the rules of the fourth layer with a code of their // own, ErrHeadInvalid: keys 3, 4 and 5, in that order. func checkHeadFields(h *Head) error { invalid := func(what string, err error) error { return fmt.Errorf("capsule: head: %s%v: %w", what, err, datekeys.ErrHeadInvalid) } if h.Comment != "" { if err := pathrule.CheckComment(h.Comment); err != nil { return invalid("comment: ", err) } } if h.Author != "" { if err := pathrule.CheckAuthor(h.Author); err != nil { return invalid("declared author: ", err) } } var end uint64 paths := make([]string, len(h.Files)) for i := range h.Files { f := &h.Files[i] if err := pathrule.CheckPath(f.Path); err != nil { return invalid(fmt.Sprintf("file %d: ", i+1), err) } switch { case f.Start != end: return invalid(fmt.Sprintf("file %d: ", i+1), fmt.Errorf("start %d is not %d, the end of the file before", f.Start, end)) case f.End < f.Start || f.End-f.Start != f.Size: return invalid(fmt.Sprintf("file %d: ", i+1), fmt.Errorf("from start %d to end %d is not the size %d", f.Start, f.End, f.Size)) } end = f.End paths[i] = f.Path } if err := pathrule.CheckTree(paths); err != nil { return invalid("", err) } return nil } // CheckHeadEnd checks that the files fill CONTENT, of c bytes: the last one // ends at C, or C is 0 without files (spec §29.4, §63 step 17.5). // ErrIntegrity if not. func CheckHeadEnd(h *Head, c uint64) error { var end uint64 if n := len(h.Files); n > 0 { end = h.Files[n-1].End } if end != c { return fmt.Errorf("capsule: BODY: the files end at byte %d of a content of %d bytes: %w", end, c, datekeys.ErrIntegrity) } return nil }