func TestHandshake(t *testing.T) { config := initialize() var frame quibit.Frame var err error // Test Version frame = *objects.MakeFrame(objects.VERSION, objects.REQUEST, &config.LocalVersion) frame.Peer = "127.0.0.1:4444" config.RecvQueue <- frame frame = <-config.SendQueue if frame.Header.Command != objects.VERSION || frame.Header.Type != objects.REPLY { fmt.Println("Frame is not a proper reply to a version request: ", frame.Header) t.FailNow() } version := new(objects.Version) err = version.FromBytes(frame.Payload) if err != nil { fmt.Println("Error parsing version reply: ", err) t.FailNow() } // Test Peer frame = *objects.MakeFrame(objects.PEER, objects.REQUEST, &config.NodeList) frame.Peer = "127.0.0.1:4444" config.RecvQueue <- frame frame = <-config.SendQueue if frame.Header.Command != objects.PEER || frame.Header.Type != objects.REPLY || frame.Header.Length != 0 { fmt.Println("Frame is not a proper reply to a peer request: ", frame.Header) t.FailNow() } // Test Obj frame = *objects.MakeFrame(objects.OBJ, objects.REQUEST, &config.NodeList) frame.Peer = "127.0.0.1:4444" config.RecvQueue <- frame frame = <-config.SendQueue if frame.Header.Command != objects.OBJ || frame.Header.Type != objects.REPLY || frame.Header.Length != 0 { fmt.Println("Frame is not a proper reply to a peer request: ", frame.Header) t.FailNow() } cleanup(config) }
func checkPubkey(config *api.ApiConfig, addrHash objects.Hash) []byte { // First check local DB detail, err := localdb.GetAddressDetail(addrHash) if err != nil { // If not in database, won't be able to decrypt anyway! return nil } if len(detail.Pubkey) > 0 { if db.Contains(addrHash) != db.PUBKEY { enc := new(objects.EncryptedPubkey) enc.IV, enc.Payload, _ = encryption.SymmetricEncrypt(detail.Address, string(detail.Pubkey)) enc.AddrHash = objects.MakeHash(detail.Address) config.RecvQueue <- *objects.MakeFrame(objects.PUBKEY, objects.BROADCAST, enc) } return detail.Pubkey } // If not there, check local database if db.Contains(addrHash) == db.PUBKEY { enc := db.GetPubkey(config.Log, addrHash) pubkey := encryption.SymmetricDecrypt(enc.IV, detail.Address, enc.Payload) pubkey = pubkey[:65] // Check public Key x, y := encryption.UnmarshalPubkey(pubkey) if x == nil { config.Log <- "Decrypted Public Key Invalid" return nil } address2 := encryption.GetAddress(config.Log, x, y) if string(detail.Address) != string(address2) { config.Log <- "Decrypted Public Key doesn't match provided address!" return nil } detail.Pubkey = pubkey err := localdb.AddUpdateAddress(detail) if err != nil { config.Log <- "Error adding pubkey to local database!" return nil } return pubkey } // If not there, send a pubkey request config.RecvQueue <- *objects.MakeFrame(objects.PUBKEY_REQUEST, objects.BROADCAST, &addrHash) return nil }
func (service *EMPService) PurgeMessage(r *http.Request, args *[]byte, reply *NilParam) error { if len(*args) != 16 { return errors.New("Invalid Txid: Bad Length") } txidHash := objects.MakeHash(*args) if localdb.Contains(txidHash) <= localdb.SENDBOX { msg, err := localdb.GetMessageDetail(txidHash) if err != nil { return errors.New(fmt.Sprintf("Problem Retrieving Message: %s", err)) } msg.MetaMessage.Purged = true localdb.AddUpdateMessage(msg, -1) // Send Purge Request purge := new(objects.Purge) purge.Txid = msg.Decrypted.Txid service.Config.RecvQueue <- *objects.MakeFrame(objects.PURGE, objects.BROADCAST, purge) return nil } return errors.New("Txid Not Found") }
func (service *EMPService) SendRawMsg(r *http.Request, args *RawMsg, reply *NilParam) error { if args == nil { return errors.New("Cannot work with nil message object!") } detail, err := localdb.GetAddressDetail(args.Message.AddrHash) if err != nil { return err } // Create New Message msg := new(objects.FullMessage) msg.Decrypted = nil msg.Encrypted = new(encryption.EncryptedMessage) msg.Encrypted.FromBytes(args.Message.Content.GetBytes()) // Fill Out Meta Message (save timestamp) msg.MetaMessage.Purged = false msg.MetaMessage.TxidHash.FromBytes(args.Message.TxidHash.GetBytes()) if args.Subscription { msg.MetaMessage.Sender = detail.String msg.MetaMessage.Recipient = "" } else { msg.MetaMessage.Sender = args.SendAddress msg.MetaMessage.Recipient = detail.String } err = localdb.AddUpdateMessage(msg, localdb.SENDBOX) if err != nil { return err } if args.Subscription { service.Config.RecvQueue <- *objects.MakeFrame(objects.PUB, objects.BROADCAST, &(args.Message)) } else { service.Config.RecvQueue <- *objects.MakeFrame(objects.MSG, objects.BROADCAST, &(args.Message)) } return nil }
func (service *EMPService) CreateAddress(r *http.Request, args *NilParam, reply *objects.AddressDetail) error { if !basicAuth(service.Config, r) { service.Config.Log <- fmt.Sprintf("Unauthorized RPC Request from: %s", r.RemoteAddr) return errors.New("Unauthorized") } // Create Address priv, x, y := encryption.CreateKey(service.Config.Log) reply.Privkey = priv if x == nil { return errors.New("Key Pair Generation Error") } reply.Pubkey = encryption.MarshalPubkey(x, y) reply.IsRegistered = true reply.Address = encryption.GetAddress(service.Config.Log, x, y) if reply.Address == nil { return errors.New("Could not create address, function returned nil.") } reply.String = encryption.AddressToString(reply.Address) // Add Address to Database err := localdb.AddUpdateAddress(reply) if err != nil { service.Config.Log <- fmt.Sprintf("Error Adding Address: ", err) return err } // Send Pubkey to Network encPub := new(objects.EncryptedPubkey) encPub.AddrHash = objects.MakeHash(reply.Address) encPub.IV, encPub.Payload, err = encryption.SymmetricEncrypt(reply.Address, string(reply.Pubkey)) if err != nil { service.Config.Log <- fmt.Sprintf("Error Encrypting Pubkey: ", err) return nil } // Record Pubkey for Network service.Config.RecvQueue <- *objects.MakeFrame(objects.PUBKEY, objects.BROADCAST, encPub) return nil }
// Starts a new TCP Server wth configuration specified in ApiConfig. // Server will terminate cleanly only when data is sent to the Quit channel. // // See (struct ApiConfig) for details. func Start(config *ApiConfig) { var err error var frame quibit.Frame defer quit(config) config.Log <- "Starting api..." // Start Database Services err = db.Initialize(config.Log, config.DbFile) defer db.Cleanup() if err != nil { config.Log <- fmt.Sprintf("Error initializing database: %s", err) config.Log <- "Quit" return } config.LocalVersion.Timestamp = time.Now().Round(time.Second) locVersion := objects.MakeFrame(objects.VERSION, objects.REQUEST, &config.LocalVersion) for str, _ := range config.NodeList.Nodes { locVersion.Peer = str config.SendQueue <- *locVersion } // Set Up Clocks second := time.Tick(2 * time.Second) minute := time.Tick(time.Minute) for { select { case frame = <-config.RecvQueue: config.Log <- fmt.Sprintf("Received %s frame...", CmdString(frame.Header.Command)) switch frame.Header.Command { case objects.VERSION: version := new(objects.Version) err = version.FromBytes(frame.Payload) if err != nil { config.Log <- fmt.Sprintf("Error parsing version: %s", err) } else { fVERSION(config, frame, version) } case objects.PEER: nodeList := new(objects.NodeList) err = nodeList.FromBytes(frame.Payload) if err != nil { config.Log <- fmt.Sprintf("Error parsing peer list: %s", err) } else { fPEER(config, frame, nodeList) } case objects.OBJ: obj := new(objects.Obj) err = obj.FromBytes(frame.Payload) if err != nil { config.Log <- fmt.Sprintf("Error parsing obj list: %s", err) } else { fOBJ(config, frame, obj) } case objects.GETOBJ: getObj := new(objects.Hash) if len(frame.Payload) == 0 { break } err = getObj.FromBytes(frame.Payload) if err != nil { config.Log <- fmt.Sprintf("Error parsing getobj hash: %s", err) } else { fGETOBJ(config, frame, getObj) } case objects.PUBKEY_REQUEST: pubReq := new(objects.Hash) err = pubReq.FromBytes(frame.Payload) if err != nil { config.Log <- fmt.Sprintf("Error parsing pubkey request hash: %s", err) } else { fPUBKEY_REQUEST(config, frame, pubReq) } case objects.PUBKEY: pub := new(objects.EncryptedPubkey) err = pub.FromBytes(frame.Payload) if err != nil { config.Log <- fmt.Sprintf("Error parsing pubkey: %s", err) } else { fPUBKEY(config, frame, pub) } case objects.MSG: msg := new(objects.Message) err = msg.FromBytes(frame.Payload) if err != nil { config.Log <- fmt.Sprintf("Error parsing message: %s", err) } else { fMSG(config, frame, msg) } case objects.PUB: msg := new(objects.Message) err = msg.FromBytes(frame.Payload) if err != nil { config.Log <- fmt.Sprintf("Error parsing publication: %s", err) } else { fPUB(config, frame, msg) } case objects.PURGE: purge := new(objects.Purge) err = purge.FromBytes(frame.Payload) if err != nil { config.Log <- fmt.Sprintf("Error parsing purge: %s", err) } else { fPURGE(config, frame, purge) } case objects.CHECKTXID: chkTxid := new(objects.Hash) if len(frame.Payload) == 0 { break } err = chkTxid.FromBytes(frame.Payload) if err != nil { config.Log <- fmt.Sprintf("Error parsing checktxid hash: %s", err) } else { fCHECKTXID(config, frame, chkTxid) } default: config.Log <- fmt.Sprintf("Received invalid frame for command: %d", frame.Header.Command) } case <-config.Quit: fmt.Println() // Dump Nodes to File DumpNodes(config) return case <-second: // Reconnection Logic for key, node := range config.NodeList.Nodes { peer := quibit.GetPeer(key) if peer == nil || !peer.IsConnected() { quibit.KillPeer(key) if node.Attempts >= 3 { config.Log <- fmt.Sprintf("Max connection attempts reached for %s, disconnecting...", key) // Max Attempts Reached, disconnect delete(config.NodeList.Nodes, key) } else { config.Log <- fmt.Sprintf("Disconnected from peer %s, trying to reconnect...", key) peer = new(quibit.Peer) peer.IP = node.IP peer.Port = node.Port config.PeerQueue <- *peer runtime.Gosched() peer = nil node.Attempts++ config.NodeList.Nodes[key] = node locVersion.Peer = key config.SendQueue <- *locVersion } } } if len(config.NodeList.Nodes) < 1 { config.Log <- "All connections lost, re-bootstrapping..." for i, str := range config.Bootstrap { if i >= bufLen { break } p := new(quibit.Peer) n := new(objects.Node) err := n.FromString(str) if err != nil { fmt.Println("Error Decoding Peer ", str, ": ", err) continue } p.IP = n.IP p.Port = n.Port config.PeerQueue <- *p runtime.Gosched() config.NodeList.Nodes[n.String()] = *n } for str, _ := range config.NodeList.Nodes { locVersion.Peer = str config.SendQueue <- *locVersion } } case <-minute: // Dump old messages err = db.SweepMessages(30 * 24 * time.Hour) if err != nil { config.Log <- fmt.Sprintf("Error Sweeping Messages: %s", err) } } } // Should NEVER get here! panic("Must've been a cosmic ray!") }
// Handle Pubkey, Message, and Purge Registration func register(config *api.ApiConfig) { var message objects.Message var txid [16]byte for { select { case pubHash := <-config.PubkeyRegister: // Check if pubkey is in database... pubkey := checkPubkey(config, pubHash) if pubkey == nil { break } outbox := localdb.GetBox(localdb.OUTBOX) for _, metamsg := range outbox { recvHash := objects.MakeHash([]byte(metamsg.Recipient)) if string(pubHash.GetBytes()) == string(recvHash.GetBytes()) { // Send message and move to sendbox msg, err := localdb.GetMessageDetail(metamsg.TxidHash) if err != nil { config.Log <- err.Error() break } msg.Encrypted = encryption.Encrypt(config.Log, pubkey, string(msg.Decrypted.GetBytes())) msg.MetaMessage.Timestamp = time.Now().Round(time.Second) err = localdb.AddUpdateMessage(msg, localdb.SENDBOX) if err != nil { config.Log <- err.Error() break } sendMsg := new(objects.Message) sendMsg.Timestamp = msg.MetaMessage.Timestamp sendMsg.TxidHash = msg.MetaMessage.TxidHash sendMsg.AddrHash = recvHash sendMsg.Content = *msg.Encrypted config.RecvQueue <- *objects.MakeFrame(objects.MSG, objects.BROADCAST, sendMsg) } } case message = <-config.MessageRegister: // If address is registered, store message in inbox detail, err := localdb.GetAddressDetail(message.AddrHash) if err != nil { config.Log <- "Message address not in database..." break } if !detail.IsRegistered { config.Log <- "Message not for registered address..." break } config.Log <- "Registering new encrypted message..." msg := new(objects.FullMessage) msg.MetaMessage.TxidHash = message.TxidHash msg.MetaMessage.Timestamp = message.Timestamp msg.MetaMessage.Recipient = detail.String msg.Encrypted = &message.Content err = localdb.AddUpdateMessage(msg, localdb.INBOX) if err != nil { config.Log <- err.Error() } case message = <-config.PubRegister: // If address is registered, store message in inbox detail, err := localdb.GetAddressDetail(message.AddrHash) if err != nil { config.Log <- "Message address not in database..." break } if !detail.IsSubscribed { config.Log <- "Not Subscribed to Address..." break } config.Log <- "Registering new publication..." msg := new(objects.FullMessage) msg.MetaMessage.TxidHash = message.TxidHash msg.MetaMessage.Timestamp = message.Timestamp msg.MetaMessage.Sender = detail.String msg.MetaMessage.Recipient = "<Subscription Message>" msg.Encrypted = &message.Content msg.Decrypted = new(objects.DecryptedMessage) msg.Decrypted.FromBytes(encryption.DecryptPub(config.Log, detail.Pubkey, msg.Encrypted)) err = localdb.AddUpdateMessage(msg, localdb.INBOX) if err != nil { config.Log <- err.Error() } case txid = <-config.PurgeRegister: // If Message in database, mark as purged detail, err := localdb.GetMessageDetail(objects.MakeHash(txid[:])) if err != nil { break } detail.MetaMessage.Purged = true err = localdb.AddUpdateMessage(detail, -1) if err != nil { config.Log <- fmt.Sprintf("Error registering purge: %s", err) } } // End select } // End for } // End register
func (service *EMPService) PublishMessage(r *http.Request, args *SendMsg, reply *SendResponse) error { if !basicAuth(service.Config, r) { service.Config.Log <- fmt.Sprintf("Unauthorized RPC Request from: %s", r.RemoteAddr) return errors.New("Unauthorized") } // Nil Check if len(args.Sender) == 0 || len(args.Plaintext) == 0 { return errors.New("All fields required.") } var err error // Get Addresses sendAddr := encryption.StringToAddress(args.Sender) if len(sendAddr) == 0 { return errors.New("Invalid sender address!") } sender, err := localdb.GetAddressDetail(objects.MakeHash(sendAddr)) if err != nil { return errors.New(fmt.Sprintf("Error pulling send address from Database: %s", err)) } if sender.Privkey == nil { return errors.New("Private Key Required to Publish Message") } // Create New Message msg := new(objects.FullMessage) msg.Decrypted = new(objects.DecryptedMessage) msg.Encrypted = nil txid := make([]byte, len(msg.Decrypted.Txid), cap(msg.Decrypted.Txid)) // Fill out decrypted message n, err := rand.Read(txid) if n < len(msg.Decrypted.Txid[:]) || err != nil { return errors.New(fmt.Sprintf("Problem with random reader: %s", err)) } copy(msg.Decrypted.Pubkey[:], sender.Pubkey) msg.Decrypted.Subject = args.Subject msg.Decrypted.MimeType = "text/plain" msg.Decrypted.Content = args.Plaintext msg.Decrypted.Length = uint32(len(msg.Decrypted.Content)) // Fill Out Meta Message (save timestamp) msg.MetaMessage.Purged = false msg.MetaMessage.TxidHash = objects.MakeHash(txid) msg.MetaMessage.Sender = sender.String msg.MetaMessage.Recipient = "<Subscription Message>" // Get Signature priv := new(ecdsa.PrivateKey) priv.PublicKey.Curve = encryption.GetCurve() priv.D = new(big.Int) priv.D.SetBytes(sender.Privkey) sign := msg.Decrypted.GetBytes() sign = sign[:len(sign)-65] signHash := objects.MakeHash(sign) x, y, err := ecdsa.Sign(rand.Reader, priv, signHash.GetBytes()) if err != nil { return err } copy(msg.Decrypted.Signature[:], encryption.MarshalPubkey(x, y)) // Send message and add to sendbox... msg.Encrypted = encryption.EncryptPub(service.Config.Log, sender.Privkey, string(msg.Decrypted.GetBytes())) msg.MetaMessage.Timestamp = time.Now().Round(time.Second) // Now Add Txid copy(msg.Decrypted.Txid[:], txid) err = localdb.AddUpdateMessage(msg, localdb.SENDBOX) if err != nil { return err } sendMsg := new(objects.Message) sendMsg.TxidHash = msg.MetaMessage.TxidHash sendMsg.AddrHash = objects.MakeHash(sender.Address) sendMsg.Timestamp = msg.MetaMessage.Timestamp sendMsg.Content = *msg.Encrypted service.Config.RecvQueue <- *objects.MakeFrame(objects.PUB, objects.BROADCAST, sendMsg) reply.IsSent = true // Finish by setting msg's txid reply.TxidHash = msg.MetaMessage.TxidHash.GetBytes() return nil }
func (service *EMPService) OpenMessage(r *http.Request, args *[]byte, reply *objects.FullMessage) error { if !basicAuth(service.Config, r) { service.Config.Log <- fmt.Sprintf("Unauthorized RPC Request from: %s", r.RemoteAddr) return errors.New("Unauthorized") } var txidHash objects.Hash txidHash.FromBytes(*args) // Get Message from Database msg, err := localdb.GetMessageDetail(txidHash) if err != nil { return err } if msg.Encrypted == nil { *reply = *msg return nil } // If not decrypted, decrypt message and purge if msg.Decrypted == nil { recipient, err := localdb.GetAddressDetail(objects.MakeHash(encryption.StringToAddress(msg.MetaMessage.Recipient))) if err != nil { return err } if recipient.Privkey == nil { *reply = *msg return nil } // Decrypt Message decrypted := encryption.Decrypt(service.Config.Log, recipient.Privkey, msg.Encrypted) if len(decrypted) == 0 { *reply = *msg return nil } msg.Decrypted = new(objects.DecryptedMessage) msg.Decrypted.FromBytes(decrypted) // Update Sender x, y := encryption.UnmarshalPubkey(msg.Decrypted.Pubkey[:]) address := encryption.GetAddress(service.Config.Log, x, y) addrStr := encryption.AddressToString(address) addrHash := objects.MakeHash(address) detail, _ := localdb.GetAddressDetail(addrHash) if detail == nil { detail = new(objects.AddressDetail) } detail.Address = address detail.String = addrStr detail.Pubkey = msg.Decrypted.Pubkey[:] localdb.AddUpdateAddress(detail) msg.MetaMessage.Sender = detail.String // Send Purge Request purge := new(objects.Purge) purge.Txid = msg.Decrypted.Txid service.Config.RecvQueue <- *objects.MakeFrame(objects.PURGE, objects.BROADCAST, purge) msg.MetaMessage.Purged = true localdb.AddUpdateMessage(msg, localdb.Contains(msg.MetaMessage.TxidHash)) } else { if msg.MetaMessage.Purged == false && localdb.Contains(txidHash) == localdb.INBOX { msg.MetaMessage.Purged = true localdb.AddUpdateMessage(msg, localdb.Contains(msg.MetaMessage.TxidHash)) } } *reply = *msg return nil }