Signature plan, steps 3 and 4 begun: what is signed and the verdicts of alg 1
capsule/signature.go has what an author signs and a seal seals, as the
spec v0.11 draft defines it (29.8, 29.11): payload_commit, control_commit
over CONTROL_SIG, without I_PAYLOAD and with L at zero, head_digest,
signers_digest, AUTHOR_MESSAGE as ASCII text of 99 bytes with its code,
SIG_PART over the exact content of key 2, and SEAL_SUBJECT.
EvaluateSecurityIn checks a signature of alg 1 with the strict profile in
the context of a capsule: F4, or F3 with a key the person saved; F2 when
it does not verify; F1 without context, as in v0.10, and for alg 2, not
yet implemented. Verdicts carries the key and the label for the texts.
Not wired yet: the writer does not sign and still writes the area of 512
bytes, and the reader still calls EvaluateSecurity without context. The
handoff of docs lists what is left.
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
6 days ago
|
|
|
package capsule
|
|
|
|
|
|
|
|
|
|
import (
|
|
|
|
|
"crypto/sha256"
|
|
|
|
|
"encoding/binary"
|
|
|
|
|
"encoding/hex"
|
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>
6 days ago
|
|
|
"errors"
|
Signature plan, steps 3 and 4 begun: what is signed and the verdicts of alg 1
capsule/signature.go has what an author signs and a seal seals, as the
spec v0.11 draft defines it (29.8, 29.11): payload_commit, control_commit
over CONTROL_SIG, without I_PAYLOAD and with L at zero, head_digest,
signers_digest, AUTHOR_MESSAGE as ASCII text of 99 bytes with its code,
SIG_PART over the exact content of key 2, and SEAL_SUBJECT.
EvaluateSecurityIn checks a signature of alg 1 with the strict profile in
the context of a capsule: F4, or F3 with a key the person saved; F2 when
it does not verify; F1 without context, as in v0.10, and for alg 2, not
yet implemented. Verdicts carries the key and the label for the texts.
Not wired yet: the writer does not sign and still writes the area of 512
bytes, and the reader still calls EvaluateSecurity without context. The
handoff of docs lists what is left.
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
6 days ago
|
|
|
"time"
|
|
|
|
|
|
|
|
|
|
"g.activething.com/go/DateKeys/codec/bech32"
|
|
|
|
|
"g.activething.com/go/DateKeys/internal/ed25519strict"
|
|
|
|
|
)
|
|
|
|
|
|
|
|
|
|
// The domain prefixes of what an author signs and a seal seals (spec v0.11,
|
|
|
|
|
// §29.8, §29.11). Each is followed by a byte 0x00, except in AUTHOR_MESSAGE,
|
|
|
|
|
// where a line feed follows it.
|
|
|
|
|
const (
|
|
|
|
|
payloadCommitPrefix = "datekeys:dkc3:payload:v1"
|
|
|
|
|
controlCommitPrefix = "datekeys:dkc3:control:v1"
|
|
|
|
|
headDigestPrefix = "datekeys:dkc3:head:v1"
|
|
|
|
|
signersDigestPrefix = "datekeys:dkc3:signers:v1"
|
|
|
|
|
sigPartPrefix = "datekeys:dkc3:sig-part:v1"
|
|
|
|
|
sealSubjectPrefix = "datekeys:dkc3:seal-subject:v1"
|
|
|
|
|
// AuthorMessagePrefix is the first line of AUTHOR_MESSAGE.
|
|
|
|
|
AuthorMessagePrefix = "datekeys:dkc3:author-signature:v1"
|
|
|
|
|
// AuthorMessageSize is the length of AUTHOR_MESSAGE: the prefix, a line
|
|
|
|
|
// feed, the 64 hexadecimal digits of its digest and a line feed.
|
|
|
|
|
AuthorMessageSize = len(AuthorMessagePrefix) + 1 + 64 + 1
|
|
|
|
|
)
|
|
|
|
|
|
|
|
|
|
// The values of alg that this version defines (spec v0.11, §29.3).
|
|
|
|
|
const (
|
|
|
|
|
// AlgEd25519 is a strict Ed25519 signature with a key of one's own (§29.9).
|
|
|
|
|
AlgEd25519 = 1
|
|
|
|
|
// AlgCMS is a CMS signature with X.509 certificates (§29.10).
|
|
|
|
|
AlgCMS = 2
|
|
|
|
|
)
|
|
|
|
|
|
|
|
|
|
// AuthorKey signs AUTHOR_MESSAGE with alg 1, as *authorkey.Key does.
|
|
|
|
|
type AuthorKey interface {
|
|
|
|
|
// Public returns the public key A, 32 bytes.
|
|
|
|
|
Public() []byte
|
|
|
|
|
// Sign returns the Ed25519 signature of message, 64 bytes.
|
|
|
|
|
Sign(message []byte) []byte
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
// CMSSigner makes the signature of alg 2 (spec v0.11, §29.10). EncryptFiles
|
|
|
|
|
// calls Sign with AUTHOR_MESSAGE once the capsule is prepared, and waits: the
|
|
|
|
|
// person signs it outside, with AutoFirma or another application, and Sign
|
|
|
|
|
// returns the DER of the CMS signature that she got, with its seals.
|
|
|
|
|
type CMSSigner interface {
|
|
|
|
|
// Signers returns the SHA-256 of the certificate of each required
|
|
|
|
|
// signer, from 1 to 16.
|
|
|
|
|
Signers() [][32]byte
|
|
|
|
|
// Sign returns the detached CMS signature of message, in DER.
|
|
|
|
|
Sign(message []byte) ([]byte, error)
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
// Sealer asks an authority for an RFC 3161 time-stamp token (spec §29.11).
|
|
|
|
|
type Sealer interface {
|
|
|
|
|
// Seal returns the DER of the token over SHA-256(subject), where subject
|
|
|
|
|
// is SEAL_SUBJECT; the imprint SHA-256 of the 32 bytes is the one that
|
|
|
|
|
// the token must hold.
|
|
|
|
|
Seal(subject [32]byte) ([]byte, error)
|
|
|
|
|
}
|
|
|
|
|
|
Signature plan, steps 3 and 4 begun: what is signed and the verdicts of alg 1
capsule/signature.go has what an author signs and a seal seals, as the
spec v0.11 draft defines it (29.8, 29.11): payload_commit, control_commit
over CONTROL_SIG, without I_PAYLOAD and with L at zero, head_digest,
signers_digest, AUTHOR_MESSAGE as ASCII text of 99 bytes with its code,
SIG_PART over the exact content of key 2, and SEAL_SUBJECT.
EvaluateSecurityIn checks a signature of alg 1 with the strict profile in
the context of a capsule: F4, or F3 with a key the person saved; F2 when
it does not verify; F1 without context, as in v0.10, and for alg 2, not
yet implemented. Verdicts carries the key and the label for the texts.
Not wired yet: the writer does not sign and still writes the area of 512
bytes, and the reader still calls EvaluateSecurity without context. The
handoff of docs lists what is left.
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
6 days ago
|
|
|
func domainHash(prefix string, parts ...[]byte) [32]byte {
|
|
|
|
|
h := sha256.New()
|
|
|
|
|
h.Write([]byte(prefix))
|
|
|
|
|
h.Write([]byte{0})
|
|
|
|
|
for _, p := range parts {
|
|
|
|
|
h.Write(p)
|
|
|
|
|
}
|
|
|
|
|
var out [32]byte
|
|
|
|
|
h.Sum(out[:0])
|
|
|
|
|
return out
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
// PayloadCommit is the commitment to I_PAYLOAD that replaces it in what is
|
|
|
|
|
// signed (spec §29.8).
|
|
|
|
|
func PayloadCommit(identity [32]byte) [32]byte {
|
|
|
|
|
return domainHash(payloadCommitPrefix, identity[:])
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
// ControlCommit is control_commit: the hash of CONTROL_SIG, the control of a
|
|
|
|
|
// capsule of format f with payload_commit in place of I_PAYLOAD and the eight
|
|
|
|
|
// bytes of L at zero (spec §29.8). It does not depend on L, so the area can
|
|
|
|
|
// grow after signing.
|
|
|
|
|
func ControlCommit(c *Control, f Format) ([32]byte, error) {
|
|
|
|
|
sig := *c
|
|
|
|
|
sig.PayloadIdentity = PayloadCommit(c.PayloadIdentity)
|
|
|
|
|
sig.PayloadLength = 0
|
|
|
|
|
b, err := EncodeControl(&sig, f)
|
|
|
|
|
if err != nil {
|
|
|
|
|
return [32]byte{}, err
|
|
|
|
|
}
|
|
|
|
|
return domainHash(controlCommitPrefix, b), nil
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
// HeadDigest is head_digest, the hash of HEAD_CBOR (spec §29.8). The salt of
|
|
|
|
|
// the head makes it a commitment that hides the files.
|
|
|
|
|
func HeadDigest(head []byte) [32]byte { return domainHash(headDigestPrefix, head) }
|
|
|
|
|
|
|
|
|
|
// SignersDigest is signers_digest for alg and the exact content of key 1 of a
|
|
|
|
|
// signature of alg 2, signers; nil with alg 1 (spec §29.8).
|
|
|
|
|
func SignersDigest(alg uint32, signers []byte) [32]byte {
|
|
|
|
|
return domainHash(signersDigestPrefix, binary.BigEndian.AppendUint32(nil, alg), signers)
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
// AuthorMessage is AUTHOR_MESSAGE, the ASCII text that an author signs:
|
|
|
|
|
// AuthorMessagePrefix, a line feed, the hexadecimal digest D of the three
|
|
|
|
|
// commitments and a line feed (spec §29.8).
|
|
|
|
|
func AuthorMessage(controlCommit, headDigest, signersDigest [32]byte) []byte {
|
|
|
|
|
d := sha256.Sum256(append(append(controlCommit[:], headDigest[:]...), signersDigest[:]...))
|
|
|
|
|
m := make([]byte, 0, AuthorMessageSize)
|
|
|
|
|
m = append(m, AuthorMessagePrefix...)
|
|
|
|
|
m = append(m, '\n')
|
|
|
|
|
m = hex.AppendEncode(m, d[:])
|
|
|
|
|
return append(m, '\n')
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
// AuthorCode is the code of AUTHOR_MESSAGE that a person compares before
|
|
|
|
|
// signing: the first 8 hexadecimal digits of its digest, in two groups of 4.
|
|
|
|
|
func AuthorCode(message []byte) string {
|
|
|
|
|
if len(message) != AuthorMessageSize {
|
|
|
|
|
return ""
|
|
|
|
|
}
|
|
|
|
|
d := message[len(AuthorMessagePrefix)+1:]
|
|
|
|
|
return string(d[:4]) + "-" + string(d[4:8])
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
// SigPart is SIG_PART: 0x00 without key 2, and 0x01 and the hash of the exact
|
|
|
|
|
// content of key 2 otherwise, whatever its alg and its verdict (spec §29.11).
|
|
|
|
|
func SigPart(signature []byte) []byte {
|
|
|
|
|
if signature == nil {
|
|
|
|
|
return []byte{0}
|
|
|
|
|
}
|
|
|
|
|
h := domainHash(sigPartPrefix, signature)
|
|
|
|
|
return append([]byte{1}, h[:]...)
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
// SealSubject is SEAL_SUBJECT, what a seal of seal_type 2 seals (spec §29.11).
|
|
|
|
|
func SealSubject(controlCommit, headDigest [32]byte, sigPart []byte) [32]byte {
|
|
|
|
|
return domainHash(sealSubjectPrefix, controlCommit[:], headDigest[:], sigPart)
|
|
|
|
|
}
|
|
|
|
|
|
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>
6 days ago
|
|
|
// 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
|
|
|
|
|
}
|
|
|
|
|
|
Signature plan, steps 3 and 4 begun: what is signed and the verdicts of alg 1
capsule/signature.go has what an author signs and a seal seals, as the
spec v0.11 draft defines it (29.8, 29.11): payload_commit, control_commit
over CONTROL_SIG, without I_PAYLOAD and with L at zero, head_digest,
signers_digest, AUTHOR_MESSAGE as ASCII text of 99 bytes with its code,
SIG_PART over the exact content of key 2, and SEAL_SUBJECT.
EvaluateSecurityIn checks a signature of alg 1 with the strict profile in
the context of a capsule: F4, or F3 with a key the person saved; F2 when
it does not verify; F1 without context, as in v0.10, and for alg 2, not
yet implemented. Verdicts carries the key and the label for the texts.
Not wired yet: the writer does not sign and still writes the area of 512
bytes, and the reader still calls EvaluateSecurity without context. The
handoff of docs lists what is left.
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
6 days ago
|
|
|
// 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
|
|
|
|
|
// label she gave them (F3). A reader builds it at step 17.6.
|
|
|
|
|
type SecurityContext struct {
|
|
|
|
|
ControlCommit, HeadDigest [32]byte
|
|
|
|
|
RoundTime time.Time
|
|
|
|
|
AuthorKeys map[string]string
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
// EvaluateSecurityIn returns the verdicts of SECURITY_CBOR in the capsule
|
|
|
|
|
// that c describes (spec §29.7). Without a context, as EvaluateSecurity, it
|
|
|
|
|
// checks only the structure: any signature is F1, as in v0.10. It never
|
|
|
|
|
// fails: security never decides the opening.
|
Review fixes: the CMS reader, the area after the signature, and the issuer on screen
The TSTInfo is read field by field in DER, with accuracy from zero and
millis and micros from 1 to 999, genTime in UTC with Z, no default written
and nothing after the last field. The ContentInfo and the SignerInfo must be
SEQUENCEs, a SignerInfo version must match its sid, an attribute needs a
value and is counted by attribute and not by value, a signing-certificate
beside the v2 decides nothing, PSS parameters come in order without the
trailer, and der.Check refuses the end of contents and the universal tags
the profile does not use.
The writer signs before L is fixed: write asks prepare for the final L, so
the area grows to 64 KiB only when what was signed does not fit and LargeArea
allows it, and nobody signs twice for it. Typed nils are nil, the exclusions
are checked before a file is read, Encrypt refuses the signing options, and
EvaluateSecurityIn gives X if a parser panics. The issuer of a certificate is
filtered like its holder.
Co-Authored-By: Claude Sonnet 5.5 <noreply@anthropic.com>
6 days ago
|
|
|
func EvaluateSecurityIn(b []byte, c *SecurityContext) (out Verdicts) {
|
Signature plan, steps 3 and 4 begun: what is signed and the verdicts of alg 1
capsule/signature.go has what an author signs and a seal seals, as the
spec v0.11 draft defines it (29.8, 29.11): payload_commit, control_commit
over CONTROL_SIG, without I_PAYLOAD and with L at zero, head_digest,
signers_digest, AUTHOR_MESSAGE as ASCII text of 99 bytes with its code,
SIG_PART over the exact content of key 2, and SEAL_SUBJECT.
EvaluateSecurityIn checks a signature of alg 1 with the strict profile in
the context of a capsule: F4, or F3 with a key the person saved; F2 when
it does not verify; F1 without context, as in v0.10, and for alg 2, not
yet implemented. Verdicts carries the key and the label for the texts.
Not wired yet: the writer does not sign and still writes the area of 512
bytes, and the reader still calls EvaluateSecurity without context. The
handoff of docs lists what is left.
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
6 days ago
|
|
|
x := Verdicts{Signature: VerdictUnreadable, Seal: VerdictUnreadable}
|
Review fixes: the CMS reader, the area after the signature, and the issuer on screen
The TSTInfo is read field by field in DER, with accuracy from zero and
millis and micros from 1 to 999, genTime in UTC with Z, no default written
and nothing after the last field. The ContentInfo and the SignerInfo must be
SEQUENCEs, a SignerInfo version must match its sid, an attribute needs a
value and is counted by attribute and not by value, a signing-certificate
beside the v2 decides nothing, PSS parameters come in order without the
trailer, and der.Check refuses the end of contents and the universal tags
the profile does not use.
The writer signs before L is fixed: write asks prepare for the final L, so
the area grows to 64 KiB only when what was signed does not fit and LargeArea
allows it, and nobody signs twice for it. Typed nils are nil, the exclusions
are checked before a file is read, Encrypt refuses the signing options, and
EvaluateSecurityIn gives X if a parser panics. The issuer of a certificate is
filtered like its holder.
Co-Authored-By: Claude Sonnet 5.5 <noreply@anthropic.com>
6 days ago
|
|
|
// Security never decides the opening (spec §29.3): whatever its parsers
|
|
|
|
|
// do with hostile input, the verdict is X and the capsule opens.
|
|
|
|
|
defer func() {
|
|
|
|
|
if recover() != nil {
|
|
|
|
|
out = x
|
|
|
|
|
}
|
|
|
|
|
}()
|
Signature plan, steps 3 and 4 begun: what is signed and the verdicts of alg 1
capsule/signature.go has what an author signs and a seal seals, as the
spec v0.11 draft defines it (29.8, 29.11): payload_commit, control_commit
over CONTROL_SIG, without I_PAYLOAD and with L at zero, head_digest,
signers_digest, AUTHOR_MESSAGE as ASCII text of 99 bytes with its code,
SIG_PART over the exact content of key 2, and SEAL_SUBJECT.
EvaluateSecurityIn checks a signature of alg 1 with the strict profile in
the context of a capsule: F4, or F3 with a key the person saved; F2 when
it does not verify; F1 without context, as in v0.10, and for alg 2, not
yet implemented. Verdicts carries the key and the label for the texts.
Not wired yet: the writer does not sign and still writes the area of 512
bytes, and the reader still calls EvaluateSecurity without context. The
handoff of docs lists what is left.
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
6 days ago
|
|
|
w, ok := decodeSecurity(b)
|
|
|
|
|
if !ok {
|
|
|
|
|
return x
|
|
|
|
|
}
|
|
|
|
|
v := Verdicts{Signature: VerdictNoSignature, Seal: VerdictNoSeal}
|
|
|
|
|
if w.signature != nil {
|
|
|
|
|
v.Signature = VerdictSignatureUnchecked
|
|
|
|
|
if c != nil {
|
|
|
|
|
evaluateSignature(&v, w, c)
|
Signature plan, steps 3 and 4 begun: what is signed and the verdicts of alg 1
capsule/signature.go has what an author signs and a seal seals, as the
spec v0.11 draft defines it (29.8, 29.11): payload_commit, control_commit
over CONTROL_SIG, without I_PAYLOAD and with L at zero, head_digest,
signers_digest, AUTHOR_MESSAGE as ASCII text of 99 bytes with its code,
SIG_PART over the exact content of key 2, and SEAL_SUBJECT.
EvaluateSecurityIn checks a signature of alg 1 with the strict profile in
the context of a capsule: F4, or F3 with a key the person saved; F2 when
it does not verify; F1 without context, as in v0.10, and for alg 2, not
yet implemented. Verdicts carries the key and the label for the texts.
Not wired yet: the writer does not sign and still writes the area of 512
bytes, and the reader still calls EvaluateSecurity without context. The
handoff of docs lists what is left.
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
6 days ago
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
if w.seal != nil {
|
|
|
|
|
s, err := decodeSeal(w.seal)
|
|
|
|
|
switch {
|
|
|
|
|
case err != nil:
|
Signature plan, steps 3 and 4 begun: what is signed and the verdicts of alg 1
capsule/signature.go has what an author signs and a seal seals, as the
spec v0.11 draft defines it (29.8, 29.11): payload_commit, control_commit
over CONTROL_SIG, without I_PAYLOAD and with L at zero, head_digest,
signers_digest, AUTHOR_MESSAGE as ASCII text of 99 bytes with its code,
SIG_PART over the exact content of key 2, and SEAL_SUBJECT.
EvaluateSecurityIn checks a signature of alg 1 with the strict profile in
the context of a capsule: F4, or F3 with a key the person saved; F2 when
it does not verify; F1 without context, as in v0.10, and for alg 2, not
yet implemented. Verdicts carries the key and the label for the texts.
Not wired yet: the writer does not sign and still writes the area of 512
bytes, and the reader still calls EvaluateSecurity without context. The
handoff of docs lists what is left.
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
6 days ago
|
|
|
v.Seal = VerdictSealUnreadable
|
|
|
|
|
case s.sealType == SealTypeRFC3161 && c != nil:
|
|
|
|
|
evaluateSeal(&v, s, w.signature, c)
|
|
|
|
|
default:
|
Signature plan, steps 3 and 4 begun: what is signed and the verdicts of alg 1
capsule/signature.go has what an author signs and a seal seals, as the
spec v0.11 draft defines it (29.8, 29.11): payload_commit, control_commit
over CONTROL_SIG, without I_PAYLOAD and with L at zero, head_digest,
signers_digest, AUTHOR_MESSAGE as ASCII text of 99 bytes with its code,
SIG_PART over the exact content of key 2, and SEAL_SUBJECT.
EvaluateSecurityIn checks a signature of alg 1 with the strict profile in
the context of a capsule: F4, or F3 with a key the person saved; F2 when
it does not verify; F1 without context, as in v0.10, and for alg 2, not
yet implemented. Verdicts carries the key and the label for the texts.
Not wired yet: the writer does not sign and still writes the area of 512
bytes, and the reader still calls EvaluateSecurity without context. The
handoff of docs lists what is left.
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
6 days ago
|
|
|
v.Seal = VerdictSealUnsupported
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
return v
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
// evaluateSignature sets the verdict of the content of key 2: F1 for content
|
|
|
|
|
// that does not decode, an alg this reader does not implement or a key or a
|
|
|
|
|
// signature of another length; F2 when the signature does not verify; F3 or
|
|
|
|
|
// F4 when it does (spec §29.7, §29.9).
|
|
|
|
|
func evaluateSignature(v *Verdicts, w *securityWire, c *SecurityContext) {
|
|
|
|
|
a, err := decodeAuthorSignature(w.signature)
|
|
|
|
|
if err != nil {
|
|
|
|
|
return
|
|
|
|
|
}
|
|
|
|
|
if a.alg == AlgCMS {
|
|
|
|
|
evaluateCMS(v, a, w.seal != nil, c)
|
|
|
|
|
return
|
|
|
|
|
}
|
|
|
|
|
if a.alg != AlgEd25519 || len(a.key) != 32 || len(a.value) != 64 {
|
Signature plan, steps 3 and 4 begun: what is signed and the verdicts of alg 1
capsule/signature.go has what an author signs and a seal seals, as the
spec v0.11 draft defines it (29.8, 29.11): payload_commit, control_commit
over CONTROL_SIG, without I_PAYLOAD and with L at zero, head_digest,
signers_digest, AUTHOR_MESSAGE as ASCII text of 99 bytes with its code,
SIG_PART over the exact content of key 2, and SEAL_SUBJECT.
EvaluateSecurityIn checks a signature of alg 1 with the strict profile in
the context of a capsule: F4, or F3 with a key the person saved; F2 when
it does not verify; F1 without context, as in v0.10, and for alg 2, not
yet implemented. Verdicts carries the key and the label for the texts.
Not wired yet: the writer does not sign and still writes the area of 512
bytes, and the reader still calls EvaluateSecurity without context. The
handoff of docs lists what is left.
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
6 days ago
|
|
|
return
|
|
|
|
|
}
|
|
|
|
|
msg := AuthorMessage(c.ControlCommit, c.HeadDigest, SignersDigest(AlgEd25519, nil))
|
|
|
|
|
if !ed25519strict.Verify(a.key, msg, a.value) {
|
|
|
|
|
v.Signature = VerdictSignatureInvalid
|
|
|
|
|
return
|
|
|
|
|
}
|
|
|
|
|
v.Signature = VerdictSignedOther
|
|
|
|
|
copy(v.AuthorKey[:], a.key)
|
|
|
|
|
if s, err := bech32.Encode("dkauthor", a.key); err == nil {
|
|
|
|
|
if label, ok := c.AuthorKeys[s]; ok {
|
|
|
|
|
v.Signature, v.AuthorLabel = VerdictSignedSaved, label
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
}
|