commit c724d4d2249b8d6df12f74cf4a5700537c310a0a Author: Jordan Orelli Date: Fri Dec 5 17:07:11 2014 -0500 oh hai diff --git a/client.go b/client.go new file mode 100644 index 0000000..76583ad --- /dev/null +++ b/client.go @@ -0,0 +1,44 @@ +package main + +import ( + "crypto/rsa" + "encoding/json" + "fmt" + "net" + "os" +) + +type Auth struct { + Nick string + Key rsa.PublicKey +} + +func (a *Auth) Kind() string { + return "auth" +} + +func connect() { + f, err := os.Open(options.key) + if err != nil { + exit(1, "unable to open private key file at %s: %v", options.key, err) + } + defer f.Close() + + d1 := json.NewDecoder(f) + var key rsa.PrivateKey + if err := d1.Decode(&key); err != nil { + exit(1, "unable to decode key: %v", err) + } + + conn, err := net.Dial("tcp", fmt.Sprintf("%s:%d", options.host, options.port)) + if err != nil { + exit(1, "unable to connect to server at %s:%d: %v", options.host, options.port, err) + } + auth := Auth{ + Nick: options.nick, + Key: key.PublicKey, + } + encodeRequest(conn, &auth) + + fmt.Println(conn) +} diff --git a/cm.go b/cm.go new file mode 100644 index 0000000..604c8b9 --- /dev/null +++ b/cm.go @@ -0,0 +1,10 @@ +package main + +import ( + "sync" +) + +// connection manager +type CM struct { + mu sync.Mutex +} diff --git a/key.go b/key.go new file mode 100644 index 0000000..c1d6f52 --- /dev/null +++ b/key.go @@ -0,0 +1,96 @@ +package main + +import ( + "bytes" + "crypto/rand" + "crypto/rsa" + "encoding/base64" + "encoding/json" + "fmt" + "io/ioutil" + "os" +) + +func generate() { + priv, err := rsa.GenerateKey(rand.Reader, keyLength) + if err != nil { + exit(1, "couldn't generate private key: %v", err) + } + json.NewEncoder(os.Stdout).Encode(priv) +} + +func encrypt() { + key, err := readKey() + if err != nil { + exit(1, "couldn't setup key: %v", err) + } + msg, err := ioutil.ReadAll(os.Stdin) + if err != nil { + exit(1, "error reading input message: %v", err) + } + ctxt, err := rsa.EncryptPKCS1v15(rand.Reader, &key.PublicKey, msg) + if err != nil { + exit(1, "error encrypting message: %v", err) + } + + var buf bytes.Buffer + enc := base64.NewEncoder(base64.StdEncoding, &buf) + if _, err := enc.Write(ctxt); err != nil { + exit(1, "error b64 encoding ciphertext: %v", err) + } + enc.Close() + + b64 := buf.Bytes() + os.Stdout.Write(b64) + os.Stdout.Close() +} + +func decrypt() { + key, err := readKey() + if err != nil { + exit(1, "couldn't setup key: %v", err) + } + + raw, err := ioutil.ReadAll(os.Stdin) + if err != nil { + exit(1, "error reading input message: %v", err) + } + + buf := bytes.NewBuffer(raw) + decoder := base64.NewDecoder(base64.StdEncoding, buf) + ctxt, err := ioutil.ReadAll(decoder) + if err != nil { + exit(1, "error reading b64 buffer %v", err) + } + + msg, err := rsa.DecryptPKCS1v15(rand.Reader, key, ctxt) + if err != nil { + exit(1, "error decrypting message: %v", err) + } + fmt.Printf("%s", msg) +} + +func readKey() (*rsa.PrivateKey, error) { + f, err := os.Open(options.key) + if err != nil { + return nil, fmt.Errorf("unable to open private key file at %s: %v", options.key, err) + } + defer f.Close() + + d1 := json.NewDecoder(f) + var key rsa.PrivateKey + if err := d1.Decode(&key); err != nil { + return nil, fmt.Errorf("unable to decode key from file %s: %v", options.key, err) + } + return &key, nil +} + +func getPublic() { + priv, err := readKey() + if err != nil { + exit(1, "unable to read private key file: %v", err) + } + if err := json.NewEncoder(os.Stdout).Encode(priv.PublicKey); err != nil { + exit(1, "unable to marshal key: %v", err) + } +} diff --git a/request.go b/request.go new file mode 100644 index 0000000..f737c4d --- /dev/null +++ b/request.go @@ -0,0 +1,48 @@ +package main + +import ( + "encoding/json" + "fmt" + "net" +) + +type Envelope struct { + Kind string + Body json.RawMessage +} + +type request interface { + Kind() string +} + +func encodeRequest(conn net.Conn, r request) { + b, err := json.Marshal(r) + if err != nil { + exit(1, "unable to encode client request body: %v", err) + } + e := Envelope{ + Kind: r.Kind(), + Body: b, + } + if err := json.NewEncoder(conn).Encode(e); err != nil { + exit(1, "unable to encode client request: %v", err) + } +} + +func decodeRequest(conn net.Conn) (request, error) { + d := json.NewDecoder(conn) + var env Envelope + if err := d.Decode(&env); err != nil { + return nil, fmt.Errorf("unable to decode client request: %v", err) + } + switch env.Kind { + case "auth": + var auth Auth + if err := json.Unmarshal(env.Body, &auth); err != nil { + return nil, fmt.Errorf("unable to unmarshal auth request: %v", err) + } + return &auth, nil + default: + return nil, fmt.Errorf("unknown request type: %s", env.Kind) + } +} diff --git a/server.go b/server.go new file mode 100644 index 0000000..88f6f06 --- /dev/null +++ b/server.go @@ -0,0 +1,49 @@ +package main + +import ( + // "crypto/rsa" + "encoding/json" + "fmt" + "net" +) + +func handleConnection(conn net.Conn) { + defer conn.Close() + d := json.NewDecoder(conn) + + // var nick string + // var key rsa.PublicKey + + var env Envelope + for { + if err := d.Decode(&env); err != nil { + error_log.Printf("unable to decode client request: %v", err) + return + } + switch env.Kind { + case "auth": + var auth Auth + if err := json.Unmarshal(env.Body, &auth); err != nil { + error_log.Printf("unable to decode auth body: %v", err) + break + } + // nick = auth.Nick + // key = auth.Key + } + } +} + +func serve() { + listener, err := net.Listen("tcp", fmt.Sprintf(":%d", options.port)) + if err != nil { + exit(1, "couldn't open tcp port for listening: %v", err) + } + for { + conn, err := listener.Accept() + if err != nil { + error_log.Printf("error accepting new connection: %v", err) + continue + } + go handleConnection(conn) + } +} diff --git a/whisper.go b/whisper.go new file mode 100644 index 0000000..75f8f8c --- /dev/null +++ b/whisper.go @@ -0,0 +1,70 @@ +package main + +import ( + "flag" + "fmt" + "log" + // "net" + "os" +) + +const keyLength = 4096 + +var ( + info_log *log.Logger + error_log *log.Logger +) + +var options struct { + port int + host string + key string + nick string +} + +func exit(status int, template string, args ...interface{}) { + if status == 0 { + info_log.Printf(template, args...) + } else { + error_log.Printf(template, args...) + } + os.Exit(status) +} + +func usage(template string, args ...interface{}) { + fmt.Fprintf(os.Stdout, template, args...) + os.Exit(1) +} + +func main() { + flag.Parse() + info_log, error_log = log.New(os.Stdout, "", 0), log.New(os.Stderr, "", 0) + + if flag.NArg() < 1 { + usage("client or server?") + } + + switch flag.Arg(0) { + case "dial": + connect() + case "listen": + serve() + case "generate": + generate() + case "encrypt": + encrypt() + case "decrypt": + decrypt() + case "get-public": + getPublic() + default: + usage("i dunno what you mean with %v", flag.Arg(0)) + } +} + +func init() { + flag.IntVar(&options.port, "port", 9000, "port number") + flag.StringVar(&options.host, "host", "localhost", "host to connect to") + flag.StringVar(&options.key, "key", "whisper_key", "rsa key to use") + flag.StringVar(&options.nick, "nick", "", "nick to use in chat") +}