// GetGlobalIndex returns the global index. func (ms MessageServer) GetGlobalIndex(w http.ResponseWriter, r *http.Request) { var pubKey *message.Curve25519Key start := int64(0) count := int64(10) w.Header().Set("Content-Type", "text/plain; charset=us-ascii") getValues := r.URL.Query() if getValues != nil { if v, ok := getValues["start"]; ok { t, err := strconv.Atoi(v[0]) if err == nil { start = int64(t) } } if v, ok := getValues["count"]; ok { t, err := strconv.Atoi(v[0]) if err == nil { count = int64(t) if count > ms.MaxIndexGlobal { count = ms.MaxIndexGlobal } } } if v, ok := getValues["auth"]; ok { err := ms.AuthenticatePeer(v[0]) if err != nil { io.WriteString(w, fmt.Sprintf("Error: %s", err)) return } } else { io.WriteString(w, "ERROR: Missing param\n") return } } else { io.WriteString(w, "ERROR: Missing param\n") return } messages, found, err := ms.DB.GetGlobalIndex(start, count) if err != nil && err != ErrNoMore { log.Debugf("List:GetIndex: %s\n", err) log.Debugf("List:GetIndex: Key %s\n", utils.B58encode(pubKey[:])) io.WriteString(w, "ERROR: List failed\n") return } io.WriteString(w, "SUCCESS: Data follows\n") for _, msg := range messages { io.WriteString(w, "IDX: "+strings.Trim(string(msg), " \t\n\r")+"\n") } if int64(found) < count { io.WriteString(w, "CMD: Exceeded\n") } else { io.WriteString(w, "CMD: Continue\n") } }
// FetchPost fetches a post from a peer and adds it. func (ms MessageServer) FetchPost(url, auth string, msgID [message.MessageIDSize]byte, expireRequest uint64) error { // Fetch the post proto := repproto.New(ms.SocksProxy, "") data, err := proto.GetSpecificAuth(url, auth, msgID[:]) // data, err := proto.GetSpecific(url, msgID[:]) if err != nil { return err } // Verify and use it signheader, err := message.Base64Message(data).GetSignHeader() if err != nil { log.Debugf("Bad fetch:GetSignHeader: %s\n", err) return err } details, err := message.VerifySignature(*signheader, ms.MinHashCashBits) if err != nil { log.Debugf("Bad fetch:VerifySignature: %s\n", err) return err } constantRecipientPub, MessageID, err := deferVerify(data) if err != nil { log.Debugf("Bad fetch:deferVerify: %s\n", err) return err } if *MessageID != details.MsgID { log.Debugs("Bad fetch:MessageID\n") return ErrBadMessageID } msgStruct := &structs.MessageStruct{ MessageID: *MessageID, ReceiverConstantPubKey: *constantRecipientPub, SignerPub: details.PublicKey, OneTime: false, Sync: false, Hidden: false, ExpireRequest: expireRequest, } if message.KeyIsSync(constantRecipientPub) { msgStruct.Sync = true } if message.KeyIsHidden(constantRecipientPub) { msgStruct.Hidden = true } sigStruct := &structs.SignerStruct{ PublicKey: details.PublicKey, Nonce: details.HashCashNonce, Bits: details.HashCashBits, } sigStruct.MaxMessagesPosted, sigStruct.MaxMessagesRetained, sigStruct.ExpireTarget = ms.calcLimits(details.HashCashBits) return ms.DB.Put(msgStruct, sigStruct, data) }
// Fetch returns a single message. func (ms MessageServer) Fetch(w http.ResponseWriter, r *http.Request) { var messageID *[message.MessageIDSize]byte w.Header().Set("Content-Type", "text/plain; charset=us-ascii") getValues := r.URL.Query() if getValues != nil { if v, ok := getValues["messageid"]; ok { t := utils.B58decode(v[0]) if len(t) < message.MessageIDSize || len(t) > message.MessageIDSize { io.WriteString(w, "ERROR: Bad parameter\n") return } messageID = new([message.MessageIDSize]byte) copy(messageID[:], t) } if ms.HubOnly { if v, ok := getValues["auth"]; ok { err := ms.AuthenticatePeer(v[0]) if err != nil { io.WriteString(w, fmt.Sprintf("Error: %s", err)) return } } else { io.WriteString(w, "ERROR: Missing param\n") return } } } if messageID == nil { io.WriteString(w, "ERROR: Missing parameter\n") return } data, err := ms.DB.Fetch(messageID) if err != nil { log.Debugf("Fetch: %s\n", err) io.WriteString(w, "ERROR: No data") return } io.WriteString(w, "SUCCESS: Data follows\n") _, err = w.Write(data) if err != nil { log.Debugf("Write: %s\n", err) return } log.Debugs("Fetch OK\n") if ms.Stat { stat.Input <- stat.Fetch } return }
// ServeID returns server information. func (ms MessageServer) ServeID(w http.ResponseWriter, r *http.Request) { ms.RandomSleep() now := CurrentTime() + ms.TimeSkew privK := [32]byte(*ms.authPrivKey) _, pubkey, challenge := keyauth.GenTempKeyTime(uint64(now), &privK) info := &ServerInfo{ Time: now, AuthPubKey: utils.B58encode(pubkey[:]), AuthChallenge: utils.B58encode(challenge[:]), MaxPostSize: int64(messagestore.MaxMessageSize), MinPostSize: ms.InfoStruct.MinPostSize, MinHashCashBits: ms.InfoStruct.MinHashCashBits, } if ms.EnablePeerHandler { info.Peers = ms.getPeerURLs() } ms.RandomSleep() w.Header().Set("Content-Type", "text/json") b, err := json.Marshal(info) if err != nil { log.Debugf("Info: %s\n", err) } w.Write(b) w.Write([]byte("\n")) return }
// ExpireFromIndex reads the expire index and expires messages as they are recorded func (store Store) ExpireFromIndex(cycles int) { // run from now backwards N cycles now := uint64(time.Now().Unix()) expireTime := (now / uint64(ExpireRun)) for i := cycles; i > 0; i-- { expireTime -= uint64(ExpireRun) log.Debugf("Expire index try: %d\n", expireTime) if expireTime > 0 { if store.expireindex.Index(utils.EncodeUInt64(expireTime)).Exists() { j := int64(0) log.Debugf("Expire index found: %d\n", expireTime) ExpireEntries: for { entry, err := store.expireindex.Index(utils.EncodeUInt64(expireTime)).ReadEntry(j) if err != nil { // no more entries log.Debugf("Expire index empty: %d\n", expireTime) break ExpireEntries } j++ if entry == nil { log.Debugf("Expire index bad entry: %d %d\n", j, expireTime) continue // bad entry } entryStruct := structs.ExpireStructDecode(entry) if entryStruct == nil { log.Debugf("Expire index bad entry decode: %d %d\n", j, expireTime) continue // bad entry } if entryStruct.ExpireTime > now { log.Debugf("Expire index bad entry not expired: %d %d\n", j, expireTime) continue // bad entry } store.messages.Index(entryStruct.MessageID[:]).Delete() // Delete message store.signers.Index(entryStruct.SignerPubKey[:]).Update(func(tx fileback.Tx) error { data := tx.GetLast() if data == nil { // we don't care about errors here return nil } signer := structs.SignerStructDecode(data) if signer == nil { return nil } signer.MessagesRetained-- tx.Append(signer.Encode().Fill()) return nil }) } // Remove expire list store.expireindex.Index(utils.EncodeUInt64(expireTime)).Delete() log.Debugf("Expire index deleted: %d\n", expireTime) } else { log.Debugf("Expire index does not exist: %d\n", expireTime) } } } }
func (store Store) expireSigners() { //s.signers = fileback.NewRoundRobin(dir+signerDir, structs.SignerStructSize, 10, ' ', []byte("\n"), workers) // We keep signer history, last 10 entries now := time.Now().Unix() indices := store.signers.Indices() if indices == nil { log.Debugs("ExpireFS: No signers\n") return } for _, signer := range indices { last := store.signers.Index(signer).LastChange(0) if last != 0 && last < now-int64(FSExpire) { store.signers.Index(signer).Truncate() // We don't delete for real, otherwise the signer would become reusable. log.Debugf("ExpireFS: Signer truncated %x\n", signer) } else { log.Debugf("ExpireFS: Signer remains %x\n", signer) } } }
func (store Store) expireKeyIndices() { //s.keyindex = fileback.NewRolling(dir+keyindexDir, structs.MessageStructSize, 2048, ' ', []byte("\n"), workers) // Maximum 2048 entries per file now := time.Now().Unix() indices := store.keyindex.Indices() if indices == nil { log.Debugs("ExpireFS: No Key Indices\n") return } for _, keylist := range indices { entry := store.signers.Index(keylist) last := entry.LastChange(0) if last != 0 && last < now-int64(FSExpire) { // go forward lastEntry := entry.Entries() - 1 if lastEntry >= 0 { last := entry.LastChange(lastEntry) if last < now-int64(FSExpire) { log.Debugf("ExpireFS: Key Index complete delete %x\n", keylist) entry.Delete() // Key lists are deleted } else { maxEntries := entry.MaxEntries() deleteBefore := int64(-1) LastLoop: for pos := lastEntry; pos > 0; pos = pos - maxEntries { // Search the first entry that has expired last := entry.LastChange(pos) if last != 0 && last < now-int64(FSExpire) { break LastLoop } deleteBefore = pos - 1 // This will actually skip the last expired object } if deleteBefore >= 0 { log.Debugf("ExpireFS: Key Index delete before %d %x\n", deleteBefore, keylist) entry.DeleteBefore(deleteBefore) } else { log.Debugf("ExpireFS: Key Index no delete match %x\n", keylist) } } } } else { log.Debugf("ExpireFS: Key Index remains %x\n", keylist) } } }
// GetNotify receives notifications. func (ms MessageServer) GetNotify(w http.ResponseWriter, r *http.Request) { var proof [keyproof.ProofTokenSize]byte w.Header().Set("Content-Type", "text/plain; charset=us-ascii") getValues := r.URL.Query() if getValues != nil { if v, ok := getValues["auth"]; ok { if len(v[0]) > keyproof.ProofTokenMax { io.WriteString(w, "ERROR: Bad Param\n") return } auth := utils.B58decode(v[0]) if auth == nil || len(auth) > keyproof.ProofTokenSize { io.WriteString(w, "ERROR: Bad Param\n") return } copy(proof[:], auth) ok, timeStamp, senderPubKey := keyproof.VerifyProofToken(&proof, ms.TokenPubKey) if !ok { io.WriteString(w, "ERROR: Authentication failure\n") if senderPubKey == nil { log.Errorf("VerifyProofToken failed: (proof) %s\n", utils.B58encode(proof[:])) } else { log.Errorf("VerifyProofToken failed: (pubkey) %s\n", utils.B58encode(senderPubKey[:])) } return } // verify that we know the peer url := ms.PeerURL(senderPubKey) if url == "" { io.WriteString(w, "ERROR: Bad peer\n") log.Errorf("Notify, bad peer: %s\n", utils.B58encode(senderPubKey[:])) return } now := CurrentTime() // Test too old, too young if enforceTimeOuts && (now > timeStamp+DefaultAuthTokenAge+ms.MaxTimeSkew || now < timeStamp-DefaultAuthTokenAge-ms.MaxTimeSkew) { io.WriteString(w, "ERROR: Authentication expired\n") log.Errorf("VerifyProofToken replay by %s\n", url) return } ok, signedToken := keyproof.CounterSignToken(&proof, ms.TokenPubKey, ms.TokenPrivKey) if !ok { io.WriteString(w, "ERROR: Authentication failure\n") return } ms.DB.UpdatePeerAuthToken(senderPubKey, signedToken) log.Debugf("Notified by %s\n", url) io.WriteString(w, "SUCCESS: Notified\n") return } } io.WriteString(w, "ERROR: Missing Param\n") return }
func (ms MessageServer) notifyPeer(PubKey *[ed25519.PublicKeySize]byte, url string) { rand.Seed(time.Now().UnixNano()) maxSleep := ms.NotifyDuration - int64(timeout) if maxSleep > 0 { sleeptime := rand.Int63() % maxSleep time.Sleep(time.Duration(sleeptime) * time.Second) } now := CurrentTime() + ms.TimeSkew token := utils.B58encode(keyproof.SignProofToken(now, PubKey, ms.TokenPubKey, ms.TokenPrivKey)[:]) // Socks call proto := repproto.New(ms.SocksProxy, "") err := proto.Notify(url, token) // Write result if err != nil { log.Debugf("Notify error: %s\n", err) ms.DB.UpdatePeerNotification(PubKey, true) } else { log.Debugf("Notified peer: %s\n", url) ms.DB.UpdatePeerNotification(PubKey, false) } }
// RunServer starts the HTTP handlers. func (ms MessageServer) RunServer() { ts, err := rand.Int(rand.Reader, big.NewInt(ms.MaxTimeSkew)) if err != nil { panic(err) } ms.TimeSkew = ts.Int64() log.Debugf("TimeSkew: %d\n", ms.TimeSkew) // Peering ms.notifyChan = make(chan bool, 3) // Load peers ms.LoadPeers() // Start statistics goroutine if ms.Stat { go stat.Run() } // Start timers go ms.notifyWatch() // static file handler httpHandlers := http.NewServeMux() httpHandlers.Handle("/", http.FileServer(http.Dir(ms.path+string(os.PathSeparator)+"static"))) httpHandlers.HandleFunc("/id", ms.ServeID) if !ms.HubOnly { httpHandlers.HandleFunc("/keyindex", ms.GetKeyIndex) httpHandlers.HandleFunc("/post", ms.GenPostHandler(false)) if ms.EnableOneTimeHandler { httpHandlers.HandleFunc("/local/post", ms.GenPostHandler(true)) } if ms.EnableDeleteHandler { httpHandlers.HandleFunc("/delete", ms.Delete) } } httpHandlers.HandleFunc("/globalindex", ms.GetGlobalIndex) httpHandlers.HandleFunc("/fetch", ms.Fetch) httpHandlers.HandleFunc("/notify", ms.GetNotify) httpServer := &http.Server{ Addr: "127.0.0.1:" + strconv.Itoa(ms.ListenPort), Handler: httpHandlers, ReadTimeout: 90 * time.Second, WriteTimeout: 90 * time.Second, MaxHeaderBytes: 1 << 20, } httpServer.ListenAndServe() }
// DecryptBody decrypts a body and verifies the hmac. func (bd *DecryptBodyDef) DecryptBody(data []byte) ([]byte, byte, error) { var content []byte log.Secretf("Shared Secret: %x\n", bd.SharedSecret) hmacKey, symmetricKey := CalcKeys(bd.SharedSecret) log.Secretf("HMAC Key: %x\n", *hmacKey) log.Secretf("Symmetric Key: %x\n", *symmetricKey) hmaccalc := hmac.New(sha256.New, hmacKey[:]) hmaccalc.Write(data[:len(data)-HMACSize]) hmaccalcSum := hmaccalc.Sum(nil) if !hmac.Equal(hmaccalcSum, data[len(data)-HMACSize:]) { log.Debugf("Bad HMAC: %x\n", hmaccalcSum) return nil, 0, ErrBadHMAC } blockcipher, err := aes.NewCipher(symmetricKey[:]) if err != nil { return nil, 0, err } ctr := cipher.NewCTR(blockcipher, bd.IV[:aes.BlockSize]) // Calculate size of first read. Read one block unless message is too short firstRead := aes.BlockSize if len(data)-HMACSize < aes.BlockSize { firstRead = len(data) } header := make([]byte, firstRead) ctr.XORKeyStream(header, data[:firstRead]) msgType := header[0] // Decode Message Type length := binary.BigEndian.Uint16(header[1:encryptedHeaderSize]) // Decode Message Length log.Secretf("Real Length: %d\n", length) // Length escapes one block (minus header which is not part of the length) if length > aes.BlockSize-encryptedHeaderSize { // We have already read aes.BlockSize, but it includes the header nlength := length - aes.BlockSize + encryptedHeaderSize content = make([]byte, nlength) // Decrypt whatever is left ctr.XORKeyStream(content, data[aes.BlockSize:aes.BlockSize+nlength]) } // Concat both reads content = append(header, content...) // Only return after header til end of message return content[encryptedHeaderSize : length+encryptedHeaderSize], msgType, nil }
func getPeers(force bool) error { log.Debugf("Peer update called. Last: %d\n", GlobalConfigVar.PeerUpdate) if GlobalConfigVar.PeerUpdate == 0 || GlobalConfigVar.PeerUpdate < (time.Now().Unix()-PeerUpdateDuration) { log.Debugs("Peer update forced. Prevent by running -peerlist.\n") force = true } if force { log.Debugs("Updating peers.\n") defer log.Debugs("Peer update done.\n") GlobalConfigVar.PeerUpdate = time.Now().Unix() server := OptionsVar.Server if server == "" && GlobalConfigVar.PasteServers != nil && len(GlobalConfigVar.PasteServers) > 0 { // no server defined, we select one servercount := len(GlobalConfigVar.PasteServers) rand.Seed(time.Now().UnixNano()) pos := rand.Int() % servercount server = GlobalConfigVar.PasteServers[pos] } if server == "" { server = GlobalConfigVar.BootStrapPeer } if server == "" { return ErrNoPeers } proto := repproto.New(OptionsVar.Socksserver, OptionsVar.Server, GlobalConfigVar.PasteServers...) serverInfo, err := proto.ID(server) if err != nil { return ErrNoPeers } if serverInfo.Peers != nil && len(serverInfo.Peers) > 0 { GlobalConfigVar.PasteServers = serverInfo.Peers GlobalConfigVar.MinHashCash = serverInfo.MinHashCashBits err := WriteConfigFile(GlobalConfigVar) if err != nil { log.Errorf("Error writing config-file: %s\n", err) } } } return nil }
// fetchPeer downloads new messages from peer. func (ms MessageServer) fetchPeer(PubKey *[ed25519.PublicKeySize]byte, url string) { // Get token, lastpos from database var doUpdate bool log.Debugf("fetch from peer: %s\n", url) peerStat := ms.DB.GetPeerStat(PubKey) if peerStat == nil { // Errors are ignored log.Debugf("fetch from peer: not found %s\n", url) return } if peerStat.LastNotifyFrom == 0 { // We never heard from him log.Debugf("fetch from peer: no notification %s\n", url) return } startDate := CurrentTime() // Check that LastNotifyFrom > LastFetch if ms.HubOnly { // Pre-Emptive fetch in hub-mode: every 4 times FetchDuration or when notified if peerStat.LastFetch != 0 && !(peerStat.LastFetch < peerStat.LastNotifyFrom || peerStat.LastFetch < uint64(startDate-(ms.FetchDuration*4))) { log.Debugf("fetch from peer: no fetch-force and no notifications since last fetch %s\n", url) log.Debugf("LastFetch: %s LastNotifyFrom: %s\n", format(peerStat.LastFetch), format(peerStat.LastNotifyFrom)) return // nothing new to gain } } else { if peerStat.LastFetch > peerStat.LastNotifyFrom && peerStat.LastFetch != 0 { log.Debugf("fetch from peer: no notifications since last fetch %s\n", url) log.Debugf("LastFetch: %s LastNotifyFrom: %s\n", format(peerStat.LastFetch), format(peerStat.LastNotifyFrom)) return // nothing new to gain } } rand.Seed(time.Now().UnixNano()) maxSleep := ms.NotifyDuration - int64(timeout) if maxSleep > 0 { sleeptime := rand.Int63() % maxSleep log.Debugf("fetch from peer: sleeping %d %s\n", sleeptime, url) time.Sleep(time.Duration(sleeptime) * time.Second) } doUpdate = true FetchLoop: for { // Make GetIndex call proto := repproto.New(ms.SocksProxy, "") nextPosition := int(peerStat.LastPosition) if nextPosition != 0 { nextPosition++ } log.Debugf("GlobalIndex fetch: %s next: %d max: %d\n", url, nextPosition, ms.FetchMax) authtoken := utils.B58encode(peerStat.AuthToken[:]) messages, more, err := proto.GetGlobalIndex(url, authtoken, nextPosition, int(ms.FetchMax)) if err != nil { peerStat.ErrorCount++ log.Debugf("GlobalIndex err: %s\n", err) // Only hit on proxy errors errstr := err.Error() if len(errstr) >= 6 && errstr[0:6] == "proxy:" { doUpdate = false } break FetchLoop } for _, msg := range messages { log.Debugf("Index: %d Message: %s\n", msg.Counter, utils.B58encode(msg.MessageID[:])) } MessageLoop: for _, msg := range messages { log.Debugf("Fetching. %d %s\n", msg.Counter, utils.B58encode(msg.MessageID[:])) if startDate+ms.FetchDuration <= CurrentTime() { // We worked for long enough log.Debugf("FetchDuration timeout\n") break MessageLoop } // Check if message exists if ms.DB.MessageExists(msg.MessageID) { peerStat.LastPosition = msg.Counter log.Debugf("fetch from peer: exists %s %s\n", utils.B58encode(msg.MessageID[:]), url) continue MessageLoop // Message exists. } // Add message err := ms.FetchPost(url, authtoken, msg.MessageID, msg.ExpireTime) if err == nil || err == messagestore.ErrDuplicate { // Reduce fetch.ErrorCount when downloads are successful log.Debugf("fetch from peer: exists now %s %s\n", utils.B58encode(msg.MessageID[:]), url) if peerStat.ErrorCount >= 2 { peerStat.ErrorCount -= 2 } } else if err != nil { peerStat.ErrorCount++ log.Debugf("Fetch err: %s %s\n", url, err) doUpdate = false // LastPosition will only advance if future downloads work continue MessageLoop } log.Debugf("fetch from peer: added %s %s\n", utils.B58encode(msg.MessageID[:]), url) peerStat.LastPosition = msg.Counter ms.notifyChan <- true } if !more { // No more messages log.Debugf("Sync done. No More.\n") break FetchLoop } if startDate+ms.FetchDuration <= CurrentTime() { // We worked for long enough log.Debugf("Sync done. FetchDuration timeout.\n") break FetchLoop } } // Write peer update log.Debugf("fetch from peer: cycle done %s\n", url) if doUpdate { ms.DB.UpdatePeerFetchStat(PubKey, uint64(CurrentTime()), peerStat.LastPosition, peerStat.ErrorCount) } else { ms.DB.UpdatePeerFetchStat(PubKey, peerStat.LastFetch, peerStat.LastPosition, peerStat.ErrorCount) } }
// CmdEncrypt encrypts data. func CmdEncrypt() int { var privkey, embedConstantPrivKey, embedTemporaryPrivKey, embedConstantPubKey, embedTemporaryPubKey *message.Curve25519Key var err error var inData, encMessage []byte var meta *message.MetaDataSend var signKeyPair *message.SignKeyPair var removeFile string if OptionsVar.Server == "" { getPeers(false) } // OptionsVar.anonymous . disable private key and signkey if OptionsVar.Anonymous { OptionsVar.Signkey = "" OptionsVar.Privkey = "" GlobalConfigVar.PrivateKey = "" } mindelay := uint32(OptionsVar.Mindelay) maxdelay := uint32(OptionsVar.Maxdelay) // Read input data maxInData := int64(GlobalConfigVar.BodyLength - (message.Curve25519KeySize * 2) - innerHeader) //maxInData := int64(MsgSizeLimit) if OptionsVar.Repost { maxInData -= utils.RepostHeaderSize - message.KeyHeaderSize - message.SignHeaderSize } log.Debugf("Size limit: %d\n", maxInData) inData, err = inputData(OptionsVar.Infile, maxInData) if err != nil { log.Fatalf("No input data: %s\n", err) return 1 } // Verify list contents if OptionsVar.MessageType == message.MsgTypeList { err := utils.VerifyListContent(inData) if err != nil { log.Errorf("Could not verify list input: %s\n", err) return 1 } } // Select private key to use privkeystr := selectPrivKey(OptionsVar.Privkey, GlobalConfigVar.PrivateKey, "") // Parse privkey if privkeystr != "" { privkey = new(message.Curve25519Key) copy(privkey[:], utils.B58decode(privkeystr)) } // Generate embedded keypairs if OptionsVar.Embedkey { if OptionsVar.Notrace || privkey == nil { embedConstantPrivKey, err = message.GenLongTermKey(OptionsVar.Hidden, OptionsVar.Sync) if err != nil { log.Errorf("Key generation error:%s\n", err) return 1 } } else { embedConstantPrivKey = privkey } embedConstantPubKey = message.GenPubKey(embedConstantPrivKey) embedTemporaryPrivKey, err = message.GenRandomKey() if err != nil { log.Errorf("Key generation error:%s\n", err) return 1 } embedTemporaryPubKey = message.GenPubKey(embedTemporaryPrivKey) } embedded := utils.EncodeEmbedded(embedConstantPubKey, embedTemporaryPubKey) recipientConstantPubKey, recipientTemporaryPubKey := utils.ParseKeyPair(OptionsVar.Recipientkey) // Find a signature keypair if we can signKeyDir := "" if GlobalConfigVar.KeyDir != "" { signKeyDir = GlobalConfigVar.KeyDir } if OptionsVar.Signdir != "" { signKeyDir = OptionsVar.Signdir } if OptionsVar.Signkey != "" { var d []byte d, err = utils.MaxReadFile(2048, OptionsVar.Signkey) if err == nil { signKeyPair = new(message.SignKeyPair) kp, err := signKeyPair.Unmarshal(d) if err != nil { log.Errorf("Sign keypair decode error: %s\n", err) signKeyPair = nil } else { signKeyPair = kp } } else { log.Errorf("Sign keypair read error: %s\n", err) } } if signKeyPair == nil && signKeyDir != "" { var d []byte d, removeFile, err = utils.ReadRandomFile(signKeyDir, 2048) if err == nil { signKeyPair = new(message.SignKeyPair) kp, err := signKeyPair.Unmarshal(d) if err != nil { log.Errorf("Sign keypair decode error: %s\n", err) signKeyPair = nil removeFile = "" } else { signKeyPair = kp } } else { log.Errorf("Sign keypair read error: %s\n", err) } } // Set up sender parameters sender := message.Sender{ Signer: signKeyPair, SenderPrivateKey: privkey, ReceiveConstantPublicKey: recipientConstantPubKey, ReceiveTemporaryPublicKey: recipientTemporaryPubKey, TotalLength: GlobalConfigVar.BodyLength, PadToLength: GlobalConfigVar.PadToLength, HashCashBits: GlobalConfigVar.MinHashCash, } // We want encryption output in realtime log.Sync() inData = append(embedded, inData...) if OptionsVar.Repost { // Generate a repost-message encMessage, meta, err = sender.EncryptRepost(byte(OptionsVar.MessageType), inData) if err == nil { rph := utils.EncodeRepostHeader(meta.PadKey, mindelay, maxdelay) encMessage = append(rph[:], encMessage...) log.Datas("STATUS (Process):\tPREPOST\n") log.Dataf("STATUS (RepostSettings):\t%d %d\n", mindelay, maxdelay) } } else { // Generate a normal message encMessage, meta, err = sender.Encrypt(byte(OptionsVar.MessageType), inData) if err == nil { log.Datas("STATUS (Process):\tPOST\n") } } if err != nil { log.Fatalf("Encryption failed: %s\n", err) return 1 } log.Sync() // Output. repost is only written to stdout or file if OptionsVar.Outfile == "-" || (OptionsVar.Repost && OptionsVar.Outfile == "") { err = utils.WriteStdout(encMessage) // Display data as necessary if err == nil { log.Dataf("STATUS (RecPubKey):\t%s\n", utils.B58encode(meta.ReceiverConstantPubKey[:])) if OptionsVar.Embedkey { log.Dataf("STATUS (EmbedPublicKey):\t%s_%s\n", utils.B58encode(embedConstantPubKey[:]), utils.B58encode(embedTemporaryPubKey[:])) log.Dataf("STATUS (EmbedPrivateKey):\t%s_%s\n", utils.B58encode(embedConstantPrivKey[:]), utils.B58encode(embedTemporaryPrivKey[:])) } if meta.MessageKey != nil { log.Dataf("STATUS (ListInput):\tNULL %s %s\n", utils.B58encode(meta.MessageID[:]), utils.B58encode(meta.MessageKey[:])) log.Dataf("STATUS (Message):\t%s_%s\n", utils.B58encode(meta.MessageID[:]), utils.B58encode(meta.MessageKey[:])) } else { log.Dataf("STATUS (ListInput):\tNULL %s NULL\n", utils.B58encode(meta.MessageID[:])) log.Dataf("STATUS (MessageID):\t%s\n", utils.B58encode(meta.MessageID[:])) } } } else if OptionsVar.Outfile != "" || OptionsVar.Repost { err = utils.WriteNewFile(OptionsVar.Outfile, encMessage) // Display data as necessary log.Dataf("STATUS (RecPubKey):\t%s\n", utils.B58encode(meta.ReceiverConstantPubKey[:])) if err == nil { if OptionsVar.Embedkey { log.Dataf("STATUS (EmbedPublicKey):\t%s_%s\n", utils.B58encode(embedConstantPubKey[:]), utils.B58encode(embedTemporaryPubKey[:])) log.Dataf("STATUS (EmbedPrivateKey):\t%s_%s\n", utils.B58encode(embedConstantPrivKey[:]), utils.B58encode(embedTemporaryPrivKey[:])) } if meta.MessageKey != nil { log.Dataf("STATUS (ListInput):\tNULL %s %s\n", utils.B58encode(meta.MessageID[:]), utils.B58encode(meta.MessageKey[:])) log.Dataf("STATUS (Message):\t%s_%s\n", utils.B58encode(meta.MessageID[:]), utils.B58encode(meta.MessageKey[:])) log.Printf("Pastebin Address:\t%s_%s\n", utils.B58encode(meta.MessageID[:]), utils.B58encode(meta.MessageKey[:])) } else { log.Dataf("STATUS (ListInput):\tNULL %s NULL\n", utils.B58encode(meta.MessageID[:])) log.Dataf("STATUS (MessageID):\t%s\n", utils.B58encode(meta.MessageID[:])) log.Printf("Pastebin Address:\t%s\n", utils.B58encode(meta.MessageID[:])) } } } else { // Post to server server := OptionsVar.Server proto := repproto.New(OptionsVar.Socksserver, OptionsVar.Server, GlobalConfigVar.PasteServers...) if server == "" { server, err = proto.Post(meta.MessageID[:], encMessage) } else { err = proto.PostSpecific(server, encMessage) } sep := "/" if server != "" && server[len(server)-1] == '/' { sep = "" } if err == nil { if meta.MessageKey != nil { log.Dataf("STATUS (URL):\t%s/%s_%s\n", server, utils.B58encode(meta.MessageID[:]), utils.B58encode(meta.MessageKey[:])) } if OptionsVar.Embedkey { log.Dataf("STATUS (EmbedPublicKey):\t%s_%s\n", utils.B58encode(embedConstantPubKey[:]), utils.B58encode(embedTemporaryPubKey[:])) log.Dataf("STATUS (EmbedPrivateKey):\t%s_%s\n", utils.B58encode(embedConstantPrivKey[:]), utils.B58encode(embedTemporaryPrivKey[:])) } if meta.MessageKey != nil { log.Dataf("STATUS (ListInput):\t%s %s %s\n", server, utils.B58encode(meta.MessageID[:]), utils.B58encode(meta.MessageKey[:])) log.Dataf("STATUS (Message):\t%s_%s\n", utils.B58encode(meta.MessageID[:]), utils.B58encode(meta.MessageKey[:])) log.Printf("Pastebin Address:\t%s%s%s_%s\n", server, sep, utils.B58encode(meta.MessageID[:]), utils.B58encode(meta.MessageKey[:])) } else { log.Dataf("STATUS (ListInput):\t%s %s NULL\n", server, utils.B58encode(meta.MessageID[:])) log.Dataf("STATUS (MessageID):\t%s\n", utils.B58encode(meta.MessageID[:])) log.Printf("Pastebin Address:\t%s%s%s\n", server, sep, utils.B58encode(meta.MessageID[:])) } } } if err != nil { log.Fatalf("Output failed: %s\n", err) log.Sync() return 1 } if removeFile != "" { // Operation has been successful, remove signer keyfile (if any) os.Remove(removeFile) } return 0 }
// encrypt message. func (sender *Sender) encrypt(messageType byte, message []byte, repost bool) (encMessage []byte, meta *MetaDataSend, err error) { var Signer *SignKeyPair meta = new(MetaDataSend) // Set defaults if sender.HashCashBits <= 0 { sender.HashCashBits = DefaultHashCashBits } if sender.TotalLength <= 0 { sender.TotalLength = DefaultTotalLength } if sender.PadToLength <= 0 { sender.PadToLength = DefaultPadToLength } // Get signer Signer = sender.Signer // Verify if (optional) signer produces enough bits, if not, generate a new one if Signer != nil { ok, _ := hashcash.TestNonce(Signer.PublicKey[:], Signer.Nonce[:], sender.HashCashBits) if !ok { log.Debugf("Given signer does not produce enough HashCash bits: %x", Signer.PublicKey) Signer = nil } } if Signer == nil { // We have no signer, so we generate one. Don't make public for sender instance log.Info("Generating new HashCash signer...") Signer, err = GenKey(sender.HashCashBits) if err != nil { return nil, nil, err } log.Info("...new HashCash signer generated") } // Generate sender keypack. if SenderPrivateKey is set, it will be used keypackSender, err := GenSenderKeys(sender.SenderPrivateKey) if err != nil { return nil, nil, err } // Generate peer's keypack. If keys are known, they are used keypackPeer, err := GenReceiveKeys(sender.ReceiveConstantPublicKey, sender.ReceiveTemporaryPublicKey) if err != nil { return nil, nil, err } // We generated new keys, they have to be made available if sender.ReceiveConstantPublicKey == nil { log.Debug("Using one-time receiver key") meta.MessageKey = keypackPeer.ConstantPrivKey // TemporaryPrivKey is deterministic } // Generate the nonce nonce, err := GenNonce() if err != nil { return nil, nil, err } myMessage := new(Message) // Create our KeyHeader myMessage.Header = PackKeyHeader(keypackSender, keypackPeer, nonce) // Calculate our shared secret. We are the sender, so last param is true sharedSecret := CalcSharedSecret(keypackSender, keypackPeer, nonce, true) // Set encryption/padding parameters bodyEncryption := EncryptBodyDef{ IV: *GenIV(myMessage.Header[:]), // Generate IV from key header, which is uniqueish SharedSecret: sharedSecret, MessageType: messageType, TotalLength: sender.TotalLength - SignHeaderSize - KeyHeaderSize, PadToLength: sender.PadToLength, } log.Debug("Encrypting...") body, err := bodyEncryption.EncryptBody(message) if err != nil { return nil, nil, err } log.Debug("...encryption done") // Convert body to bytes myMessage.Body = body.Bytes() // Generate the message ID. This has to happen the same way for repost and standard message meta.MessageID = *myMessage.CalcMessageID() meta.ReceiverConstantPubKey = keypackPeer.ConstantPubKey myMessage.SignatureHeader = Signer.Sign(meta.MessageID) if repost { log.Debug("This is a repost-message!") // Cut out padding and set padkey meta.PadKey = &body.PadKey myMessage.Body = body.BytesNoPadding() // Repost messages are not base64 encoded since they are embedded anyways return myMessage.Bytes(), meta, nil } return EncodeBase64(myMessage.Bytes()), meta, nil }
// ProcessPost verifies and adds a post to the database. func (ms MessageServer) ProcessPost(postdata io.ReadCloser, oneTime bool, expireRequest uint64) string { data, err := utils.MaxRead(ms.MaxPostSize, postdata) if err != nil { return "ERROR: Message too big\n" } if len(data) < ms.MinPostSize { return "ERROR: Message too small\n" } signheader, err := message.Base64Message(data).GetSignHeader() if err != nil { log.Debugf("Post:GetSignHeader: %s\n", err) return "ERROR: Sign Header\n" } details, err := message.VerifySignature(*signheader, ms.MinHashCashBits) if err != nil { log.Debugf("Post:VerifySignature: %s\n", err) return "ERROR: HashCash\n" } constantRecipientPub, MessageID, err := deferVerify(data) if err != nil { log.Debugf("Post:deferVerify: %s\n", err) return "ERROR: Verify\n" } if *MessageID != details.MsgID { log.Debugs("Post:MessageID\n") return "ERROR: MessageID\n" } msgStruct := &structs.MessageStruct{ MessageID: *MessageID, ReceiverConstantPubKey: *constantRecipientPub, SignerPub: details.PublicKey, OneTime: oneTime, Sync: false, Hidden: false, ExpireRequest: expireRequest, } if !oneTime { if message.KeyIsSync(constantRecipientPub) { msgStruct.Sync = true } } else { msgStruct.Sync = false } if message.KeyIsHidden(constantRecipientPub) { msgStruct.Hidden = true } sigStruct := &structs.SignerStruct{ PublicKey: details.PublicKey, Nonce: details.HashCashNonce, Bits: details.HashCashBits, } sigStruct.MaxMessagesPosted, sigStruct.MaxMessagesRetained, sigStruct.ExpireTarget = ms.calcLimits(details.HashCashBits) _, _ = msgStruct, sigStruct ms.RandomSleep() // err = ms.DB.Put(msgStruct, sigStruct, data) err = ms.DB.PutNotify(msgStruct, sigStruct, data, ms.notifyChan) ms.RandomSleep() if err != nil { log.Debugf("Post:MessageDB: %s\n", err) return fmt.Sprintf("ERROR: %s\n", err) } log.Debugf("Post:Added: %x\n", MessageID[:12]) if ms.Stat { stat.Input <- stat.Post } return "SUCCESS: Connection close\n" }
// GetKeyIndex returns the index for a key. func (ms MessageServer) GetKeyIndex(w http.ResponseWriter, r *http.Request) { var pubKey *message.Curve25519Key var auth []byte start := int64(0) count := int64(10) w.Header().Set("Content-Type", "text/plain; charset=us-ascii") getValues := r.URL.Query() if getValues != nil { if v, ok := getValues["start"]; ok { t, err := strconv.Atoi(v[0]) if err == nil { start = int64(t) } } if v, ok := getValues["count"]; ok { t, err := strconv.Atoi(v[0]) if err == nil { count = int64(t) if count > ms.MaxIndexKey { count = ms.MaxIndexKey } } } if v, ok := getValues["key"]; ok { if len(v[0]) > message.Curve25519KeySize*10 { io.WriteString(w, "ERROR: Bad param\n") return } t := utils.B58decode(v[0]) if len(t) != message.Curve25519KeySize { io.WriteString(w, "ERROR: Bad param\n") return } pubKey = new(message.Curve25519Key) copy(pubKey[:], t) if v, ok := getValues["auth"]; ok { if len(v[0]) > keyauth.AnswerSize*10 { io.WriteString(w, "ERROR: Bad param\n") return } auth = utils.B58decode(v[0]) if len(auth) != keyauth.AnswerSize { io.WriteString(w, "ERROR: Bad param\n") return } } } else { io.WriteString(w, "ERROR: Missing param\n") return } } else { io.WriteString(w, "ERROR: Missing param\n") return } if pubKey == nil { io.WriteString(w, "ERROR: Missing param\n") return } if message.KeyIsHidden(pubKey) { if auth == nil { log.Debugs("List:Auth missing\n") io.WriteString(w, "ERROR: Authentication required\n") return } answer := [keyauth.AnswerSize]byte{} copy(answer[:], auth) now := uint64(time.Now().Unix() + ms.TimeSkew) if !keyauth.VerifyTime(&answer, now, ms.TimeGrace) { log.Debugs("List:Auth timeout\n") io.WriteString(w, "ERROR: Authentication failed: Timeout\n") return } privK := [32]byte(*ms.authPrivKey) testK := [32]byte(*pubKey) if !keyauth.Verify(&answer, &privK, &testK) { log.Debugs("List:Auth no verify\n") io.WriteString(w, "ERROR: Authentication failed: No Match\n") return } } messages, found, err := ms.DB.GetIndex(pubKey, start, count) if err != nil && err != fileback.ErrNoMore { log.Debugf("List:GetIndex: %s\n", err) log.Debugf("List:GetIndex: Key %x\n", pubKey) io.WriteString(w, "ERROR: List failed\n") return } io.WriteString(w, "SUCCESS: Data follows\n") for _, msg := range messages { io.WriteString(w, "IDX: "+strings.Trim(string(msg), " \t\n\r")+"\n") } if int64(found) < count { io.WriteString(w, "CMD: Exceeded\n") } else { io.WriteString(w, "CMD: Continue\n") } }
// Run starts the statistics handler. func Run() { start := now() postList1 := list.New() postList5 := list.New() postList60 := list.New() postList1440 := list.New() fetchList1 := list.New() fetchList5 := list.New() fetchList60 := list.New() fetchList1440 := list.New() for { m := <-Input t := now() switch m { case Post: postList1.PushBack(t) postList5.PushBack(t) postList60.PushBack(t) postList1440.PushBack(t) case Fetch: fetchList1.PushBack(t) fetchList5.PushBack(t) fetchList60.PushBack(t) fetchList1440.PushBack(t) case Show: removeOld(postList1, t-oneMin) removeOld(postList5, t-fiveMin) removeOld(postList60, t-oneHour) removeOld(postList1440, t-oneDay) removeOld(fetchList1, t-oneMin) removeOld(fetchList5, t-fiveMin) removeOld(fetchList60, t-oneHour) removeOld(fetchList1440, t-oneDay) postOneMin := float64(postList1.Len()) postFiveMin := float64(postList5.Len()) / 5.0 postOneHour := float64(postList60.Len()) / 60.0 postOneDay := float64(postList1440.Len()) / 1440.0 fetchOneMin := float64(fetchList1.Len()) fetchFiveMin := float64(fetchList5.Len()) / 5.0 fetchOneHour := float64(fetchList60.Len()) / 60.0 fetchOneDay := float64(fetchList1440.Len()) / 1440.0 if t-oneDay >= start { log.Debugf("Posts/minute: %8.3f (1m) %8.3f (5m) %8.3f (1h) %8.3f (24h)\n", postOneMin, postFiveMin, postOneHour, postOneDay) log.Debugf("Fetches/minute: %8.3f (1m) %8.3f (5m) %8.3f (1h) %8.3f (24h)\n", fetchOneMin, fetchFiveMin, fetchOneHour, fetchOneDay) } else if t-oneHour >= start { log.Debugf("Posts/minute: %8.3f (1m) %8.3f (5m) %8.3f (1h)\n", postOneMin, postFiveMin, postOneHour) log.Debugf("Fetches/minute: %8.3f (1m) %8.3f (5m) %8.3f (1h)\n", fetchOneMin, fetchFiveMin, fetchOneHour) } else if t-fiveMin >= start { log.Debugf("Posts/minute: %8.3f (1m) %8.3f (5m)\n", postOneMin, postFiveMin) log.Debugf("Fetches/minute: %8.3f (1m) %8.3f (5m)\n", fetchOneMin, fetchFiveMin) } else { log.Debugf("Posts/minute: %8.3f (1m)\n", postOneMin) log.Debugf("Fetches/minute: %8.3f (1m)\n", fetchOneMin) } } } }