1. 23
    1. 12

      I’m also no cryptographer, but it seems to me that deterministically generating private keys from passwords… almost totally subverts the security model of public/private key based cryptography? Like the private key is supposed to be “something you have” bit, not the “something you know” bit; you generate them for each e.g. device, not each user?

      1. 4

        Yea, I’m agreeing that it kinda subverts it, that’s why “toy” or whatever you wanna label it :) However looking at the crypto currency (I’m referring to BIP-39 and BIP-32 for example) they have done it for many years now. If you get a hardware wallet, be prepared to have to write down 24 words on a paper which could recover your key if lost.

        My idea was to do the same with backup keys for servers, cause I’m working in environments with FreeIPA and/or CA authentication which sadly can fail and where you wish you had a physical vault somewhere with root ssh keys. (Luckly never happened to my workplace, but it’s a realistic threat if the right services/servers goes down at the same time - recall facebook’s lockout back some months)

        So at least for my usage, it’s not for everyday usage but only for having a analog (pen-paper) backup where I don’t have to manually type in a PEM encoded private key in vim which seemed horrible :)

        So at least in my case, it’s not something I know, it’s something I have - secret is too long for me to remember it :)

        BIP refs; https://github.com/bitcoin/bips/blob/master/bip-0039/bip-0039-wordlists.md https://github.com/bitcoin/bips/blob/master/bip-0032.mediawiki

        1. 4

          A quick glance at BIP-39 suggests that they’re deriving the 24 words from a randomly generated key, rather than deriving a key from a list of 24 words. Is that what you’re trying to accomplish? You could generate an ed25519 key and convert it to a word list using the same methodology as BIP-39 (i.e. divide the key in 11-bit chunks and lookup the unique word associated with each chunk), no? (Correct me if I’m wrong about what BIP-39 is doing).

          1. 1

            Yea, my toy tool is in the opposite order with BIP39.

            In BIP39 they make a seed first, then calculate 24 words for you to save which is revert-able back into the seed. The way I use it myself for the backup keys is that I pipe something equal to the entropy of the seed into it, which for example could look like (a random generated seed from https://iancoleman.io/bip39/ I just did ) “470273efc564f694df89980967f9ee9d26df59ac7ea1eda9dda7458fc952fe3f4affa6162e71c70d5c0db44fdbac67f06619334412d82412319c0dc96510a1b4”

            However in my opinion I think this (a hex key) is waaaay better to re-type into the digital world in a disaster recovery than something PEM base64 encoded. Like the example bellow. Fun when you lack a “A” or something in the rows of A’s for example, or miss a capital/downcase. Exactly what you would need when you’re stressed due to production is down :P It’s like installing “sl” on production servers if you know the prank tool.

            So for me, it’s about pleasure in a future horrible disaster recovery. What someone else that find this useful for is up to them, but I also tried to make it very clear that stupid usage of this tool can be a disaster in itself :)

            —–BEGIN OPENSSH PRIVATE KEY—– b3BlbnNzaC1rZXktdjEAAAAABG5vbmUAAAAEbm9uZQAAAAAAAAABAAAAMwAAAAtz c2gtZWQyNTUxOQAAACB2LyhtKUdHZGdnpktmRSsBf2zW1/zorATpx2yPdqdSUQAA AIiaywRCmssEQgAAAAtzc2gtZWQyNTUxOQAAACB2LyhtKUdHZGdnpktmRSsBf2zW 1/zorATpx2yPdqdSUQAAAEA0YjU5ODZiZTk4MTY2MzA4Y2NiZGMwMzU1NTVkYjc3 ZHYvKG0pR0dkZ2emS2ZFKwF/bNbX/OisBOnHbI92p1JRAAAAAAECAwQF —–END OPENSSH PRIVATE KEY—–

            1. 1

              base32 or even base58 would be improvements over base64 for manual entry for sure.

              rsa keys were quite large though back in the day, and I dont think manual entry was much expected, so base64 made a bit more sense then.

    2. 7

      Looking through the code, it appears to first hex encode and then truncate the resulting hash. That means no matter how much input you feed it, you don’t have a 256 bit input seed, you only have at best a 128 bit input seed. Now, ed25519 claims to target a 128 bit security level. I’m also not a cryptographer, but I’d guess that because the constructor expects 32 byte inputs or it panics, 256 bits of entropy is probably required to get the claimed security levels, and your keys may be significantly weaker.

      Additionally, the tool doesn’t consume the full input, just the first line, which means that if you do ( echo foo; echo bar ) | anything2ed25519, you get the same key as ( echo foo ) | anything2ed25519. That seems like a dangerous footgun on an already dangerous tool.

      Given the above problems, I don’t think the disclaimer of “the crypto is safe” on your README is warranted.

      1. 1

        Good spot. I’ll fix that and remove the safe crypto at once. :)

        However, the limit is in golang’s implementation;

        NewKeyFromSeed calculates a private key from a seed. It will panic if len(seed) is not SeedSize. This function is provided for interoperability with RFC 8032. RFC 8032’s private keys correspond to seeds in this package.

        https://cs.opensource.google/go/go/+/refs/tags/go1.17.6:src/crypto/ed25519/ed25519.go;l=113

        That limit doesn’t really make sense either since the next thing the code does is to sha512 sums it.

        1. 1

          That limit, and the sha512, are both described to be those specific values by the referenced RFC. I’d imagine they exist for a reason.

      2. 1

        That means no matter how much input you feed it, you don’t have a 256 bit input seed, you only have at best a 128 bit input seed.

        Please read more carefully then. It’s SHA256 summed before the step you’re talking about meaning everything you input will be taken into the seed, but as a sum of a 256 bit hash. The first line bug was true, and it’s patched. (Thanks a lot for telling also)

        It was written 3-4 or maybe more years ago so I had to refresh myself with the code and I realised it is hashed before truncate. See https://github.com/mikalv/anything2ed25519/blob/main/main.go#L52

        1. 2

          I’m not sure how to more clearly explain this, but I’ll try again. The output of a sha256 hash is 256 bits (32 bytes). By hex encoding it, you end up with 512 bits (64 bytes) of data representing the original 256 bits. By then passing the first 32 bytes of the hex encoded data to the seed generation, you are only actually passing the first 16 bytes (or 128 bits) of the hash.

          In other words, hexEncode(data[:16]) == hexEncode(data)[:32] (https://go.dev/play/p/DApGUgSuDy6)

          1. 2

            Oh thanks. Perfectly now, I see my mistake and has pushed a fix for it where I don’t encode it before passing it. I should probably have reviewed it much more closely before publishing as I’m more aware of such and other common crypto mistakes today than back some years. My bad, sorry!

            And again, thanks a lot for the constructive feedback :)

            1. 1

              No problem! Thanks for hearing it out and fixing the problems.

              1. 1

                Yea, sorry I read your first comment once again and I did get it on the first comment as well, but I had to have misread it or something the first time. I also updated the blog post itself warning about it’s being patched and a thanks to you for pointing it out :)

    3. 5

      Not sure why you’re showing a complicated block diagram, since you can do this in two or three lines with libSodium or Monocypher — you’re just SHA’ing the input and then deriving a key-pair from it, which is 2 API calls.

      Anyway, that is absolutely not how you derive a key from a password, which is what you seem to be using it for. There are specialized algorithms for that, like Argon8 or bcrypt. You want key derivation to be very slow and expensive, and also incorporate an app-specific salt value, otherwise an attacker can sit around feeding a (widely available) list of the top hundred million most common passwords through your algorithm and hitting your system with the resulting keys until they find one that works.

      Much better than that is to store the Ed25519 key in a secure storage system like a password manager or the Mac/iOS Keychain. That way the user doesn’t need yet another password.

      1. 2

        This, yes! “Always feed passwords through argon2 before anything else” will get you pretty far.

        That said, argon2 will require a salt and parameters to be stored somewhere, so it doesn’t apply quite so readily here.

      2. 1

        Sorry now I’m tired of repeating myself. This is for offline storage. meaning NO password manager or keychain…

        If you read this tool as a password to ssh key then please read again or explain me where you misunderstood what I wrote?

        The seed can be at any size you wish, and at some point your random data would move from “category password” to “pleasant recovery - private key” as it can have the size of any other ECC curve key or even RSA (even it’s hashed before entering the curve in the end).

        Why?

        Well because it’s much better to write a (in my case) HEX string with sufficient entropy back into a computer from a paper rather than base64.

        why? Like the example bellow. Fun when you lack a “A” or something in the rows of A’s for example, or miss a capital/downcase. Exactly what you would need when you’re stressed due to production is down in a disaster recovery. It’s like installing “sl” on production servers if you know the prank tool.

        —–BEGIN OPENSSH PRIVATE KEY—– b3BlbnNzaC1rZXktdjEAAAAABG5vbmUAAAAEbm9uZQAAAAAAAAABAAAAMwAAAAtz

        So my counter question then: How would keychain or password manager help when the systems having it is down or misconfigured so you can’t enter??

        Won’t recovery ed25519 keys make you sleep safer than having SSH allow root login with “recovery password”?

        Lastly, I’m not pushing this on you. Use your own tools instead, I’m happy when you use the tools you’re okay with, then I can use what I’m ok with :)

    4. 2

      What’s your motivation for releasing this into the world? By your own admission, it’s a “dangerous toy”; to me, that means it 1) could cause damage, and 2) doesn’t have much real-world use. So if it’s dangerous and it doesn’t provide much real value, what are you or anyone else gaining by making this easily available to anyone who can use GitHub? And how does that balance with the risk?

      Please don’t be stupid and use this key, or anything else with a WEAK seed.

      I’m also not a cryptographer, and when I read this phrase, my first question is “so what qualifies as a weak seed?”. I don’t feel confident answering that question, because I’m not a cryptographer, and so I’m not going to use this tool for anything. But is someone else who is less cautious or is more confident in their own understanding of cryptography going to come to the same conclusion?

      1. 3

        What’s your motivation for releasing this into the world?

        I’ve used open source all my life, just wanted to share something back in case anyone needed such a tool.

        I disagree in the real world usage since it works as disaster recovery keys for me. But if we all where to go around deciding what’s usable or not for everyone we’ll have a dark future ahead of us.

        RFC 8032 says; The private key is 32 octets (256 bits, corresponding to b) of cryptographically secure random data.

        which then is sufficient.

        1. 2

          Not sure that releasing something dangerously misuseable counts as giving back.

          I really don’t understand why you don’t just read 32 bytes from /dev/random, generate a Ed25519 key-pair from that, and then print out a base64 encoding of the private key to keep somewhere safe. It’s only 43 characters to type in. Going the other direction by saving a passphrase and generating the key from that has far less entropy.

          1. 1

            People release ransomware and other shit on github, I don’t feel my knife is sharp enough to be categorised on that level. :)

            But I’m also doing what you’re saying, my example on my post uses “openssl rand -base64 64” to generate the secret. The only difference is the order things are happening, but I still always go for a secure random string.

            It’s when you start with human brain entropy you’re in bad place and this will start hurt you. We’re totally agreeing about that part.

            Since yesterday I updated the code and you’ll even have to use a force flag to use anything less than 32 chars. It don’t protect people from creating keys out of 32x A’s either, but I’m a coder / wannabe crytpo and not some kindergarten assistant for the internet :)

    5. 2
    6. [Comment removed by author]

    7. 1

      Barring the cryptographic security issues already mentioned in this thread, this is a pretty cool toy and idea.