diff --git a/main.go b/main.go index ccfa8a4..17e4e78 100644 --- a/main.go +++ b/main.go @@ -1,96 +1,161 @@ package main +/* +based on +https://github.com/ipfs/go-ipfs-keystore/blob/master/keystore.go +https://github.com/ipfs/go-ipfs/blob/master/core/coreapi/key.go +https://github.com/ipfs/go-ipfs/blob/master/core/node/identity.go +*/ + import ( "crypto/rand" "fmt" - "io/ioutil" "log" "os" "regexp" "runtime" - "strings" + //"strings" + "encoding/base64" + //"encoding/base32" "github.com/libp2p/go-libp2p-core/crypto" "github.com/libp2p/go-libp2p-core/peer" ) var ( - alphabet = regexp.MustCompile("^[123456789abcdefghijklmnopqrstuvwxyz]+$") numWorkers = runtime.NumCPU() ) // Key stores PrettyID containing desired substring at Index type Key struct { PrettyID string - Index int + Index int + Part string + PrivateKey64 string } func main() { if len(os.Args) != 2 { fmt.Printf(` -This tool generates IPFS public and private keypair until it finds public key -which contains required substring. Keys are stored in local directory. If you -like one of them, you can move it to ~/.ipfs/keystore/ to use it with IPFS. +This tool can generate IPFS public and private keypairs +and filter the public keys by regex, to find vanity IPFS public keys + +Keypairs are printed to stdout and to output.txt + +To use a key, edit your ~/.ipfs/config file, which is generated by ipfs init +Identity.PeerID is your public key +Identity.PrivKey is your private key in base64 encoding Usage: - %s {part} - For fast results suggested length of public key part is 4-5 characters + %s {regex} + For fast results, use a short substring of 4-5 characters + +Alphabet of public key: + [1-9a-zA-Z] + +Regex samples: + hello$ + (?i)hello + (foo|bar)$ + +Regex basics: + (?i) add this prefix to make the regex case-insensitive + (a|b) a or b + $ end of string + ^ start of string. not needed here, the start is mostly constant + +Regex docs: + https://duckduckgo.com/?q=golang+regexp + https://yourbasic.org/golang/regexp-cheat-sheet/ + https://golangdocs.com/regex-in-golang-regexp-package + `, os.Args[0]) os.Exit(1) } - part := strings.ToLower(os.Args[1]) - if !alphabet.MatchString(part) { - fmt.Println("{part} must match the alphabet:", alphabet.String()) - os.Exit(2) + + partRegex := regexp.MustCompile(os.Args[1]) + // TODO? validate regex with alphabet + //alphabet = regexp.MustCompile("^[123456789abcdefghijklmnopqrstuvwxyz]+$") + + outputFile, err := os.OpenFile("output.txt", os.O_APPEND | os.O_CREATE | os.O_WRONLY, 0644) + if err != nil { + log.Println(err) } + defer outputFile.Close() + runtime.GOMAXPROCS(numWorkers) keyChan := make(chan Key) for i := 0; i < numWorkers; i++ { go func() { - err := generateKey(part, keyChan) + err := generateKey(partRegex, keyChan) if err != nil { log.Fatal(err) } }() } for key := range keyChan { + fmt.Printf( - "%s\u001b[32m%s\u001b[0m%s\n", + // TODO allow to disable color + "%s %s\u001b[32m%s\u001b[0m%s %s\n", + //"%s %s%s%s %s\n", + key.Part, key.PrettyID[:key.Index], - key.PrettyID[key.Index:len(part)+key.Index], - key.PrettyID[len(part)+key.Index:]) + key.PrettyID[key.Index:len(key.Part)+key.Index], + key.PrettyID[len(key.Part)+key.Index:], + key.PrivateKey64, + ) + // TODO allow to print json format for ~/.ipfs/config + /* + fmt.Printf( + "%s\n \"Identity\": {\n \"PeerID\": \"%s\",\n \"PrivKey\": \"%s\",\n },\n\n", + key.Part, key.PrettyID, key.PrivateKey64, + ) + */ + + // TODO allow to disable writing to output.txt + _, err := outputFile.WriteString( + fmt.Sprintf("%s %s %s\n", key.Part, key.PrettyID, key.PrivateKey64), + ) + if err != nil { + log.Println(err) + } } } -func generateKey(part string, keyChan chan Key) error { +func generateKey(partRegex *regexp.Regexp, keyChan chan Key) error { for { privateKey, publicKey, err := crypto.GenerateEd25519Key(rand.Reader) if err != nil { return err } + peerID, err := peer.IDFromPublicKey(publicKey) if err != nil { return err } prettyID := peerID.Pretty() - lowerID := strings.ToLower(prettyID) - idx := strings.Index(lowerID, part) - if idx == -1 { + + if !partRegex.MatchString(prettyID) { continue } - privateKeyBytes, err := privateKey.Raw() - if err != nil { - return err - } - err = ioutil.WriteFile(prettyID, privateKeyBytes, 0600) - // TODO print privateKey in base64, as in ~/.ipfs/config .Identity.PrivKey + + part := partRegex.FindString(prettyID) + idx := partRegex.FindStringIndex(prettyID)[0] + + privateKeyBytes, err := crypto.MarshalPrivateKey(privateKey) if err != nil { return err } + + privateKey64 := base64.StdEncoding.EncodeToString(privateKeyBytes) + keyChan <- Key{ PrettyID: prettyID, Index: idx, + Part: part, + PrivateKey64: privateKey64, } } }