gcp

Container Signing with Cosign and TPM PKCS-11

2022-10-22

Simple demo of signing a container image usng cosign where the private key is embedded inside a Trusted Platform Module (TPM)

We will achieve that through cosign’s PKCS-11 integration which is enabled when its compiled with a specific flag.

Also see

images/cosign.png

Anyway,


Create VM

gcloud compute instances create cosign-test \
     --zone=us-central1-a --machine-type=e2-medium --no-service-account --no-scopes  \
     --image=debian-11-bullseye-v20211105 --image-project=debian-cloud \
     --shielded-secure-boot --shielded-vtpm --shielded-integrity-monitoring

gcloud compute ssh cosign-test
sudo su -
apt-get update

Install docker

We’re only installing docker here to pull the public image

apt -y install apt-transport-https ca-certificates curl gnupg2 software-properties-common wget jq

curl -fsSL https://download.docker.com/linux/debian/gpg | gpg --dearmor -o /usr/share/keyrings/docker-archive-keyring.gpg

echo "deb [arch=amd64 signed-by=/usr/share/keyrings/docker-archive-keyring.gpg] https://download.docker.com/linux/debian $(lsb_release -cs) stable" | sudo tee /etc/apt/sources.list.d/docker.list

apt-get update
apt-get install docker-ce docker-ce-cli containerd.io

Install golang

wget https://go.dev/dl/go1.19.2.linux-amd64.tar.gz
rm -rf /usr/local/go && tar -C /usr/local -xzf go1.19.2.linux-amd64.tar.gz
export PATH=$PATH:/usr/local/go/bin/

Install TPM-PKCS

Now install the PKCS-11 library provided through libtpm2-pkcs11-1

apt-get update && apt-get install libtpm2-pkcs11-1 tpm2-tools libengine-pkcs11-openssl opensc -y

Initialize the TPM with an RSA KeyPair

pkcs11-tool --module /usr/lib/x86_64-linux-gnu/libtpm2_pkcs11.so.1 --slot-index= --init-token --label="token1" --so-pin="mysopin"

pkcs11-tool --module /usr/lib/x86_64-linux-gnu/libtpm2_pkcs11.so.1 --label="token1" --init-pin --so-pin mysopin --pin mynewpin

pkcs11-tool --module /usr/lib/x86_64-linux-gnu/libtpm2_pkcs11.so.1 --list-token-slots

pkcs11-tool --module /usr/lib/x86_64-linux-gnu/libtpm2_pkcs11.so.1 --list-mechanisms --slot-index 0

pkcs11-tool --module /usr/lib/x86_64-linux-gnu/libtpm2_pkcs11.so.1 --label="keylabel1" --login  --pin=mynewpin --id 0  --keypairgen --key-type rsa:2048

pkcs11-tool --module /usr/lib/x86_64-linux-gnu/libtpm2_pkcs11.so.1  --label="keylabel1" --pin mynewpin --generate-random 50 | xxd -p

Install cosign with PKCS support

Now install cosign with the pkcs-11 support (see cosign pkcs-11)

apt-get install gcc git

git clone https://github.com/sigstore/cosign.git
cd cosign
go build -tags=pkcs11key ./cmd/cosign
export PATH=$PATH:`pwd`

Pull and sign

Pull the image

export IMAGE=docker.io/salrashid123/myimage:server
export IMAGE_DIGEST=$IMAGE@sha256:83ab2ba6689713f2d68104cd208feadfebdd6bc881c455dcb55d2b45ac3a0753

docker pull $IMAGE_DIGEST

Set the PKCS URI values specific to the slot,key,id we setup earlier

export COSIGN_PKCS11_MODULE_PATH="/usr/lib/x86_64-linux-gnu/libtpm2_pkcs11.so.1"
export COSIGN_PKCS11_PIN="mynewpin"

export PKCS11_PUBLIC_KEY="pkcs11:model=vTPM;manufacturer=GOOG;serial=0000000000000000;token=token1;type=public;object=keylabel1?pin-value=mynewpin"
export PKCS11_PRIVATE_KEY="pkcs11:model=vTPM;manufacturer=GOOG;serial=0000000000000000;token=token1;type=private;object=keylabel1?pin-value=mynewpin"

