You can not select more than 25 topics Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.
 
 
 

151 lines
3.1 KiB

package main
import (
"bytes"
"crypto/ed25519"
"encoding/hex"
"errors"
"fmt"
"io/ioutil"
"net/http"
"os"
"os/user"
"path/filepath"
"runtime"
"sync"
"time"
)
func validKey() (foundPublicKey ed25519.PublicKey, foundPrivateKey ed25519.PrivateKey) {
expiryYear := time.Now().Year() + 1
keyEnd := fmt.Sprintf("ed%d", expiryYear)
nRoutines := runtime.NumCPU() - 1
var waitGroup sync.WaitGroup
var once sync.Once
fmt.Println(" - looking for a key that ends in", keyEnd)
fmt.Println(" - using", nRoutines, "cores")
waitGroup.Add(nRoutines)
for i := 0; i < nRoutines; i++ {
go func(num int) {
for foundPublicKey == nil {
pub, priv, err := ed25519.GenerateKey(nil)
if err != nil {
panic(err)
}
target, err := hex.DecodeString(keyEnd)
if err != nil {
panic(err)
}
if bytes.Compare(pub[29:32], target) == 0 {
once.Do(func() {
fmt.Printf("%s\n", fmt.Sprintf("%x", pub))
foundPublicKey = pub
foundPrivateKey = priv
})
}
}
waitGroup.Done()
}(i)
}
waitGroup.Wait()
return
}
func fileExists(name string) bool {
if _, err := os.Stat(name); errors.Is(err, os.ErrNotExist) {
return false
}
return true
}
func getKeys() (ed25519.PublicKey, ed25519.PrivateKey) {
user, err := user.Current()
if err != nil {
panic(err)
}
configPath := os.Getenv("XDG_CONFIG_HOME")
if configPath == "" {
configPath = filepath.Join(user.HomeDir, ".config", "spring83")
}
if err = os.MkdirAll(configPath, os.ModePerm); err != nil {
panic(err)
}
pubfile := filepath.Join(configPath, "key.pub")
privfile := filepath.Join(configPath, "key.priv")
var pubkey ed25519.PublicKey
var privkey ed25519.PrivateKey
if fileExists(pubfile) && fileExists(privfile) {
pubkey, err = ioutil.ReadFile(pubfile)
if err != nil {
panic(err)
}
privkey, err = ioutil.ReadFile(privfile)
if err != nil {
panic(err)
}
} else {
fmt.Printf("I am fishing in the sea of all possible keys for a valid spring83 key. This may take a bit...\n")
pubkey, privkey = validKey()
os.WriteFile(pubfile, pubkey, 0666)
os.WriteFile(privfile, privkey, 0600)
}
return pubkey, privkey
}
func main() {
pubkey, privkey := getKeys()
client := &http.Client{}
body, err := ioutil.ReadAll(os.Stdin)
if err != nil {
panic(err)
}
if len(body) == 0 {
panic(fmt.Errorf("input required"))
}
if len(body) > 2217 {
panic(fmt.Errorf("input body too long"))
}
// TODO: take the URL as a command line param
url := fmt.Sprintf("http://localhost:8000/%x", pubkey)
fmt.Printf("URL: %s\n", url)
req, err := http.NewRequest(http.MethodPut, url, bytes.NewBuffer(body))
if err != nil {
panic(err)
}
sig := ed25519.Sign(privkey, body)
fmt.Printf("Spring-83 Signature=%x\n", sig)
req.Header.Set("Authorization", fmt.Sprintf("Spring-83 Signature=%x", sig))
dt := time.Now().Format(time.RFC1123)
req.Header.Set("If-Unmodified-Since", dt)
resp, err := client.Do(req)
if err != nil {
panic(err)
}
defer resp.Body.Close()
responseBody, err := ioutil.ReadAll(resp.Body)
if err != nil {
panic(err)
}
fmt.Printf("%s: %s\n", resp.Status, responseBody)
}