What it does

After I told a friend about Constellation, he straight out asked me about hardware wallet support. I mentioned that Ledger still takes some time, but ideally I'd like to carry my wallet on a Yubikey.

From that point onwards I was hooked. I wondered: does the Yubikey has the capabilities to sign DAG transactions?I looked up if the Yubikey supports the required signature curve secp256k1, and sure it does! The OpenPGP spec requires it and it is supported by the Yubikey.

How we built it

Next I had an extensive chat with ChatGPT as to how both security and broad usability is ensured. As most dapps are used through wallets like Stargazer, a solution like this must be implemented into Stargazer Wallet directly. ChatGPT told me I can use a Native Messaging Host that allows a browser extension to run a process on the local host machine. Which that I'd be able to call the gpg command line utility and interact with the Yubikey through that.

So I implemented it. I started digging into Stargazer. I reverse-engineered the Bitfi and Ledger hardware wallet UIs. I've implemented the local briding software that calls gpg. I extracted the public keys from the Yubikey. Generated DAG addresses from them. But then..

Challenges we ran into

The signatures are invalid. I noted that signatures of the same data are different. I wondered whether the network requires deterministic signatures? That'd be bad as we can't control the salt generation of the Yubikey. It must be random. I asked the team and they ensured that random salts are accepted by the network. So that's not it, and there's still hope for a Yubikey Wallet.

It turns out, gpg --sign adds a lot of unrelated metadata to the signed message, altering the SHA512 hash digest that is signed, resulting in the network rejecting the signatures.

Accomplishments that we're proud of

But that's not to give up. I dug further. I found out that one can send raw binary APDU commands to the Yubikey. This is what gets us to sign arbitrary SHA512 digest hashes directly on the key, with the raw signature returned.

However, the transactions still got rejected. What was wrong?

What we learned

After more fiddling we got Javascript libraries and dag4.js iteself considering the Yubikey-generated signatures as valid. We extracted the raw r and s values of the ECDSA signatures in a structured way and fixed some encoding issues that way. Also we allow low s values. But the network still rejects them.

We then found out that the Yubikey returns the signature as two raw 32 byte hex values, whereas the DAG L1 expects the signatures to be ASN.1 DER encoded. So we fixed that. But still, transactions are rejected by the network.

What's next for Constellation Yubikey Hardware Wallet

So I've built a full wallet integration just to sign invalid transactions? No, that's not the end of the story. I'm committed to debug this signing pipeline until the 1 DAG is moved out of my Yubikey! Never gonna give you up, never gonna let you down.

Actually the 1 DAG moved out of my wallet by now, so in some way the Yubikey-generated signature was accepted by the network 🥳

Built With

Share this project:

Updates