16 Jan 2017
Android Loadable Module Signing
Recently, there have been several questions posted to Customer Support (https://www.intrinsyc.com/contact-support/) here at Intrinsyc that asks how one signs a Linux loadable module for our Android and Linux BSPs. While this blog focuses on the Android BSP, the logic is the same for the Linux BSP. Also, this blog post assumes one already has the knowledge to create and compile a proper loadable module for the Linux kernel as well as the use of ADB. Further relevant information, in regards to compiling a Linux kernel module, can be found by doing an internet search for “Linux kernel driver”.
The Linux kernel powering both our Android and Linux 820-based BSP is based on 3.18. In particular, the kernel tag is LA.HB.1.3.2-24000-8x96.0 and the source code can be found at the following CodeAurora URL https://source.codeaurora.org/quic/la/kernel/msm-3.18/ . This is the basis for the kernel in both our Android and Linux 820 BSPs.
Linux kernel module signing is a means by which only verified kernel modules will be loaded into a running Linux kernel.  As a result, each kernel module will need to use the exact same key and key certificate used by the kernel, otherwise the module will not be loadable into the kernel. This is important for maintaining the chain of security for a driver. This process works to prevent rogue software from being used by the kernel.
Let’s start our discussion with the options in the kernel config file that are germane to us.
Here is the list of kernel config options, module signing related, that are found and their default setting:.
These config options are well documented for the Linux kernel in Linux/Documentation/module-signing.txt.  For our example, we will ignore all of the options except for the following:
CONFIG_MODULE_SIG - By setting this option, you are enabling the module signing facility in the kernel.
CONFIG_MODULE_SIG_FORCE - This option will require ALL modules to be validly signed. The module signature will be checked upon loading for EVERY kernel module.
CONFIG_MODULE_SIG_ALL - Tells the kernel build process if modules will be automatically signed during the modules_install phase of a build. This is a process that is normally executed when building the general Linux kernel. Not germane to us as the Android build process will sign automatically sign the kernel module. We will still sign the module by hand.
CONFIG_MODULE_SIG_HASH - Which hashing function is used by the kernel. We will need to know this when we actually sign the kernel module.
We are still not done yet as we need the proper keys for the kernel. At this point the module needs to be signed with a public key and key certificate that is known by the kernel before loading the driver. In the Linux kernel shipped with the Android BSP these keys are located in the ./out/target/product/msm8996/obj/kernel/msm-3.18 directory that is supplied with our Android BSP.
user@host:~/repos/8096-OpenQ-3.0-reference$ ls -l ./out/target/product/msm8996/obj/kernel/msm-3.18/
-rw-r--r-- 1 user user 3272 Dec 2 13:50 signing_key.priv
-rw-r--r-- 1 user user 1446 Dec 2 13:50 signing_key.x509
It's important to note this key and certificate are the same one used by the kernel. If you do not have the correct key the module will not load.
Now let's run through an example of this entire process. For example, we will use the example_module driver that is the most basic example of a Linux device driver. All it simply does is load and unload. We have included the source here since it is so basic.
* Header files
static int __init
printk(KERN_INFO "example_module loaded.\n");
static void __exit
printk(KERN_INFO "example_module unloaded.\n");
MODULE_AUTHOR("John Doe <firstname.lastname@example.org>");
MODULE_DESCRIPTION("Example of kernel loadable module");
ifneq (,$(filter arm aarch64 arm64, $(TARGET_ARCH)))
LOCAL_PATH := $(call my-dir)
DLKM_DIR := device/qcom/common/dlkm
LOCAL_MODULE := example_module.ko
LOCAL_MODULE_CLASS := EXECUTABLES
LOCAL_SRC_FILES := example_module.c
LOCAL_MODULE_TAGS := debug
LOCAL_MODULE_PATH := $(TARGET_OUT_DATA)/kernel-tests
Okay, now that we have all the information that we need, let’s pull this all together and actually use it to demonstrate an example.
Step 1 – Build the module
user@host:~/repos/8096-OpenQ-3.0-reference$ make -C kernel/msm-3.18 M=../../vendor/qcom/opensource/kernel-tests/example_module O=../../out/target/product/msm8996/obj/kernel/msm-3.18 ARCH=arm64 CROSS_COMPILE=aarch64-linux-android-
make: Entering directory '/home/user/repos/8096-OpenQ-3.0-reference/kernel/msm-3.18'
make: Entering directory '/home/user/repos/8096-OpenQ-3.0-reference/out/target/product/msm8996/obj/kernel/msm-3.18'
Building modules, stage 2.
MODPOST 1 modules
LD [M] ../../vendor/qcom/opensource/kernel-tests/example_module/example_module.ko
make: Leaving directory '/home/user/repos/8096-OpenQ-3.0-reference/out/target/product/msm8996/obj/kernel/msm-3.18'
make: Leaving directory '/home/user/repos/8096-OpenQ-3.0-reference/kernel/msm-3.18'
#### make completed successfully (1 seconds) ####
user@host:~/repos/8096-OpenQ-3.0-reference$ ls -l ./out/target/product/msm8996/obj/vendor/qcom/opensource/kernel-tests/example_module/example_module.ko
Step 2 – Load/Test the module (* This should fail as we have not signed the module yet *)
Use adb to install the module onto the test platform.
msm8996:/data/tmp # insmod example_module.ko
insmod: failed to load example_module.ko: Required key not available
Step 3 – Properly sign the module
user@host:~/repos/8096-OpenQ-3.0-reference$ perl ./kernel/msm-3.18/scripts/sign-file -v sha512 out/target/product/msm8996/obj/kernel/msm-3.18/signing_key.priv out/target/product/msm8996/obj/kernel/msm-3.18/signing_key.x509 ./out/target/product/msm8996/obj/vendor/qcom/opensource/kernel-tests/example_module/example_module.ko /tmp/example_module.ko
Size of unsigned module: 137088
Size of signer's name : 30
Size of key identifier : 20
Size of signature : 514
Size of information : 12
Size of magic number : 28
Signer's name : 'Magrathea: Glacier signing key'
Digest : sha512
Step 4 – Load/Test the module (* This should succeed *)
msm8996:/data/tmp # insmod example_module.ko
msm8996:/data/tmp # lsmod
Module Size Used by
example_module 12515 0
msm8996:/data/tmp # dmesg | tail
[450573.394960] example_module loaded.
Notice how the module is now properly loaded into the kernel.
For your Linux development work, Intrinsyc is always ready to help. Please contact us at email@example.com
Author: Brad Walker is a Senior Software Engineer at Intrinsyc in Boulder, CO. He has over 10 years’ experience working on embedded computing problems using Linux using different processor families. He enjoys working on difficult problems that span the interface between software & hardware.