Make sure cosign integration is working and the pkcs-11 module is up

$ cosign pkcs11-tool list-tokens --module-path /usr/lib/x86_64-linux-gnu/libtpm2_pkcs11.so.1

    Listing tokens of PKCS11 module '/usr/lib/x86_64-linux-gnu/libtpm2_pkcs11.so.1'
    Token in slot 1
        Label: token1
        Manufacturer: GOOG
        Model: vTPM
        S/N: 0000000000000000

    Token in slot 2
        Label: 
        Manufacturer: GOOG
        Model: vTPM
        S/N: 0000000000000000

Finally sign

$  cosign sign --key "$PKCS11_PRIVATE_KEY" --upload=false $IMAGE_DIGEST --no-tlog-upload=true --output-signature sig.txt

# your signature value will clearly be different
$ cat sig.txt 
HHLheqKOy/ra6KAs3018Pmb4idl3BqIn/haosST6dDSafHJqd8NHycC7cQyK6ua6g5wGjx9ktr59aDt875Usfpn0x1RCXqeAnmx3vyxVv1FSGZO2xrt1p/aT3JctIAn0ys+VCESpvQ4Ag+qDNvh0/gnNVxTXXoQ0g4ffOwEwAv9+bFNUMB4AeL7ngz6MgLo0Ll8MI41ee2xDen+M8ttUpRLWMLo63jFb9hftAxFNiJI8ls83jlTtbnPsr9fQUHHHdJgE50TdngtPBAaL5Gvpxvyy32oKcCBGFrz+6jLjIT1zbdT5Xxmg5kKV83WEwvG4XbOcL0WFDrhEYmJeD27FxA==

export public key to file and verify

pkcs11-tool --module /usr/lib/x86_64-linux-gnu/libtpm2_pkcs11.so.1 --slot 1 --read-object --type pubkey --id 0  -o rsa01pub.key

openssl rsa -pubin -inform DER -in rsa01pub.key -outform PEM -out rsa01pub.pem

cosign verify --key rsa01pub.pem $IMAGE --signature sig.txt | jq '.'

Alternatively, use pkcs-11 public key reference

cosign verify --key "$PKCS11_PUBLIC_KEY" $IMAGE --signature sig.txt | jq '.'

Either way, you’ll see verification completed

Verification for index.docker.io/salrashid123/myimage:server --
The following checks were performed on each of these signatures:
  - The cosign claims were validated
  - The signatures were verified against the specified public key
[
  {
    "critical": {
      "identity": {
        "docker-reference": "index.docker.io/salrashid123/myimage"
      },
      "image": {
        "docker-manifest-digest": "sha256:83ab2ba6689713f2d68104cd208feadfebdd6bc881c455dcb55d2b45ac3a0753"
      },
      "type": "cosign container image signature"
    },
    "optional": null
  }
]

THats it


Appendix

The following is just references showing details of the embedded device

$ pkcs11-tool --module /usr/lib/x86_64-linux-gnu/libtpm2_pkcs11.so.1 --list-mechanisms --slot-index 0

    Using slot with index 0 (0x1)
    Supported mechanisms:
    RSA-PKCS-KEY-PAIR-GEN, keySize={1024,2048}, hw, generate_key_pair
    RSA-X-509, keySize={1024,2048}, hw, encrypt, decrypt, sign, verify
    RSA-PKCS, keySize={1024,2048}, hw, encrypt, decrypt, sign, verify
    RSA-PKCS-OAEP, keySize={1024,2048}, hw, encrypt, decrypt
    SHA1-RSA-PKCS, keySize={1024,2048}, hw, sign, verify
    SHA256-RSA-PKCS, keySize={1024,2048}, hw, sign, verify
    SHA384-RSA-PKCS, keySize={1024,2048}, hw, sign, verify
    ECDSA-KEY-PAIR-GEN, keySize={224,521}, hw, generate_key_pair
    ECDSA, keySize={224,521}, hw, sign, verify
    ECDSA-SHA1, keySize={224,521}, hw, sign, verify
    AES-KEY-GEN, keySize={16,32}, hw, generate
    AES-CBC, keySize={16,32}, hw, encrypt, decrypt
    mechtype-0x2107, keySize={16,32}, hw, encrypt, decrypt
    AES-ECB, keySize={16,32}, hw, encrypt, decrypt
    SHA-1, digest
    SHA256, digest
    SHA384, digest
    SHA512, digest


