-
Notifications
You must be signed in to change notification settings - Fork 11
Commit
This commit does not belong to any branch on this repository, and may belong to a fork outside of the repository.
pass regex by argv, use new PrivKey format
- Loading branch information
Showing
1 changed file
with
92 additions
and
27 deletions.
There are no files selected for viewing
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -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 := crypto.ConfigEncodeKey(privateKeyBytes) | ||
|
||
keyChan <- Key{ | ||
PrettyID: prettyID, | ||
Index: idx, | ||
Part: part, | ||
PrivateKey64: privateKey64, | ||
} | ||
} | ||
} |