LXR | KVM | PM | Time | Interrupt | Systems Performance | Bootup Optimization

OpenWRT(21):sysupgrade中签名和验签流程

 sysupgrade支持在编译的时候生成签名和metadata,在设备进行upgrade进行验签和metadata检查。

1 签名和metadata生成流程

签名和metadata流程如下:

文件类型核心作用
key-build 私钥 签名固件和软件包。
key-build.pub 公钥 验证签名合法性。
key-build.ucert 证书 扩展信任链,支持复杂证书体系。
key-build.sig 签名文件 验证特定文件的完整性和来源。
key-build.ucert.revoke 证书撤销列表 声明失效证书,防止非法访问。

 usign基于Ed25519非对称算法和SHA512,用于生成密钥对、签名、验签等:

Usage: staging_dir/host/bin/usign <command> <options>
Commands:
  -V:                   verify (needs at least -m and -p|-P)
  -S:                   sign (needs at least -m and -s)
  -F:                   print key fingerprint of public/secret key or signature
  -G:                   generate a new keypair (needs at least -p and -s)
Options:
  -c <comment>:         add comment to keys
  -m <file>:            message file
  -p <file>:            public key file (verify/fingerprint only)
  -P <path>:            public key directory (verify only)
  -q:                   quiet (do not print verification result, use return code only)
  -s <file>:            secret key file (sign/fingerprint only)
  -x <file>:            signature file (defaults to <message file>.sig)

ucert用于生成证书、附加签名、验签等:

Usage: staging_dir/host/bin/ucert <command> <options>
Commands:
  -A:                   append signature (needs -c and -x)
  -D:                   dump (needs -c)
  -I:                   issue cert and revoker (needs -c and -p and -s)
  -R:                   process revoker certificate (needs -c and -P)
  -V:                   verify (needs -c and -p|-P, may have -m)
Options:
  -c <file>:            certificate file
  -m <file>:            message file (verify only)
  -p <file>:            public key file
  -P <path>:            public key directory (verify only)
  -q:                   quiet (do not print verification result, use return code only)
  -s <file>:            secret key file (issue only)
  -x <file>:            signature file (append only)

fwtool将签名和metadata打包到固件中,或者从固件解包:

Usage: staging_dir/host/bin/fwtool <options> <firmware>

Options:
  -S <file>:            Append signature file to firmware image
  -I <file>:            Append metadata file to firmware image
  -s <file>:            Extract signature file from firmware image
  -i <file>:            Extract metadata file from firmware image
  -t:                   Remove extracted chunks from firmare image (using -s, -i)
  -T:                   Output firmware image without extracted chunks to stdout (using -s, -i)
  -q:                   Quiet (suppress error messages)

package/base-files/Makefile中生成私钥、公钥、证书:

ifdef CONFIG_SIGNED_PACKAGES
  define Build/Configure
        [ -s $(BUILD_KEY) -a -s $(BUILD_KEY).pub ] || \
                $(STAGING_DIR_HOST)/bin/usign -G -s $(BUILD_KEY) -p $(BUILD_KEY).pub -c "Local build key"--生成一对私钥和公钥,注释为Local build key。

        [ -s $(BUILD_KEY).ucert ] || \
                $(STAGING_DIR_HOST)/bin/ucert -I -c $(BUILD_KEY).ucert -p $(BUILD_KEY).pub -s $(BUILD_KEY)--根据私钥和公钥生成证书。

  endef
...
endif

package/base-files/Makefile中安装公钥到rootfs:

  define Package/base-files/install-key
        mkdir -p $(1)/etc/opkg/keys
        $(CP) $(BUILD_KEY).pub $(1)/etc/opkg/keys/`$(STAGING_DIR_HOST)/bin/usign -F -p $(BUILD_KEY).pub`--拷贝的pubkey名称为key-public.key的fingerprint。所以rootfs中的pubkey要和sysupgrade升级包中一致。

  endef

include/image-commands.mk中对镜像进行签名:

