Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Age malformed recipient - invalid type "age1yubikey" #1103

Open
dot-mike opened this issue Aug 6, 2022 · 19 comments
Open

Age malformed recipient - invalid type "age1yubikey" #1103

dot-mike opened this issue Aug 6, 2022 · 19 comments

Comments

@dot-mike
Copy link

dot-mike commented Aug 6, 2022

Hello!
I'm using Age with Yubikey. Support for plugins was recently added to Age v1.1.0-rc.1 repository, but the validation still fails in SOPS.
Looks like the validation for identity fails because the parse-method of Age was never updated outside of Cmd...
Please look into adding support using Age with Yubikey, the plugin I'm using is this: https://github.com/str4d/age-plugin-yubikey

The commits inAge repo that adds plugin functionality : FiloSottile/age@cff70cf...87a982b

Example error:

sops --verbose --encrypt --age age1yubikeyreallylongstringherethatmakesupmypublickey file.yml
failed to parse input as Bech32-encoded age public key: malformed recipient "age1yubikeyreallylongstringherethatmakesupmypublickey": invalid type "age1yubikey"
@yoadey
Copy link

yoadey commented Aug 9, 2022

Have the same problem, latest version of sops (v3.7.3) and age (v1.0.0).

This probably requires to include age v1.1.0, as only there was included the plugin mechanism required for the age-plugin-yubikey.
Having age v1.1.0-rc in the path does not seem to be of any help, as it is included as a library, so library dependency must be updated once age v1.1.0 is released.

@aellwein
Copy link

I have the same problem using latest versions of sops and age.

@fifofonix
Copy link

I have the same problem even when doing a sops make install from the latest master branch having bumped the age package dependency to v1.1.0-rc.1.

@Threnklyn
Copy link

Age got a v1.1.1 release a few days ago. Now it isn't a release candidate anymore.

@fifofonix
Copy link

A bump to the latest age release makes 100% sense but I did a make build with the newest version specified as the age dependency but still experienced this issue. Seems that something in the sops code needs to change to enable on-yubikey encryption keys still.

@Threnklyn
Copy link

check your age version. Maybe the brew wasnt updated already.
You need version 1.1.x

@glennlawyer
Copy link

check your age version. Maybe the brew wasnt updated already. You need version 1.1.x

Yes, running brew update, brew upgrade did the trick, by upgrading age from 1.0.x to 1.1.x

@bamhm182
Copy link

bamhm182 commented Jan 5, 2023

Just dropping in to confirm that this issue still exists with age 1.1.1 and sops build from the develop branch as well as 3.7.3.

@fifofonix
Copy link

Still getting the same issue when trying to encrypt with a yubikey-based age identity.

In my environment age --version shows v1.1.1 and I am able to encrypt/decrypt directly with age without issue with both yubikey-based and non-yubikey-based age identities. I am using the brew installed version.

Regardless of whether I use the brew installed version of sops (v3.7.3) or I build it from source (first updating the vendored age go module dependency to v1.1.1), I get the original OP's error on encryption operations. And, if I use a non-yubikey-based age identity everything works fine.

The yubikey-based age public key (aka receipient) is of the form: 'age1yubikey'. This is a different format to that of a regular public age key which is of the form: 'age'.

@myoung34
Copy link

myoung34 commented Jan 27, 2023

✗ sops --version                                                                             
sops 3.7.3 (latest)

✗ age --version    
v1.1.1

✗ sops --encrypt --age $(age-plugin-yubikey -l --serial 11087061 --slot 1 | grep -vE '^#') controlplane.yaml        
failed to parse input as Bech32-encoded age public key: malformed recipient "age1yubikey1qgsrfvr...snip...": invalid type "age1yubikey"

However it should be noted this is with age, not sops

➜  cat main.go 
package main

import (
        "filippo.io/age"
        "fmt"
)

func main() {
        foo, err := age.ParseX25519Recipient("age1yubikey1qgsr...snip")
        if err != nil {
                fmt.Println("failed to parse input as Bech32-encoded age public key: %w", err)
        } else {
                fmt.Println(fmt.Sprintf("%+v", foo))
        }

}

➜  go run main.go
failed to parse input as Bech32-encoded age public key: %w malformed recipient 
"age1yubikey1qgsr...snip": invalid type "age1yubikey"

from here

Ive started a discussion upstream here if anyone has anything to add or wants to follow

@BrokenStandards
Copy link

