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

mendersoftware / mender-artifact / 1279561887

06 May 2024 07:18AM UTC coverage: 73.657% (-3.8%) from 77.428%
1279561887

Pull #605

gitlab-ci

alfrunes
chore(make): Refactor `coverage` target and disable pkcs11 on non-linux

Signed-off-by: Alf-Rune Siqveland <alf.rune@northern.tech>
Pull Request #605: Allow using mender-artifact without `cgo`

4 of 6 new or added lines in 1 file covered. (66.67%)

271 existing lines in 13 files now uncovered.

5469 of 7425 relevant lines covered (73.66%)

77.7 hits per line

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

76.35
/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/base64"
24
        "encoding/pem"
25
        "math/big"
26

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

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

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

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

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

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

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

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

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

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

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

77
// ECDSA Crypto interface implementation
78
const ecdsa256curveBits = 256
79
const ecdsa256keySize = 32
80

81
type ECDSA256 struct{}
82

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

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

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

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

10✔
110
        // validate key
10✔
111
        if ecdsaKey, ok = key.(*ecdsa.PublicKey); !ok {
12✔
112
                return errors.New("signer: invalid ecdsa public key")
2✔
113
        }
2✔
114

115
        r, s, err := UnmarshalECDSASignature(sig)
8✔
116
        if err != nil {
10✔
117
                return err
2✔
118
        }
2✔
119

120
        h := sha256.Sum256(message)
6✔
121

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

128
}
129

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

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

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

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

4✔
157
        return serialized, nil
4✔
158
}
159

160
func UnmarshalECDSASignature(sig []byte) (r, s *big.Int, e error) {
8✔
161
        // check if the size of the key matches provided one
8✔
162
        if len(sig) != 2*ecdsa256keySize {
10✔
163
                return nil, nil, errors.Errorf("signer: invalid ecdsa key size: %d", len(sig))
2✔
164
        }
2✔
165

166
        // get the signature; see corresponding `Sign` function for more details
167
        // about serialization
168
        r = big.NewInt(0).SetBytes(sig[:ecdsa256keySize])
6✔
169
        s = big.NewInt(0).SetBytes(sig[ecdsa256keySize:])
6✔
170
        return r, s, nil
6✔
171
}
172

173
type SigningMethod struct {
174
        // Key can be private or public depending if we want to sign or verify message
175
        Key    interface{}
176
        Public []byte
177
        Method Crypto
178
}
179

180
// PKISigner implements public-key encryption and supports X.509-encodded keys.
181
// For now both RSA and 256 bits ECDSA are supported.
182
type PKISigner struct {
183
        signMethod, verifyMethod *SigningMethod
184
}
185

186
func NewPKISigner(privateKey []byte) (*PKISigner, error) {
4✔
187
        if len(privateKey) == 0 {
4✔
188
                return nil, errors.New("signer: missing key")
×
189
        }
×
190
        signMethod, err := GetKeyAndSignMethod(privateKey)
4✔
191
        if err != nil {
4✔
192
                return nil, err
×
193
        }
×
194
        pub, err := x509.ParsePKIXPublicKey(signMethod.Public)
4✔
195
        if err != nil {
4✔
196
                return nil, errors.Wrap(err, "failed to parse encoded public key")
×
197
        }
×
198
        return &PKISigner{
4✔
199
                signMethod: signMethod,
4✔
200
                verifyMethod: &SigningMethod{
4✔
201
                        Key:    pub,
4✔
202
                        Method: signMethod.Method,
4✔
203
                },
4✔
204
        }, nil
4✔
205
}
206

207
func NewPKIVerifier(publicKey []byte) (*PKISigner, error) {
13✔
208
        if len(publicKey) == 0 {
13✔
UNCOV
209
                return nil, errors.New("signer: missing key")
×
UNCOV
210
        }
×
211
        verifyMethod, err := GetKeyAndVerifyMethod(publicKey)
13✔
212
        if err != nil {
15✔
213
                return nil, err
2✔
214
        }
2✔
215
        return &PKISigner{
11✔
216
                verifyMethod: verifyMethod,
11✔
217
        }, nil
11✔
218
}
219

220
func (s *PKISigner) Sign(message []byte) ([]byte, error) {
6✔
221
        if s.signMethod == nil {
6✔
222
                return nil, errors.New("signer: only verification allowed with this signer")
×
223
        }
×
224
        sig, err := s.signMethod.Method.Sign(message, s.signMethod.Key)
6✔
225
        if err != nil {
6✔
226
                return nil, errors.Wrap(err, "signer: error signing image")
×
227
        }
×
228
        enc := make([]byte, base64.StdEncoding.EncodedLen(len(sig)))
6✔
229
        base64.StdEncoding.Encode(enc, sig)
6✔
230
        return enc, nil
6✔
231
}
232

