mirror of
https://github.com/syncthing/syncthing.git
synced 2024-11-16 18:41:59 -07:00
213 lines
4.9 KiB
Go
213 lines
4.9 KiB
Go
|
// 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 http://mozilla.org/MPL/2.0/.
|
||
|
|
||
|
package main
|
||
|
|
||
|
import (
|
||
|
"crypto/ecdsa"
|
||
|
"crypto/elliptic"
|
||
|
"crypto/rand"
|
||
|
"crypto/rsa"
|
||
|
"crypto/x509"
|
||
|
"crypto/x509/pkix"
|
||
|
"encoding/pem"
|
||
|
"flag"
|
||
|
"fmt"
|
||
|
"math/big"
|
||
|
mr "math/rand"
|
||
|
"os"
|
||
|
"runtime"
|
||
|
"strings"
|
||
|
"sync"
|
||
|
"sync/atomic"
|
||
|
"time"
|
||
|
|
||
|
"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.Replace(flag.Arg(0), "-", "", -1))
|
||
|
if len(prefix) > 7 {
|
||
|
prefix = prefix[:7] + "-" + prefix[7:]
|
||
|
}
|
||
|
|
||
|
found := make(chan result)
|
||
|
stop := make(chan struct{})
|
||
|
var count 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 *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.Public(), priv)
|
||
|
if err != nil {
|
||
|
fmt.Println(err)
|
||
|
os.Exit(1)
|
||
|
}
|
||
|
|
||
|
id := protocol.NewDeviceID(derBytes)
|
||
|
atomic.AddInt64(count, 1)
|
||
|
|
||
|
if strings.HasPrefix(id.String(), prefix) {
|
||
|
select {
|
||
|
case found <- result{id, priv, derBytes}:
|
||
|
case <-stop:
|
||
|
}
|
||
|
return
|
||
|
}
|
||
|
}
|
||
|
}
|
||
|
|
||
|
func printProgress(prefix string, count *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 := atomic.LoadInt64(count)
|
||
|
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, 0600)
|
||
|
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, fmt.Errorf("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)
|
||
|
}
|