• Home
  • Features
  • Pricing
  • Docs
  • Announcements
  • Sign In

mendersoftware / mender-artifact / 1480279814

03 Oct 2024 12:40PM UTC coverage: 78.798%. First build
1480279814

Pull #639

gitlab-ci

lluiscampos
fix: hardware-security returned signatures support for ECDSA256.

Changelog: Fixes signature verification with ECDSA keys when the signing has been done externally with PKCS#11. Previously mender-artifact would always assume that the signature has been done with the built-in engine, which then wouldn't validate correctly. The bug affected only ECDSA key pairs.
Ticket: MEN-7523
Signed-off-by: Peter Grzybowski <peter@northern.tech>
(cherry picked from commit f47a406ca)
Pull Request #639: 3.11.x: fix: hardware-security returned signatures support for ECDSA256.

10 of 34 new or added lines in 1 file covered. (29.41%)

5716 of 7254 relevant lines covered (78.8%)

186.19 hits per line

Source File
Press 'n' to go to next uncovered line, 'b' for previous

70.46
/artifact/signer.go
1
// Copyright 2023 Northern.tech AS
2
//
3
//    Licensed under the Apache License, Version 2.0 (the "License");
4
//    you may not use this file except in compliance with the License.
5
//    You may obtain a copy of the License at
6
//
7
//        http://www.apache.org/licenses/LICENSE-2.0
8
//
9
//    Unless required by applicable law or agreed to in writing, software
10
//    distributed under the License is distributed on an "AS IS" BASIS,
11
//    WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
12
//    See the License for the specific language governing permissions and
13
//    limitations under the License.
14

15
package artifact
16

17
import (
18
        "crypto"
19
        "crypto/ecdsa"
20
        "crypto/rand"
21
        "crypto/rsa"
22
        "crypto/x509"
23
        "encoding/asn1"
24
        "encoding/base64"
25
        "encoding/pem"
26
        "math/big"
27

28
        "github.com/minio/sha256-simd"
29
        "github.com/pkg/errors"
30
)
31

32
// Signer is returning a signature of the provided message.
33
type Signer interface {
34
        Sign(message []byte) ([]byte, error)
35
}
36

37
// Verifier is verifying if provided message and signature matches.
38
type Verifier interface {
39
        Verify(message, sig []byte) error
40
}
41

42
// Crypto is an interface each specific signature algorithm must implement
43
// in order to be used with PKISigner.
44
type Crypto interface {
45
        Sign(message []byte, key interface{}) ([]byte, error)
46
        Verify(message, sig []byte, key interface{}) error
47
}
48

49
// RSA Crypto interface implementation
50
type RSA struct{}
51

52
func (r *RSA) Sign(message []byte, key interface{}) ([]byte, error) {
78✔
53
        var rsaKey *rsa.PrivateKey
78✔
54
        var ok bool
78✔
55

78✔
56
        // validate key
78✔
57
        if rsaKey, ok = key.(*rsa.PrivateKey); !ok {
80✔
58
                return nil, errors.New("signer: invalid private key")
2✔
59
        }
2✔
60

61
        h := sha256.Sum256(message)
76✔
62
        return rsa.SignPKCS1v15(rand.Reader, rsaKey, crypto.SHA256, h[:])
76✔
63
}
64

65
func (r *RSA) Verify(message, sig []byte, key interface{}) error {
42✔
66
        var rsaKey *rsa.PublicKey
42✔
67
        var ok bool
42✔
68

42✔
69
        // validate key
42✔
70
        if rsaKey, ok = key.(*rsa.PublicKey); !ok {
44✔
71
                return errors.New("signer: invalid rsa public key")
2✔
72
        }
2✔
73

74
        h := sha256.Sum256(message)
40✔
75
        return rsa.VerifyPKCS1v15(rsaKey, crypto.SHA256, h[:], sig)
40✔
76
}
77

78
// ECDSA Crypto interface implementation
79
const ecdsa256curveBits = 256
80
const ecdsa256keySize = 32
81
const ecdsa256Asn1SizeBytes = 72
82

83
type ECDSA256 struct{}
84

85
func (e *ECDSA256) Sign(message []byte, key interface{}) ([]byte, error) {
14✔
86
        var ecdsaKey *ecdsa.PrivateKey
14✔
87
        var ok bool
14✔
88

14✔
89
        // validate key
14✔
90
        if ecdsaKey, ok = key.(*ecdsa.PrivateKey); !ok {
16✔
91
                return nil, errors.New("signer: invalid private key")
2✔
92
        }
2✔
93

94
        // calculate the hash of the message to be signed
95
        h := sha256.Sum256(message)
12✔
96
        r, s, err := ecdsa.Sign(rand.Reader, ecdsaKey, h[:])
12✔
97
        if err != nil {
12✔
98
                return nil, errors.Wrap(err, "signer: error signing message")
×
99
        }
×
100
        // check if the size of the curve matches expected one;
101
        // for now we are supporting only 256 bit ecdsa
102
        if ecdsaKey.Curve.Params().BitSize != ecdsa256curveBits {
14✔
103
                return nil, errors.New("signer: invalid ecdsa curve size")
2✔
104
        }
2✔
105
        return MarshalECDSASignature(r, s)
10✔
106
}
107

