func rootKeyAgreementSender( senderHeaderPub *[32]byte, senderIdentity, recipientIdentity string, senderSession, senderID, recipientKI, recipientID *uid.KeyEntry, previousRootKeyHash *[64]byte, numOfKeys uint64, keyStore session.Store, ) error { senderIdentityPub := senderID.PublicKey32() senderIdentityPriv := senderID.PrivateKey32() senderSessionPub := senderSession.PublicKey32() senderSessionPriv := senderSession.PrivateKey32() recipientIdentityPub := recipientID.PublicKey32() recipientKeyInitPub := recipientKI.PublicKey32() log.Debugf("senderIdentityPub: %s", base64.Encode(senderIdentityPub[:])) log.Debugf("senderSessionPub: %s", base64.Encode(senderSessionPub[:])) log.Debugf("recipientIdentityPub: %s", base64.Encode(recipientIdentityPub[:])) log.Debugf("recipientKeyInitPub: %s", base64.Encode(recipientKeyInitPub[:])) // check keys to prevent reflection attacks and replays err := checkKeys(senderHeaderPub, senderIdentityPub, senderSessionPub, recipientIdentityPub, recipientKeyInitPub) if err != nil { return err } // compute t1 t1, err := cipher.ECDH(senderIdentityPriv, recipientKeyInitPub, senderIdentityPub) if err != nil { return err } // compute t2 t2, err := cipher.ECDH(senderSessionPriv, recipientKeyInitPub, senderSessionPub) if err != nil { return err } // compute t3 t3, err := cipher.ECDH(senderSessionPriv, recipientIdentityPub, senderSessionPub) if err != nil { return err } // derive root key rootKey, err := deriveRootKey(t1, t2, t3, previousRootKeyHash) if err != nil { return err } // generate message keys err = generateMessageKeys(senderIdentity, recipientIdentity, senderID.HASH, recipientID.HASH, rootKey, false, senderSessionPub, recipientKeyInitPub, numOfKeys, keyStore) if err != nil { return err } return nil }
func readHeader( senderHeaderPub *[32]byte, identities []*uid.Message, r io.Reader, ) (*uid.Message, *header, error) { var hp headerPacket // read nonce if _, err := io.ReadFull(r, hp.Nonce[:]); err != nil { return nil, nil, log.Error(err) } //log.Debugf("hp.Nonce: %s", base64.Encode(hp.Nonce[:])) // read length of encrypted header if err := binary.Read(r, binary.BigEndian, &hp.LengthEncryptedHeader); err != nil { return nil, nil, log.Error(err) } //log.Debugf("hp.LengthEncryptedHeader: %d", hp.LengthEncryptedHeader) // read encrypted header hp.EncryptedHeader = make([]byte, hp.LengthEncryptedHeader) if _, err := io.ReadFull(r, hp.EncryptedHeader); err != nil { return nil, nil, log.Error(err) } // try to decrypt header var jsn []byte var suc bool var identity *uid.Message for _, uidMsg := range identities { log.Debugf("try identity %s (#%d)", uidMsg.Identity(), uidMsg.UIDContent.MSGCOUNT) log.Debugf("recvPub=%s\n", base64.Encode(uidMsg.PubKey().PublicKey32()[:])) jsn, suc = box.Open(jsn, hp.EncryptedHeader, &hp.Nonce, senderHeaderPub, uidMsg.PubKey().PrivateKey32()) if suc { identity = uidMsg break } } if !suc { return nil, nil, log.Error(ErrNoPreHeaderKey) } var h header if err := json.Unmarshal(jsn, &h); err != nil { return nil, nil, err } // verify header if err := h.verify(); err != nil { return nil, nil, err } return identity, &h, nil }
// GetPublicKeyEntry implements corresponding method for msg.KeyStore interface. func (ce *CryptEngine) GetPublicKeyEntry(uidMsg *uid.Message) (*uid.KeyEntry, string, error) { log.Debugf("ce.FindKeyEntry: uidMsg.Identity()=%s", uidMsg.Identity()) // get KeyInit sigKeyHash, err := uidMsg.SigKeyHash() if err != nil { return nil, "", err } ki, err := ce.keyDB.GetPublicKeyInit(sigKeyHash) if err != nil { if err == sql.ErrNoRows { return nil, "", session.ErrNoKeyEntry } return nil, "", err } // decrypt SessionAnchor sa, err := ki.SessionAnchor(uidMsg.SigPubKey()) if err != nil { return nil, "", err } // get KeyEntry message from SessionAnchor ke, err := sa.KeyEntry("ECDHE25519") if err != nil { return nil, "", err } return ke, sa.NymAddress(), nil }
func (ce *CryptEngine) fetchUID( domain string, UIDIndex []byte, ) (*uid.MessageReply, error) { // get JSON-RPC client client, _, err := ce.cache.Get(domain, ce.keydPort, ce.keydHost, ce.homedir, "KeyRepository.FetchUID") if err != nil { return nil, err } // Call KeyRepository.FetchUID content := make(map[string]interface{}) content["UIDIndex"] = base64.Encode(UIDIndex) reply, err := client.JSONRPCRequest("KeyRepository.FetchUID", content) if err != nil { return nil, err } // Parse entry var entry uid.Entry rep, ok := reply["UIDMessageReply"].(map[string]interface{}) if !ok { return nil, log.Error("cryptengine: KeyRepository.FetchUID reply has the wrong return type") } e, ok := rep["ENTRY"].(map[string]interface{}) if !ok { return nil, log.Error("cryptengine: KeyRepository.FetchUID ENTRY has the wrong type") } entry.UIDMESSAGEENCRYPTED, ok = e["UIDMESSAGEENCRYPTED"].(string) if !ok { return nil, log.Error("cryptengine: KeyRepository.FetchUID UIDMESSAGEENCRYPTED has the wrong type") } log.Debugf("cryptengine: UIDMessageEncrypted=%s", entry.UIDMESSAGEENCRYPTED) entry.HASHCHAINENTRY, ok = e["HASHCHAINENTRY"].(string) if !ok { return nil, log.Error("cryptengine: KeyRepository.FetchUID HASHCHAINENTRY has the wrong type") } hcPos, ok := e["HASHCHAINPOS"].(float64) if !ok { return nil, log.Error("cryptengine: KeyRepository.FetchUID HASHCHAINPOS has the wrong type") } entry.HASHCHAINPOS = uint64(hcPos) // Parse server signature srvSig, ok := rep["SERVERSIGNATURE"].(string) if !ok { return nil, log.Error("cryptengine: KeyRepository.FetchUID SERVERSIGNATURE has the wrong type") } msgReply := &uid.MessageReply{ ENTRY: entry, SERVERSIGNATURE: srvSig, } return msgReply, err }
// TODO: better selection of identities. Take NOTBEFORE and NOTAFTER into account. // At the moment only the most current UID message is used for every identity. func (ce *CryptEngine) getRecipientIdentities() ([]*uid.Message, error) { var uidMsgs []*uid.Message identities, err := ce.keyDB.GetPrivateIdentities() if err != nil { return nil, err } for _, identity := range identities { log.Debugf("identity=%s", identity) // TODO: get all UID messages for given identity which are not expired uidMsg, _, err := ce.keyDB.GetPrivateUID(identity, true) if err != nil { return nil, err } uidMsgs = append(uidMsgs, uidMsg) } return uidMsgs, nil }
// GetPrivateKeyEntry implements corresponding method for msg.KeyStore interface. func (ce *CryptEngine) GetPrivateKeyEntry(pubKeyHash string) (*uid.KeyEntry, error) { log.Debugf("ce.FindKeyEntry: pubKeyHash=%s", pubKeyHash) ki, sigPubKey, privateKey, err := ce.keyDB.GetPrivateKeyInit(pubKeyHash) if err != nil { if err == sql.ErrNoRows { return nil, session.ErrNoKeyEntry } return nil, err } // decrypt KeyEntry ke, err := ki.KeyEntryECDHE25519(sigPubKey) if err != nil { return nil, err } // set private key if err := ke.SetPrivateKey(privateKey); err != nil { return nil, err } return ke, nil }
func newHeaderPacket( h *header, recipientIdentityPub, senderHeaderPriv *[32]byte, rand io.Reader, ) (*headerPacket, error) { var hp headerPacket jsn, err := json.Marshal(h) if err != nil { return nil, err } if _, err := io.ReadFull(rand, hp.Nonce[:]); err != nil { return nil, log.Error(err) } log.Debugf("recvPub=%s\n", base64.Encode(recipientIdentityPub[:])) hp.EncryptedHeader = box.Seal(hp.EncryptedHeader, jsn, &hp.Nonce, recipientIdentityPub, senderHeaderPriv) hp.LengthEncryptedHeader = uint16(len(hp.EncryptedHeader)) if hp.LengthEncryptedHeader != lengthEncryptedHeader { return nil, log.Errorf("msg: encrypted header has wrong length (%d != %d)", hp.LengthEncryptedHeader, lengthEncryptedHeader) } return &hp, nil }
func (ce *CtrlEngine) procInQueue(c *cli.Context, host string) error { log.Debug("procInQueue()") for { // get message from msgDB iqIdx, myID, contactID, msg, envelope, err := ce.msgDB.GetInQueue() if err != nil { return err } if myID == "" { log.Debug("no more messages in inqueue") break // no more messages in inqueue } if envelope { log.Debugf("decrypt envelope (iqIdx=%d)", iqIdx) // decrypt envelope message, err := base64.Decode(msg) if err != nil { return log.Error(err) } privkey, server, secret, _, _, _, err := ce.msgDB.GetAccount(myID, contactID) if err != nil { return err } receiveTemplate := nymaddr.AddressTemplate{ Secret: secret[:], } var pubkey [32]byte copy(pubkey[:], privkey[32:]) dec, nym, err := mixcrypt.ReceiveFromMix(receiveTemplate, util.MailboxAddress(&pubkey, server), message) if err != nil { return log.Error(err) } if !bytes.Equal(nym, cipher.SHA256([]byte(myID))) { // discard message log.Warnf("ctrlengine: hashed nym does not match %s -> discard message", myID) if err := ce.msgDB.DelInQueue(iqIdx); err != nil { return err } } else { log.Info("envelope successfully decrypted") err := ce.msgDB.SetInQueue(iqIdx, base64.Encode(dec)) if err != nil { return err } } } else { log.Debugf("decrypt message (iqIdx=%d)", iqIdx) senderID, plainMsg, err := mutecryptDecrypt(c, ce.passphrase, []byte(msg), ce.fileTable.StatusFP) if err != nil { return err } if senderID == "" { // message could not be decrypted, but we do not want to fail if err := ce.msgDB.DelInQueue(iqIdx); err != nil { return err } continue } // check if contact exists contact, _, contactType, err := ce.msgDB.GetContact(myID, senderID) if err != nil { return log.Error(err) } // TODO: we do not have to do request UID message from server // here, but we should use the one contained in the message and // compare it with hash chain entry (doesn't compromise anonymity) var drop bool if contact == "" { err := ce.contactAdd(myID, senderID, "", host, msgdb.GrayList, c) if err != nil { return log.Error(err) } } else if contactType == msgdb.BlackList { // messages from black listed contacts are dropped directly log.Debug("message from black listed contact dropped") drop = true } err = ce.msgDB.RemoveInQueue(iqIdx, plainMsg, senderID, drop) if err != nil { return err } } } return nil }
func muteprotoFetch( myID, contactID string, msgDB *msgdb.MsgDB, c *cli.Context, privkey, server string, lastMessageTime int64, ) (newMessageTime int64, err error) { log.Debug("muteprotoFetch()") args := []string{ "--homedir", c.GlobalString("homedir"), "--loglevel", c.GlobalString("loglevel"), "--logdir", c.GlobalString("logdir"), "fetch", "--server", server, "--last-message-time", strconv.FormatInt(lastMessageTime, 10), } cmd := exec.Command("muteproto", args...) stdout, err := cmd.StdoutPipe() if err != nil { return 0, err } stderr, err := cmd.StderrPipe() if err != nil { return 0, err } ppR, ppW, err := os.Pipe() if err != nil { return 0, err } defer ppR.Close() ppW.Write([]byte(privkey)) ppW.Close() cmd.ExtraFiles = append(cmd.ExtraFiles, ppR) cmdR, cmdW, err := os.Pipe() if err != nil { return 0, err } defer cmdR.Close() defer cmdW.Close() cmd.ExtraFiles = append(cmd.ExtraFiles, cmdR) if err := cmd.Start(); err != nil { return 0, err } var outbuf bytes.Buffer status := bufio.NewReader(stderr) input := make(chan []byte) go func() { for { buf := make([]byte, 4096) n, err := stdout.Read(buf) if n > 0 { input <- buf[:n] } if err != nil { if err == io.EOF { return } log.Error(err) return } } }() firstMessage := true cache, err := msgDB.GetMessageIDCache(myID, contactID) if err != nil { return 0, err } for { // read status output line, err := status.ReadString('\n') if err != nil { return 0, log.Error(err) } line = strings.TrimSpace(line) if line == "NONE" { log.Debug("read: NONE") break } if strings.HasSuffix(line, "accountdb: nothing found") { log.Info("account has no messages") return 0, nil } parts := strings.Split(line, "\t") if len(parts) != 2 || parts[0] != "MESSAGEID:" { return 0, log.Errorf("ctrlengine: MESSAGEID line expected from muteproto, got: %s", line) } messageID := parts[1] log.Debugf("read: MESSAGEID:\t%s", messageID) if cache[messageID] { // message known -> abort fetching messages and remove old IDs from cache log.Debug("write: QUIT") fmt.Fprintln(cmdW, "QUIT") err := msgDB.RemoveMessageIDCache(myID, contactID, messageID) if err != nil { return 0, log.Error(err) } break } else { // message unknown -> fetch it and add messageID to cache log.Debug("write: NEXT") fmt.Fprintln(cmdW, "NEXT") err := msgDB.AddMessageIDCache(myID, contactID, messageID) if err != nil { return 0, log.Error(err) } } // read message stop := make(chan uint64) done := make(chan bool) go func() { for { select { case buf := <-input: outbuf.Write(buf) case length := <-stop: for uint64(outbuf.Len()) < length { buf := <-input outbuf.Write(buf) } done <- true return } } }() // read LENGTH line, err = status.ReadString('\n') if err != nil { return 0, log.Error(err) } parts = strings.Split(strings.TrimRight(line, "\n"), "\t") if len(parts) != 2 || parts[0] != "LENGTH:" { return 0, log.Errorf("ctrlengine: LENGTH line expected from muteproto, got: %s", line) } length, err := strconv.ParseUint(parts[1], 10, 64) if err != nil { return 0, log.Error(err) } log.Debugf("read: LENGTH:\t%d", length) // read RECEIVETIME line, err = status.ReadString('\n') if err != nil { return 0, log.Error(err) } parts = strings.Split(strings.TrimRight(line, "\n"), "\t") if len(parts) != 2 || parts[0] != "RECEIVETIME:" { return 0, log.Errorf("ctrlengine: RECEIVETIME line expected from muteproto, got: %s", line) } receiveTime, err := strconv.ParseInt(parts[1], 10, 64) if err != nil { return 0, log.Error(err) } log.Debugf("read: RECEIVETIME:\t%d", receiveTime) stop <- length <-done err = msgDB.AddInQueue(myID, contactID, receiveTime, outbuf.String()) if err != nil { return 0, err } if firstMessage { newMessageTime = receiveTime firstMessage = false } outbuf.Reset() } if err := cmd.Wait(); err != nil { return 0, err } return }
// Decrypt decrypts a message with the argument given in args. // The senderID is returned. // If the message was signed and the signature could be verified successfully // the base64 encoded signature is returned. If the message was signed and the // signature could not be verfied an error is returned. func Decrypt(args *DecryptArgs) (senderID, sig string, err error) { log.Debug("msg.Decrypt()") // set default if args.NumOfKeys == 0 { args.NumOfKeys = NumOfFutureKeys } // read pre-header ph, err := readPreHeader(bytes.NewBuffer(args.PreHeader)) if err != nil { return "", "", err } if ph.LengthSenderHeaderPub != 32 { return "", "", log.Errorf("msg: ph.LengthSenderHeaderPub != 32") } var senderHeaderPub [32]byte copy(senderHeaderPub[:], ph.SenderHeaderPub) // read header packet oh, err := readOuterHeader(args.Reader) if err != nil { return "", "", err } if oh.Type != encryptedHeader { return "", "", log.Error(ErrNotEncryptedHeader) } count := uint32(1) if oh.PacketCount != count { return "", "", log.Error(ErrWrongCount) } count++ identity, h, err := readHeader(&senderHeaderPub, args.Identities, bytes.NewBuffer(oh.inner)) if err != nil { return "", "", err } senderID = h.SenderIdentity recipientID := identity.PubKey() log.Debugf("senderID: %s", h.SenderIdentityPub.HASH) log.Debugf("recipientID: %s", recipientID.HASH) log.Debugf("h.SenderSessionCount: %d", h.SenderSessionCount) log.Debugf("h.SenderMessageCount: %d", h.SenderMessageCount) log.Debugf("h.SenderSessionPub: %s", h.SenderSessionPub.HASH) if h.NextSenderSessionPub != nil { log.Debugf("h.NextSenderSessionPub: %s", h.NextSenderSessionPub.HASH) } if h.NextRecipientSessionPubSeen != nil { log.Debugf("h.NextRecipientSessionPubSeen: %s", h.NextRecipientSessionPubSeen.HASH) } // proc sender UID in parallel res := make(chan *procUIDResult, 1) go procUID(h.SenderUID, res) // get session state sender := h.SenderIdentity recipient := identity.Identity() log.Debugf("%s -> %s", sender, recipient) sessionStateKey := session.CalcStateKey(recipientID.PublicKey32(), h.SenderIdentityPub.PublicKey32()) ss, err := args.KeyStore.GetSessionState(sessionStateKey) if err != nil { return "", "", err } sessionKey := session.CalcKey(recipientID.HASH, h.SenderIdentityPub.HASH, h.RecipientTempHash, h.SenderSessionPub.HASH) if !args.KeyStore.HasSession(sessionKey) { // session unknown // try to start session from KeyInit message recipientKI, err := args.KeyStore.GetPrivateKeyEntry(h.RecipientTempHash) if err != nil && err != session.ErrNoKeyEntry { return "", "", err } if err != session.ErrNoKeyEntry { // KeyInit message found // root key agreement err = rootKeyAgreementRecipient(&senderHeaderPub, sender, recipient, &h.SenderSessionPub, &h.SenderIdentityPub, recipientKI, recipientID, nil, args.NumOfKeys, args.KeyStore) if err != nil { return "", "", err } // TODO: delete single-use KeyInit message // use the 'smaller' session as the definite one // TODO: h.SenderSessionPub.HASH < ss.SenderSessionPub.HASH if ss == nil || (ss.KeyInitSession && sender < recipient) { // create next session key var nextSenderSession uid.KeyEntry if err := nextSenderSession.InitDHKey(args.Rand); err != nil { return "", "", err } // store next session key err := addSessionKey(args.KeyStore, &nextSenderSession) if err != nil { return "", "", err } // if we already got h.NextSenderSessionPub prepare next session if h.NextSenderSessionPub != nil { previousRootKeyHash, err := args.KeyStore.GetRootKeyHash(sessionKey) if err != nil { return "", "", err } // root key agreement err = rootKeyAgreementSender(&senderHeaderPub, recipient, sender, &nextSenderSession, recipientID, h.NextSenderSessionPub, &h.SenderIdentityPub, previousRootKeyHash, args.NumOfKeys, args.KeyStore) if err != nil { return "", "", err } } // set session state ss = &session.State{ SenderSessionCount: 0, SenderMessageCount: 0, MaxRecipientCount: 0, RecipientTemp: h.SenderSessionPub, SenderSessionPub: *recipientKI, NextSenderSessionPub: &nextSenderSession, NextRecipientSessionPubSeen: h.NextSenderSessionPub, NymAddress: h.NymAddress, KeyInitSession: false, } err = args.KeyStore.SetSessionState(sessionStateKey, ss) if err != nil { return "", "", err } } } else { // no KeyInit message found // TODO: ??? } } else { // session known log.Debug("session known") // check if session state reflects that session if h.RecipientTempHash == ss.SenderSessionPub.HASH && h.SenderSessionPub.HASH == ss.RecipientTemp.HASH { log.Debug("session state reflects that session") if h.NextSenderSessionPub != nil { log.Debug("h.NextSenderSessionPub is defined") } if h.NextRecipientSessionPubSeen != nil { log.Debug("h.NextRecipientSessionPubSeen is defined") } if h.NextSenderSessionPub != nil { // if other side has set its NextSenderSessionPubKey we set // ours immediately if ss.NextSenderSessionPub == nil { // prepare upcoming session, but do not switch to it yet nextSenderSession, err := setNextSenderSessionPub(args.KeyStore, ss, sessionStateKey, args.Rand) if err != nil { return "", "", err } previousRootKeyHash, err := args.KeyStore.GetRootKeyHash(sessionKey) if err != nil { return "", "", err } // root key agreement err = rootKeyAgreementSender(&senderHeaderPub, recipient, sender, nextSenderSession, recipientID, h.NextSenderSessionPub, &h.SenderIdentityPub, previousRootKeyHash, args.NumOfKeys, args.KeyStore) if err != nil { return "", "", err } if ss.NextRecipientSessionPubSeen == nil { // save h.NextSenderSessionPub, if necessary ss.NextRecipientSessionPubSeen = h.NextSenderSessionPub err := args.KeyStore.SetSessionState(sessionStateKey, ss) if err != nil { return "", "", err } } } else if h.NextRecipientSessionPubSeen != nil && h.NextRecipientSessionPubSeen.HASH == ss.NextSenderSessionPub.HASH { // switch to next session nextSenderSession, err := getSessionKey(args.KeyStore, ss.NextSenderSessionPub.HASH) if err != nil { return "", "", err } previousRootKeyHash, err := args.KeyStore.GetRootKeyHash(sessionKey) if err != nil { return "", "", err } // root key agreement err = rootKeyAgreementRecipient(&senderHeaderPub, sender, recipient, h.NextSenderSessionPub, &h.SenderIdentityPub, nextSenderSession, recipientID, previousRootKeyHash, args.NumOfKeys, args.KeyStore) if err != nil { return "", "", err } // store new session state ss = &session.State{ SenderSessionCount: ss.SenderSessionCount + ss.SenderMessageCount, SenderMessageCount: 0, MaxRecipientCount: 0, RecipientTemp: *h.NextSenderSessionPub, SenderSessionPub: *nextSenderSession, NextSenderSessionPub: nil, NextRecipientSessionPubSeen: nil, NymAddress: h.NymAddress, KeyInitSession: false, } err = args.KeyStore.SetSessionState(sessionStateKey, ss) if err != nil { return "", "", err } } } } else { // check if session matches next session if ss.NextSenderSessionPub != nil && ss.NextRecipientSessionPubSeen != nil && ss.NextSenderSessionPub.HASH == h.RecipientTempHash && ss.NextRecipientSessionPubSeen.HASH == h.SenderSessionPub.HASH { // switch session ss = &session.State{ SenderSessionCount: ss.SenderSessionCount + ss.SenderMessageCount, SenderMessageCount: 0, MaxRecipientCount: 0, RecipientTemp: h.SenderSessionPub, SenderSessionPub: *ss.NextSenderSessionPub, NextSenderSessionPub: nil, NextRecipientSessionPubSeen: nil, NymAddress: h.NymAddress, KeyInitSession: false, } err = args.KeyStore.SetSessionState(sessionStateKey, ss) if err != nil { return "", "", err } } } // a message with this session key has been decrypted -> delete key if err := args.KeyStore.DelPrivSessionKey(h.RecipientTempHash); err != nil { return "", "", err } } // make sure we got enough message keys n, err := args.KeyStore.NumMessageKeys(sessionKey) if err != nil { return "", "", err } if h.SenderMessageCount >= n { // generate more message keys log.Debugf("generate more message keys (h.SenderMessageCount=%d, n=%d)", h.SenderMessageCount, n) chainKey, err := args.KeyStore.GetChainKey(sessionKey) if err != nil { return "", "", err } // prevent denial of service attack by very large h.SenderMessageCount numOfKeys := h.SenderMessageCount / args.NumOfKeys if h.SenderMessageCount%args.NumOfKeys > 0 { numOfKeys++ } numOfKeys *= args.NumOfKeys if numOfKeys > mime.MaxMsgSize/MaxContentLength+NumOfFutureKeys { return "", "", log.Errorf("msg: requested number of message keys too large") } log.Debugf("numOfKeys=%d", numOfKeys) var recipientPub *[32]byte if h.RecipientTempHash == ss.SenderSessionPub.HASH { recipientPub = ss.SenderSessionPub.PublicKey32() } else { log.Debug("different session") recipientKI, err := args.KeyStore.GetPrivateKeyEntry(h.RecipientTempHash) if err != nil && err != session.ErrNoKeyEntry { return "", "", err } if err != session.ErrNoKeyEntry { recipientPub = recipientKI.PublicKey32() } else { recipientKE, err := getSessionKey(args.KeyStore, h.RecipientTempHash) if err != nil { return "", "", err } recipientPub = recipientKE.PublicKey32() } } err = generateMessageKeys(sender, recipient, h.SenderIdentityPub.HASH, recipientID.HASH, chainKey, true, h.SenderSessionPub.PublicKey32(), recipientPub, numOfKeys, args.KeyStore) if err != nil { return "", "", err } } // get message key messageKey, err := args.KeyStore.GetMessageKey(sessionKey, false, h.SenderMessageCount) if err != nil { return "", "", err } // derive symmetric keys cryptoKey, hmacKey, err := deriveSymmetricKeys(messageKey) if err != nil { return "", "", err } // read crypto setup packet oh, err = readOuterHeader(args.Reader) if err != nil { return "", "", err } if oh.Type != cryptoSetup { return "", "", log.Error(ErrNotCryptoSetup) } if oh.PacketCount != count { return "", "", log.Error(ErrWrongCount) } count++ if oh.PLen != aes.BlockSize { return "", "", log.Error(ErrWrongCryptoSetup) } iv := oh.inner // start HMAC calculation mac := hmac.New(sha512.New, hmacKey) if err := oh.write(mac, true); err != nil { return "", "", err } // actual decryption oh, err = readOuterHeader(args.Reader) if err != nil { return "", "", err } if oh.Type != encryptedPacket { return "", "", log.Error(ErrNotEncryptedPacket) } if oh.PacketCount != count { return "", "", log.Error(ErrWrongCount) } count++ ciphertext := oh.inner plaintext := make([]byte, len(ciphertext)) stream := cipher.AES256CTRStream(cryptoKey, iv) stream.XORKeyStream(plaintext, ciphertext) ih, err := readInnerHeader(bytes.NewBuffer(plaintext)) if err != nil { return "", "", err } if ih.Type&dataType == 0 { return "", "", log.Error(ErrNotData) } var contentHash []byte if ih.Type&signType != 0 { // create signature hash contentHash = cipher.SHA512(ih.content) } if _, err := args.Writer.Write(ih.content); err != nil { return "", "", log.Error(err) } // continue HMAC calculation if err := oh.write(mac, true); err != nil { return "", "", err } // verify signature var sigBuf [ed25519.SignatureSize]byte if contentHash != nil { oh, err = readOuterHeader(args.Reader) if err != nil { return "", "", err } if oh.Type != encryptedPacket { return "", "", log.Error(ErrNotEncryptedPacket) } if oh.PacketCount != count { return "", "", log.Error(ErrWrongCount) } count++ // continue HMAC calculation if err := oh.write(mac, true); err != nil { return "", "", err } ciphertext = oh.inner plaintext = make([]byte, len(ciphertext)) stream.XORKeyStream(plaintext, ciphertext) ih, err = readInnerHeader(bytes.NewBuffer(plaintext)) if err != nil { return "", "", err } if ih.Type&signatureType == 0 { return "", "", log.Error(ErrNotSignaturePacket) } if len(ih.content) != ed25519.SignatureSize { return "", "", log.Error(ErrWrongSignatureLength) } copy(sigBuf[:], ih.content) } else { oh, err = readOuterHeader(args.Reader) if err != nil { return "", "", err } if oh.Type != encryptedPacket { return "", "", log.Error(ErrNotEncryptedPacket) } if oh.PacketCount != count { return "", "", log.Error(ErrWrongCount) } count++ // continue HMAC calculation if err := oh.write(mac, true); err != nil { return "", "", err } ciphertext = oh.inner plaintext = make([]byte, len(ciphertext)) stream.XORKeyStream(plaintext, ciphertext) ih, err = readInnerHeader(bytes.NewBuffer(plaintext)) if err != nil { return "", "", err } if ih.Type&paddingType == 0 { return "", "", log.Error(ErrNotPaddingPacket) } } // get processed sender UID uidRes := <-res if uidRes.err != nil { return "", "", uidRes.err } // verify signature, if necessary if contentHash != nil { if !ed25519.Verify(uidRes.msg.PublicSigKey32(), contentHash, &sigBuf) { return "", "", log.Error(ErrInvalidSignature) } // encode signature to base64 as return value sig = base64.Encode(sigBuf[:]) } // read HMAC packet oh, err = readOuterHeader(args.Reader) if err != nil { return "", "", err } if oh.Type != hmacPacket { return "", "", log.Error(ErrNotHMACPacket) } if oh.PacketCount != count { return "", "", log.Error(ErrWrongCount) } count++ if err := oh.write(mac, false); err != nil { return "", "", err } sum := mac.Sum(nil) log.Debugf("HMAC: %s", base64.Encode(sum)) if !hmac.Equal(sum, oh.inner) { return "", "", log.Error(ErrHMACsDiffer) } // delete message key err = args.KeyStore.DelMessageKey(sessionKey, false, h.SenderMessageCount) if err != nil { return "", "", err } return }
func (pe *ProtoEngine) fetch( output io.Writer, status io.Writer, server string, lastMessageTime int64, command io.Reader, ) error { // read passphrase log.Infof("read private key from fd %d", pe.fileTable.PassphraseFD) pks, err := util.Readline(pe.fileTable.PassphraseFP) if err != nil { return err } log.Info("done") pk, err := base64.Decode(string(pks)) if err != nil { return log.Error(err) } var privkey [ed25519.PrivateKeySize]byte copy(privkey[:], pk) log.Debugf("lastMessageTime=%d", lastMessageTime) messages, err := client.ListMessages(&privkey, lastMessageTime, server, def.CACert) if err != nil { // TODO: handle this better if err.Error() == "accountdb: Nothing found" { // no messages found log.Info("write: NONE") fmt.Fprintln(status, "NONE") return nil } return log.Error(err) } /* for _, message := range messages { messageID := base64.Encode(message.MessageID) log.Debugf("messageID=%s, ReceiveTime=%d, ReadTime=%d", messageID, message.ReceiveTime, message.ReadTime) } */ scanner := bufio.NewScanner(command) for _, message := range messages { msg, err := client.FetchMessage(&privkey, message.MessageID, server, def.CACert) if err != nil { return log.Error(err) } messageID := base64.Encode(message.MessageID) log.Debugf("write: MESSAGEID:\t%s", messageID) fmt.Fprintf(status, "MESSAGEID:\t%s\n", messageID) var command string if scanner.Scan() { command = scanner.Text() } else { return log.Error("protoengine: expecting command input") } if err := scanner.Err(); err != nil { fmt.Fprintln(os.Stderr, "reading standard input:", err) } if command == "NEXT" { log.Debug("read: NEXT") enc := base64.Encode(msg) if _, err := io.WriteString(output, enc); err != nil { return log.Error(err) } log.Debugf("write: LENGTH:\t%d", len(enc)) fmt.Fprintf(status, "LENGTH:\t%d\n", len(enc)) log.Debugf("write: RECEIVETIME:\t%d", message.ReceiveTime) fmt.Fprintf(status, "RECEIVETIME:\t%d\n", message.ReceiveTime) } else if command == "QUIT" { log.Debug("read: QUIT") return nil } else { return log.Errorf("protoengine: unknown command '%s'", command) } } // no more messages log.Info("write: NONE") fmt.Fprintln(status, "NONE") return nil }
func (ce *CryptEngine) lookupHashChain(id string) error { // map identity mappedID, domain, err := identity.MapPlus(id) if err != nil { return err } // get JSON-RPC client client, _, err := ce.cache.Get(domain, ce.keydPort, ce.keydHost, ce.homedir, "KeyHashchain.LookupUID") if err != nil { return err } // Call KeyHashchain.LookupUID content := make(map[string]interface{}) content["Identity"] = mappedID reply, err := client.JSONRPCRequest("KeyHashchain.LookupUID", content) if err != nil { return err } hcPositions, ok := reply["HCPositions"].([]interface{}) if !ok { if _, ok := reply["HCPositions"].(interface{}); !ok { return log.Errorf("lookup found no entry of id '%s'", id) } return log.Error("cryptengine: lookup ID reply has the wrong type") } var TYPE, NONCE, HashID, CrUID, UIDIndex []byte var matchFound bool for k, v := range hcPositions { hcPosFloat, ok := v.(float64) if !ok { return log.Errorf("cryptengine: lookup ID reply position entry %d has the wrong type", k) } hcPos := uint64(hcPosFloat) hcEntry, err := ce.keyDB.GetHashChainEntry(domain, hcPos) if err != nil { return err } _, TYPE, NONCE, HashID, CrUID, UIDIndex, err = hashchain.SplitEntry(hcEntry) if err != nil { return err } if !bytes.Equal(TYPE, hashchain.Type) { return log.Error("cryptengine: invalid entry type") } // Compute k1, k2 = CKDF(NONCE) k1, k2 := cipher.CKDF(NONCE) // Compute: HashIDTest = HASH(k1 | Identity) tmp := make([]byte, len(k1)+len(mappedID)) copy(tmp, k1) copy(tmp[len(k1):], mappedID) HashIDTest := cipher.SHA256(tmp) // If NOT: HashID == HashIDTest: Continue if !bytes.Equal(HashID, HashIDTest) { return log.Error("cryptengine: lookup ID returned bogus position") } log.Debugf("cryptengine: UIDIndex=%s", base64.Encode(UIDIndex)) // Check UID already exists in keyDB _, pos, found, err := ce.keyDB.GetPublicUID(mappedID, hcPos) if err != nil { return err } if found && pos == hcPos { // UID exists already -> skip entry matchFound = true continue } // Compute: IDKEY = HASH(k2 | Identity) tmp = make([]byte, len(k2)+len(mappedID)) copy(tmp, k2) copy(tmp[len(k2):], mappedID) IDKEY := cipher.SHA256(tmp) // Fetch from Key Repository: UIDMessageReply = GET(UIDIndex) msgReply, err := ce.fetchUID(domain, UIDIndex) if err != nil { return err } // Decrypt UIDHash = AES_256_CBC_Decrypt( IDKEY, CrUID) UIDHash := cipher.AES256CBCDecrypt(IDKEY, CrUID) log.Debugf("cryptengine: UIDHash=%s", base64.Encode(UIDHash)) // Decrypt UIDMessageReply.UIDMessage with UIDHash index, uid, err := msgReply.Decrypt(UIDHash) if err != nil { return err } log.Debugf("cryptengine: UIDMessage=%s", uid.JSON()) // Check index if !bytes.Equal(index, UIDIndex) { return log.Errorf("cryptengine: index != UIDIndex") } // Verify self signature if err := uid.VerifySelfSig(); err != nil { return log.Error(err) } // Verify server signature if err := ce.verifyServerSig(uid, msgReply, hcPos); err != nil { return err } // TODO: make sure the whole chain of UIDMessages is valid // Store UIDMessage if err := ce.keyDB.AddPublicUID(uid, hcPos); err != nil { return err } matchFound = true // If no further entry can be found, the latest UIDMessage entry has been found } if matchFound { return nil } return log.Errorf("lookup found no entry of id '%s'", id) }
// searchHashChain searches the local hash chain corresponding to the given id // for the id. It talks to the corresponding key server to retrieve necessary // UIDMessageReplys and stores found UIDMessages in the local keyDB. func (ce *CryptEngine) searchHashChain(id string, searchOnly bool) error { // map identity mappedID, domain, err := identity.MapPlus(id) if err != nil { return err } // make sure we have a hashchain for the given domain max, found, err := ce.keyDB.GetLastHashChainPos(domain) if err != nil { return err } if !found { return log.Errorf("no hash chain entries found for domain '%s'", domain) } var TYPE, NONCE, HashID, CrUID, UIDIndex []byte var matchFound bool for i := uint64(0); i <= max; i++ { hcEntry, err := ce.keyDB.GetHashChainEntry(domain, i) if err != nil { return err } log.Debugf("cryptengine: search hash chain entry %d: %s", i, hcEntry) _, TYPE, NONCE, HashID, CrUID, UIDIndex, err = hashchain.SplitEntry(hcEntry) if err != nil { return err } if !bytes.Equal(TYPE, hashchain.Type) { return log.Error("cryptengine: invalid hash chain entry type") } // Compute k1, k2 = CKDF(NONCE) k1, k2 := cipher.CKDF(NONCE) // Compute: HashIDTest = HASH(k1 | Identity) tmp := make([]byte, len(k1)+len(mappedID)) copy(tmp, k1) copy(tmp[len(k1):], mappedID) HashIDTest := cipher.SHA256(tmp) // If NOT: HashID == HashIDTest: Continue if !bytes.Equal(HashID, HashIDTest) { continue } if searchOnly { return nil } log.Debugf("cryptengine: UIDIndex=%s", base64.Encode(UIDIndex)) // Check UID already exists in keyDB _, pos, found, err := ce.keyDB.GetPublicUID(mappedID, i) if err != nil { return err } if found && pos == i { // UID exists already -> skip entry matchFound = true continue } // Compute: IDKEY = HASH(k2 | Identity) tmp = make([]byte, len(k2)+len(mappedID)) copy(tmp, k2) copy(tmp[len(k2):], mappedID) IDKEY := cipher.SHA256(tmp) // Fetch from Key Repository: UIDMessageReply = GET(UIDIndex) msgReply, err := ce.fetchUID(domain, UIDIndex) if err != nil { return err } // Decrypt UIDHash = AES_256_CBC_Decrypt( IDKEY, CrUID) UIDHash := cipher.AES256CBCDecrypt(IDKEY, CrUID) log.Debugf("cryptengine: UIDHash=%s", base64.Encode(UIDHash)) // Decrypt UIDMessageReply.UIDMessage with UIDHash index, uid, err := msgReply.Decrypt(UIDHash) if err != nil { return err } log.Debugf("cryptengine: UIDMessage=%s", uid.JSON()) // Check index if !bytes.Equal(index, UIDIndex) { return log.Errorf("cryptengine: index != UIDIndex") } // Verify self signature if err := uid.VerifySelfSig(); err != nil { return log.Error(err) } // Verify server signature if err := ce.verifyServerSig(uid, msgReply, i); err != nil { return err } // TODO: make sure the whole chain of UIDMessages is valid // Store UIDMessage if err := ce.keyDB.AddPublicUID(uid, i); err != nil { return err } matchFound = true // If no further entry can be found, the latest UIDMessage entry has been found } if matchFound { return nil } return log.Errorf("no hash chain entry found of id '%s'", id) }
// syncHashChain brings local hash chain in sync with key server at the given // domain. It just downloads the new entries and does not validate them // whatsoever. func (ce *CryptEngine) syncHashChain(domain string) error { // get JSON-RPC client client, _, err := ce.cache.Get(domain, ce.keydPort, ce.keydHost, ce.homedir, "KeyHashchain.FetchLastHashChain") if err != nil { return err } // get last hash chain entry from key server reply, err := client.JSONRPCRequest("KeyHashchain.FetchLastHashChain", nil) if err != nil { return err } // parse hash chain entry hcEntry, ok := reply["HCEntry"].(string) if !ok { return log.Error("cryptengine: fetch last hash chain entry reply has the wrong type") } // parse hash chain position hcPosFloat := reply["HCPos"].(float64) if !ok { return log.Error("cryptengine: fetch last hash chain position reply has the wrong type") } hcPos := uint64(hcPosFloat) log.Debugf("cryptengine: last HC#%d: %s", hcPos, hcEntry) // determine what we already have pos, found, err := ce.keyDB.GetLastHashChainPos(domain) if err != nil { return err } var start, end uint64 if found { log.Debugf("cryptengine: last position for domain '%s': %d", domain, pos) if pos >= hcPos { // already in sync log.Debugf("cryptengine: hash chain already in sync") return nil } // sync the missing entries start = pos + 1 end = hcPos } else { // no entries found -> get everything start = 0 end = hcPos log.Debugf("cryptengine: no entry found for domain '%s'", domain) } // get JSON-RPC client client, _, err = ce.cache.Get(domain, ce.keydPort, ce.keydHost, ce.homedir, "KeyHashchain.FetchHashChain") if err != nil { return err } // get missing chain entries content := make(map[string]interface{}) content["StartPosition"] = start content["EndPosition"] = end reply, err = client.JSONRPCRequest("KeyHashchain.FetchHashChain", content) // parse hash chain entries hcEntries, ok := reply["HCEntries"].([]interface{}) if !ok { return log.Error("cryptengine: fetch hash chain entries reply has the wrong type") } // parse first hash chain position hcPosFirstFloat := reply["HCFirstPos"].(float64) if !ok { return log.Error("cryptengine: fetch hash chain first position reply has the wrong type") } hcFirstPos := uint64(hcPosFirstFloat) for i := hcFirstPos; i <= hcPos; i++ { entry, ok := hcEntries[i-hcFirstPos].(string) if !ok { return log.Error("cryptengine: fetch hash chain entry is not a string") } log.Debugf("cryptengine: HC#%d: %s", i, entry) // store entry in database err := ce.keyDB.AddHashChainEntry(domain, i, entry) if err != nil { return nil } } return nil }
// validateHashChain validates the local hash chain for the given domain. // That is, it checks that each entry has the correct length and the links are // valid. func (ce *CryptEngine) validateHashChain(domain string) error { // make sure we have a hashchain for the given domain max, found, err := ce.keyDB.GetLastHashChainPos(domain) if err != nil { return err } if !found { return log.Errorf("no hash chain entries found for domain '%s'", domain) } var hashEntryN, TYPE, NONCE, HashID, CrUID, UIDIndex, hashEntryNminus1 []byte for i := uint64(0); i <= max; i++ { entry, err := ce.keyDB.GetHashChainEntry(domain, i) if err != nil { return err } log.Debugf("cryptengine: validate entry %d: %s", i, entry) if i == 0 { hashEntryNminus1 = make([]byte, sha256.Size) } else { hashEntryNminus1 = hashEntryN } hashEntryN, TYPE, NONCE, HashID, CrUID, UIDIndex, err = hashchain.SplitEntry(entry) if err != nil { return err } if !bytes.Equal(TYPE, hashchain.Type) { return log.Error("cryptengine: invalid hash chain entry type") } entryN := make([]byte, 153) copy(entryN, TYPE) copy(entryN[1:], NONCE) copy(entryN[9:], HashID) copy(entryN[41:], CrUID) copy(entryN[89:], UIDIndex) copy(entryN[121:], hashEntryNminus1) if !bytes.Equal(hashEntryN, cipher.SHA256(entryN)) { return log.Errorf("cryptengine: hash chain entry %d invalid", i) } } // get all private identities for the given domain ids, err := ce.keyDB.GetPrivateIdentitiesForDomain(domain) if err != nil { return err } // make sure UIDMessageReplies are recorded in hash chain for _, id := range ids { _, msgReply, err := ce.keyDB.GetPrivateUID(id, false) if err != nil { return err } if msgReply != nil && msgReply.ENTRY.HASHCHAINPOS <= max { entry, err := ce.keyDB.GetHashChainEntry(domain, msgReply.ENTRY.HASHCHAINPOS) if err != nil { return err } if entry != msgReply.ENTRY.HASHCHAINENTRY { return log.Errorf("cryptengine: hash chain entry differs from UIDMessageReply (%s)", id) } } } return nil }
// Encrypt encrypts a message with the argument given in args and returns the // nymAddress the message should be delivered to. func Encrypt(args *EncryptArgs) (nymAddress string, err error) { log.Debugf("msg.Encrypt(): %s -> %s", args.From.Identity(), args.To.Identity()) // set defaults if args.NumOfKeys == 0 { args.NumOfKeys = NumOfFutureKeys } if args.AvgSessionSize == 0 { args.AvgSessionSize = AverageSessionSize } // create sender key senderHeaderKey, err := cipher.Curve25519Generate(cipher.RandReader) if err != nil { return "", log.Error(err) } // create pre-header ph := newPreHeader(senderHeaderKey.PublicKey()[:]) // create base64 encoder var out bytes.Buffer wc := base64.NewEncoder(&out) // write pre-header var buf bytes.Buffer var count uint32 if err := ph.write(&buf); err != nil { return "", err } oh := newOuterHeader(preHeaderPacket, count, buf.Bytes()) if err := oh.write(wc, true); err != nil { return "", err } count++ // get session state sender := args.From.Identity() recipient := args.To.Identity() sessionStateKey := session.CalcStateKey(args.From.PubKey().PublicKey32(), args.To.PubKey().PublicKey32()) var ss *session.State if args.StatusCode != StatusReset { ss, err = args.KeyStore.GetSessionState(sessionStateKey) if err != nil { return "", err } } if ss == nil { // no session found -> start first session log.Debug("no session found -> start first session") var recipientTemp *uid.KeyEntry recipientTemp, nymAddress, err = args.KeyStore.GetPublicKeyEntry(args.To) if err != nil { return "", err } // create session key var senderSession uid.KeyEntry if err := senderSession.InitDHKey(args.Rand); err != nil { return "", err } // store session key if err := addSessionKey(args.KeyStore, &senderSession); err != nil { return "", err } // root key agreement err = rootKeyAgreementSender(senderHeaderKey.PublicKey(), args.From.Identity(), args.To.Identity(), &senderSession, args.From.PubKey(), recipientTemp, args.To.PubKey(), nil, args.NumOfKeys, args.KeyStore) if err != nil { return "", err } // set session state ss = &session.State{ SenderSessionCount: 0, SenderMessageCount: 0, MaxRecipientCount: 0, RecipientTemp: *recipientTemp, SenderSessionPub: senderSession, NextSenderSessionPub: nil, NextRecipientSessionPubSeen: nil, NymAddress: nymAddress, KeyInitSession: true, } log.Debugf("set session: %s", ss.SenderSessionPub.HASH) err = args.KeyStore.SetSessionState(sessionStateKey, ss) if err != nil { return "", err } } else if args.StatusCode != StatusError { // do not update sessions for StatusError messages log.Debug("session found") log.Debugf("got session: %s", ss.SenderSessionPub.HASH) nymAddress = ss.NymAddress if ss.NextSenderSessionPub == nil { // start new session in randomized fashion n, err := rand.Int(cipher.RandReader, big.NewInt(int64(args.AvgSessionSize))) if err != nil { return "", err } if n.Int64() == 0 { _, err := setNextSenderSessionPub(args.KeyStore, ss, sessionStateKey, args.Rand) if err != nil { return "", err } } } } // create header log.Debugf("senderID: %s", args.From.UIDContent.PUBKEYS[0].HASH) log.Debugf("recipientID: %s", args.To.UIDContent.PUBKEYS[0].HASH) log.Debugf("ss.SenderSessionCount: %d", ss.SenderSessionCount) log.Debugf("ss.SenderMessageCount: %d", ss.SenderMessageCount) log.Debugf("ss.RecipientTempHash: %s", ss.RecipientTemp.HASH) h, err := newHeader(args.From, args.To, ss.RecipientTemp.HASH, &ss.SenderSessionPub, ss.NextSenderSessionPub, ss.NextRecipientSessionPubSeen, args.NymAddress, ss.SenderSessionCount, ss.SenderMessageCount, args.SenderLastKeychainHash, args.Rand, args.StatusCode) if err != nil { return "", err } log.Debugf("h.SenderSessionPub: %s", h.SenderSessionPub.HASH) if h.NextSenderSessionPub != nil { log.Debugf("h.NextSenderSessionPub: %s", h.NextSenderSessionPub.HASH) } if h.NextRecipientSessionPubSeen != nil { log.Debugf("h.NextRecipientSessionPubSeen: %s", h.NextRecipientSessionPubSeen.HASH) } // create (encrypted) header packet recipientIdentityPub, err := args.To.PublicKey() if err != nil { return "", err } hp, err := newHeaderPacket(h, recipientIdentityPub, senderHeaderKey.PrivateKey(), args.Rand) if err != nil { return "", err } // write (encrypted) header packet buf.Reset() if err := hp.write(&buf); err != nil { return "", err } oh = newOuterHeader(encryptedHeader, count, buf.Bytes()) if err := oh.write(wc, true); err != nil { return "", err } count++ sessionKey := session.CalcKey(args.From.PubKey().HASH, args.To.PubKey().HASH, ss.SenderSessionPub.HASH, ss.RecipientTemp.HASH) // make sure we got enough message keys n, err := args.KeyStore.NumMessageKeys(sessionKey) if err != nil { return "", err } if ss.SenderMessageCount >= n { // generate more message keys log.Debugf("generate more message keys (ss.SenderMessageCount=%d)", ss.SenderMessageCount) chainKey, err := args.KeyStore.GetChainKey(sessionKey) if err != nil { return "", err } err = generateMessageKeys(sender, recipient, args.From.PubKey().HASH, args.To.PubKey().HASH, chainKey, false, ss.SenderSessionPub.PublicKey32(), ss.RecipientTemp.PublicKey32(), args.NumOfKeys, args.KeyStore) if err != nil { return "", err } } // get message key messageKey, err := args.KeyStore.GetMessageKey(sessionKey, true, ss.SenderMessageCount) if err != nil { return "", err } // derive symmetric keys cryptoKey, hmacKey, err := deriveSymmetricKeys(messageKey) if err != nil { return "", err } // write crypto setup packet iv := make([]byte, aes.BlockSize) if _, err := io.ReadFull(args.Rand, iv); err != nil { return "", log.Error(err) } oh = newOuterHeader(cryptoSetup, count, iv) if err := oh.write(wc, true); err != nil { return "", err } count++ // start HMAC calculation mac := hmac.New(sha512.New, hmacKey) if err := oh.write(mac, true); err != nil { return "", err } // actual encryption var content []byte if args.StatusCode == StatusOK { // StatusReset and StatusError messages are empty content, err = ioutil.ReadAll(args.Reader) if err != nil { return "", log.Error(err) } } // enforce maximum content length if len(content) > MaxContentLength { return "", log.Errorf("len(content) = %d > %d = MaxContentLength)", len(content), MaxContentLength) } // encrypted packet var contentHash []byte var innerType uint8 if args.PrivateSigKey != nil { contentHash = cipher.SHA512(content) innerType = dataType | signType } else { innerType = dataType } ih := newInnerHeader(innerType, false, content) buf.Reset() if err := ih.write(&buf); err != nil { return "", err } stream := cipher.AES256CTRStream(cryptoKey, iv) stream.XORKeyStream(buf.Bytes(), buf.Bytes()) oh = newOuterHeader(encryptedPacket, count, buf.Bytes()) if err := oh.write(wc, true); err != nil { return "", err } count++ // continue HMAC calculation if err := oh.write(mac, true); err != nil { return "", err } // signature header & padding buf.Reset() if args.PrivateSigKey != nil { sig := ed25519.Sign(args.PrivateSigKey, contentHash) // signature ih = newInnerHeader(signatureType, true, sig[:]) if err := ih.write(&buf); err != nil { return "", err } // padding padLen := MaxContentLength - len(content) pad, err := padding.Generate(padLen, cipher.RandReader) if err != nil { return "", err } ih = newInnerHeader(paddingType, false, pad) if err := ih.write(&buf); err != nil { return "", err } } else { // just padding padLen := MaxContentLength + signatureSize - encryptedPacketSize + innerHeaderSize - len(content) pad, err := padding.Generate(padLen, cipher.RandReader) if err != nil { return "", err } ih = newInnerHeader(paddingType, false, pad) if err := ih.write(&buf); err != nil { return "", err } } // encrypt inner header stream.XORKeyStream(buf.Bytes(), buf.Bytes()) oh = newOuterHeader(encryptedPacket, count, buf.Bytes()) if err := oh.write(wc, true); err != nil { return "", err } count++ // continue HMAC calculation if err := oh.write(mac, true); err != nil { return "", err } // create HMAC header oh = newOuterHeader(hmacPacket, count, nil) oh.PLen = sha512.Size if err := oh.write(mac, false); err != nil { return "", err } oh.inner = mac.Sum(oh.inner) log.Debugf("HMAC: %s", base64.Encode(oh.inner)) if err := oh.write(wc, true); err != nil { return "", err } count++ // write output wc.Close() if out.Len() != EncodedMsgSize { return "", log.Errorf("out.Len() = %d != %d = EncodedMsgSize)", out.Len(), EncodedMsgSize) } if _, err := io.Copy(args.Writer, &out); err != nil { return "", log.Error(err) } // delete message key err = args.KeyStore.DelMessageKey(sessionKey, true, ss.SenderMessageCount) if err != nil { return "", err } // increase SenderMessageCount ss.SenderMessageCount++ err = args.KeyStore.SetSessionState(sessionStateKey, ss) if err != nil { return "", err } return }