* How to verify a wireguard public key? @ 2020-12-24 16:00 Nico Schottelius 2020-12-24 23:30 ` Jason A. Donenfeld 2020-12-24 23:42 ` Adam Stiles 0 siblings, 2 replies; 9+ messages in thread From: Nico Schottelius @ 2020-12-24 16:00 UTC (permalink / raw) To: wireguard Good morning, I am currently extending uncloud [0] to support wireguard tunnels and keys. At the moment it is not entirely clear how to verify that a certain string is a valid wireguard key. I first tried checking that it is valid base64, but not all base64 strings are valid wireguard keys. Then I tried using `echo $key | wg pubkey && echo ok` - which seems to check the key format, however the intended behaviour here is misused. Does anyone have a pointer on how to reliably identify wireguard public keys? Is the wireguard key always 32 bytes when decoded from base64? Tests with a number of public keys seems to indicate that. Best regards, Nico [0] https://code.ungleich.ch/uncloud/uncloud -- Modern, affordable, Swiss Virtual Machines. Visit www.datacenterlight.ch ^ permalink raw reply [flat|nested] 9+ messages in thread
* Re: How to verify a wireguard public key? 2020-12-24 16:00 How to verify a wireguard public key? Nico Schottelius @ 2020-12-24 23:30 ` Jason A. Donenfeld 2020-12-24 23:42 ` Adam Stiles 1 sibling, 0 replies; 9+ messages in thread From: Jason A. Donenfeld @ 2020-12-24 23:30 UTC (permalink / raw) To: Nico Schottelius; +Cc: wireguard It's probably wisest to ignore differences between public keys and private keys and set aside any structure they might have by virtue of being related to elliptic curves, and instead just regard them as 32-byte strings encoded in base64. Not 31 bytes or 33 bytes, but exactly 32. This matters, because 32 does not divide evenly by .75, so there's a padding character and the penultimate character does not include the whole base64 alphabet. 43 base64 chars can represent up to 258bits, which is more than 256bits. So, you can either validate this with a base64 parser and checking that it returns exactly 32 bytes, or you can match against this simple regex: ^[A-Za-z0-9+/]{42}[A|E|I|M|Q|U|Y|c|g|k|o|s|w|4|8|0]=$ You can convince yourself that's correct by running this for a while and seeing that it never fails: while true; do [[ $(head -c 32 /dev/urandom | base64) =~ ^[A-Za-z0-9+/]{42}[A|E|I|M|Q|U|Y|c|g|k|o|s|w|4|8|0]=$ ]] || echo "FAILURE"; done The endings are valid because those are the only ones that don't end in 01, 10, or 11, so that the string doesn't exceed 256 bits. And again we can have bash bruteforce those for us: for i in {A..Z} {a..z} {0..9} + /; do a="AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA$i="; [[ $(echo $a|base64 -d|base64) == $a ]] && echo -n $i; done; echo AEIMQUYcgkosw048 ^ permalink raw reply [flat|nested] 9+ messages in thread
* Re: How to verify a wireguard public key? 2020-12-24 16:00 How to verify a wireguard public key? Nico Schottelius 2020-12-24 23:30 ` Jason A. Donenfeld @ 2020-12-24 23:42 ` Adam Stiles 2020-12-25 9:10 ` Nico Schottelius 2020-12-25 22:16 ` Matthias Urlichs 1 sibling, 2 replies; 9+ messages in thread From: Adam Stiles @ 2020-12-24 23:42 UTC (permalink / raw) To: Nico Schottelius; +Cc: wireguard Hi Nico, WireGuard uses Curve25519 keys. A Curve25519 secret key is a random 32 byte value with a few special bits flipped, and a public key is calculated from a secret key. There's some good info here (https://cr.yp.to/ecdh.html), including this questions and answer: "How do I validate Curve25519 public keys?" "Don't. The Curve25519 function was carefully designed to allow all 32-byte strings as Diffie-Hellman public keys." I just saw Jason's response, and so this is a bit redundant, but the reference above is a good one. Best, Adam On Thu, Dec 24, 2020 at 3:21 PM Nico Schottelius <nico.schottelius@ungleich.ch> wrote: > > > Good morning, > > I am currently extending uncloud [0] to support wireguard tunnels and > keys. At the moment it is not entirely clear how to verify that a > certain string is a valid wireguard key. > > I first tried checking that it is valid base64, but not all base64 > strings are valid wireguard keys. > > Then I tried using `echo $key | wg pubkey && echo ok` - which seems to > check the key format, however the intended behaviour here is misused. > > Does anyone have a pointer on how to reliably identify wireguard public > keys? > > Is the wireguard key always 32 bytes when decoded from base64? Tests > with a number of public keys seems to indicate that. > > Best regards, > > Nico > > > [0] https://code.ungleich.ch/uncloud/uncloud > > -- > Modern, affordable, Swiss Virtual Machines. Visit www.datacenterlight.ch ^ permalink raw reply [flat|nested] 9+ messages in thread
* Re: How to verify a wireguard public key? 2020-12-24 23:42 ` Adam Stiles @ 2020-12-25 9:10 ` Nico Schottelius 2020-12-25 23:37 ` Matthias May 2020-12-25 22:16 ` Matthias Urlichs 1 sibling, 1 reply; 9+ messages in thread From: Nico Schottelius @ 2020-12-25 9:10 UTC (permalink / raw) To: Adam Stiles; +Cc: Nico Schottelius, wireguard Good morning Adam and Jason, thanks for your qualified and fast answers! It's nice to see Dan's website still referenced in almost 2021 and also that it can be easily enough verified. For reference and if anyone ever looks up this thread, I am using the following code within the Django Rest Framework [0]: -------------------------------------------------------------------------------- def validate_wireguard_public_key(self, value): msg = _("Supplied key is not a valid wireguard public key") """ Verify wireguard key. See https://lists.zx2c4.com/pipermail/wireguard/2020-December/006221.html """ try: decoded_key = base64.standard_b64decode(value) except Exception as e: raise serializers.ValidationError(msg) if not len(decoded_key) == 32: raise serializers.ValidationError(msg) return value -------------------------------------------------------------------------------- Thanks again and enjoy the quite time over Christmas! Best regards, Nico [0] https://code.ungleich.ch/uncloud/uncloud/-/blob/master/uncloud_net/serializers.py#L37 Adam Stiles <ajstiles@gmail.com> writes: > Hi Nico, > > WireGuard uses Curve25519 keys. A Curve25519 secret key is a random 32 > byte value with a few special bits flipped, and a public key is > calculated from a secret key. > > There's some good info here (https://cr.yp.to/ecdh.html), including > this questions and answer: > > "How do I validate Curve25519 public keys?" > > "Don't. The Curve25519 function was carefully designed to allow all > 32-byte strings as Diffie-Hellman public keys." > > I just saw Jason's response, and so this is a bit redundant, but the > reference above is a good one. > > Best, > > Adam > > > On Thu, Dec 24, 2020 at 3:21 PM Nico Schottelius > <nico.schottelius@ungleich.ch> wrote: >> >> >> Good morning, >> >> I am currently extending uncloud [0] to support wireguard tunnels and >> keys. At the moment it is not entirely clear how to verify that a >> certain string is a valid wireguard key. >> >> I first tried checking that it is valid base64, but not all base64 >> strings are valid wireguard keys. >> >> Then I tried using `echo $key | wg pubkey && echo ok` - which seems to >> check the key format, however the intended behaviour here is misused. >> >> Does anyone have a pointer on how to reliably identify wireguard public >> keys? >> >> Is the wireguard key always 32 bytes when decoded from base64? Tests >> with a number of public keys seems to indicate that. >> >> Best regards, >> >> Nico >> >> >> [0] https://code.ungleich.ch/uncloud/uncloud >> >> -- >> Modern, affordable, Swiss Virtual Machines. Visit www.datacenterlight.ch -- Modern, affordable, Swiss Virtual Machines. Visit www.datacenterlight.ch ^ permalink raw reply [flat|nested] 9+ messages in thread
* Re: How to verify a wireguard public key? 2020-12-25 9:10 ` Nico Schottelius @ 2020-12-25 23:37 ` Matthias May 2020-12-25 23:47 ` Jason A. Donenfeld 0 siblings, 1 reply; 9+ messages in thread From: Matthias May @ 2020-12-25 23:37 UTC (permalink / raw) To: Nico Schottelius, Adam Stiles; +Cc: wireguard On 25/12/2020 10:10, Nico Schottelius wrote: > > Good morning Adam and Jason, > > thanks for your qualified and fast answers! It's nice to see Dan's > website still referenced in almost 2021 and also that it can be easily > enough verified. > > For reference and if anyone ever looks up this thread, I am using > the following code within the Django Rest Framework [0]: > > -------------------------------------------------------------------------------- > def validate_wireguard_public_key(self, value): > msg = _("Supplied key is not a valid wireguard public key") > > """ > Verify wireguard key. > See https://urldefense.com/v3/__https://lists.zx2c4.com/pipermail/wireguard/2020-December/006221.html__;!!I9LPvj3b!XDmxiY_v3yY5wQI9GfFvshrCIUcqg4vvKg35qvL0fFajgNHTwr3LcySSqHrNgCByOzQ$ > """ > > try: > decoded_key = base64.standard_b64decode(value) > except Exception as e: > raise serializers.ValidationError(msg) > > if not len(decoded_key) == 32: > raise serializers.ValidationError(msg) > > return value > -------------------------------------------------------------------------------- > > Thanks again and enjoy the quite time over Christmas! > > Best regards, > > Nico > > [0] https://urldefense.com/v3/__https://code.ungleich.ch/uncloud/uncloud/-/blob/master/uncloud_net/serializers.py*L37__;Iw!!I9LPvj3b!XDmxiY_v3yY5wQI9GfFvshrCIUcqg4vvKg35qvL0fFajgNHTwr3LcySSqHrNb-aKgNg$ > > > Adam Stiles <ajstiles@gmail.com> writes: > >> Hi Nico, >> >> WireGuard uses Curve25519 keys. A Curve25519 secret key is a random 32 >> byte value with a few special bits flipped, and a public key is >> calculated from a secret key. >> >> There's some good info here (https://urldefense.com/v3/__https://cr.yp.to/ecdh.html__;!!I9LPvj3b!XDmxiY_v3yY5wQI9GfFvshrCIUcqg4vvKg35qvL0fFajgNHTwr3LcySSqHrNDoxdskk$ ), including >> this questions and answer: >> >> "How do I validate Curve25519 public keys?" >> >> "Don't. The Curve25519 function was carefully designed to allow all >> 32-byte strings as Diffie-Hellman public keys." >> >> I just saw Jason's response, and so this is a bit redundant, but the >> reference above is a good one. >> >> Best, >> >> Adam >> >> >> On Thu, Dec 24, 2020 at 3:21 PM Nico Schottelius >> <nico.schottelius@ungleich.ch> wrote: >>> >>> >>> Good morning, >>> >>> I am currently extending uncloud [0] to support wireguard tunnels and >>> keys. At the moment it is not entirely clear how to verify that a >>> certain string is a valid wireguard key. >>> >>> I first tried checking that it is valid base64, but not all base64 >>> strings are valid wireguard keys. >>> >>> Then I tried using `echo $key | wg pubkey && echo ok` - which seems to >>> check the key format, however the intended behaviour here is misused. >>> >>> Does anyone have a pointer on how to reliably identify wireguard public >>> keys? >>> >>> Is the wireguard key always 32 bytes when decoded from base64? Tests >>> with a number of public keys seems to indicate that. >>> >>> Best regards, >>> >>> Nico >>> >>> >>> [0] https://urldefense.com/v3/__https://code.ungleich.ch/uncloud/uncloud__;!!I9LPvj3b!XDmxiY_v3yY5wQI9GfFvshrCIUcqg4vvKg35qvL0fFajgNHTwr3LcySSqHrNE6JpRjQ$ >>> >>> -- >>> Modern, affordable, Swiss Virtual Machines. Visit https://urldefense.com/v3/__http://www.datacenterlight.ch__;!!I9LPvj3b!XDmxiY_v3yY5wQI9GfFvshrCIUcqg4vvKg35qvL0fFajgNHTwr3LcySSqHrNmbUvisY$ > > > -- > Modern, affordable, Swiss Virtual Machines. Visit https://urldefense.com/v3/__http://www.datacenterlight.ch__;!!I9LPvj3b!XDmxiY_v3yY5wQI9GfFvshrCIUcqg4vvKg35qvL0fFajgNHTwr3LcySSqHrNmbUvisY$ > Hi On this topic, i recently implemented a check if a key is valid in cpp with the following rather crude code: bool isValidWgKey(const string& usage, const string& key) { /* Wireguard keys are BASE64 encoded */ unsigned int _key_length = 44; unsigned int _key_offset = _key_length -1; if (key.length() != _key_length) { log("Wireguard " + usage + " has wrong length (" + to_string(key.length()) + " instead of 44)!"); return false; } size_t found = key.substr(0,_key_offset).find_first_not_of("abcdefghijklmnopqrstuvwxyz" "ABCDEFGHIJKLMNOPQRSTUVWXYZ0123456789+/"); if (found != std::string::npos) { log("Wireguard " + usage + " contains invalid character '" + key.substr(found, 1) + "'"); return false; } if (key.substr(_key_offset,1) != "=") { log("Wireguard " + usage + " ends with invalid character '" + key.substr(found, 1) + "' instead of '='"); return false; } return true; } Maybe it's useful to someone. BR Matthias ^ permalink raw reply [flat|nested] 9+ messages in thread
* Re: How to verify a wireguard public key? 2020-12-25 23:37 ` Matthias May @ 2020-12-25 23:47 ` Jason A. Donenfeld 0 siblings, 0 replies; 9+ messages in thread From: Jason A. Donenfeld @ 2020-12-25 23:47 UTC (permalink / raw) To: Matthias May; +Cc: Nico Schottelius, Adam Stiles, WireGuard mailing list On Sat, Dec 26, 2020 at 12:38 AM Matthias May <matthias.may@westermo.com> wrote: > > On 25/12/2020 10:10, Nico Schottelius wrote: > > > > Good morning Adam and Jason, > > > > thanks for your qualified and fast answers! It's nice to see Dan's > > website still referenced in almost 2021 and also that it can be easily > > enough verified. > > > > For reference and if anyone ever looks up this thread, I am using > > the following code within the Django Rest Framework [0]: > > > > -------------------------------------------------------------------------------- > > def validate_wireguard_public_key(self, value): > > msg = _("Supplied key is not a valid wireguard public key") > > > > """ > > Verify wireguard key. > > See https://urldefense.com/v3/__https://lists.zx2c4.com/pipermail/wireguard/2020-December/006221.html__;!!I9LPvj3b!XDmxiY_v3yY5wQI9GfFvshrCIUcqg4vvKg35qvL0fFajgNHTwr3LcySSqHrNgCByOzQ$ > > """ > > > > try: > > decoded_key = base64.standard_b64decode(value) > > except Exception as e: > > raise serializers.ValidationError(msg) > > > > if not len(decoded_key) == 32: > > raise serializers.ValidationError(msg) > > > > return value > > -------------------------------------------------------------------------------- > > > > Thanks again and enjoy the quite time over Christmas! > > > > Best regards, > > > > Nico > > > > [0] https://urldefense.com/v3/__https://code.ungleich.ch/uncloud/uncloud/-/blob/master/uncloud_net/serializers.py*L37__;Iw!!I9LPvj3b!XDmxiY_v3yY5wQI9GfFvshrCIUcqg4vvKg35qvL0fFajgNHTwr3LcySSqHrNb-aKgNg$ > > > > > > Adam Stiles <ajstiles@gmail.com> writes: > > > >> Hi Nico, > >> > >> WireGuard uses Curve25519 keys. A Curve25519 secret key is a random 32 > >> byte value with a few special bits flipped, and a public key is > >> calculated from a secret key. > >> > >> There's some good info here (https://urldefense.com/v3/__https://cr.yp.to/ecdh.html__;!!I9LPvj3b!XDmxiY_v3yY5wQI9GfFvshrCIUcqg4vvKg35qvL0fFajgNHTwr3LcySSqHrNDoxdskk$ ), including > >> this questions and answer: > >> > >> "How do I validate Curve25519 public keys?" > >> > >> "Don't. The Curve25519 function was carefully designed to allow all > >> 32-byte strings as Diffie-Hellman public keys." > >> > >> I just saw Jason's response, and so this is a bit redundant, but the > >> reference above is a good one. > >> > >> Best, > >> > >> Adam > >> > >> > >> On Thu, Dec 24, 2020 at 3:21 PM Nico Schottelius > >> <nico.schottelius@ungleich.ch> wrote: > >>> > >>> > >>> Good morning, > >>> > >>> I am currently extending uncloud [0] to support wireguard tunnels and > >>> keys. At the moment it is not entirely clear how to verify that a > >>> certain string is a valid wireguard key. > >>> > >>> I first tried checking that it is valid base64, but not all base64 > >>> strings are valid wireguard keys. > >>> > >>> Then I tried using `echo $key | wg pubkey && echo ok` - which seems to > >>> check the key format, however the intended behaviour here is misused. > >>> > >>> Does anyone have a pointer on how to reliably identify wireguard public > >>> keys? > >>> > >>> Is the wireguard key always 32 bytes when decoded from base64? Tests > >>> with a number of public keys seems to indicate that. > >>> > >>> Best regards, > >>> > >>> Nico > >>> > >>> > >>> [0] https://urldefense.com/v3/__https://code.ungleich.ch/uncloud/uncloud__;!!I9LPvj3b!XDmxiY_v3yY5wQI9GfFvshrCIUcqg4vvKg35qvL0fFajgNHTwr3LcySSqHrNE6JpRjQ$ > >>> > >>> -- > >>> Modern, affordable, Swiss Virtual Machines. Visit https://urldefense.com/v3/__http://www.datacenterlight.ch__;!!I9LPvj3b!XDmxiY_v3yY5wQI9GfFvshrCIUcqg4vvKg35qvL0fFajgNHTwr3LcySSqHrNmbUvisY$ > > > > > > -- > > Modern, affordable, Swiss Virtual Machines. Visit https://urldefense.com/v3/__http://www.datacenterlight.ch__;!!I9LPvj3b!XDmxiY_v3yY5wQI9GfFvshrCIUcqg4vvKg35qvL0fFajgNHTwr3LcySSqHrNmbUvisY$ > > > > > Hi > On this topic, i recently implemented a check if a key is valid in cpp with the following rather crude code: > > bool isValidWgKey(const string& usage, const string& key) > { > /* Wireguard keys are BASE64 encoded */ > unsigned int _key_length = 44; > unsigned int _key_offset = _key_length -1; > if (key.length() != _key_length) { > log("Wireguard " + usage + " has wrong length (" + to_string(key.length()) + " instead of 44)!"); > return false; > } > size_t found = key.substr(0,_key_offset).find_first_not_of("abcdefghijklmnopqrstuvwxyz" > "ABCDEFGHIJKLMNOPQRSTUVWXYZ0123456789+/"); > if (found != std::string::npos) { > log("Wireguard " + usage + " contains invalid character '" + key.substr(found, 1) + "'"); > return false; > } > if (key.substr(_key_offset,1) != "=") { > log("Wireguard " + usage + " ends with invalid character '" + > key.substr(found, 1) + "' instead of '='"); > return false; > } > return true; > } This code is incorrect because it allows keys that are up to 258bits, instead of being exactly 256bits. See my post a few messages ago about different rules for the penultimate character. https://lists.zx2c4.com/pipermail/wireguard/2020-December/006222.html Jason ^ permalink raw reply [flat|nested] 9+ messages in thread
* Re: How to verify a wireguard public key? 2020-12-24 23:42 ` Adam Stiles 2020-12-25 9:10 ` Nico Schottelius @ 2020-12-25 22:16 ` Matthias Urlichs 2020-12-26 8:09 ` Nico Schottelius 1 sibling, 1 reply; 9+ messages in thread From: Matthias Urlichs @ 2020-12-25 22:16 UTC (permalink / raw) To: wireguard [-- Attachment #1.1: Type: text/plain, Size: 337 bytes --] On 25.12.20 00:42, Adam Stiles wrote: > "How do I validate Curve25519 public keys?" You send a handshake packet to the owner of the corresponding private key and observe whether it accepted it. The question is, why do you think you need a different/additional way of verifying the public key? -- -- Matthias Urlichs [-- Attachment #2: OpenPGP digital signature --] [-- Type: application/pgp-signature, Size: 840 bytes --] ^ permalink raw reply [flat|nested] 9+ messages in thread
* Re: How to verify a wireguard public key? 2020-12-25 22:16 ` Matthias Urlichs @ 2020-12-26 8:09 ` Nico Schottelius 2020-12-26 9:03 ` Matthias Urlichs 0 siblings, 1 reply; 9+ messages in thread From: Nico Schottelius @ 2020-12-26 8:09 UTC (permalink / raw) To: Matthias Urlichs; +Cc: wireguard Matthias Urlichs <matthias@urlichs.de> writes: > On 25.12.20 00:42, Adam Stiles wrote: >> "How do I validate Curve25519 public keys?" > > You send a handshake packet to the owner of the corresponding private > key and observe whether it accepted it. > > The question is, why do you think you need a different/additional way > of verifying the public key? That answer is easy: if you add an incorrect key to your wgX.conf, wg setconf will complain and not apply it. And if you are providing automated VPNs... well, then this is something you do want to prevent. Cheers, Nico -- Modern, affordable, Swiss Virtual Machines. Visit www.datacenterlight.ch ^ permalink raw reply [flat|nested] 9+ messages in thread
* Re: How to verify a wireguard public key? 2020-12-26 8:09 ` Nico Schottelius @ 2020-12-26 9:03 ` Matthias Urlichs 0 siblings, 0 replies; 9+ messages in thread From: Matthias Urlichs @ 2020-12-26 9:03 UTC (permalink / raw) To: Nico Schottelius; +Cc: wireguard [-- Attachment #1.1: Type: text/plain, Size: 605 bytes --] On 26.12.20 09:09, Nico Schottelius wrote: > That answer is easy: if you add an incorrect key to your wgX.conf, wg > setconf will complain and not apply it. And if you are providing > automated VPNs... well, then this is something you do want to prevent. Umm, sure, but then the question is why an incorrect key would be sent through your automated VPN deployment in the first place. And if it passes the length check but is still corrupted then that's a worse failure mode than "wg setconf" complaining, 'cause at least you'd notice the latter immediately. -- -- Matthias Urlichs [-- Attachment #2: OpenPGP digital signature --] [-- Type: application/pgp-signature, Size: 840 bytes --] ^ permalink raw reply [flat|nested] 9+ messages in thread
end of thread, other threads:[~2020-12-26 9:04 UTC | newest] Thread overview: 9+ messages (download: mbox.gz / follow: Atom feed) -- links below jump to the message on this page -- 2020-12-24 16:00 How to verify a wireguard public key? Nico Schottelius 2020-12-24 23:30 ` Jason A. Donenfeld 2020-12-24 23:42 ` Adam Stiles 2020-12-25 9:10 ` Nico Schottelius 2020-12-25 23:37 ` Matthias May 2020-12-25 23:47 ` Jason A. Donenfeld 2020-12-25 22:16 ` Matthias Urlichs 2020-12-26 8:09 ` Nico Schottelius 2020-12-26 9:03 ` Matthias Urlichs
This is a public inbox, see mirroring instructions for how to clone and mirror all data and code used for this inbox; as well as URLs for NNTP newsgroup(s).