108
func (e *ECDSA256) Verify(message, sig []byte, key interface{}) error {
20✔
109
        var ecdsaKey *ecdsa.PublicKey
20✔
110
        var ok bool
20✔
111

20✔
112
        // validate key
20✔
113
        if ecdsaKey, ok = key.(*ecdsa.PublicKey); !ok {
22✔
114
                return errors.New("signer: invalid ecdsa public key")
2✔
115
        }
2✔
116

117
        r, s, err := UnmarshalECDSASignature(sig)
18✔
118
        if err != nil {
22✔
119
                return err
4✔
120
        }
4✔
121

122
        h := sha256.Sum256(message)
14✔
123

14✔
124
        ok = ecdsa.Verify(ecdsaKey, h[:], r, s)
14✔
125
        if !ok {
18✔
126
                return errors.New("signer: verification failed")
4✔
127
        }
4✔
128
        return nil
10✔
129

130
}
131

132
func MarshalECDSASignature(r, s *big.Int) ([]byte, error) {
18✔
133
        // we serialize the r and s into one array where the first
18✔
134
        // half is the r and the other one s;
18✔
135
        // as both values are ecdsa256curveBits size we need
18✔
136
        // 2*ecdsa256keySize size slice to store both
18✔
137

18✔
138
        // MEN-1740 In some cases the size of the r and s can be different
18✔
139
        // than expected ecdsa256keySize. In this case we need to make sure
18✔
140
        // we are serializing those using correct offset. We can use leading
18✔
141
        // zeros easily as this has no impact on serializing and deserializing.
18✔
142
        rSize := len(r.Bytes())
18✔
143
        sSize := len(s.Bytes())
18✔
144
        if rSize > ecdsa256keySize || sSize > ecdsa256keySize {
18✔
145
                return nil,
×
146
                        errors.Errorf("signer: invalid size of ecdsa keys: r: %d; s: %d",
×
147
                                rSize, sSize)
×
148
        }
×
149

150
        // if the keys are shorter than expected we need to use correct offset
151
        // while serializing
152
        rOffset := ecdsa256keySize - rSize
18✔
153
        sOffset := ecdsa256keySize - sSize
18✔
154

18✔
155
        serialized := make([]byte, 2*ecdsa256keySize)
18✔
156
        copy(serialized[rOffset:], r.Bytes())
18✔
157
        copy(serialized[ecdsa256keySize+sOffset:], s.Bytes())
18✔
158

18✔
159
        return serialized, nil
18✔
160
}
161

162
func UnmarshalECDSASignature(sig []byte) (r, s *big.Int, e error) {
18✔
163
        // if the length of the signature is the double of key size, we know (and assume)
18✔
164
        // that it was us who created it via MarshalECDSASignature.
18✔
165
        // After the introduction of the hardware security support there is another possibility
18✔
166
        // (see below)
18✔
167
        if len(sig) == 2*ecdsa256keySize {
32✔
168
                return UnmarshalECDSASignatureMenderArtifact(sig)
14✔
169
        }
14✔
170

171
        // in case of a key supplied via PKCS#11 URI, we have no control over what the signature is
172
        // since it is designed to be actually verified via the same mechanism (PKCS#11 URI).
173
        // We know here that it is ECDSA key, and judging form the size we can assume
174
        // that it is ASN.1 encoded. If so, then it should be 72 bytes, the less or equal is here
175
        // to support keys of size 71 bytes (strangely seen in the wild). In other words:
176
        // if the signature has not been created with MarshalECDSASignature, then we assume
177
        // it is to be decoded via ASN.1, with the protection on the signature length.
178
        if len(sig) == ecdsa256Asn1SizeBytes ||
4✔
179
                len(sig) == ecdsa256Asn1SizeBytes-1 {
4✔
NEW
180
                return UnmarshalECDSASignatureASN1(sig)
×
NEW
181
        }
×
182

183
        return nil, nil, errors.Errorf("signer: invalid ecdsa key size: %d", len(sig))
4✔
184
}
185

