コード例 #1
0
ファイル: encrypt.go プロジェクト: JonathanLogan/mute
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
}
コード例 #2
0
ファイル: header.go プロジェクト: JonathanLogan/mute
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
}
コード例 #3
0
ファイル: keystore.go プロジェクト: JonathanLogan/mute
// 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
}
コード例 #4
0
ファイル: hashchain.go プロジェクト: JonathanLogan/mute
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
}
コード例 #5
0
ファイル: decrypt.go プロジェクト: JonathanLogan/mute
// 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
}
コード例 #6
0
ファイル: keystore.go プロジェクト: JonathanLogan/mute
// 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
}
コード例 #7
0
ファイル: header.go プロジェクト: JonathanLogan/mute
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
}
コード例 #8
0
ファイル: msg.go プロジェクト: JonathanLogan/mute
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
}
コード例 #9
0
ファイル: msg.go プロジェクト: JonathanLogan/mute
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
}
コード例 #10
0
ファイル: decrypt.go プロジェクト: JonathanLogan/mute
// 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
}
コード例 #11
0
ファイル: fetch.go プロジェクト: JonathanLogan/mute
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
}
コード例 #12
0
ファイル: hashchain.go プロジェクト: JonathanLogan/mute
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)
}
コード例 #13
0
ファイル: hashchain.go プロジェクト: JonathanLogan/mute
// 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)
}
コード例 #14
0
ファイル: hashchain.go プロジェクト: JonathanLogan/mute
// 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
}
コード例 #15
0
ファイル: hashchain.go プロジェクト: JonathanLogan/mute
// 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
}
コード例 #16
0
ファイル: encrypt.go プロジェクト: JonathanLogan/mute
// 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
}