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 · 13 comments
Open

Age malformed recipient - invalid type "age1yubikey" #1103

dot-mike opened this issue Aug 6, 2022 · 13 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

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