#!/bin/bash

tmp_workspace=$(mktemp -d /tmp/_DEB_XXXXXX)

declare certs_info_show
declare cert_selected
declare wosign_container
declare wosign_container_id  # for wosign config
declare container_id         # for export certificate

usage() {
    echo
    echo "$0  CONF_FILE -no-tsa"
    echo "CONF_FILE is a sign configure file."
    echo "-no-tsa denotes signature without timestamp."
    echo "There are some key-value pairs in CONF_FILE"
    echo "Each line place a key-value pair with key=value format"
    echo "The following keys you should configure before executing."
    echo "Key PIN denoting  PIN of U-key to login"
    echo "Key DEB_IN_DIR denoting directory of deb files to sign"
    echo "Key DEB_OUT_DIR denoting direcotry to save signed deb files"
    echo "Key TRUST_LEVEL is trust level for elf signature"
    echo "Key MACHINE_ID_CONF is for entreprise debug certificate signature" 
    echo "      to limit which machine should be permit to run"
    echo "Key FEITIAN_CONF is for feitian ukey configure "
    echo "Key FEITIAN_CERT is for feitian ukey cert "
    echo
}

clean_workspace() {
    rm -rf $tmp_workspace
}

module_depend=/usr/lib/libmkpkcs11.so
cmd_tool=pkcs-tool

                                                                                                                                                                                 
function list_certificates() {    
    local slot_info=$(pkcs11-tool --module=/usr/lib/libmkpkcs11.so --list-slot 2>/dev/null | sed -n '/Available slots:/!p')
    if [[ -z $slot_info ]]
    then
         exit -1
    fi                                                                                                                                                  
    pkcs11-tool  --module=$module_depend -y cert -O 2>&1 | \
    awk -v cert_no=0 '/label/ { cert_no++; sub(/@/, ""); sub(/LABEL#SIGN/, "");  cert_dict[cert_no]=$2; } \
                      /ID/ { id_dict[cert_no]=$2 }\
                      END { print "There "length(cert_dict) " certificate(s)"; \
                      for (i in cert_dict) \
                      {print i":","container:"cert_dict[i], "id:"id_dict[i];} }'                                                                                                                                                                                                    
}                                                                                                                                                                                   
                                                                                                                                                                                     

export_certificate() {
    local container_id=$1
    local cert_name=$2
    pkcs11-tool -vvv --slot-index 0 --module /usr/lib/libmkpkcs11.so --read-object --type=cert --id=$container_id -o $cert_name
    if openssl x509 -in $cert_name -inform DER -outform PEM -out $cert_name.crt
    then
        keyType=$(openssl x509 -in $cert_name.crt  -noout -text | sed -n 's/Public Key Algorithm://p' | sed -n 's/ //gp')
    fi
    cert_path=$cert_name.crt
}


load_signature_config() {
    local sig_cfg_file=$1
    awk 'BEGIN {FS="="}; {
        if ($1 ~ /(^| )DEB_OUT_DIR( |$)/) { gsub(/ /,""); print $0}
        if ($1 ~ /(^| )PIN( |$)/) { gsub(/ /,""); print $0}
        if ($1 ~ /(^| )DEB_IN_DIR( |$)/) { gsub(/ /, ""); print $0}
        if ($1 ~ /(^| )TRUST_LEVEL( |$)/) { gsub(/ /, ""); print $0}
        if ($1 ~ /(^| )MACHINE_ID_CONF( |$)/) { gsub(/ /, ""); print $0}
        if ($1 ~ /(^| )FEITIAN_CONF( |$)/) { gsub(/ /, ""); print $0}
        if ($1 ~ /(^| )FEITIAN_CERT( |$)/) { gsub(/ /, ""); print $0}
        if ($1 ~ /(^| )CLEAROLDSIGN( |$)/) { gsub(/ /, ""); print $0}
    }' $sig_cfg_file
}

