コード例 #1
0
ファイル: procuid.go プロジェクト: JonathanLogan/mute
// procUID allows to process a sender UID from the header in parallel.
// senderUID is the JSON encoded UID message parsed from the header, res is
// the result channel used to communicate the result of the calculation.
func procUID(senderUID string, res chan *procUIDResult) {
	var r procUIDResult
	var err error
	r.uidIndex = cipher.SHA256(cipher.SHA256([]byte(senderUID)))
	r.msg, err = uid.NewJSON(senderUID)
	if err != nil {
		res <- &procUIDResult{uidIndex: nil, msg: nil, err: err}
		return
	}
	// return results
	res <- &r
}
コード例 #2
0
ファイル: uid.go プロジェクト: JonathanLogan/mute
// Encrypt encryptes the given UID message.
func (msg *Message) Encrypt() (UIDHash, UIDIndex []byte, UIDMessageEncrypted string) {
	Message := msg.JSON()
	// Calculate hash: UIDHash = sha256(UIDMessage)
	UIDHash = cipher.SHA256(Message)
	// Calculate hash: UIDIndex = sha256(UIDHash)
	UIDIndex = cipher.SHA256(UIDHash)
	// Encrypt UIDMessage: UIDMessageEncrypted = UIDIndex | nonce | aes_ctr(nonce, key=UIDHash, UIDMessage)
	enc := cipher.AES256CTREncrypt(UIDHash, Message, cipher.RandReader)
	uidEnc := make([]byte, sha256.Size+len(enc))
	copy(uidEnc, UIDIndex)
	copy(uidEnc[sha256.Size:], enc)
	UIDMessageEncrypted = base64.Encode(uidEnc)
	return
}
コード例 #3
0
ファイル: nymaddr.go プロジェクト: JonathanLogan/mute
// NewNymAddress generates a new nym address.
func NewNymAddress(
	domain string,
	secret []byte,
	expire int64,
	singleUse bool,
	minDelay, maxDelay int32,
	id string,
	pubkey *[ed25519.PublicKeySize]byte,
	server string,
	caCert []byte,
) (mixaddress, nymaddress string, err error) {
	if err := identity.IsMapped(id); err != nil {
		return "", "", log.Error(err)
	}
	if MixAddress == "" {
		return "", "", log.Error("util: MixAddress undefined")
	}
	mixAddresses, err := client.GetMixKeys(MixAddress, caCert)
	if err != nil {
		return "", "", log.Error(err)
	}
	tmp := nymaddr.AddressTemplate{
		Secret:        secret,
		System:        0,
		MixCandidates: mixAddresses.Addresses,
		Expire:        expire,
		SingleUse:     singleUse,
		MinDelay:      minDelay,
		MaxDelay:      maxDelay,
	}
	nymAddress, err := tmp.NewAddress(MailboxAddress(pubkey, server),
		cipher.SHA256([]byte(id)))
	if err != nil {
		return "", "", log.Error(err)
	}
	addr, err := nymaddr.ParseAddress(nymAddress)
	if err != nil {
		return "", "", log.Error(err)
	}
	return string(addr.MixAddress), base64.Encode(nymAddress), nil
}
コード例 #4
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
}
コード例 #5
0
ファイル: session_test.go プロジェクト: JonathanLogan/mute
func TestSessions(t *testing.T) {
	tmpdir, keyDB, err := createDB()
	if err != nil {
		t.Fatal(err)
	}
	defer os.RemoveAll(tmpdir)
	defer keyDB.Close()
	// make sure sessions are empty initially
	sessionKey := base64.Encode(cipher.SHA512([]byte("key")))
	rootKeyHash, _, _, err := keyDB.GetSession(sessionKey)
	if err != sql.ErrNoRows {
		t.Error("should fail with sql.ErrNoRows")
	}
	if rootKeyHash != "" {
		t.Error("rootKeyHash is supposed to be empty")
	}
	// store root key hash
	rk := base64.Encode(cipher.SHA256([]byte("rootkey")))
	master := make([]byte, 96)
	if _, err := io.ReadFull(cipher.RandReader, master); err != nil {
		t.Fatal(err)
	}
	kdf := hkdf.New(sha512.New, master, nil, nil)
	chainKey := make([]byte, 32)
	if _, err := io.ReadFull(kdf, chainKey); err != nil {
		t.Fatal(err)
	}
	send, recv, err := deriveKeys(chainKey, kdf)
	if err != nil {
		t.Fatal(err)
	}
	err = keyDB.AddSession(sessionKey, rk, base64.Encode(chainKey), send, recv)
	if err != nil {
		t.Fatal(err)
	}
	// check root key hash
	rootKeyHash, _, n, err := keyDB.GetSession(sessionKey)
	if err != nil {
		t.Fatal(err)
	}
	if rootKeyHash != rk {
		t.Error("rootKeyHash is supposed to equal rk")
	}
	if n != msg.NumOfFutureKeys {
		t.Error("n is supposed to equal msg.NumOfFutureKeys")
	}
	// update root key hash
	chainKey = make([]byte, 32)
	if _, err := io.ReadFull(kdf, chainKey); err != nil {
		t.Fatal(err)
	}
	send, recv, err = deriveKeys(chainKey, kdf)
	if err != nil {
		t.Fatal(err)
	}
	err = keyDB.AddSession(sessionKey, rk, base64.Encode(chainKey), send, recv)
	if err != nil {
		t.Fatal(err)
	}
	// check updated root key hash
	rootKeyHash, _, n, err = keyDB.GetSession(sessionKey)
	if err != nil {
		t.Fatal(err)
	}
	if rootKeyHash != rk {
		t.Error("rootKeyHash is supposed to equal rk")
	}
	if n != 2*msg.NumOfFutureKeys {
		t.Error("n is supposed to equal 2*msg.NumOfFutureKeys")
	}

	// TODO: improve tests for message keys
	_, err = keyDB.GetMessageKey(sessionKey, true, 0)
	if err != nil {
		t.Fatal(err)
	}
	if err := keyDB.DelMessageKey(sessionKey, true, 0); err != nil {
		t.Fatal(err)
	}
}
コード例 #6
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)
}
コード例 #7
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)
}
コード例 #8
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
}