blob: 45c62ae3fe58600aadfece0352a86c0837f307f3 [file] [log] [blame]
package dkms
import (
"bytes"
"crypto"
"crypto/rand"
"crypto/rsa"
"crypto/x509"
"crypto/x509/pkix"
"encoding/asn1"
"encoding/binary"
"encoding/pem"
"fmt"
"math/big"
"os"
"strings"
"github.com/golang/glog"
)
const (
// PKEYIDPKCS7 is the constant PKEY_ID_PKCS7 defined in https://github.com/torvalds/linux/blob/master/scripts/sign-file.c
PKEYIDPKCS7 = byte(2)
// magicNumber is the constant magic_number defined in https://github.com/torvalds/linux/blob/master/scripts/sign-file.c
magicNumber = "~Module signature appended~\n"
)
// https://www.rfc-editor.org/rfc/rfc2315#section-9.1
// SignedData ::= SEQUENCE {
// version Version,
// digestAlgorithms DigestAlgorithmIdentifiers,
// contentInfo ContentInfo,
// certificates
// [0] IMPLICIT ExtendedCertificatesAndCertificates
// OPTIONAL,
// crls
// [1] IMPLICIT CertificateRevocationLists OPTIONAL,
// signerInfos SignerInfos }
type signedData struct {
Version int
DigestAlgorithmIdentifiers []pkix.AlgorithmIdentifier `asn1:"set"`
ContentInfo contentInfo
SignerInfos []signerInfo `asn1:"set"`
}
// https://www.rfc-editor.org/rfc/rfc2315#section-7
// ContentInfo ::= SEQUENCE {
//
// contentType ContentType,
// content // NOTE: not needed for detached signature
// [0] EXPLICIT ANY DEFINED BY contentType OPTIONAL }
//
// ContentType ::= OBJECT IDENTIFIER
type contentInfo struct {
ContentType asn1.ObjectIdentifier
}
// https://www.rfc-editor.org/rfc/rfc2315#section-9.2
// SignerInfo ::= SEQUENCE {
//
// version Version,
// issuerAndSerialNumber IssuerAndSerialNumber,
// digestAlgorithm DigestAlgorithmIdentifier,
// authenticatedAttributes // NOTE: not used in Linux kernel module signature
// [0] IMPLICIT Attributes OPTIONAL,
// digestEncryptionAlgorithm
// DigestEncryptionAlgorithmIdentifier,
// encryptedDigest EncryptedDigest,
// unauthenticatedAttributes // NOTE: not used in Linux kernel module signature
// [1] IMPLICIT Attributes OPTIONAL }
//
// EncryptedDigest ::= OCTET STRING
type signerInfo struct {
Version int
IssuerAndSerialNumber issuerAndSerialNumber
DigestAlgorithm pkix.AlgorithmIdentifier
DigestEncryptionAlgorithm pkix.AlgorithmIdentifier
EncryptedDigest []byte
}
// https://www.rfc-editor.org/rfc/rfc2315#section-6.7
// IssuerAndSerialNumber ::= SEQUENCE {
//
// issuer Name,
// serialNumber CertificateSerialNumber }
type issuerAndSerialNumber struct {
Issuer asn1.RawValue
SerialNumber *big.Int
}
type pkcs7Blob struct {
Oid asn1.ObjectIdentifier
SignedData signedData `asn1:"tag:0,explicit"`
}
type digestAlgorithm struct {
identifier asn1.ObjectIdentifier
hash crypto.Hash
}
var (
oidData = asn1.ObjectIdentifier{1, 2, 840, 113549, 1, 7, 1}
oidRsaEncryption = asn1.ObjectIdentifier{1, 2, 840, 113549, 1, 1, 1}
oidSignedData = asn1.ObjectIdentifier{1, 2, 840, 113549, 1, 7, 2}
digests = map[string]digestAlgorithm{
"sha256": {
identifier: asn1.ObjectIdentifier{2, 16, 840, 1, 101, 3, 4, 2, 1},
hash: crypto.SHA256,
},
"sha384": {
identifier: asn1.ObjectIdentifier{2, 16, 840, 1, 101, 3, 4, 2, 2},
hash: crypto.SHA384,
},
"sha512": {
identifier: asn1.ObjectIdentifier{2, 16, 840, 1, 101, 3, 4, 2, 3},
hash: crypto.SHA512,
},
"sha3-256": {
identifier: asn1.ObjectIdentifier{2, 16, 840, 1, 101, 3, 4, 2, 8},
hash: crypto.SHA3_256,
},
"sha3-384": {
identifier: asn1.ObjectIdentifier{2, 16, 840, 1, 101, 3, 4, 2, 9},
hash: crypto.SHA3_384,
},
"sha3-512": {
identifier: asn1.ObjectIdentifier{2, 16, 840, 1, 101, 3, 4, 2, 10},
hash: crypto.SHA3_512,
},
}
)
// SignModules signs compiled kernel modules with a private key and certificate.
//
// Currently, only rsa private keys are supported. It creates a pksc7 signature
// and appends it to the end of a compiled kernel module.
// This is an implementation of https://github.com/torvalds/linux/blob/master/scripts/sign-file.c
func SignModules(modules []Module, keyPath, certificatePath, hash string) error {
keyBytes, err := os.ReadFile(keyPath)
if err != nil {
return fmt.Errorf("failed to read private key file (%s): %v", keyPath, err)
}
key, err := parsePrivateKey(keyBytes)
if err != nil {
return fmt.Errorf("failed to retrieve private key: %v", err)
}
certBytes, err := os.ReadFile(certificatePath)
if err != nil {
return fmt.Errorf("failed to read certificate key file (%s): %v", certificatePath, err)
}
cert, err := x509.ParseCertificate(certBytes)
if err != nil {
return fmt.Errorf("failed to retrieve certificate (%s): %v", certificatePath, err)
}
digest, ok := digests[strings.ToLower(hash)]
if !ok {
return fmt.Errorf("unsupported digest algorithm: %s", hash)
}
var signedModules, skippedModules []string
for _, module := range modules {
glog.Infof("signing module %s", module.BuiltName)
if err := signModule(module, key, cert, digest); err != nil {
skippedModules = append(skippedModules, module.BuiltName)
glog.Errorf("failed to sign module (%s) [skipping]: %v", module.BuiltName, err)
continue
}
signedModules = append(signedModules, module.BuiltName)
glog.Infof("successfully signed module '%s'", module.BuiltName)
}
glog.Infof("\nSummary:")
if len(signedModules) > 0 {
glog.Infof("Successfully signed module(s): %v", signedModules)
}
if len(skippedModules) > 0 {
glog.Errorf("Failed to sign module(s): %v", skippedModules)
}
return nil
}
func parsePrivateKey(contents []byte) (crypto.PrivateKey, error) {
// Retrive pem encodded block.
block, _ := pem.Decode(contents)
if block == nil {
return nil, fmt.Errorf("failed to decode pem-formatted key or invalid key type")
}
var privateKey crypto.PrivateKey
var err error
switch block.Type {
case "RSA PRIVATE KEY":
privateKey, err = x509.ParsePKCS1PrivateKey(block.Bytes)
if err != nil {
return nil, fmt.Errorf("failed to parse rsa private key: %v", err)
}
case "PRIVATE KEY":
key, err := x509.ParsePKCS8PrivateKey(block.Bytes)
if err != nil {
return nil, fmt.Errorf("failed to parse private key: %v", err)
}
switch k := key.(type) {
case *rsa.PrivateKey:
privateKey = k
default:
return nil, fmt.Errorf("unsupported key type: %T", key)
}
default:
return nil, fmt.Errorf("unsupported block type: %s", block.Type)
}
return privateKey, nil
}
func signModule(module Module, key crypto.PrivateKey, certificate *x509.Certificate, digest digestAlgorithm) error {
moduleName := module.BuiltName
modulePath := module.BuiltPath()
moduleBytes, err := os.ReadFile(modulePath)
if err != nil {
return fmt.Errorf("failed to read module file (%s): %v", modulePath, err)
}
signature, err := generateModuleSignature(key, moduleBytes, digest.hash)
if err != nil {
return fmt.Errorf("failed to generate signature for module (%s): %v", moduleName, err)
}
digestAlg := &pkix.AlgorithmIdentifier{Algorithm: digest.identifier, Parameters: asn1.NullRawValue}
encrypAlg, err := retrieveDigestEncryptionAlgo(key)
if err != nil {
return fmt.Errorf("failed to retrieve digest encryption algorithm for module (%s): %v", moduleName, err)
}
sinfo := signerInfo{
Version: 1,
IssuerAndSerialNumber: issuerAndSerialNumber{
Issuer: asn1.RawValue{FullBytes: certificate.RawIssuer},
SerialNumber: certificate.SerialNumber,
},
DigestAlgorithm: *digestAlg,
DigestEncryptionAlgorithm: *encrypAlg,
EncryptedDigest: signature,
}
blob := pkcs7Blob{
Oid: oidSignedData,
SignedData: signedData{
Version: 1,
DigestAlgorithmIdentifiers: []pkix.AlgorithmIdentifier{*digestAlg},
ContentInfo: contentInfo{ContentType: oidData},
SignerInfos: []signerInfo{sinfo},
},
}
marshalled, err := asn1.Marshal(blob)
if err != nil {
return fmt.Errorf("failed to marshal pkcs7 blob for module (%s): %v", moduleName, err)
}
signedBytes, err := appendSignature(marshalled, moduleBytes)
if err != nil {
return fmt.Errorf("failed to append signature to module (%s): %v", moduleName, err)
}
// Write the buffer content to the output file, overwriting it.
if err := os.WriteFile(modulePath, signedBytes, 0644); err != nil {
return fmt.Errorf("failed to write signed module to %s: %v", modulePath, err)
}
return nil
}
func generateModuleSignature(privateKey crypto.PrivateKey, moduleBytes []byte, hash crypto.Hash) ([]byte, error) {
// Retrieve the crypto signer if it is implemented for the private key
signer, ok := privateKey.(crypto.Signer)
if !ok {
return nil, fmt.Errorf("could not retrieve crypto signer for key type: %T", privateKey)
}
h := hash.New()
h.Write(moduleBytes)
moduleDigest := h.Sum(nil)
signature, err := signer.Sign(rand.Reader, moduleDigest, hash)
if err != nil {
return nil, fmt.Errorf("failed to sign the module digest: %v", err)
}
return signature, nil
}
func retrieveDigestEncryptionAlgo(key crypto.PrivateKey) (*pkix.AlgorithmIdentifier, error) {
switch key.(type) {
case *rsa.PrivateKey:
return &pkix.AlgorithmIdentifier{Algorithm: oidRsaEncryption, Parameters: asn1.NullRawValue}, nil
default:
return nil, fmt.Errorf("unsupported digest encryption algorithm: %T", key)
}
}
// appendSignature appends a raw PKCS#7 signature to the end of a given kernel module.
func appendSignature(signature []byte, moduleBytes []byte) ([]byte, error) {
var buf bytes.Buffer
// Write bytes of kernel module.
if _, err := buf.Write(moduleBytes); err != nil {
return nil, fmt.Errorf("failed to write module bytes to buffer: %v", err)
}
// Copy bytes of module signature.
sigSize, err := buf.Write(signature)
if err != nil {
return nil, fmt.Errorf("failed to write signature to buffer: %v", err)
}
// Append the marker and the PKCS#7 message.
// signatureInfo is the struct module_signature defined in
// https://github.com/torvalds/linux/blob/master/scripts/sign-file.c
signatureInfo := [12]byte{}
// signatureInfo[2] is the id_type of struct module_signature
signatureInfo[2] = PKEYIDPKCS7
// signatureInfo[8:12] is the sig_len of struct module_signature.
// Using BigEndian as the sig_len should be in network byte order.
binary.BigEndian.PutUint32(signatureInfo[8:12], uint32(sigSize))
if _, err := buf.Write(signatureInfo[:]); err != nil {
return nil, fmt.Errorf("failed to write module signature struct to buffer: %v", err)
}
if _, err := buf.WriteString(magicNumber); err != nil {
return nil, fmt.Errorf("failed to write magic number to buffer: %v", err)
}
return buf.Bytes(), nil
}