123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215 |
- // Copyright (C) 2016 The Syncthing Authors.
- //
- // This Source Code Form is subject to the terms of the Mozilla Public
- // License, v. 2.0. If a copy of the MPL was not distributed with this file,
- // You can obtain one at https://mozilla.org/MPL/2.0/.
- package main
- import (
- "crypto/ecdsa"
- "crypto/elliptic"
- "crypto/rand"
- "crypto/rsa"
- "crypto/x509"
- "crypto/x509/pkix"
- "encoding/pem"
- "errors"
- "flag"
- "fmt"
- "math/big"
- mr "math/rand"
- "os"
- "runtime"
- "strings"
- "sync"
- "sync/atomic"
- "time"
- _ "github.com/syncthing/syncthing/lib/automaxprocs"
- "github.com/syncthing/syncthing/lib/protocol"
- )
- type result struct {
- id protocol.DeviceID
- priv *ecdsa.PrivateKey
- derBytes []byte
- }
- func main() {
- flag.Parse()
- prefix := strings.ToUpper(strings.ReplaceAll(flag.Arg(0), "-", ""))
- if len(prefix) > 7 {
- prefix = prefix[:7] + "-" + prefix[7:]
- }
- found := make(chan result)
- stop := make(chan struct{})
- var count atomic.Int64
- // Print periodic progress reports.
- go printProgress(prefix, &count)
- // Run one certificate generator per CPU core.
- var wg sync.WaitGroup
- for i := 0; i < runtime.GOMAXPROCS(-1); i++ {
- wg.Add(1)
- go func() {
- generatePrefixed(prefix, &count, found, stop)
- wg.Done()
- }()
- }
- // Save the result, when one has been found.
- res := <-found
- close(stop)
- wg.Wait()
- fmt.Println("Found", res.id)
- saveCert(res.priv, res.derBytes)
- fmt.Println("Saved to cert.pem, key.pem")
- }
- // Try certificates until one is found that has the prefix at the start of
- // the resulting device ID. Increments count atomically, sends the result to
- // found, returns when stop is closed.
- func generatePrefixed(prefix string, count *atomic.Int64, found chan<- result, stop <-chan struct{}) {
- notBefore := time.Now()
- notAfter := time.Date(2049, 12, 31, 23, 59, 59, 0, time.UTC)
- template := x509.Certificate{
- SerialNumber: new(big.Int).SetInt64(mr.Int63()),
- Subject: pkix.Name{
- CommonName: "syncthing",
- },
- NotBefore: notBefore,
- NotAfter: notAfter,
- KeyUsage: x509.KeyUsageKeyEncipherment | x509.KeyUsageDigitalSignature,
- ExtKeyUsage: []x509.ExtKeyUsage{x509.ExtKeyUsageServerAuth, x509.ExtKeyUsageClientAuth},
- BasicConstraintsValid: true,
- }
- priv, err := ecdsa.GenerateKey(elliptic.P384(), rand.Reader)
- if err != nil {
- fmt.Println(err)
- os.Exit(1)
- }
- for {
- select {
- case <-stop:
- return
- default:
- }
- derBytes, err := x509.CreateCertificate(rand.Reader, &template, &template, &priv.PublicKey, priv)
- if err != nil {
- fmt.Println(err)
- os.Exit(1)
- }
- id := protocol.NewDeviceID(derBytes)
- count.Add(1)
- if strings.HasPrefix(id.String(), prefix) {
- select {
- case found <- result{id, priv, derBytes}:
- case <-stop:
- }
- return
- }
- }
- }
- func printProgress(prefix string, count *atomic.Int64) {
- started := time.Now()
- wantBits := 5 * len(prefix)
- if wantBits > 63 {
- fmt.Printf("Want %d bits for prefix %q, refusing to boil the ocean.\n", wantBits, prefix)
- os.Exit(1)
- }
- expectedIterations := float64(int(1) << uint(wantBits))
- fmt.Printf("Want %d bits for prefix %q, about %.2g certs to test (statistically speaking)\n", wantBits, prefix, expectedIterations)
- for range time.NewTicker(15 * time.Second).C {
- tried := count.Load()
- elapsed := time.Since(started)
- rate := float64(tried) / elapsed.Seconds()
- expected := timeStr(expectedIterations / rate)
- fmt.Printf("Trying %.0f certs/s, tested %d so far in %v, expect ~%s total time to complete\n", rate, tried, elapsed/time.Second*time.Second, expected)
- }
- }
- func saveCert(priv interface{}, derBytes []byte) {
- certOut, err := os.Create("cert.pem")
- if err != nil {
- fmt.Println(err)
- os.Exit(1)
- }
- err = pem.Encode(certOut, &pem.Block{Type: "CERTIFICATE", Bytes: derBytes})
- if err != nil {
- fmt.Println(err)
- os.Exit(1)
- }
- err = certOut.Close()
- if err != nil {
- fmt.Println(err)
- os.Exit(1)
- }
- keyOut, err := os.OpenFile("key.pem", os.O_WRONLY|os.O_CREATE|os.O_TRUNC, 0o600)
- if err != nil {
- fmt.Println(err)
- os.Exit(1)
- }
- block, err := pemBlockForKey(priv)
- if err != nil {
- fmt.Println(err)
- os.Exit(1)
- }
- err = pem.Encode(keyOut, block)
- if err != nil {
- fmt.Println(err)
- os.Exit(1)
- }
- err = keyOut.Close()
- if err != nil {
- fmt.Println(err)
- os.Exit(1)
- }
- }
- func pemBlockForKey(priv interface{}) (*pem.Block, error) {
- switch k := priv.(type) {
- case *rsa.PrivateKey:
- return &pem.Block{Type: "RSA PRIVATE KEY", Bytes: x509.MarshalPKCS1PrivateKey(k)}, nil
- case *ecdsa.PrivateKey:
- b, err := x509.MarshalECPrivateKey(k)
- if err != nil {
- return nil, err
- }
- return &pem.Block{Type: "EC PRIVATE KEY", Bytes: b}, nil
- default:
- return nil, errors.New("unknown key type")
- }
- }
- func timeStr(seconds float64) string {
- if seconds < 60 {
- return fmt.Sprintf("%.0fs", seconds)
- }
- if seconds < 3600 {
- return fmt.Sprintf("%.0fm", seconds/60)
- }
- if seconds < 86400 {
- return fmt.Sprintf("%.0fh", seconds/3600)
- }
- if seconds < 86400*365 {
- return fmt.Sprintf("%.0f days", seconds/3600)
- }
- return fmt.Sprintf("%.0f years", seconds/86400/365)
- }
|