You can not select more than 25 topics
Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.
153 lines
4.7 KiB
153 lines
4.7 KiB
package cmstest
|
|
|
|
import (
|
|
"bytes"
|
|
"slices"
|
|
|
|
"g.activething.com/go/DateKeys/internal/der"
|
|
)
|
|
|
|
// The edits of the DER of a signature, a token or a certificate: what an
|
|
// attacker, or a signing application of another country, does to the bytes
|
|
// after they are signed. A path lists the index of a child at each level,
|
|
// from the element given: in a signature or a token, 1, 0 is the SignedData.
|
|
|
|
// Children returns the encodings of the children of the constructed element b.
|
|
func Children(b []byte) [][]byte {
|
|
_, kids, err := der.Split(b)
|
|
if err != nil {
|
|
panic(err)
|
|
}
|
|
return kids
|
|
}
|
|
|
|
// At returns the element at path in b.
|
|
func At(b []byte, path ...int) []byte {
|
|
for _, i := range path {
|
|
b = Children(b)[i]
|
|
}
|
|
return b
|
|
}
|
|
|
|
// Edit returns b with the element at path replaced by what f returns for it,
|
|
// and the lengths of its ancestors written again. f may return nil, to remove
|
|
// the element, or several elements one after the other.
|
|
func Edit(b []byte, f func(old []byte) []byte, path ...int) []byte {
|
|
if len(path) == 0 {
|
|
return f(b)
|
|
}
|
|
kids := slices.Clone(Children(b))
|
|
kids[path[0]] = Edit(kids[path[0]], f, path[1:]...)
|
|
return tlv(b[0], kids...)
|
|
}
|
|
|
|
// Retag returns an edit that changes the identifier octet of an element.
|
|
func Retag(tag byte) func([]byte) []byte {
|
|
return func(b []byte) []byte { return append([]byte{tag}, b[1:]...) }
|
|
}
|
|
|
|
// Replace returns an edit that puts elems in the place of an element.
|
|
func Replace(elems ...[]byte) func([]byte) []byte {
|
|
return func([]byte) []byte { return bytes.Join(elems, nil) }
|
|
}
|
|
|
|
// Append returns an edit that adds elems at the end of the children of a
|
|
// constructed element.
|
|
func Append(elems ...[]byte) func([]byte) []byte {
|
|
return func(b []byte) []byte { return tlv(b[0], append(slices.Clone(Children(b)), elems...)...) }
|
|
}
|
|
|
|
// Indefinite returns the constructed element b with an indefinite length:
|
|
// BER, which DER forbids (X.690 10.1).
|
|
func Indefinite(b []byte) []byte {
|
|
c, err := der.Content(b)
|
|
if err != nil {
|
|
panic(err)
|
|
}
|
|
out := append([]byte{b[0], 0x80}, c...)
|
|
return append(out, 0, 0)
|
|
}
|
|
|
|
// SignedDataPath is the path of the SignedData in a signature or a token.
|
|
var SignedDataPath = []int{1, 0}
|
|
|
|
// SignerInfosPath returns the path of the signerInfos of the signature or the
|
|
// token b: the last field of its SignedData.
|
|
func SignerInfosPath(b []byte) []int {
|
|
return append(slices.Clone(SignedDataPath), len(Children(At(b, SignedDataPath...)))-1)
|
|
}
|
|
|
|
// sid returns the two forms of the SignerIdentifier of s.
|
|
func sid(s Signer) (issuerAndSerial, ski []byte) {
|
|
return Seq(s.Cert.RawIssuer, s.Cert.Serial), tlv(0x80, s.Cert.SubjectKeyId)
|
|
}
|
|
|
|
// editSigner applies f to the SignerInfo of s in the signature b, and removes
|
|
// it when f returns nil.
|
|
func editSigner(b []byte, s Signer, f func(info []byte) []byte) []byte {
|
|
ias, ski := sid(s)
|
|
return Edit(b, func(set []byte) []byte {
|
|
var out [][]byte
|
|
for _, info := range Children(set) {
|
|
if id := Children(info)[1]; bytes.Equal(id, ias) || s.Cert.SubjectKeyId != nil && bytes.Equal(id, ski) {
|
|
if info = f(info); info == nil {
|
|
continue
|
|
}
|
|
}
|
|
out = append(out, info)
|
|
}
|
|
return Set(0x31, out...)
|
|
}, SignerInfosPath(b)...)
|
|
}
|
|
|
|
// Withdraw returns the signature b without the SignerInfo of s: a signature
|
|
// withdrawn. Its certificate stays.
|
|
func Withdraw(b []byte, s Signer) []byte {
|
|
return editSigner(b, s, func([]byte) []byte { return nil })
|
|
}
|
|
|
|
// WithoutTimeStamp returns the signature b with the unsigned attributes of
|
|
// the SignerInfo of s removed: its CAdES-T withdrawn. What the SignerInfo
|
|
// signs does not change.
|
|
func WithoutTimeStamp(b []byte, s Signer) []byte {
|
|
return editSigner(b, s, func(info []byte) []byte {
|
|
f := Children(info)
|
|
if n := len(f); n > 0 && f[n-1][0] == 0xa1 {
|
|
f = f[:n-1]
|
|
}
|
|
return Seq(f...)
|
|
})
|
|
}
|
|
|
|
// Merge returns the co-signature that joins the signatures sigs of one
|
|
// message, as a signing application adds a SignerInfo to a signature: the
|
|
// digest algorithms and the certificates of all, each once, and their
|
|
// SignerInfo, each in DER order.
|
|
func Merge(sigs ...[]byte) []byte {
|
|
var algs, certs, infos [][]byte
|
|
var version, encap []byte
|
|
for _, s := range sigs {
|
|
f := Children(At(s, SignedDataPath...))
|
|
version, encap = f[0], f[2]
|
|
algs = append(algs, Children(f[1])...)
|
|
for _, x := range f[3:] {
|
|
switch x[0] {
|
|
case 0xa0:
|
|
certs = append(certs, Children(x)...)
|
|
case 0x31:
|
|
infos = append(infos, Children(x)...)
|
|
}
|
|
}
|
|
}
|
|
once := func(e [][]byte) [][]byte {
|
|
slices.SortFunc(e, bytes.Compare)
|
|
return slices.CompactFunc(e, bytes.Equal)
|
|
}
|
|
fields := [][]byte{version, Set(0x31, once(algs)...), encap}
|
|
if len(certs) > 0 {
|
|
fields = append(fields, Set(0xa0, once(certs)...))
|
|
}
|
|
fields = append(fields, Set(0x31, infos...))
|
|
return Seq(OID(OIDSignedData), tlv(0xa0, Seq(fields...)))
|
|
}
|