Kushal Das

FOSS and life. Kushal Das talks here.


SamNet Vinterkonferensen 2023

This Tuesday I attended SamNet Vinterkonferensen, jointly organized by ISOC-SE, SNUS, DFRI and Dataskydd.net, focusing on technology, the internet, privacy, and decentralization. The organizers gave me caution before hand as the whole conference was in Swedish :)

selfie from 83

The venue was Internetstiftelsen, which is already one of my favorite small conference venues in Stockholm (as we have done many open spaces there in the last 1 year).

After the morning coffee and breakfast, the day started with a talk about "blockchain", it felt more like a 2015 version of the presentation :) After that a very good detailed description of IPv6 and adoption. The third talk was on DNS from Mikael Kullberg. This presentation was a perfect mix of technical details and fun :)

slide from DNS talk

After the fika break, there was another govt talk about e-identification. And it broke my brain. The level of Swedish was too much, and my brain refused to do any real-time translation/understanding of the Swedish afterward. So, I spent the time in the lobby talking to people and writing some code.


The second half starts with Tobias Pulls talking about his work on anonymity and Tor network. There are a few slides with detailed graphs, and I had difficulty to understand them. Though Pulls mentioned before that he had to work hard to get all the English terms translated into Swedish. Next, MC took the stage to talk about Tillitis.


Last part of the day I spent listening to folks discussion different DNS/packets/anonymity related topics.


My goal was to meet more people and listen to more technical discussions in Swedish. So, I count the conference a success :)

Using YubiKeys for your linux system

You can use your Yubikey 4 or 5 for the rest of the tutorial.


If you mark your Yubikey presence is required to unlock your computer, then one not only needs your password, they will have to gain physical access to your Yubikey.

Install the required packages

$ sudo dnf install ykclient* ykpers* pam_yubico*

Getting the Yubikey(s) ready

Connect the Yubikey to your system, and see if it is not getting detected.

$ ykinfo -v
version: 5.2.7

If the system can not find the Yubikey, then it will show the following error.

Yubikey core error: no yubikey present

Then, for each of the Yubikey, we have the run the following command once:

$ ykpersonalize -2 -ochal-resp -ochal-hmac -ohmac-lt64 -ochal-btn-trig -oserial-api-visible
Firmware version 4.2.7 Touch level 517 Program sequence 1

Configuration data to be written to key configuration 2:

fixed: m:
uid: n/a
key: h:9d97972ff90267d7cff02b49d41f85a68325805c
acc_code: h:000000000000
ticket_flags: CHAL_RESP
extended_flags: SERIAL_API_VISIBLE

Commit? (y/n) [n]: y

Here we are configuring the slot 2, with challenge-response mode, and HMAC (even less than 64 bytes), and also saying that the human has to touch the physical key by providing CHAL_BTN_TRIG, also making the serial API visible.

$ ykpamcfg -2 -v
debug: util.c:219 (check_firmware_version): YubiKey Firmware version: 5.2.7

Sending 63 bytes HMAC challenge to slot 2
Sending 63 bytes HMAC challenge to slot 2
Stored initial challenge and expected response in '/home/kdas/.yubico/challenge-16038846'.

Remember to touch the key button twice after the command sends in 63 bytes, the LED on the key should blink that that time.

Setting up GDM

Now, we can mark that the Yubikey must be present during login, and after touching the key, one still has to type in the password, or for lesser security context, one needs either the Yubikey or password to login.

For the first scenario, add the following to the /etc/pam.d/gdm-password file, just above the auth substack password-auth line.

auth        required      pam_yubico.so mode=challenge-response

If you want either password or Yubikey to work, then replace required with sufficient.

Verify the setup

You will have to logout of Gnome, and then when you click your username while relogin, you will notice that the Yubikey is blinking. Touch it, and then enter password to complete login.

To setup sudo

The similar configuration changes required to be made in /etc/pam.d/sudo. But, remember to keep the sudo session open in one terminal, then try to test the sudo command in another one. Just in case :)

To learn more about the pam configuration, read man pam.conf.

Introducing Tugpgp

At Sunet, we have heavy OpenPGP usage. But, every time a new employee joins, it takes hours (and sometime days for some remote folks) to have their Yubikey + OpenPGP setup ready.

Final screen

