// Encode an expireStruct func (es ExpireStruct) Encode() ExpireStructEncoded { out := make([]byte, 0, ExpireStructSize) out = append(out, []byte(strconv.FormatUint(es.ExpireTime, 10)+" ")...) out = append(out, []byte(utils.B58encode(es.MessageID[:])+" ")...) out = append(out, []byte(utils.B58encode(es.SignerPubKey[:])+" ")...) return out[:len(out)] }
// 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() { // ExpireRun delMessages, err := store.db.SelectMessageExpire(CurrentTime()) if err != nil { log.Errorf("ExpireFromIndex, SelectMessageExpire: %s\n", err) return } for _, msg := range delMessages { err := store.db.DeleteBlob(&msg.MessageID) if err != nil { log.Errorf("ExpireFromIndex, DeleteBlob: %s %s\n", err, utils.B58encode(msg.MessageID[:])) continue } err = store.db.DeleteMessageByID(&msg.MessageID) if err != nil { log.Errorf("ExpireFromIndex, DeleteMessageByID: %s %s\n", err, utils.B58encode(msg.MessageID[:])) } } _, _, err = store.db.ExpireSigners(MaxAgeSigners) if err != nil { log.Errorf("ExpireFromIndex, ExpireSigners: %s\n", err) } err = store.db.ExpireMessageCounter(MaxAgeRecipients) if err != nil { log.Errorf("ExpireFromIndex, ExpireMessageCounter: %s\n", err) } err = store.db.ForgetMessages(CurrentTime() - MaxAgeRecipients) if err != nil { log.Errorf("ExpireFromIndex, ForgetMessages: %s\n", err) } }
// New returns a MessageServer. func New(driver, url, path string, pubKey, privKey []byte) (*MessageServer, error) { var err error ms := new(MessageServer) ms.DB = messagestore.New(driver, url, path, Workers) if err != nil { return nil, err } ms.path = path ms.PeerFile = path + "peers.config" ms.AddToPeer = DefaultAddToPeer ms.MaxTimeSkew = DefaultMaxTimeSkew ms.MinPostSize = MinPostSize ms.MaxPostSize = MaxPostSize ms.MinHashCashBits = MinHashCashBits ms.MaxSleep = DefaultMaxSleep ms.MaxIndexGlobal = DefaultIndexGlobal ms.MaxIndexKey = DefaultIndexKey ms.TimeGrace = DefaultTimeGrace ms.MaxAuthTokenAge = DefaultAuthTokenAge ms.NotifyDuration = DefaultNotifyDuration ms.FetchDuration = DefaultFetchDuration ms.FetchMax = DefaultFetchMax ms.ExpireDuration = DefaultExpireDuration ms.ExpireFSDuration = DefaultExpireFSDuration ms.StepLimit = DefaultStepLimit ms.ListenPort = DefaultListenPort ms.MinStoreTime = DefaultMinStoreTime ms.MaxStoreTime = DefaultMaxStoreTime ms.MaxAgeSigners = DefaultMaxAgeSigners ms.MaxAgeRecipients = DefaultMaxAgeRecipients messagestore.MaxAgeRecipients = DefaultMaxAgeRecipients messagestore.MaxAgeSigners = DefaultMaxAgeSigners ms.EnablePeerHandler = true ms.authPrivKey, err = message.GenLongTermKey(true, false) if err != nil { return nil, err } ms.AuthPubKey = message.GenPubKey(ms.authPrivKey) if pubKey != nil && privKey != nil { ms.TokenPubKey = new([ed25519.PublicKeySize]byte) ms.TokenPrivKey = new([ed25519.PrivateKeySize]byte) copy(ms.TokenPubKey[:], pubKey) copy(ms.TokenPrivKey[:], privKey) } else { ms.TokenPubKey, ms.TokenPrivKey, err = ed25519.GenerateKey(rand.Reader) log.Printf("Peer authentication public key: %s\n", utils.B58encode(ms.TokenPubKey[:])) log.Printf("Peer authentication private key: %s\n", utils.B58encode(ms.TokenPrivKey[:])) if err != nil { return nil, err } } ms.InfoStruct = new(ServerInfo) ms.InfoStruct.MinHashCashBits = ms.MinHashCashBits ms.InfoStruct.MinPostSize = ms.MinPostSize ms.InfoStruct.MaxPostSize = ms.MaxPostSize ms.InfoStruct.AuthPubKey = utils.B58encode(ms.AuthPubKey[:]) return ms, nil }
// 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 }
// showConfig shows current (default) config func showConfig() { if defaultSettings.PeeringPrivateKey == "" { pubkey, privkey, err := ed25519.GenerateKey(rand.Reader) if err == nil { defaultSettings.PeeringPrivateKey = utils.B58encode(privkey[:]) defaultSettings.PeeringPublicKey = utils.B58encode(pubkey[:]) } } config, _ := json.MarshalIndent(defaultSettings, "", " ") fmt.Println(string(config)) }
// Encode a SignerStruct to human readable representation func (ss SignerStruct) Encode() SignerStructEncoded { out := make([]byte, 0, SignerStructSize) out = append(out, []byte(utils.B58encode(ss.PublicKey[:])+" ")...) out = append(out, []byte(utils.B58encode(ss.Nonce[:])+" ")...) out = append(out, []byte(strconv.FormatUint(uint64(ss.Bits), 10)+" ")...) out = append(out, []byte(strconv.FormatUint(ss.MessagesPosted, 10)+" ")...) out = append(out, []byte(strconv.FormatUint(ss.MessagesRetained, 10)+" ")...) out = append(out, []byte(strconv.FormatUint(ss.MaxMessagesPosted, 10)+" ")...) out = append(out, []byte(strconv.FormatUint(ss.MaxMessagesRetained, 10)+" ")...) out = append(out, []byte(strconv.FormatUint(ss.ExpireTarget, 10)+" ")...) return out[:len(out)] }
// CmdGenKey generates a long-term public key func CmdGenKey() int { privkey, err := message.GenLongTermKey(OptionsVar.Hidden, OptionsVar.Sync) if err != nil { log.Errorf("Key generation error:%s\n", err) return 1 } pubkey := message.GenPubKey(privkey) log.Dataf("STATUS (PrivateKey):\t%s\n", utils.B58encode(privkey[:])) // log.Dataf("STATUS (PublicKey):\t%s\n", utils.B58encode(pubkey[:])) log.Printf("PRIVATE key: %s\n\n", utils.B58encode(privkey[:])) // log.Printf("Public key: %s\n", utils.B58encode(pubkey[:])) _ = pubkey return 0 }
// Encode encodes a MessageStruct in human-readable format for storage func (ms MessageStruct) Encode() MessageStructEncoded { out := make([]byte, 0, MessageStructSize) out = append(out, []byte(strconv.FormatUint(ms.Counter, 10)+" ")...) out = append(out, []byte(strconv.FormatUint(ms.PostTime, 10)+" ")...) out = append(out, []byte(strconv.FormatUint(ms.ExpireTime, 10)+" ")...) out = append(out, []byte(strconv.FormatUint(ms.ExpireRequest, 10)+" ")...) out = append(out, []byte(utils.B58encode(ms.MessageID[:])+" ")...) out = append(out, []byte(utils.B58encode(ms.ReceiverConstantPubKey[:])+" ")...) out = append(out, []byte(utils.B58encode(ms.SignerPub[:])+" ")...) out = append(out, []byte(strconv.FormatUint(ms.Distance, 10)+" ")...) out = append(out, []byte(BoolToString(ms.OneTime)+" ")...) out = append(out, []byte(BoolToString(ms.Sync)+" ")...) out = append(out, []byte(BoolToString(ms.Hidden))...) return out[:len(out)] }
// KeyCallBack implements a callback function to request keys from file-descriptor func KeyCallBack(keyMgtFd int) (*os.File, func(*message.Curve25519Key) *message.Curve25519Key) { knownKeys := make(map[message.Curve25519Key]message.Curve25519Key) fd := os.NewFile(uintptr(keyMgtFd), "fd/"+strconv.Itoa(keyMgtFd)) return fd, func(pubkey *message.Curve25519Key) *message.Curve25519Key { // KeyCallBack func(*Curve25519Key) *Curve25519Key log.Sync() if v, ok := knownKeys[*pubkey]; ok { // Return from cache if we can return &v } b := make([]byte, 120) log.Dataf("STATUS (KeyMGTRequest):\t%s\n", utils.B58encode(pubkey[:])) log.Sync() n, _ := fd.Read(b) if n == 0 { log.Datas("STATUS (KeyMGT):\tREAD FAILURE\n") return nil } log.Datas("STATUS (KeyMGT):\tREAD DONE\n") k1, k2 := utils.ParseKeyPair(strings.Trim(string(b[:n]), " \t\r\n")) if k1 != nil { pub1 := message.GenPubKey(k1) // Add to cache knownKeys[*pub1] = *k1 } if k2 != nil { pub2 := message.GenPubKey(k2) // Add to cache knownKeys[*pub2] = *k2 } if v, ok := knownKeys[*pubkey]; ok { // Return from cache if we can return &v } return nil } }
// GetPeerStat returns the last entry of peer statistics for pubkey func (store Store) GetPeerStat(pubkey *[ed25519.PublicKeySize]byte) *structs.PeerStruct { st, err := store.db.SelectPeer(pubkey) if err != nil { log.Errorf("GetPeerStat: %s, %s\n", err, utils.B58encode(pubkey[:])) return nil } return st }
// GetSpecificAuth fetches a message from a specific server using authentication func (proto *Proto) GetSpecificAuth(server, auth string, messageID []byte) ([]byte, error) { messageIDenc := utils.B58encode(messageID) body, err := socks.Proxy(proto.SocksServer).LimitGet(constructURL(server, "/fetch", "?messageid=", messageIDenc, "&auth=", auth), 512000) if err != nil { return nil, err } return parseError(body) }
// Encode peerstruct into human readable form func (ps PeerStruct) Encode() PeerStructEncoded { out := make([]byte, 0, PeerStructSize) out = append(out, []byte(utils.B58encode(ps.AuthToken[:])+" ")...) out = append(out, []byte(strconv.FormatUint(ps.LastNotifySend, 10)+" ")...) out = append(out, []byte(strconv.FormatUint(ps.LastNotifyFrom, 10)+" ")...) out = append(out, []byte(strconv.FormatUint(ps.LastFetch, 10)+" ")...) out = append(out, []byte(strconv.FormatUint(ps.ErrorCount, 10)+" ")...) out = append(out, []byte(strconv.FormatUint(ps.LastPosition, 10)+" ")...) return out[:len(out)] }
// 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") } }
// Auth creates an authentication for server and privKey func (proto *Proto) Auth(server string, privKey []byte) (string, error) { var challenge [keyauth.ChallengeSize]byte var secret [keyauth.PrivateKeySize]byte info, err := proto.ID(server) if err != nil { return "", err } challengeS := utils.B58decode(info.AuthChallenge) copy(challenge[:], challengeS) copy(secret[:], privKey[:]) answer := keyauth.Answer(&challenge, &secret) return utils.B58encode(answer[:]), nil }
// 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.Debugf("Fetch OK: %s\n", utils.B58encode(messageID[:])) if ms.Stat { stat.Input <- stat.Fetch } return }
// CmdGenTempKey generates a temporary key for a given private key func CmdGenTempKey() int { var privkey message.Curve25519Key privkeystr := selectPrivKey(OptionsVar.Privkey, GlobalConfigVar.PrivateKey, "tty") if privkeystr == "" { log.Fatal("No private key given (-privkey)") return 1 } copy(privkey[:], utils.B58decode(privkeystr)) pubkey := message.GenPubKey(&privkey) privkeytemp, err := message.GenRandomKey() if err != nil { log.Errorf("Key generation error:%s\n", err) return 1 } pubkeytemp := message.GenPubKey(privkeytemp) log.Dataf("STATUS (PrivateKey):\t%s_%s\n", utils.B58encode(privkey[:]), utils.B58encode(privkeytemp[:])) log.Dataf("STATUS (PublicKey):\t%s_%s\n", utils.B58encode(pubkey[:]), utils.B58encode(pubkeytemp[:])) log.Printf("PRIVATE key: %s_%s\n\n", utils.B58encode(privkey[:]), utils.B58encode(privkeytemp[:])) log.Printf("Public key: %s_%s\n", utils.B58encode(pubkey[:]), utils.B58encode(pubkeytemp[:])) return 0 }
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 := time.Now().Unix() + 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 { ms.DB.UpdatePeerNotification(PubKey, false) } }
// Delete implements the delete call for messages. func (ms MessageServer) Delete(w http.ResponseWriter, r *http.Request) { var messageID *[message.MessageIDSize]byte var privateKey *message.Curve25519Key for i := 0; i < 4; i++ { ms.RandomSleep() // Let's not give instant gratification here } 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 v, ok := getValues["privkey"]; ok { t := utils.B58decode(v[0]) if len(t) < message.Curve25519KeySize || len(t) > message.Curve25519KeySize { io.WriteString(w, "ERROR: Bad parameter\n") return } privateKey = new(message.Curve25519Key) copy(privateKey[:], t) } if privateKey != nil && messageID != nil { publicKey := message.GenPubKey(privateKey) err := ms.DB.PreExpire(messageID, publicKey) if err == nil { log.Errorf("Message censored: %s, asshole.\n", utils.B58encode(messageID[:])) io.WriteString(w, "SUCCESS: If you want to call it that\n") return } log.Errorf("Message censoring failed! %s\n", err) } } io.WriteString(w, "ERROR: Missing parameters\n") return }
// ListSpecific lists the messages for pubKey from a specific server func (proto *Proto) ListSpecific(server string, pubKey, privKey []byte, start, count int) (messages []*structs.MessageStruct, more bool, err error) { var authStr string var myPubKey message.Curve25519Key copy(myPubKey[:], pubKey) if message.KeyIsHidden(&myPubKey) { if privKey == nil { return nil, false, ErrPrivKey } auth, err := proto.Auth(server, privKey) if err != nil { return nil, false, err } authStr = "&auth=" + auth } url := constructURL(server, "/keyindex?key=", utils.B58encode(pubKey[:]), "&start=", strconv.Itoa(start), "count=", strconv.Itoa(count), authStr) body, err := socks.Proxy(proto.SocksServer).LimitGet(url, 512000) if err != nil { return nil, false, err } return parseListResponse(body) }
// 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) } }
// UpdatePeerFetchStat writes fetch-specific data func (store Store) UpdatePeerFetchStat(pubkey *[ed25519.PublicKeySize]byte, lastFetch, lastPos, lastErrors uint64) { err := store.db.UpdatePeerStats(pubkey, lastFetch, lastPos, lastErrors) if err != nil { log.Errorf("UpdatePeerStats: %s, %s\n", err, utils.B58encode(pubkey[:])) } }
// UpdatePeerNotification updates the peer stat after notification send func (store Store) UpdatePeerNotification(pubkey *[ed25519.PublicKeySize]byte, hasError bool) { err := store.db.UpdatePeerNotification(pubkey, hasError) if err != nil { log.Errorf("UpdatePeerNotification: %s, %s\n", err, utils.B58encode(pubkey[:])) } }
// 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 }
// UpdatePeerAuthToken updates the peer record when a new auth token has been received func (store Store) UpdatePeerAuthToken(senderPubKey *[ed25519.PublicKeySize]byte, signedToken *[keyproof.ProofTokenSignedSize]byte) { err := store.db.UpdatePeerToken(senderPubKey, signedToken) if err != nil { log.Errorf("UpdatePeerAuthToken: %s, %s\n", err, utils.B58encode(senderPubKey[:])) } }
// 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: %s\n", utils.B58encode(MessageID[:])) if ms.Stat { stat.Input <- stat.Post } return "SUCCESS: Connection close\n" }
// CmdIndex returns the index for a private key func CmdIndex() int { var server string var err error var pubkey *message.Curve25519Key var privkey message.Curve25519Key var messages []*structs.MessageStruct var moreMessages bool if OptionsVar.Server == "" { // getPeers(false). Meaningless for index calls log.Fatal("--index requires --server to be specified.\n") return 1 } server = OptionsVar.Server if OptionsVar.Outdir != "" && !isDir(OptionsVar.Outdir) { log.Fatalf("outdir does not exist or is no directory: %s", OptionsVar.Outdir) return 1 } // privkey must be set privkeystr := selectPrivKey(OptionsVar.Privkey, GlobalConfigVar.PrivateKey, "tty") if privkeystr == "" { log.Fatal("Private key missing: --privkey\n") return 1 } // Test if long key is given, if yes, use only first part if pos := strings.Index(privkeystr, "_"); pos > 0 { privkeystr = privkeystr[:pos] } privT := utils.B58decode(privkeystr) copy(privkey[:], privT) pubkey = message.CalcPub(&privkey) proto := repproto.New(OptionsVar.Socksserver, OptionsVar.Server) // If we want to change to using the server list instead. Not a great idea // proto := repproto.New(OptionsVar.Socksserver, OptionsVar.Server, GlobalConfigVar.PasteServers...) // re-enable if we want to use the server list: server, messages, moreMessages, err = proto.List(pubkey[:], privkey[:], OptionsVar.Start, OptionsVar.Count) log.Dataf("STATUS (Process):\tLIST\n") messages, moreMessages, err = proto.ListSpecific(server, pubkey[:], privkey[:], OptionsVar.Start, OptionsVar.Count) if err != nil { log.Fatalf("List error: %s\n", err) return 1 } if len(messages) > 0 { fmt.Print("Index\t\tMessageID\n") fmt.Print("------------------------------------------------------------\n") } for _, msg := range messages { log.Dataf("STATUS (MessageList):\t%d %s %s %d %d\n", msg.Counter, utils.B58encode(msg.MessageID[:]), utils.B58encode(msg.SignerPub[:]), msg.PostTime, msg.ExpireTime) fmt.Printf("%d\t\t%s\n", msg.Counter, utils.B58encode(msg.MessageID[:])) } log.Dataf("STATUS (ListResult):\t%d %d %t\n", OptionsVar.Start, OptionsVar.Count, moreMessages) if len(messages) > 0 { fmt.Print("------------------------------------------------------------\n") } if moreMessages { fmt.Print("More messages may be available\n") } else { fmt.Print("Listing complete\n") } if OptionsVar.Outdir != "" { log.Dataf("STATUS (Process):\tFETCHMANY\n") fmt.Print("Fetching messages...") hasErrors := 0 for _, msg := range messages { log.Dataf("STATUS (Fetch):\t%s\n", utils.B58encode(msg.MessageID[:])) err = loadStoreMessage(server, msg.MessageID[:], OptionsVar.Outdir+string(os.PathSeparator)+utils.B58encode(msg.MessageID[:])) // use index server for download, store with name messageID if err != nil { fmt.Print(".F") hasErrors++ log.Dataf("STATUS (FetchError):\t%s\n", utils.B58encode(msg.MessageID[:])) } else { fmt.Print(".o") log.Dataf("STATUS (FetchComplete):\t%s\n", utils.B58encode(msg.MessageID[:])) } } if hasErrors > 0 { fmt.Print(". Some errors during download.\n") log.Dataf("STATUS (FetchResult):\tERROS %d\n", hasErrors) } else { fmt.Print(". Download complete.\n") log.Dataf("STATUS (FetchResult):\tOK\n") } } log.Sync() // server can be set return 0 }
// CmdIndex returns the index for a private key func CmdIndex() int { var server string var err error var pubkey *message.Curve25519Key var privkey message.Curve25519Key var messages []*structs.MessageStruct var moreMessages bool if OptionsVar.Server == "" { getPeers(false) } if OptionsVar.Outdir != "" && !isDir(OptionsVar.Outdir) { log.Fatalf("outdir does not exist or is no directory: %s", OptionsVar.Outdir) return 1 } // privkey must be set privkeystr := selectPrivKey(OptionsVar.Privkey, GlobalConfigVar.PrivateKey, "tty") if privkeystr == "" { log.Fatal("Private key missing: --privkey\n") return 1 } privT := utils.B58decode(privkeystr) copy(privkey[:], privT) pubkey = message.CalcPub(&privkey) if OptionsVar.Server != "" { server = OptionsVar.Server } proto := repproto.New(OptionsVar.Socksserver, OptionsVar.Server) log.Dataf("STATUS (Process):\tLIST\n") if server != "" { fmt.Println("Specific server") messages, moreMessages, err = proto.ListSpecific(server, pubkey[:], privkey[:], OptionsVar.Start, OptionsVar.Count) } else { fmt.Println("Any server") server, messages, moreMessages, err = proto.List(pubkey[:], privkey[:], OptionsVar.Start, OptionsVar.Count) } if err != nil { log.Fatalf("List error: %s\n", err) return 1 } if len(messages) > 0 { fmt.Print("Index\t\tMessageID\n") fmt.Print("------------------------------------------------------------\n") } for _, msg := range messages { log.Dataf("STATUS (MessageList):\t%d %s %s %d %d\n", msg.Counter, utils.B58encode(msg.MessageID[:]), utils.B58encode(msg.SignerPub[:]), msg.PostTime, msg.ExpireTime) fmt.Printf("%d\t\t%s\n", msg.Counter, utils.B58encode(msg.MessageID[:])) } log.Dataf("STATUS (ListResult):\t%d %d %t\n", OptionsVar.Start, OptionsVar.Count, moreMessages) if len(messages) > 0 { fmt.Print("------------------------------------------------------------\n") } if moreMessages { fmt.Print("More messages may be available\n") } else { fmt.Print("Listing complete\n") } if OptionsVar.Outdir != "" { log.Dataf("STATUS (Process):\tFETCHMANY\n") fmt.Print("Fetching messages...") hasErrors := 0 for _, msg := range messages { log.Dataf("STATUS (Fetch):\t%s\n", utils.B58encode(msg.MessageID[:])) err = loadStoreMessage(server, msg.MessageID[:], OptionsVar.Outdir+string(os.PathSeparator)+utils.B58encode(msg.MessageID[:])) // use index server for download, store with name messageID if err != nil { fmt.Print(".F") hasErrors++ log.Dataf("STATUS (FetchError):\t%s\n", utils.B58encode(msg.MessageID[:])) } else { fmt.Print(".o") log.Dataf("STATUS (FetchComplete):\t%s\n", utils.B58encode(msg.MessageID[:])) } } if hasErrors > 0 { fmt.Print(". Some errors during download.\n") log.Dataf("STATUS (FetchResult):\tERROS %d\n", hasErrors) } else { fmt.Print(". Download complete.\n") log.Dataf("STATUS (FetchResult):\tOK\n") } } log.Sync() // server can be set return 0 }
// 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(CurrentTime() + 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 != 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") } }
// CmdDecrypt implements decryption functions func CmdDecrypt() int { var inData, decMessage []byte var err error var meta *message.MetaDataRecieve var privkeystr string var nullKey message.Curve25519Key if OptionsVar.Server == "" { getPeers(false) } if OptionsVar.Stmdir != "" && !isDir(OptionsVar.Stmdir) { log.Fatalf("stmdir does not exist or is no directory: %s\n", OptionsVar.Stmdir) return 1 } // Read input data maxInData := int64(GlobalConfigVar.BodyLength+message.KeyHeaderSize+message.SignHeaderSize) * 4 // Read data from stdin or file if len(flag.Args()) == 0 { inData, err = inputData(OptionsVar.Infile, maxInData) if err != nil { log.Fatalf("No input data: %s\n", err) return 1 } } else { // read data from server (Get). CMDLine is [server/]messageid[_privatekey] var messageidcl []byte var server string server, messageidcl, privkeystr = cmdlineURLparse(flag.Args()...) if server == "" { server = OptionsVar.Server } proto := repproto.New(OptionsVar.Socksserver, OptionsVar.Server, GlobalConfigVar.PasteServers...) if server == "" { server, inData, err = proto.Get(messageidcl) } else { inData, err = proto.GetSpecific(server, messageidcl) } if err != nil { log.Fatalf("Fetch error: %s\n", err) return 1 } log.Dataf("STATUS (FetchServer):\t%s\n", server) } if len(inData) < (message.KeyHeaderSize+message.SignHeaderSize)*4 { log.Fatals("No input data.\n") return 1 } // Set up receiver parameters receiver := message.Receiver{ HashCashBits: GlobalConfigVar.MinHashCash, } if OptionsVar.Senderkey != "" { receiver.SenderPublicKey, _ = utils.ParseKeyPair(OptionsVar.Senderkey) } // Select private key to use if OptionsVar.Keymgt < 0 { if privkeystr == "" { // might have been set from commandline privkeystr = selectPrivKey(OptionsVar.Privkey, GlobalConfigVar.PrivateKey, "tty") } // Parse privkey if privkeystr != "" { receiver.ReceiveConstantPrivateKey, receiver.ReceiveTemporaryPrivateKey = utils.ParseKeyPair(privkeystr) } } else { // Register callback, OptionsVar.keymgt == fd keyMgtFile, callback := KeyCallBack(OptionsVar.Keymgt) defer keyMgtFile.Close() receiver.KeyCallBack = callback } log.Datas("STATUS (Process):\tREAD\n") // Decrypt decMessage, meta, err = receiver.Decrypt(inData) if err != nil { log.Fatalf("%s\n", err) return 1 } log.Dataf("STATUS (MessageID):\t%s\n", utils.B58encode(meta.MessageID[:])) log.Dataf("STATUS (RecPubKey):\t%s\n", utils.B58encode(meta.ReceiveConstantPublicKey[:])) log.Dataf("STATUS (SenderPubKey):\t%s\n", utils.B58encode(meta.SenderConstantPublicKey[:])) // Get replyKeys embedConstant, embedTemporary := utils.DecodeEmbedded(decMessage[:message.Curve25519KeySize*2]) if *embedConstant != nullKey { log.Dataf("STATUS (EmbedPublicKey):\t%s_%s\n", utils.B58encode(embedConstant[:]), utils.B58encode(embedTemporary[:])) } // If messageType list: print list to DATA if meta.MessageType == message.MsgTypeList { log.Datas("STATUS (Process):\tLIST\n") err := utils.VerifyListContent(decMessage[message.Curve25519KeySize*2:]) if err != nil { log.Fatalf("%s\n", err) return 1 } lines := bytes.Split(decMessage[message.Curve25519KeySize*2:], []byte("\n")) for _, l := range lines { log.Dataf("STATUS (ListItem):\t%s\n", string(l)) } return 0 } decMessage = decMessage[message.Curve25519KeySize*2:] // meta.MessageType if meta.MessageType == message.MsgTypeRepost { log.Datas("STATUS (Process):\tREPOST\n") // If messageType repost: get padkey,mindelay,maxdelay. Repad padkey, minDelay, maxDelay := utils.DecodeRepostHeader(decMessage[:utils.RepostHeaderSize]) timePoint := utils.STM(int(minDelay), int(maxDelay)) log.Dataf("STATUS (STM):\t%d %d %d\n", minDelay, maxDelay, timePoint) repostMsgt := message.RePad(decMessage[utils.RepostHeaderSize:], padkey, GlobalConfigVar.BodyLength) signHeader := new([message.SignHeaderSize]byte) copy(signHeader[:], repostMsgt[:message.SignHeaderSize]) details, err := message.VerifySignature(*signHeader, GlobalConfigVar.MinHashCash) if err != nil { log.Fatalf("%s\n", err) return 1 } msgIDMsg := message.CalcMessageID(repostMsgt) if *msgIDMsg != details.MsgID { log.Fatals("MessageID conflict\n") return 1 } log.Dataf("STATUS (MessageIDSig):\t%s\n", utils.B58encode(details.MsgID[:])) log.Dataf("STATUS (PubKeySig):\t%s\n", utils.B58encode(details.PublicKey[:])) log.Dataf("STATUS (NonceSig):\t%x\n", details.HashCashNonce[:]) log.Dataf("STATUS (BitsSig):\t%d\n", details.HashCashBits) decMessage = message.EncodeBase64(repostMsgt) if OptionsVar.Stmdir != "" { // Exist/Dir test done early filename := fmt.Sprintf("%s%s%d.%s", OptionsVar.Stmdir, string(os.PathSeparator), timePoint, utils.B58encode(msgIDMsg[:])) log.Dataf("STATUS (STMFile):\t%s\n", filename) err := utils.WriteNewFile(filename, decMessage) if err != nil { log.Fatalf("%s\n", err) return 1 } return 0 } } err = outputData(OptionsVar.Outfile, decMessage) if err != nil { log.Fatalf("Output failed: %s\n", err) return 1 } return 0 }