compat_version=$(if $(DEVICE_COMPAT_VERSION),$(DEVICE_COMPAT_VERSION),1.0)
json_quote=$(subst ','\'',$(subst ",\",$(1)))
#")')

legacy_supported_message=$(SUPPORTED_DEVICES) - Image version mismatch: image $(compat_version), \
        device 1.0. Please wipe config during upgrade (force required) or reinstall. \
        $(if $(DEVICE_COMPAT_MESSAGE),Reason: $(DEVICE_COMPAT_MESSAGE),Please check documentation ...)

metadata_devices=$(if $(1),$(subst "$(space)","$(comma)",$(strip $(foreach v,$(1),"$(call json_quote,$(v))"))))
metadata_json = \
        '{ $(if $(IMAGE_METADATA),$(IMAGE_METADATA)$(comma)) \
                "metadata_version": "1.1", \
                "compat_version": "$(call json_quote,$(compat_version))", \
                $(if $(DEVICE_COMPAT_MESSAGE),"compat_message": "$(call json_quote,$(DEVICE_COMPAT_MESSAGE))"$(comma)) \
                $(if $(filter-out 1.0,$(compat_version)),"new_supported_devices": \
                        [$(call metadata_devices,$(SUPPORTED_DEVICES))]$(comma) \
                        "supported_devices": ["$(call json_quote,$(legacy_supported_message))"]$(comma)) \
                $(if $(filter 1.0,$(compat_version)),"supported_devices":[$(call metadata_devices,$(SUPPORTED_DEVICES))]$(comma)) \--取SUPPORTED_DEVICES定义的设备名称。
                "version": { \
                        "dist": "$(call json_quote,$(VERSION_DIST))", \--指的是OpenWrt。
                        "version": "$(call json_quote,$(VERSION_NUMBER))", \--OpenWrt的版本号。
                        "revision": "$(call json_quote,$(REVISION))", \
                        "target": "$(call json_quote,$(TARGETID))", \
                        "board": "$(call json_quote,$(if $(BOARD_NAME),$(BOARD_NAME),$(DEVICE_NAME)))" \--如果定义BOARD_NAME,则取值;否则取DEVICE_NAME。
                } \
        }'

define Build/append-metadata
        $(if $(SUPPORTED_DEVICES),-echo $(call metadata_json) | fwtool -I - $@)
        [ ! -s "$(BUILD_KEY)" -o ! -s "$(BUILD_KEY).ucert" -o ! -s "$@" ] || { \
                cp "$(BUILD_KEY).ucert" "$@.ucert" ;\
                usign -S -m "$@" -s "$(BUILD_KEY)" -x "$@.sig" ;\--对-m指定的输入文件,使用-s私钥进行签名,写入-x指定签名中。
                ucert -A -c "$@.ucert" -x "$@.sig" ;\--将签名附加到证书中。
                fwtool -S "$@.ucert" "$@" ;\--将带签名的证书附加到镜像中。
        }
endef

然后将append-metada放在sysupgrade生成命令之后,对其进行签名添加metadata。

2 验签和metadata检查流程

验签和metada检查流程如下:

sysupgrade进行升级包验签:

  • 升级过程中如果REQUIRE_IMAGE_SIGNATURE为1,则必须检查签名。REQUIRE_IMAGE_METADATA为1,则必须检查metadata。失败则停止。除非使用sysupgrade -F强制更新。
json_load  "$(/usr/libexec/validate_firmware_image "$IMAGE")"
  fw_check_signature(/lib/upgrade/fwtool.sh)
    -x /usr/bin/ucert--检查ucert工具是否存在。如果不存在或不可执行,则检查REQUIRE_IMAGE_SIGNATURE是否为1,为1表示需要签名,返回1,表示错误;否则返回0,表示成功。
    fwtool -q -s /tmp/sysupgrade.ucert $IMAGE--从升级包中提取签名文件到/tmp/sysupgrade.ucert。
    fwtool -q -T -s /dev/null $IMAGE | ucert -V -m - -c "/tmp/sysupgrade.ucert" -P /etc/opkg/keys--fwtool从升级包中提取固件文件,并使用公钥和签名进行验签。并将公钥的内容放入到/etc/opkg.keys目录下。
  fw_check_image(/lib/upgrade/fwtool.sh)
    /usr/share/libubox/jshn.sh
    fwtool -q -i /tmp/sysupgrade.meta $IMAGE--从升级包中提取metadata文件。
    json_get_keys dev_keys--获取当前设备名称和支持设备名称比较,以及进行版本兼容检查。

fwtool_check_signature提取证书签名,然后进行验签。

fwtool_check_signature() {
        [ $# -gt 1 ] && return 1

        [ ! -x /usr/bin/ucert ] && {
                if [ "$REQUIRE_IMAGE_SIGNATURE" = 1 ]; then
                        return 1
                else
                        return 0
                fi
        }

        if ! fwtool -q -s /tmp/sysupgrade.ucert "$1"; then
                v "Image signature not present"
                [ "$REQUIRE_IMAGE_SIGNATURE" = 1 -a "$FORCE" != 1 ] && {
                        v "Use sysupgrade -F to override this check when downgrading or flashing to vendor firmware"
                }
                [ "$REQUIRE_IMAGE_SIGNATURE" = 1 ] && return 1
                return 0
        fi

        fwtool -q -T -s /dev/null "$1" | \
                ucert -V -m - -c "/tmp/sysupgrade.ucert" -P /etc/opkg/keys

        return $?
}

fwtool_check_image提取metata,然后检查版本号和设备兼容性。

fwtool_check_image() {
    [ $# -gt 1 ] && return 1

    . /usr/share/libubox/jshn.sh

    if ! fwtool -q -i /tmp/sysupgrade.meta "$1"; then
        v "Image metadata not present"
        [ "$REQUIRE_IMAGE_METADATA" = 1 -a "$FORCE" != 1 ] && {
            v "Use sysupgrade -F to override this check when downgrading or flashing to vendor firmware"
        }
        [ "$REQUIRE_IMAGE_METADATA" = 1 ] && return 1
        return 0
    fi

    json_load "$(cat /tmp/sysupgrade.meta)" || {--解析metadata到json。
        v "Invalid image metadata"
        return 1
    }

    device="$(cat /tmp/sysinfo/board_name)"--从board_name获取当前设备名称。
    devicecompat="$(uci -q get system.@system[0].compat_version)"--获取设备兼容版本号,否则默认1.0。
    [ -n "$devicecompat" ] || devicecompat="1.0"

    json_get_var imagecompat compat_version--从metada获取compart_version。
    json_get_var compatmessage compat_message
    [ -n "$imagecompat" ] || imagecompat="1.0"

    # select correct supported list based on compat_version
    # (using this ensures that compatibility check works for devices
    #  not knowing about compat-version)
    local supported=supported_devices--获取metada中supported_devices列表。
    [ "$imagecompat" != "1.0" ] && supported=new_supported_devices
    json_select $supported || return 1

    json_get_keys dev_keys
    for k in $dev_keys; do--遍历supported_devices列表。
        json_get_var dev "$k"
        if [ "$dev" = "$device" ]; then--判断当前设备device是否在supported_devices中。
            # major compat version -> no sysupgrade
            if [ "${devicecompat%.*}" != "${imagecompat%.*}" ]; then--比较从设备获取的版本号devicecompat和metadata中版本号imagecompat的点号之前的部分是否一致。不一致则返回错误。
                v "The device is supported, but this image is incompatible for sysupgrade based on the image version ($devicecompat->$imagecompat)."
                [ -n "$compatmessage" ] && v "$compatmessage"
                return 1
            fi

            # minor compat version -> sysupgrade with -n required
            if [ "${devicecompat#.*}" != "${imagecompat#.*}" ] && [ "$SAVE_CONFIG" = "1" ]; then--版本号点号后部分兼容性检查。
                v "The device is supported, but the config is incompatible to the new image ($devicecompat->$imagecompat). Please upgrade without keeping config (sysupgrade -n)."
                [ -n "$compatmessage" ] && v "$compatmessage"
                return 1
            fi

            return 0--设备名称匹配,并且版本号匹配则表示metadata支持。
        fi
    done

    v "Device $device not supported by this image"
    vn "Supported devices:"
    for k in $dev_keys; do
        json_get_var dev "$k"
        _vn " $dev"
    done
    _v

    return 1
}

 

posted on 2025-04-12 23:59  ArnoldLu  阅读(615)  评论(0)    收藏  举报

导航