1 package cosesign1
2
3 import (
4 "crypto/x509"
5 "fmt"
6
7 didx509resolver "github.com/Microsoft/hcsshim/internal/did-x509-resolver"
8
9 "github.com/sirupsen/logrus"
10
11 "github.com/veraison/go-cose"
12 )
13
14
15
16
17
18 func getStringValue(m cose.ProtectedHeader, k any) string {
19 val, ok := m[k]
20 if !ok {
21 return ""
22 }
23 str, ok := val.(string)
24 if !ok {
25 return ""
26 }
27 return str
28 }
29
30
31
32
33
34
35 func isDERChain(val interface{}) bool {
36 valArray, ok := val.([]interface{})
37 if !ok {
38 return false
39 }
40
41 for _, element := range valArray {
42 _, ok = element.([]byte)
43 if !ok {
44 return false
45 }
46 }
47
48 return len(valArray) > 0
49 }
50
51
52 func isDEROnly(val interface{}) bool {
53 _, ok := val.([]byte)
54 return ok
55 }
56
57 type UnpackedCoseSign1 struct {
58 Issuer string
59 Feed string
60 ContentType string
61 Pubkey string
62 Pubcert string
63 ChainPem string
64 Payload []byte
65 CertChain []*x509.Certificate
66 }
67
68
69
70
71
72
73
74
75
76
77 func UnpackAndValidateCOSE1CertChain(raw []byte) (*UnpackedCoseSign1, error) {
78 var msg cose.Sign1Message
79 err := msg.UnmarshalCBOR(raw)
80 if err != nil {
81 return nil, err
82 }
83
84 protected := msg.Headers.Protected
85 val, ok := protected[cose.HeaderLabelAlgorithm]
86 if !ok {
87 return nil, fmt.Errorf("algorithm missing")
88 }
89
90 algo, ok := val.(cose.Algorithm)
91 if !ok {
92 return nil, fmt.Errorf("algorithm wrong type")
93 }
94
95 logrus.Debugf("COSE Sign1 unpack: algorithm %d", algo)
96
97
98 chainDER, ok := protected[cose.HeaderLabelX5Chain]
99
100 if !ok {
101 return nil, fmt.Errorf("x5Chain missing")
102 }
103
104 var chainIA []interface{}
105
106
107 if isDERChain(chainDER) {
108 chainIA = chainDER.([]interface{})
109 } else if isDEROnly(chainDER) {
110 chainIA = append(chainIA, chainDER)
111 } else {
112 return nil, fmt.Errorf("x5Chain wrong type")
113 }
114
115 var chain []*x509.Certificate
116
117 for index, element := range chainIA {
118 cert, err := x509.ParseCertificate(element.([]byte))
119 if err == nil {
120 chain = append(chain, cert)
121 } else {
122 logrus.Debugf("Parse certificate failed on %d: %s", index, err.Error())
123 return nil, err
124 }
125 }
126
127
128
129 chainLen := len(chain)
130 if chainLen > 100 || chainLen < 1 {
131 return nil, fmt.Errorf("unreasonable number of certs (%d) in COSE_Sign1 document", chainLen)
132 }
133
134 var leafCert = chain[0]
135 var leafCertBase64 = x509ToBase64(leafCert)
136 var leafPubKey = leafCert.PublicKey
137 var leafPubKeyBase64 = keyToBase64(leafPubKey)
138
139 var chainPEM string
140 for i, c := range chain {
141 if i > 0 {
142 chainPEM += "\n"
143 }
144 chainPEM += convertx509ToPEM(c)
145 }
146
147 logrus.Debugln("Certificate chain:")
148 logrus.Debugln(chainPEM)
149
150
151
152
153
154
155 if len(chain) > 1 {
156
157
158 _, err = didx509resolver.VerifyCertificateChain(chain, chain[len(chain)-1:], false)
159
160 if err != nil {
161 return nil, fmt.Errorf("certificate chain verification failed - %w", err)
162 }
163 }
164
165
166
167
168 verifier, err := cose.NewVerifier(algo, leafPubKey)
169 if err != nil {
170 logrus.Debugf("cose.NewVerifier failed (algo %d): %s", algo, err.Error())
171 return nil, err
172 }
173
174 err = msg.Verify(nil, verifier)
175 if err != nil {
176 logrus.Debugf("msg.Verify failed: algo = %d err = %s", algo, err.Error())
177 return nil, err
178 }
179
180 issuer := getStringValue(protected, "iss")
181 feed := getStringValue(protected, "feed")
182 contenttype := getStringValue(protected, cose.HeaderLabelContentType)
183
184 return &UnpackedCoseSign1{
185 Pubcert: leafCertBase64,
186 Feed: feed,
187 Issuer: issuer,
188 Pubkey: leafPubKeyBase64,
189 ChainPem: chainPEM,
190 ContentType: contenttype,
191 Payload: msg.Payload,
192 CertChain: chain,
193 }, nil
194 }
195
View as plain text