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

mendersoftware / mender-artifact / 887452516

pending completion
887452516

Pull #498

gitlab-ci

merlin-northern
chore: gofmt

Ticket: None
Signed-off-by: Peter Grzybowski <peter@northern.tech>
Pull Request #498: Load keys with ParsePKCS8PrivateKey

0 of 19 new or added lines in 1 file covered. (0.0%)

3550 of 7182 relevant lines covered (49.43%)

77.07 hits per line

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

50.74
/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) {
12✔
52
        var rsaKey *rsa.PrivateKey
12✔
53
        var ok bool
12✔
54

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

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

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

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

73
        h := sha256.Sum256(message)
1✔
74
        return rsa.VerifyPKCS1v15(rsaKey, crypto.SHA256, h[:], sig)
1✔
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) {
10✔
84
        var ecdsaKey *ecdsa.PrivateKey
10✔
85
        var ok bool
10✔
86

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

92
        // calculate the hash of the message to be signed
93
        h := sha256.Sum256(message)
10✔
94
        r, s, err := ecdsa.Sign(rand.Reader, ecdsaKey, h[:])
10✔
95
        if err != nil {
10✔
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 {
10✔
101
                return nil, errors.New("signer: invalid ecdsa curve size")
×
102
        }
×
103
        return MarshalECDSASignature(r, s)
10✔
104
}
105

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

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

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

120
        h := sha256.Sum256(message)
×
121

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

128
}
129

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

10✔
136
        // MEN-1740 In some cases the size of the r and s can be different
10✔
137
        // than expected ecdsa256keySize. In this case we need to make sure
10✔
138
        // we are serializing those using correct offset. We can use leading
10✔
139
        // zeros easily as this has no impact on serializing and deserializing.
10✔
140
        rSize := len(r.Bytes())
10✔
141
        sSize := len(s.Bytes())
10✔
142
        if rSize > ecdsa256keySize || sSize > ecdsa256keySize {
10✔
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
10✔
151
        sOffset := ecdsa256keySize - sSize
10✔
152

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

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

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

166
        // get the signature; see corresponding `Sign` function for more details
167
        // about serialization
168
        r = big.NewInt(0).SetBytes(sig[:ecdsa256keySize])
×
169
        s = big.NewInt(0).SetBytes(sig[ecdsa256keySize:])
×
170
        return r, s, nil
×
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) {
22✔
187
        if len(privateKey) == 0 {
22✔
188
                return nil, errors.New("signer: missing key")
×
189
        }
×
190
        signMethod, err := GetKeyAndSignMethod(privateKey)
22✔
191
        if err != nil {
22✔
192
                return nil, err
×
193
        }
×
194
        pub, err := x509.ParsePKIXPublicKey(signMethod.Public)
22✔
195
        if err != nil {
22✔
196
                return nil, errors.Wrap(err, "failed to parse encoded public key")
×
197
        }
×
198
        return &PKISigner{
22✔
199
                signMethod: signMethod,
22✔
200
                verifyMethod: &SigningMethod{
22✔
201
                        Key:    pub,
22✔
202
                        Method: signMethod.Method,
22✔
203
                },
22✔
204
        }, nil
22✔
205
}
206

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

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

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

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

247
        return s.verifyMethod.Method.Verify(message, dec[:decLen], s.verifyMethod.Key)
1✔
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) {
1✔
259
        block, _ := pem.Decode(keyPEM)
1✔
260
        if block == nil {
1✔
261
                return nil, errors.New("signer: failed to parse public key")
×
262
        }
×
263

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

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

284
        rsaKey, err := x509.ParsePKCS1PrivateKey(block.Bytes)
22✔
285
        if err == nil {
34✔
286
                pub, keyErr := x509.MarshalPKIXPublicKey(rsaKey.Public())
12✔
287
                if keyErr != nil {
12✔
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
12✔
291
        }
292

293
        ecdsaKey, err := x509.ParseECPrivateKey(block.Bytes)
10✔
294
        if err == nil {
20✔
295
                pub, keyErr := x509.MarshalPKIXPublicKey(ecdsaKey.Public())
10✔
296
                if keyErr != nil {
10✔
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
10✔
300
        }
301

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

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

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

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