declare -i args_no_tsa_opt
last_arg_deb_batch_sign=${!#}
no_tsa_deb_batch_sign=""
args_no_tsa_opt=$#
if [ $last_arg_deb_batch_sign == "-no-tsa" ]; then
    no_tsa_deb_batch_sign=-no-tsa
    args_no_tsa_opt=$#-1
fi
if [[ $args_no_tsa_opt -eq 0 || $args_no_tsa_opt -gt 1 ]]; then
    clean_workspace
    usage
    exit -1
fi

eval $(load_signature_config $1)

if [ ! -d "$DEB_IN_DIR" ];then
    echo "The directory of deb files $DEB_IN_DIR to sign doesn't exist"
    clean_workspace
    exit -1
fi

if [ ! -d "$DEB_OUT_DIR" ];then
    echo "The directory of deb files $DEB_OUT_DIR to store signed files doesn't exist"
    clean_workspace
    exit -1
fi

if [ "$DEB_IN_DIR" = "$DEB_OUT_DIR" ]; then
    echo "The directory of deb files to sign is same to the directory putting signed deb files."
    echo "You should put signed DEB files in a different directory to `realpath $DEB_IN_DIR`!"
    clean_workspace
    exit -1
fi

if [[ -n "$PIN" ]];then
    if [[ -n $FEITIAN_CERT || -n "$FEITIAN_CONF" ]];then
        echo "wosign conflicts with feitian config"
        exit -1
    fi
fi
if [[ -n "$FEITIAN_CONF" ]];then
    if [[ -z "$FEITIAN_CERT" ]];then
        echo "FEITIAN_CERT must work with FEITIAN_CERT"
        exit -1
    fi
	FEITIAN_CONF_OPT="-ft $FEITIAN_CONF"
    cert_path=$FEITIAN_CERT
fi

if [ x"$CLEAROLDSIGN" = xyes ]; then
    export CLEAR_EMBEDED_SIGNATURE=yes
fi

if [[ -n "$PIN" ]];then # wosign ukey
    certs_info_show=$(list_certificates | sed -n  'p')

    if [[ -z "$certs_info_show" ]]; then
        echo "there no certificate to export！"
        clean_workspace
        exit -1
    fi

    echo "$certs_info_show"
    echo 
    echo
    read -p "Please select a certificate for signature:" cert_selected_no
    selected_cert_line=$(echo "$certs_info_show" | sed -n "/^$cert_selected_no[^0-9]/p")
    if [ -n "$selected_cert_line" ]; then
        echo "Your selected cert: $selected_cert_line"
        wosign_container=$(echo $selected_cert_line | cut -d":" -f3 | awk '{print $1}')
        container_id=$(echo $selected_cert_line | cut -d":" -f4)
        wosign_container_id=$(echo $container_id | xxd -r -p)
    else
        echo "There no certificate for your choice"
        clean_workspace
        exit -1
    fi
    echo
    if [ -z "$PIN" ]; then
        while read -p "Please input the PIN for export certificate:" PIN
        do
            if [ -n "$PIN" ]; then
                read -p  "Do you confirm the PIN is $PIN (y or n)?"  confirm
                if [ "$confirm" = y ]; then
                    break
                fi
            fi
        done
    fi

    echo
    cert_name=$(mktemp $tmp_workspace/_certXXXXXX)

    declare -r wosign_conf_tmp_file=$tmp_workspace/wosign.conf
    # construct wosign configure file for signature
    echo "pin:$PIN" >> $wosign_conf_tmp_file
    echo "container:$wosign_container" >> $wosign_conf_tmp_file
    echo "id:$wosign_container_id" >> $wosign_conf_tmp_file
    export_certificate $container_id $cert_name
    if [ "$keyType" = rsaEncryption ]; then
        echo "algorithm:0x00000040" >> $wosign_conf_tmp_file
    else
        echo "algorithm:0x00010004" >> $wosign_conf_tmp_file
    fi
fi

if [ "$TRUST_LEVEL"x != x ];then
	TRUST_OPT="-t $TRUST_LEVEL"
fi

if [  "$MACHINE_ID_CONF"x != x ];then
	MACHINE_IDS_OPT="-m $MACHINE_ID_CONF"
fi

export DEB_OUT_DIR
# start to sign deb files ....
declare -a arr_signed_fail_deb
sign_success_counter=0
for file in `ls $DEB_IN_DIR` ; do
    file_path=$DEB_IN_DIR/$file
    if [ "${file_path##*.}" = deb ]; then
       if deepin-elf-sign-deb $file_path $cert_path $wosign_conf_tmp_file $no_tsa_deb_batch_sign $TRUST_OPT $MACHINE_IDS_OPT $FEITIAN_CONF_OPT
       then
            let "sign_success_counter+=1"
       else
            arr_signed_fail_deb[${#arr_signed_fail_deb[*]}]=$(basename $file_path)
            rm -rf $DEB_OUT_DIR/$(basename $file_path)
       fi
    fi
done

echo
echo
echo "There has $sign_success_counter deb file(s) signed success"
echo "And ${#arr_signed_fail_deb[*]} deb file(s) signed failed!"

if [ ${#arr_signed_fail_deb[*]} -ne 0 ]; then
    echo "Signed failed deb list as follows:"
    for f in ${arr_signed_fail_deb[@]}
        do
            echo $f
        done
fi
clean_workspace