NEW
186
func UnmarshalECDSASignatureASN1(sig []byte) (r *big.Int, s *big.Int, err error) {
×
NEW
187
        var value asn1.RawValue
×
NEW
188
        _, err = asn1.Unmarshal(sig, &value)
×
NEW
189
        if err != nil {
×
NEW
190
                return nil, nil, errors.Wrap(err, "cannot unmarshal asn1 data")
×
NEW
191
        }
×
NEW
192
        bytesValue := value.Bytes
×
NEW
193
        if len(bytesValue) > 0 {
×
NEW
194
                var v asn1.RawValue
×
NEW
195
                bytesValue, err = asn1.Unmarshal(bytesValue, &v)
×
NEW
196
                if err != nil {
×
NEW
197
                        return nil, nil, errors.Wrap(err, "cannot unmarshal asn1 r value")
×
NEW
198
                }
×
NEW
199
                r = big.NewInt(0).SetBytes(v.Bytes)
×
200
        }
NEW
201
        if len(bytesValue) > 0 {
×
NEW
202
                var v asn1.RawValue
×
NEW
203
                _, err = asn1.Unmarshal(bytesValue, &v)
×
NEW
204
                if err != nil {
×
NEW
205
                        return nil, nil, errors.Wrap(err, "cannot unmarshal asn1 s value")
×
NEW
206
                }
×
NEW
207
                s = big.NewInt(0).SetBytes(v.Bytes)
×
208
        }
NEW
209
        return r, s, nil
×
210
}
211

212
func UnmarshalECDSASignatureMenderArtifact(sig []byte) (r *big.Int, s *big.Int, err error) {
14✔
213
        // get the signature; see corresponding `Sign` function for more details
14✔
214
        // about serialization
14✔
215
        r = big.NewInt(0).SetBytes(sig[:ecdsa256keySize])
14✔
216
        s = big.NewInt(0).SetBytes(sig[ecdsa256keySize:])
14✔
217
        return r, s, nil
14✔
218
}
14✔
219

220
type SigningMethod struct {
221
        // Key can be private or public depending if we want to sign or verify message
222
        Key    interface{}
223
        Public []byte
224
        Method Crypto
225
}
226

227
// PKISigner implements public-key encryption and supports X.509-encodded keys.
228
// For now both RSA and 256 bits ECDSA are supported.
229
type PKISigner struct {
230
        signMethod, verifyMethod *SigningMethod
231
}
232

233
func NewPKISigner(privateKey []byte) (*PKISigner, error) {
84✔
234
        if len(privateKey) == 0 {
84✔
235
                return nil, errors.New("signer: missing key")
×
236
        }
×
237
        signMethod, err := GetKeyAndSignMethod(privateKey)
84✔
238
        if err != nil {
84✔
239
                return nil, err
×
240
        }
×
241
        pub, err := x509.ParsePKIXPublicKey(signMethod.Public)
84✔
242
        if err != nil {
84✔
243
                return nil, errors.Wrap(err, "failed to parse encoded public key")
×
244
        }
×
245
        return &PKISigner{
84✔
246
                signMethod: signMethod,
84✔
247
                verifyMethod: &SigningMethod{
84✔
248
                        Key:    pub,
84✔
249
                        Method: signMethod.Method,
84✔
250
                },
84✔
251
        }, nil
84✔
252
}
253

254
func NewPKIVerifier(publicKey []byte) (*PKISigner, error) {
50✔
255
        if len(publicKey) == 0 {
52✔
256
                return nil, errors.New("signer: missing key")
2✔
257
        }
2✔
258
        verifyMethod, err := GetKeyAndVerifyMethod(publicKey)
48✔
259
        if err != nil {
54✔
260
                return nil, err
6✔
261
        }
6✔
262
        return &PKISigner{
42✔
263
                verifyMethod: verifyMethod,
42✔
264
        }, nil
42✔
265
}
266

267
func (s *PKISigner) Sign(message []byte) ([]byte, error) {
86✔
268
        if s.signMethod == nil {
86✔
269
                return nil, errors.New("signer: only verification allowed with this signer")
×
270
        }
×
271
        sig, err := s.signMethod.Method.Sign(message, s.signMethod.Key)
86✔
272
        if err != nil {
86✔
273
                return nil, errors.Wrap(err, "signer: error signing image")
×
274
        }
×
275
        enc := make([]byte, base64.StdEncoding.EncodedLen(len(sig)))
86✔
276
        base64.StdEncoding.Encode(enc, sig)
86✔
277
        return enc, nil
86✔
278
}
279

280
func (s *PKISigner) Verify(message, sig []byte) error {
44✔
281
        dec := make([]byte, base64.StdEncoding.DecodedLen(len(sig)))
44✔
282
        decLen, err := base64.StdEncoding.Decode(dec, sig)
44✔
283
        if err != nil {
44✔
284
                return errors.Wrap(err, "signer: error decoding signature")
×
285
        }
×
286

287
        if s.verifyMethod == nil {
44✔
288
                return errors.New("verifyMethod is nil")
×
289
        }
×
290
        if s.verifyMethod.Method == nil {
44✔
291
                return errors.New("verifyMethod.Method is nil")
×
292
        }
×
293

294
        return s.verifyMethod.Method.Verify(message, dec[:decLen], s.verifyMethod.Key)
44✔
295
}
296

