mirror of
				https://github.com/siderolabs/talos.git
				synced 2025-11-04 02:11:12 +01:00 
			
		
		
		
	This time use `eg` from `x/tools` repo tool to do this. Signed-off-by: Dmitriy Matrenichev <dmitry.matrenichev@siderolabs.com>
		
			
				
	
	
		
			192 lines
		
	
	
		
			5.6 KiB
		
	
	
	
		
			Go
		
	
	
	
	
	
			
		
		
	
	
			192 lines
		
	
	
		
			5.6 KiB
		
	
	
	
		
			Go
		
	
	
	
	
	
// This Source Code Form is subject to the terms of the Mozilla Public
 | 
						|
// License, v. 2.0. If a copy of the MPL was not distributed with this
 | 
						|
// file, You can obtain one at http://mozilla.org/MPL/2.0/.
 | 
						|
 | 
						|
// main is the entrypoint for the program.
 | 
						|
package main
 | 
						|
 | 
						|
import (
 | 
						|
	"bytes"
 | 
						|
	"crypto"
 | 
						|
	"crypto/rsa"
 | 
						|
	"crypto/sha512"
 | 
						|
	"crypto/x509"
 | 
						|
	"encoding/binary"
 | 
						|
	"errors"
 | 
						|
	"flag"
 | 
						|
	"fmt"
 | 
						|
	"io"
 | 
						|
	"os"
 | 
						|
 | 
						|
	"go.mozilla.org/pkcs7"
 | 
						|
)
 | 
						|
 | 
						|
// Reverse engineered from the kernel source code: https://git.kernel.org/pub/scm/linux/kernel/git/torvalds/linux.git/tree/scripts/extract-module-sig.pl
 | 
						|
// Ref:
 | 
						|
// * https://git.kernel.org/pub/scm/linux/kernel/git/torvalds/linux.git/tree/Documentation/admin-guide/module-signing.rst
 | 
						|
// * https://unix.stackexchange.com/questions/493170/how-to-verify-a-kernel-module-signature
 | 
						|
// * https://wiki.gentoo.org/wiki/Signed_kernel_module_support
 | 
						|
//
 | 
						|
// A signed kernel module has the following structure:
 | 
						|
// * module data in ELF format followed by
 | 
						|
// * 12 bytes of signature info (https://github.com/torvalds/linux/blob/master/scripts/sign-file.c#L62-L70) followed by
 | 
						|
// * the signature itself followed by
 | 
						|
// * the magic string "~Module signature appended~\n"
 | 
						|
 | 
						|
const (
 | 
						|
	// SignedModuleMagic is the magic string appended to the end of a signed module.
 | 
						|
	SignedModuleMagic = "~Module signature appended~\n"
 | 
						|
	// SignedModuleMagicLength is the length of the magic string.
 | 
						|
	SignedModuleMagicLength = int64(len(SignedModuleMagic))
 | 
						|
	// ModuleSignatureInfoLength is the length of the signature info.
 | 
						|
	ModuleSignatureInfoLength int64 = 12
 | 
						|
)
 | 
						|
 | 
						|
var (
 | 
						|
	cert   string
 | 
						|
	module string
 | 
						|
)
 | 
						|
 | 
						|
func main() {
 | 
						|
	flag.StringVar(&cert, "cert", "", "X.509 certificate used to sign the module")
 | 
						|
	flag.StringVar(&module, "module", "", "path to the module to verify, if '-' is passed, the module data will be read from stdin")
 | 
						|
	flag.Parse()
 | 
						|
 | 
						|
	if cert == "" || module == "" {
 | 
						|
		flag.Usage()
 | 
						|
		os.Exit(1)
 | 
						|
	}
 | 
						|
 | 
						|
	cert, err := os.ReadFile(cert)
 | 
						|
	if err != nil {
 | 
						|
		fmt.Printf("failed to read certificate file %s: %v", cert, err)
 | 
						|
		os.Exit(1)
 | 
						|
	}
 | 
						|
 | 
						|
	crt, err := x509.ParseCertificate(cert)
 | 
						|
	if err != nil {
 | 
						|
		fmt.Printf("failed to parse certificate file %s: %v", cert, err)
 | 
						|
		os.Exit(1)
 | 
						|
	}
 | 
						|
 | 
						|
	moduleData, err := parseModuleInput(module)
 | 
						|
	if err != nil {
 | 
						|
		fmt.Println(err)
 | 
						|
		os.Exit(1)
 | 
						|
	}
 | 
						|
	defer moduleData.Close() //nolint:errcheck
 | 
						|
 | 
						|
	if err := verifyModule(crt, moduleData); err != nil {
 | 
						|
		fmt.Println(err)
 | 
						|
		os.Exit(1) //nolint:gocritic
 | 
						|
	}
 | 
						|
}
 | 
						|
 | 
						|
type noOPCloser struct {
 | 
						|
	io.ReadSeeker
 | 
						|
}
 | 
						|
 | 
						|
func (noOPCloser) Close() error { return nil }
 | 
						|
 | 
						|