$ pkcs11-tool --module  /usr/lib/x86_64-linux-gnu/libtpm2_pkcs11.so.1  --list-token-slots 

    Available slots:
    Slot 0 (0x1): token1                          GOOG
    token label        : token1
    token manufacturer : GOOG
    token model        : vTPM
    token flags        : login required, rng, token initialized, PIN initialized
    hardware version   : 1.42
    firmware version   : 22.17
    serial num         : 0000000000000000
    pin min/max        : 0/128
    Slot 1 (0x2):                                 GOOG
    token state:   uninitialized

$ pkcs11-tool --module /usr/lib/x86_64-linux-gnu/libtpm2_pkcs11.so.1  --list-objects

    Using slot 0 with a present token (0x1)
    Public Key Object; RSA 2048 bits
    label:      keylabel1
    ID:         00
    Usage:      encrypt, verify
    Access:     local

$ pkcs11-tool --module /usr/lib/x86_64-linux-gnu/libtpm2_pkcs11.so.1 --slot-index= --init-token --label="token1" --so-pin="mysopin"

    Using slot with index 0 (0x1)
    Token successfully initialized

$ pkcs11-tool --module /usr/lib/x86_64-linux-gnu/libtpm2_pkcs11.so.1 --label="token1" --init-pin --so-pin mysopin --pin mynewpin

    Using slot 0 with a present token (0x1)
    User PIN successfully initialized

$ pkcs11-tool --module /usr/lib/x86_64-linux-gnu/libtpm2_pkcs11.so.1 --list-token-slots

    Available slots:
    Slot 0 (0x1): token1                          GOOG
    token label        : token1
    token manufacturer : GOOG
    token model        : vTPM
    token flags        : login required, rng, token initialized, PIN initialized
    hardware version   : 1.42
    firmware version   : 22.17
    serial num         : 0000000000000000
    pin min/max        : 0/128
    Slot 1 (0x2):                                 GOOG
    token state:   uninitialized


$ pkcs11-tool --module /usr/lib/x86_64-linux-gnu/libtpm2_pkcs11.so.1 --label="keylabel1" --login  --pin=mynewpin --id 0  --keypairgen --key-type rsa:2048

    Using slot 0 with a present token (0x1)
    Key pair generated:
    Private Key Object; RSA 
    label:      keylabel1
    ID:         00
    Usage:      decrypt, sign
    Access:     sensitive, always sensitive, never extractable, local
    Allowed mechanisms: RSA-X-509,RSA-PKCS-OAEP,RSA-PKCS,SHA1-RSA-PKCS,SHA256-RSA-PKCS,SHA384-RSA-PKCS,SHA512-RSA-PKCS,RSA-PKCS-PSS,SHA1-RSA-PKCS-PSS,SHA256-RSA-PKCS-PSS,SHA384-RSA-PKCS-PSS,SHA512-RSA-PKCS-PSS
    Public Key Object; RSA 2048 bits
    label:      keylabel1
    ID:         00
    Usage:      encrypt, verify
    Access:     local

$ pkcs11-tool --module /usr/lib/x86_64-linux-gnu/libtpm2_pkcs11.so.1  --label="keylabel1" --pin mynewpin --generate-random 50 | xxd -p

    Using slot 0 with a present token (0x1)
    2f3fe08ffdb6c65dbc24dbccec277ae9614005b63ed586ba0f813814ae08
    18db453c29db67867bcdc2e51ecd2f568f312a9f

This site supports webmentions. Send me a mention via this form.