233
func (s *PKISigner) Verify(message, sig []byte) error {
11✔
234
        dec := make([]byte, base64.StdEncoding.DecodedLen(len(sig)))
11✔
235
        decLen, err := base64.StdEncoding.Decode(dec, sig)
11✔
236
        if err != nil {
11✔
237
                return errors.Wrap(err, "signer: error decoding signature")
×
238
        }
×
239

240
        if s.verifyMethod == nil {
11✔
241
                return errors.New("verifyMethod is nil")
×
242
        }
×
243
        if s.verifyMethod.Method == nil {
11✔
244
                return errors.New("verifyMethod.Method is nil")
×
245
        }
×
246

247
        return s.verifyMethod.Method.Verify(message, dec[:decLen], s.verifyMethod.Key)
11✔
248
}
249

250
func GetPublic(private []byte) ([]byte, error) {
×
251
        sm, err := GetKeyAndSignMethod(private)
×
252
        if err != nil {
×
253
                return nil, errors.Wrap(err, "signer: error parsing private key")
×
254
        }
×
255
        return sm.Public, nil
×
256
}
257

258
func GetKeyAndVerifyMethod(keyPEM []byte) (*SigningMethod, error) {
25✔
259
        block, _ := pem.Decode(keyPEM)
25✔
260
        if block == nil {
29✔
261
                return nil, errors.New("signer: failed to parse public key")
4✔
262
        }
4✔
263

264
        pub, err := x509.ParsePKIXPublicKey(block.Bytes)
21✔
265
        if err != nil {
23✔
266
                return nil, errors.Wrap(err, "failed to parse encoded public key")
2✔
267
        }
2✔
268
        switch pub := pub.(type) {
19✔
269
        case *rsa.PublicKey:
7✔
270
                return &SigningMethod{Key: pub, Method: new(RSA)}, nil
7✔
271
        case *ecdsa.PublicKey:
10✔
272
                return &SigningMethod{Key: pub, Method: new(ECDSA256)}, nil
10✔
273
        default:
2✔
274
                return nil, errors.Errorf("unsupported public key type: %v", pub)
2✔
275
        }
276
}
277

278
func GetKeyAndSignMethod(keyPEM []byte) (*SigningMethod, error) {
14✔
279
        block, _ := pem.Decode(keyPEM)
14✔
280
        if block == nil {
16✔
281
                return nil, errors.New("signer: failed to parse private key")
2✔
282
        }
2✔
283

284
        rsaKey, err := x509.ParsePKCS1PrivateKey(block.Bytes)
12✔
285
        if err == nil {
16✔
286
                pub, keyErr := x509.MarshalPKIXPublicKey(rsaKey.Public())
4✔
287
                if keyErr != nil {
4✔
288
                        return nil, errors.Wrap(err, "signer: can not extract public RSA key")
×
289
                }
×
290
                return &SigningMethod{Key: rsaKey, Public: pub, Method: new(RSA)}, nil
4✔
291
        }
292

293
        ecdsaKey, err := x509.ParseECPrivateKey(block.Bytes)
8✔
294
        if err == nil {
14✔
295
                pub, keyErr := x509.MarshalPKIXPublicKey(ecdsaKey.Public())
6✔
296
                if keyErr != nil {
6✔
297
                        return nil, errors.Wrap(err, "signer: can not extract public ECDSA key")
×
298
                }
×
299
                return &SigningMethod{Key: ecdsaKey, Public: pub, Method: new(ECDSA256)}, nil
6✔
300
        }
301

302
        key, err := x509.ParsePKCS8PrivateKey(block.Bytes)
2✔
303
        if ecdsaKey, ok := key.(*ecdsa.PrivateKey); ok {
2✔
304
                if err == nil {
×
305
                        pub, keyErr := x509.MarshalPKIXPublicKey(ecdsaKey.Public())
×
306
                        if keyErr != nil {
×
307
                                return nil, errors.Wrap(err, "signer: can not extract public ECDSA key")
×
308
                        }
×
309
                        return &SigningMethod{Key: ecdsaKey, Public: pub, Method: new(ECDSA256)}, nil
×
310
                }
311
        }
312

313
        if rsaKey, ok := key.(*rsa.PrivateKey); ok {
2✔
314
                if err == nil {
×
315
                        pub, keyErr := x509.MarshalPKIXPublicKey(rsaKey.Public())
×
316
                        if keyErr != nil {
×
317
                                return nil, errors.Wrap(err, "signer: can not extract public RSA key")
×
318
                        }
×
319
                        return &SigningMethod{Key: rsaKey, Public: pub, Method: new(RSA)}, nil
×
320
                }
321
        }
322

323
        if err == nil {
2✔
324
                err = errors.New("unsupported private key type")
×
325
        }
×
326

327
        return nil, errors.Wrap(err, "signer: unsupported private key type or error occurred")
2✔
328
}
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