func parseModuleInput(module string) (io.ReadSeekCloser, error) {
 | 
						|
	if module == "-" {
 | 
						|
		moduleData, err := io.ReadAll(os.Stdin)
 | 
						|
		if err != nil {
 | 
						|
			return nil, fmt.Errorf("failed to read module from stdin: %w", err)
 | 
						|
		}
 | 
						|
 | 
						|
		return noOPCloser{bytes.NewReader(moduleData)}, nil
 | 
						|
	}
 | 
						|
 | 
						|
	moduleData, err := os.Open(module)
 | 
						|
	if err != nil {
 | 
						|
		return nil, fmt.Errorf("failed to open file %s: %w", module, err)
 | 
						|
	}
 | 
						|
 | 
						|
	return moduleData, nil
 | 
						|
}
 | 
						|
 | 
						|
func verifyModule(crt *x509.Certificate, moduleData io.ReadSeeker) error {
 | 
						|
	_, err := moduleData.Seek(-SignedModuleMagicLength, io.SeekEnd)
 | 
						|
	if err != nil {
 | 
						|
		return fmt.Errorf("failed to seek to %d in file %s: %w", -SignedModuleMagicLength, module, err)
 | 
						|
	}
 | 
						|
 | 
						|
	magicBytes := make([]byte, len(SignedModuleMagic))
 | 
						|
 | 
						|
	_, err = moduleData.Read(magicBytes)
 | 
						|
	if err != nil {
 | 
						|
		return fmt.Errorf("failed to read %d bytes from file %s: %w", SignedModuleMagicLength, module, err)
 | 
						|
	}
 | 
						|
 | 
						|
	if string(magicBytes) != SignedModuleMagic {
 | 
						|
		return fmt.Errorf("file %s is not a signed module", module)
 | 
						|
	}
 | 
						|
 | 
						|
	_, err = moduleData.Seek(-SignedModuleMagicLength-ModuleSignatureInfoLength, io.SeekCurrent)
 | 
						|
	if err != nil {
 | 
						|
		return fmt.Errorf("failed to seek to %d in file %s: %w", -SignedModuleMagicLength-ModuleSignatureInfoLength, module, err)
 | 
						|
	}
 | 
						|
 | 
						|
	signatureBytes := make([]byte, ModuleSignatureInfoLength)
 | 
						|
 | 
						|
	_, err = moduleData.Read(signatureBytes)
 | 
						|
	if err != nil {
 | 
						|
		return fmt.Errorf("failed to read %d bytes from file %s: %w", ModuleSignatureInfoLength, module, err)
 | 
						|
	}
 | 
						|
 | 
						|
	// The signature length is encoded in the last 4 bytes of the signature info.
 | 
						|
	// https://github.com/torvalds/linux/blob/master/scripts/sign-file.c#L62-L70
 | 
						|
	signatureLength := int64(binary.BigEndian.Uint32(signatureBytes[(len(signatureBytes) - 4):]))
 | 
						|
 | 
						|
	_, err = moduleData.Seek(-ModuleSignatureInfoLength-signatureLength, io.SeekCurrent)
 | 
						|
	if err != nil {
 | 
						|
		return fmt.Errorf("failed to seek to %d in file %s: %w", -ModuleSignatureInfoLength-signatureLength, module, err)
 | 
						|
	}
 | 
						|
 | 
						|
	signature := make([]byte, signatureLength)
 | 
						|
 | 
						|
	_, err = moduleData.Read(signature)
 | 
						|
	if err != nil {
 | 
						|
		return fmt.Errorf("failed to read %d bytes from file %s: %w", signatureLength, module, err)
 | 
						|
	}
 | 
						|
 | 
						|
	unsignedModuleLength, err := moduleData.Seek(-signatureLength, io.SeekCurrent)
 | 
						|
	if err != nil {
 | 
						|
		return fmt.Errorf("failed to seek to %d in file %s: %w", 0, module, err)
 | 
						|
	}
 | 
						|
 | 
						|
	_, err = moduleData.Seek(0, 0)
 | 
						|
	if err != nil {
 | 
						|
		return fmt.Errorf("failed to seek to %d in file %s: %w", 0, module, err)
 | 
						|
	}
 | 
						|
 | 
						|
	unsignedModuleData := make([]byte, unsignedModuleLength)
 | 
						|
 | 
						|
	_, err = moduleData.Read(unsignedModuleData)
 | 
						|
	if err != nil {
 | 
						|
		return fmt.Errorf("failed to read %d bytes from file %s: %w", unsignedModuleLength, module, err)
 | 
						|
	}
 | 
						|
 | 
						|
	p7, err := pkcs7.Parse(signature)
 | 
						|
	if err != nil {
 | 
						|
		return fmt.Errorf("failed to parse signature: %w", err)
 | 
						|
	}
 | 
						|
 | 
						|
	signatureSigned := p7.Signers[0].EncryptedDigest
 | 
						|
	hashed := sha512.Sum512(unsignedModuleData)
 | 
						|
 | 
						|
	pubKey, ok := crt.PublicKey.(*rsa.PublicKey)
 | 
						|
	if !ok {
 | 
						|
		return errors.New("failed to convert public key to RSA key")
 | 
						|
	}
 | 
						|
 | 
						|
	if err := rsa.VerifyPKCS1v15(pubKey, crypto.SHA512, hashed[:], signatureSigned); err != nil {
 | 
						|
		return fmt.Errorf("failed to verify signature for module %s", module)
 | 
						|
	}
 | 
						|
 | 
						|
	fmt.Printf("module %s is signed by %s\n", module, crt.Subject.CommonName)
 | 
						|
 | 
						|
	return nil
 | 
						|
}
 |