297
func GetPublic(private []byte) ([]byte, error) {
×
298
        sm, err := GetKeyAndSignMethod(private)
×
299
        if err != nil {
×
300
                return nil, errors.Wrap(err, "signer: error parsing private key")
×
301
        }
×
302
        return sm.Public, nil
×
303
}
304

305
func GetKeyAndVerifyMethod(keyPEM []byte) (*SigningMethod, error) {
86✔
306
        block, _ := pem.Decode(keyPEM)
86✔
307
        if block == nil {
92✔
308
                return nil, errors.New("signer: failed to parse public key")
6✔
309
        }
6✔
310

311
        pub, err := x509.ParsePKIXPublicKey(block.Bytes)
80✔
312
        if err != nil {
84✔
313
                return nil, errors.Wrap(err, "failed to parse encoded public key")
4✔
314
        }
4✔
315
        switch pub := pub.(type) {
76✔
316
        case *rsa.PublicKey:
50✔
317
                return &SigningMethod{Key: pub, Method: new(RSA)}, nil
50✔
318
        case *ecdsa.PublicKey:
24✔
319
                return &SigningMethod{Key: pub, Method: new(ECDSA256)}, nil
24✔
320
        default:
2✔
321
                return nil, errors.Errorf("unsupported public key type: %v", pub)
2✔
322
        }
323
}
324

325
func GetKeyAndSignMethod(keyPEM []byte) (*SigningMethod, error) {
114✔
326
        block, _ := pem.Decode(keyPEM)
114✔
327
        if block == nil {
116✔
328
                return nil, errors.New("signer: failed to parse private key")
2✔
329
        }
2✔
330

331
        rsaKey, err := x509.ParsePKCS1PrivateKey(block.Bytes)
112✔
332
        if err == nil {
200✔
333
                pub, keyErr := x509.MarshalPKIXPublicKey(rsaKey.Public())
88✔
334
                if keyErr != nil {
88✔
335
                        return nil, errors.Wrap(err, "signer: can not extract public RSA key")
×
336
                }
×
337
                return &SigningMethod{Key: rsaKey, Public: pub, Method: new(RSA)}, nil
88✔
338
        }
339

340
        ecdsaKey, err := x509.ParseECPrivateKey(block.Bytes)
24✔
341
        if err == nil {
46✔
342
                pub, keyErr := x509.MarshalPKIXPublicKey(ecdsaKey.Public())
22✔
343
                if keyErr != nil {
22✔
344
                        return nil, errors.Wrap(err, "signer: can not extract public ECDSA key")
×
345
                }
×
346
                return &SigningMethod{Key: ecdsaKey, Public: pub, Method: new(ECDSA256)}, nil
22✔
347
        }
348

349
        key, err := x509.ParsePKCS8PrivateKey(block.Bytes)
2✔
350
        if ecdsaKey, ok := key.(*ecdsa.PrivateKey); ok {
2✔
351
                if err == nil {
×
352
                        pub, keyErr := x509.MarshalPKIXPublicKey(ecdsaKey.Public())
×
353
                        if keyErr != nil {
×
354
                                return nil, errors.Wrap(err, "signer: can not extract public ECDSA key")
×
355
                        }
×
356
                        return &SigningMethod{Key: ecdsaKey, Public: pub, Method: new(ECDSA256)}, nil
×
357
                }
358
        }
359

360
        if rsaKey, ok := key.(*rsa.PrivateKey); ok {
2✔
361
                if err == nil {
×
362
                        pub, keyErr := x509.MarshalPKIXPublicKey(rsaKey.Public())
×
363
                        if keyErr != nil {
×
364
                                return nil, errors.Wrap(err, "signer: can not extract public RSA key")
×
365
                        }
×
366
                        return &SigningMethod{Key: rsaKey, Public: pub, Method: new(RSA)}, nil
×
367
                }
368
        }
369

370
        if err == nil {
2✔
371
                err = errors.New("unsupported private key type")
×
372
        }
×
373

374
        return nil, errors.Wrap(err, "signer: unsupported private key type or error occurred")
2✔
375
}
STATUS · Troubleshooting · Open an Issue · Sales · Support · CAREERS · ENTERPRISE · START FREE · SCHEDULE DEMO
ANNOUNCEMENTS · TWITTER · TOS & SLA · Supported CI Services · What's a CI service? · Automated Testing

© 2025 Coveralls, Inc