Tugpgp is a small application built with these specific requirements for creating OpenPGP keys & uploading to Yubikeys as required in Sunet. The requirements are the following:

  • It will create RSA 4096 Key
  • There will be a primacy key with Signing & Certification capability.
  • There will be an encryption and one authentication subkey.
  • All keys have 1 year expiry date.
  • During the process the secret key will not be written to the disk.
  • Encryption & signing has touch policy fixed in the Yubikey (it can not be changed).
  • Authentication has touch policy on (means it can be turned off by the user).
  • The OTP application in the Yubikey will be disabled at the end.

We have an Apple Silicon dmg and AppImage (for Ubuntu 20.04 onwards) in the release page. This is my first ever AppImage build, the application still needs pcscd running on the host system. I tested it on Debian 11, Fedora 37 with Yubikey 4 & Yubikey 5.

Oh, there is also a specific command line argument if you really want to save the private key :) But, you will have to find it yourself :).

demo gif

If you are looking for the generic all purpose application which will allow everyone of us to deal with OpenPGP keys and Yubikeys, then you should check the upcoming release of Tumpa, we have a complete redesign done there (after proper user research done by professionals).

Johnnycanencrypt 0.13.0 released

I just now released v0.13.0 of my johnnycanencrypt project. It is a Python module written in Rust, which provides OpenPGP functionality including allows usage of Yubikey 4/5 as smartcards. From 0.12.0 it is now licensed as LGPL-3.0-or-later.

Major updates in this release (and in the previous one are):

  • Adds enable_otp_usb in rjce.
  • Adds disable_otp_usb in rjce.
  • Changed license to LGPL-3.0-or-later
  • We can now disable OTP for both YubiKey4/5 #131.

For many folks disabling the OTP application in the Yubikey is must, otherwise we all saw random bytes dropping on the shell/document/chat thanks to a touch to the key. I tested the code in both Yubikey 4 and 5. I hope this will work well in the field.

Johnnycanencrypt 0.11.0 released

A couple of days ago I released Johnnycanencrypt v0.11.0. It is a Python module for OpenPGP written in Rust.

The most interesting update is for Linux users, now we have pre-built wheels for Python 3.8, 3.9, 3.10 & 3.11. You can just install that via python3 -m pip install johnnycanencrypt. You can also do the same on Intel Macs (for Python 3.10 and 3.11). But, I failed to build for Apple Silicon systems. I will work on it in the coming weeks.

To know the Yubikey card version we can call get_card_version function written in Rust.

>>> rjce.get_card_version()
(4, 3, 1)

Or directly use the function get_card_touch_policies to get a list of policies for the current Yubikey.

>>> import johnnycanencrypt as jce
>>> jce.get_card_touch_policies()
[TouchMode.On, TouchMode.Off, TouchMode.Fixed]

Then we now can also set the touch policies for a given Yubikey. For example:

    b"12345678", rjce.KeySlot.Authentication, rjce.TouchMode.On

It will be nice to see what people will build on top this. I also missed to blog about the v0.10.0 release. You should that changelog too.

khata, under WASI

While I am learning about WebAssembly slowly, I was also trying to figure out where all I can use it. That is the way I general learn all new things. So, as a start, I thought of compiling my static blogging tool khata into WASM and then run it under WASI.

While trying to do that, the first thing I noticed that I can not just create a subprocess (I was doing it to call rsync internally), so that was top priority thing to fix in the list. First, I tried to use rusync crate, but then it failed at runtime as it is using threads internally. After asking around a bit more in various discord channels, I understood that the easiest way would be to just write that part of the code myself.

Which is now done, means this blog post is actually rendered using wasmtime.

wasmtime --dir=. khata.wasm

I am very slowly learning about the SPEC, and the limitations. But, this is very interesting and exciting learning journey so far.

Johnnycanencrypt 0.9.0 release

3 days ago I released Johnnycanencrypt 0.9.0. Here is the changelog:

- Adds `setuptools-rust` as build system.
- Key.uids now contains the certification details of each user id.
- `merge_keys` in rjce now takes a force boolean argument.
- `certify_key` can sign/certify another key by both card and on disk primary key.

The first biggest change is related to build system, now we are using setuptools-rust to build. This change happened as dkg is working towards packaging the module for Debian.