Quick summary here.
Age Plugin api is marked as internal right now. No exposed api exists yet for supporting plugins directly.
age command currently handles dispatching age1/age1pluginName1 (yes, 1 is the deliminator) itself in the command library age/cmd/age/parse.go. This file can probably be used as a hacky way to add support, but it comes with a terminalUI which sops may want to but cannot replace.

I outright stole the code from parse.go and moved the plugin out of internal to test it locally to confirm it works with just code from these 2 files. If its not clear, I am not recommending this experiment. Just noting it does work and as hacky as it is, itsn't terribly hard to enable once you know where everything is. So it will likely be trivial once the api is finalized and exposed. Hardest part is probably deciding how to handle pin input and prompts. terminal? pinentry? Is SOPS_AGE_PIN env variable a terrible idea to support?

@phaer
Copy link

phaer commented Jul 28, 2023

Age Plugin api is marked as internal right now. No exposed api exists yet for supporting plugins directly.

Not sure if I find time to try this myself anytime soon, but as sops pgp backend supports both, using go-crypto/opengpg a library and shelling out to gpg, a similar solution might be possible for age? i.e. if we had an option to just call age or rage, support for plugins would be free, if I am not mistaken?

@robinp
Copy link

robinp commented Dec 21, 2023

Getting inspiration from FiloSottile/age#86 (comment), this is how you can approximate using yubikey-stored age key with sops today:

The idea is to create an age key, but store it encrypted, where the encryption key is the yubikey-stored age key. Then when invoking sops, decrypt the stored key on the fly using the yubikey. I think this is less secure than using the yubikey-age-key directly, but better than nothing (as long as you don't store the decrypted key anywhere just pass to sops).

Edit: Elaboration on why this is not entirely like a yubikey-stored key: if the host is compromised, an attacker can intercept the (temporarily) decrypted age key and steal it, performing further decrypts (or even encrypts) with it in the future. If the decompression of the sops-protected secret were done using a fully yubikey-stored age key, then the attacker can only steal the decompressed secret values, but not the key itself. So "only" the current set of secrets is compromised, but not the key itself.

Demonstration:

$ age-keygen  | age -e -r age1yubikey1q0p7guhr6dg2d56y66yxkzmwy98wxxcfuak7dt36ndqz95ld4tvr6fuah4n -o key.enc
Public key: age1uyvqk9kqjenmnwn08szr87cp6vgrr6dltt8cgardhhd5x0qfjcxqtdjucz
$ sops -e --age age1uyvqk9kqjenmnwn08szr87cp6vgrr6dltt8cgardhhd5x0qfjcxqtdjucz -i y.yaml
$ SOPS_AGE_KEY=$(age -i /path/to/the/age-yubikey-identity-0f5da44b.txt -d key.enc) sops -d y.yaml
# Enter PIN
# touch

@Swoorup
Copy link

Swoorup commented Jan 11, 2025

Although I realize I could probably use gpg keys instead of age for yubikey support, gpg are tad complex than age. Would be great to have this support built in to sops.

@felixfontein
Copy link
Contributor

I think #1641 (and its predecessors) implement this. It's basically waiting for FiloSottile/age#591, which has received no feedback at all from the age maintainer so far.

@sylvorg
Copy link

sylvorg commented Jan 11, 2025

Should sops just temporarily use the fork instead, then switch back when the age maintainer (hopefully) merges the changes, or is that not good practice...? 😅

@felixfontein
Copy link
Contributor

IMO using a fork is not a good idea, since someone needs to make sure it is always up-to-date with respect to updates to upstream age (in praticular with regard to potential security issues). A fork is a measure of last resort, if there is no better way, but I don't think it's that far yet.

(A fork would be ok from my side if it's clear that it is only needed temporarily, and if it is either low-risk (from security POV) or under our (maintainer team's) own control. Since it's unclear whether the age PR even has a remote chance of getting merged, and age is a high-risk dependency, I'd prefer not to depend on a fork.)

@sylvorg
Copy link

sylvorg commented Jan 12, 2025

Forgive my ignorance on the matter, but could a CI not automatically pull changes from the main repo and merge them to the fork, and the fork maintainer would only need to deal with conflicts?

@TahlonBrahic
Copy link

I am maintaining a fork with age1yubikey support at https://github.com/TahlonBrahic/age

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
None yet
Projects
None yet
Development

No branches or pull requests