1
1
mirror of https://github.com/goreleaser/nfpm synced 2024-09-28 20:01:55 +02:00
nfpm/internal/sign/rsa.go
Benno b196efa03f
test: Add APK and RSA sign test (#233)
* test: Add APK and RSA sign test

To increase test coverage.

See #229

* fix: lint errors

Fix gosec lint errors

Update #233

* fix: create temp file in OS temp

* test: Improve RSA error testing.

Co-authored-by: Erik Geiser <erik.geiser@posteo.net>
2020-11-21 10:34:35 -03:00

123 lines
3.0 KiB
Go

package sign
import (
"crypto"
"crypto/rand"
"crypto/rsa"
"crypto/sha1" // nolint:gosec
"crypto/x509"
"encoding/pem"
"errors"
"fmt"
"io"
"io/ioutil"
)
var (
errNoPemBlock = errors.New("no PEM block found")
errDigestNotSH1 = errors.New("digest is not a SHA1 hash")
errNoPassphrase = errors.New("key is encrypted but no passphrase was provided")
errNoRSAKey = errors.New("key is not an RSA key")
)
// RSASignSHA1Digest signs the provided SHA1 message digest. The key file
// must be in the PEM format and can either be encrypted or not.
func RSASignSHA1Digest(sha1Digest []byte, keyFile, passphrase string) ([]byte, error) {
if len(sha1Digest) != sha1.Size {
return nil, errDigestNotSH1
}
keyFileContent, err := ioutil.ReadFile(keyFile)
if err != nil {
return nil, fmt.Errorf("reading key file: %w", err)
}
block, _ := pem.Decode(keyFileContent)
if block == nil {
return nil, errNoPemBlock
}
blockData := block.Bytes
if x509.IsEncryptedPEMBlock(block) {
if passphrase == "" {
return nil, errNoPassphrase
}
var decryptedBlockData []byte
decryptedBlockData, err = x509.DecryptPEMBlock(block, []byte(passphrase))
if err != nil {
return nil, fmt.Errorf("decrypt private key PEM block: %w", err)
}
blockData = decryptedBlockData
}
priv, err := x509.ParsePKCS1PrivateKey(blockData)
if err != nil {
return nil, fmt.Errorf("parse PKCS1 private key: %w", err)
}
signature, err := priv.Sign(rand.Reader, sha1Digest, crypto.SHA1)
if err != nil {
return nil, fmt.Errorf("signing: %w", err)
}
return signature, nil
}
func rsaSign(message io.Reader, keyFile, passphrase string) ([]byte, error) {
sha1Hash := sha1.New() // nolint:gosec
_, err := io.Copy(sha1Hash, message)
if err != nil {
return nil, fmt.Errorf("create SHA1 message digest: %w", err)
}
return RSASignSHA1Digest(sha1Hash.Sum(nil), keyFile, passphrase)
}
// RSAVerifySHA1Digest is exported for use in tests and verifies a signature over the
// provided SHA1 hash of a message. The key file must be in the PEM format.
func RSAVerifySHA1Digest(sha1Digest, signature []byte, publicKeyFile string) error {
if len(sha1Digest) != sha1.Size {
return errDigestNotSH1
}
keyFileContent, err := ioutil.ReadFile(publicKeyFile)
if err != nil {
return fmt.Errorf("reading key file: %w", err)
}
block, _ := pem.Decode(keyFileContent)
if block == nil {
return errNoPemBlock
}
pub, err := x509.ParsePKIXPublicKey(block.Bytes)
if err != nil {
return fmt.Errorf("parse PKIX public key: %w", err)
}
rsaPub, ok := pub.(*rsa.PublicKey)
if !ok {
return errNoRSAKey
}
err = rsa.VerifyPKCS1v15(rsaPub, crypto.SHA1, sha1Digest, signature)
if err != nil {
return fmt.Errorf("verify PKCS1v15 signature: %w", err)
}
return nil
}
func rsaVerify(message io.Reader, signature []byte, publicKeyFile string) error {
sha1Hash := sha1.New() // nolint:gosec
_, err := io.Copy(sha1Hash, message)
if err != nil {
return fmt.Errorf("create SHA1 message digest: %w", err)
}
return RSAVerifySHA1Digest(sha1Hash.Sum(nil), signature, publicKeyFile)
}