From 0b89e3173962c817e389d80ef5d18eadae8fe14f Mon Sep 17 00:00:00 2001 From: Anton Tolchanov Date: Wed, 19 Nov 2025 09:40:48 +0000 Subject: [PATCH] cmd/nodekey: add a tool to print nodekey --- cmd/nodekey/nodekey.go | 85 ++++++++++++++++++++++++++++++++++++++++++ 1 file changed, 85 insertions(+) create mode 100644 cmd/nodekey/nodekey.go diff --git a/cmd/nodekey/nodekey.go b/cmd/nodekey/nodekey.go new file mode 100644 index 000000000..97ff6cbcf --- /dev/null +++ b/cmd/nodekey/nodekey.go @@ -0,0 +1,85 @@ +package main + +import ( + "fmt" + "log" + "os" + + "go4.org/mem" + "tailscale.com/types/key" +) + +func main() { + for _, arg := range os.Args[1:] { + k, err := key.ParseNodePublicUntyped(mem.B([]byte(arg))) + if err != nil { + log.Printf("invalid node key %q: %v", arg, err) + } + b, err := parseDebug32(arg) + if err != nil { + log.Printf("invalid debug32 format for %q: %v", arg, err) + } + k = key.NodePublicFromRaw32(mem.B(b[:])) + log.Printf("node key: %s", k.ShortString()) + log.Printf("node key: %s", k.String()) + } +} + +// parseDebug32 attempts to reconstruct the first 4 bytes of a key +// from the Tailscale debug representation like "[abcde]". +// The remaining bytes will be zero. +func parseDebug32(s string) ([32]byte, error) { + var k [32]byte + + if s == "" { + return k, nil + } + if len(s) != 7 || s[0] != '[' || s[6] != ']' { + return k, fmt.Errorf("invalid debug32 format %q", s) + } + + decode64 := func(c byte) (byte, bool) { + switch { + case 'A' <= c && c <= 'Z': + return c - 'A', true + case 'a' <= c && c <= 'z': + return c - 'a' + 26, true + case '0' <= c && c <= '9': + return c - '0' + 52, true + case c == '+': + return 62, true + case c == '/': + return 63, true + default: + return 0, false + } + } + + v0, ok := decode64(s[1]) + if !ok { + return k, fmt.Errorf("invalid debug32 format %q", s) + } + v1, ok := decode64(s[2]) + if !ok { + return k, fmt.Errorf("invalid debug32 format %q", s) + } + v2, ok := decode64(s[3]) + if !ok { + return k, fmt.Errorf("invalid debug32 format %q", s) + } + v3, ok := decode64(s[4]) + if !ok { + return k, fmt.Errorf("invalid debug32 format %q", s) + } + v4, ok := decode64(s[5]) + if !ok { + return k, fmt.Errorf("invalid debug32 format %q", s) + } + + k[0] = v0<<2 | v1>>4 + k[1] = v1<<4 | v2>>2 + k[2] = v2<<6 | v3 + k[3] = v4 << 2 // low 2 bits were discarded by debug32 + + return k, nil +}