The other big change is about certifying someone's key. We can use the primary key (either on disk or on Yubikey) to do the signing.

k = ks.certify_key(
    ["Kushal Das <kushaldas@gmail.com>", "Kushal Das <kushal@fedoraproject.org>"],

In the above example I am signing two user ids of the key k using my_key with a PositiveCertification.

johnnycanencrypt 0.7.0 released

Today I released Johnnycanencrypt 0.7.0. It has breaking change of some function names.

  • create_newkey renamed to create_key
  • import_cert renamed to import_key

But, the major work done are in few different places:

  • Handling errors better, no more normal Rust panics, instead providing better Python exceptions as CryptoError.
  • We can now sign bytes/files in both detached & in normal compressed binary form.
  • Signature can be done via smartcards, and verification works as usual.

In the Github release page you can find an OpenPGP signature, which you can use to verify the release. You can also verify via sigstore.

SIGSTORE_LOGLEVEL=debug python -m sigstore verify --cert-email mail@kushaldas.in --cert-oidc-issuer https://github.com/login/oauth johnnycanencrypt-0.7.0.tar.gz
DEBUG:sigstore._cli:parsed arguments Namespace(subcommand='verify', certificate=None, signature=None, cert_email='mail@kushaldas.in', cert_oidc_issuer='https://github.com/login/oauth', rekor_url='https://rekor.sigstore.dev', staging=False, files=[PosixPath('johnnycanencrypt-0.7.0.tar.gz')])
DEBUG:sigstore._cli:Using certificate from: johnnycanencrypt-0.7.0.tar.gz.crt
DEBUG:sigstore._cli:Using signature from: johnnycanencrypt-0.7.0.tar.gz.sig
DEBUG:sigstore._cli:Verifying contents from: johnnycanencrypt-0.7.0.tar.gz
DEBUG:sigstore._verify:Successfully verified signing certificate validity...
DEBUG:sigstore._verify:Successfully verified signature...
DEBUG:urllib3.connectionpool:Starting new HTTPS connection (1): rekor.sigstore.dev:443
DEBUG:urllib3.connectionpool:https://rekor.sigstore.dev:443 "POST /api/v1/index/retrieve/ HTTP/1.1" 200 85
DEBUG:urllib3.connectionpool:https://rekor.sigstore.dev:443 "GET /api/v1/log/entries/362f8ecba72f4326972bc321d658ba3c9197b29bb8015967e755a97e1fa4758c13222bc07f26d27c HTTP/1.1" 200 None
DEBUG:sigstore._verify:Successfully verified Rekor entry...
OK: johnnycanencrypt-0.7.0.tar.gz

I took 8 months for this release, now time to write some tools to use it in more places :)

Using sigstore-python to sign and verify your software release

Sigstore allows software developers to quickly sign and verify the software they release. Many of the bigger projects use hardware-based OpenPGP keys to sign and release. But the steps used to make sure that the end-users are correctly verifying those signatures are long, and people make mistakes. Also, not every project has access to hardware smartcards, air-gapped private keys etc. Sigstore solves (or at least makes it way easier) these steps for most developers. It uses existing known (right now only 3) big OIDC providers using which one can sign and verify any data/software.

For this blog post, I will use the python tool called sigstore-python.

The first step is to create a virtual environment and then install the tool.

$ python3 -m venv .venv
$ source .venv/bin/activate
$ python -m pip install -r install/requirements.txt

Next, we create a file called message.txt with the data. This can be our actual release source code tarball.

$ echo "Kushal loves Python!" > message.txt

Signing the data

The next step is to actually sign the file.

$ python -m sigstore sign message.txt 
Waiting for browser interaction...
Using ephemeral certificate:

Transparency log entry created at index: 2844439
Signature written to file message.txt.sig
Certificate written to file message.txt.crt

The command will open up the default browser, and we will have the choice to select one of the 3 following OIDC providers.

oidc providers

This will also create message.txt.crt & message.txt.sig files in the same directory.

We can use the openssl command to see the contents of the certificate file.

$ openssl x509 -in message.txt.crt -noout -text
        Version: 3 (0x2)
        Serial Number:
        Signature Algorithm: ecdsa-with-SHA384
        Issuer: O = sigstore.dev, CN = sigstore-intermediate
            Not Before: Jul  5 14:45:23 2022 GMT
            Not After : Jul  5 14:55:23 2022 GMT
        Subject Public Key Info:
            Public Key Algorithm: id-ecPublicKey
                Public-Key: (384 bit)
                ASN1 OID: secp384r1
                NIST CURVE: P-384
        X509v3 extensions:
            X509v3 Key Usage: critical
                Digital Signature
            X509v3 Extended Key Usage: 
                Code Signing
            X509v3 Subject Key Identifier: 
            X509v3 Authority Key Identifier: 
            X509v3 Subject Alternative Name: critical
            CT Precertificate SCTs: 
                Signed Certificate Timestamp:
                    Version   : v1 (0x0)
                    Log ID    : 08:60:92:F0:28:52:FF:68:45:D1:D1:6B:27:84:9C:45:
                    Timestamp : Jul  5 14:45:23.112 2022 GMT
                    Extensions: none
                    Signature : ecdsa-with-SHA256
    Signature Algorithm: ecdsa-with-SHA384
    Signature Value:

Verifying the signature

We can verify the signature, just make sure that the certificate & signature files are in the same directory.

$ python -m sigstore verify message.txt 
OK: message.txt

Now, to test this with some real software releases, we will download the cosign RPM package and related certificate & signature files. The certificate in this case, is base64 encoded, so we decode that file first.

$ curl -sOL https://github.com/sigstore/cosign/releases/download/v1.9.0/cosign-1.9.0.x86_64.rpm
$ curl -sOL https://github.com/sigstore/cosign/releases/download/v1.9.0/cosign-1.9.0.x86_64.rpm-keyless.sig
$ curl -sOL https://github.com/sigstore/cosign/releases/download/v1.9.0/cosign-1.9.0.x86_64.rpm-keyless.pem
$ base64 -d cosign-1.9.0.x86_64.rpm-keyless.pem > cosign-1.9.0.x86_64.rpm.pem

Now let us verify the downloaded RPM package along with the email address and signing OIDC issuer URL. We are also printing the debug statements, so that we can see what is actually happening for verification.

$ SIGSTORE_LOGLEVEL=debug python -m sigstore verify --certificate cosign-1.9.0.x86_64.rpm.pem --signature cosign-1.9.0.x86_64.rpm-keyless.sig --cert-email keyless@projectsigstore.iam.gserviceaccount.com --cert-oidc-issuer https://accounts.google.com  cosign-1.9.0.x86_64.rpm

DEBUG:sigstore._cli:parsed arguments Namespace(subcommand='verify', certificate=PosixPath('cosign-1.9.0.x86_64.rpm.pem'), signature=PosixPath('cosign-1.9.0.x86_64.rpm-keyless.sig'), cert_email='keyless@projectsigstore.iam.gserviceaccount.com', cert_oidc_issuer='https://accounts.google.com', rekor_url='https://rekor.sigstore.dev', staging=False, files=[PosixPath('cosign-1.9.0.x86_64.rpm')])
DEBUG:sigstore._cli:Using certificate from: cosign-1.9.0.x86_64.rpm.pem
DEBUG:sigstore._cli:Using signature from: cosign-1.9.0.x86_64.rpm-keyless.sig
DEBUG:sigstore._cli:Verifying contents from: cosign-1.9.0.x86_64.rpm
DEBUG:sigstore._verify:Successfully verified signing certificate validity...
DEBUG:sigstore._verify:Successfully verified signature...
DEBUG:urllib3.connectionpool:Starting new HTTPS connection (1): rekor.sigstore.dev:443
DEBUG:urllib3.connectionpool:https://rekor.sigstore.dev:443 "POST /api/v1/index/retrieve/ HTTP/1.1" 200 69
DEBUG:urllib3.connectionpool:https://rekor.sigstore.dev:443 "GET /api/v1/log/entries/9ee91f2c5444e4ff77a3a18885f46fa2b6f7e629450904d67b5920333327b90d HTTP/1.1" 200 None
DEBUG:sigstore._verify:Successfully verified Rekor entry...
OK: cosign-1.9.0.x86_64.rpm

Oh, one more important thing. The maintainers of the tool are amazing about feedback. I had some trouble initially (a few weeks ago). They sat down with me to make sure that they could understand the problem & also solved the issue I had. You can talk to the team (and other users, including me) in the slack room.