コード例 #1
0
ファイル: encrypt.go プロジェクト: JonathanLogan/mute
// encrypt reads data from r, encrypts it for identity to (with identity from
// as sender), and writes it to w.
func (ce *CryptEngine) encrypt(
	w io.Writer,
	from, to string,
	sign bool,
	nymAddress string,
	r io.Reader,
	statusfp *os.File,
) error {
	// map pseudonyms
	fromID, fromDomain, err := identity.MapPlus(from)
	if err != nil {
		return err
	}
	toID, err := identity.Map(to)
	if err != nil {
		return err
	}
	// get fromUID from keyDB
	fromUID, _, err := ce.keyDB.GetPrivateUID(fromID, true)
	if err != nil {
		return err
	}
	// get toUID from keyDB
	toUID, _, found, err := ce.keyDB.GetPublicUID(toID, math.MaxInt64) // TODO: use simpler API
	if err != nil {
		return err
	}
	if !found {
		return log.Errorf("not UID for '%s' found", toID)
	}
	// encrypt message
	senderLastKeychainHash, err := ce.keyDB.GetLastHashChainEntry(fromDomain)
	if err != nil {
		return err
	}
	var privateSigKey *[64]byte
	if sign {
		privateSigKey = fromUID.PrivateSigKey64()
	}
	args := &msg.EncryptArgs{
		Writer:                 w,
		From:                   fromUID,
		To:                     toUID,
		NymAddress:             nymAddress,
		SenderLastKeychainHash: senderLastKeychainHash,
		PrivateSigKey:          privateSigKey,
		Reader:                 r,
		Rand:                   cipher.RandReader,
		KeyStore:               ce,
	}
	nymAddress, err = msg.Encrypt(args)
	if err != nil {
		return err
	}
	// show nymaddress on status-fd
	fmt.Fprintf(statusfp, "NYMADDRESS:\t%s\n", nymAddress)
	return nil
}
コード例 #2
0
ファイル: keyinit.go プロジェクト: JonathanLogan/mute
func (ce *CryptEngine) fetchKeyInit(pseudonym string) error {
	// map pseudonym
	id, domain, err := identity.MapPlus(pseudonym)
	if err != nil {
		return err
	}
	// get corresponding public ID
	msg, _, found, err := ce.keyDB.GetPublicUID(id, math.MaxInt64) // TODO: use simpler API
	if err != nil {
		return err
	}
	if !found {
		return log.Errorf("not UID for '%s' found", id)
	}
	// get SIGKEYHASH
	sigKeyHash, err := msg.SigKeyHash()
	if err != nil {
		return err
	}
	// get JSON-RPC client and capabilities
	client, _, err := ce.cache.Get(domain, ce.keydPort, ce.keydHost,
		ce.homedir, "KeyInitRepository.FetchKeyInit")
	if err != nil {
		return err
	}
	// call server
	content := make(map[string]interface{})
	content["SigKeyHash"] = sigKeyHash
	reply, err := client.JSONRPCRequest("KeyInitRepository.FetchKeyInit", content)
	if err != nil {
		return err
	}
	rep, ok := reply["KeyInit"].(string)
	if !ok {
		return log.Errorf("cryptengine: could not fetch key init for '%s'", sigKeyHash)
	}
	ki, err := uid.NewJSONKeyInit([]byte(rep))
	if err != nil {
		return err
	}
	// store public key init message
	if err := ce.keyDB.AddPublicKeyInit(ki); err != nil {
		return err
	}
	return nil
}
コード例 #3
0
ファイル: uid.go プロジェクト: JonathanLogan/mute
// generate a new nym and store it in keydb.
func (ce *CryptEngine) generate(
	pseudonym string,
	keyserver bool,
	outputfp *os.File,
) error {
	// map pseudonym
	id, domain, err := identity.MapPlus(pseudonym)
	if err != nil {
		return err
	}
	// create new UID
	// TODO: allow different PFS preferences
	var lastEntry string
	if !keyserver {
		// no lastEntry, because this will be the first entry in hashchain
		lastEntry, err = ce.keyDB.GetLastHashChainEntry(domain)
		if err != nil {
			return err
		}
	}
	uid, err := uid.Create(id, false, "", "", uid.Strict, lastEntry,
		cipher.RandReader)
	if err != nil {
		return err
	}
	if !keyserver {
		// store UID in keyDB
		if err := ce.keyDB.AddPrivateUID(uid); err != nil {
			return err
		}
	} else {
		// a private key for the keyserver is not stored in the keyDB
		var out bytes.Buffer
		if err := json.Indent(&out, []byte(uid.JSON()), "", "  "); err != nil {
			return err
		}
		fmt.Fprintln(outputfp, out.String())
		if keyserver {
			fmt.Fprintf(outputfp, "{\"PRIVSIGKEY\": %q}\n", uid.PrivateSigKey())
		}
	}
	log.Infof("nym '%s' generated successfully", id)
	return nil
}
コード例 #4
0
ファイル: keyinit.go プロジェクト: JonathanLogan/mute
func (ce *CryptEngine) flushKeyInit(pseudonym string) error {
	// map pseudonym
	id, domain, err := identity.MapPlus(pseudonym)
	if err != nil {
		return err
	}
	// get corresponding public ID
	msg, _, err := ce.keyDB.GetPrivateUID(id, true)
	if err != nil {
		return err
	}
	// get JSON-RPC client and capabilities
	client, _, err := ce.cache.Get(domain, ce.keydPort, ce.keydHost,
		ce.homedir, "KeyInitRepository.FlushKeyInit")
	if err != nil {
		return err
	}
	// call server
	content := make(map[string]interface{})
	nonce, signature := msg.SignNonce()
	content["SigPubKey"] = msg.UIDContent.SIGKEY.PUBKEY
	content["Nonce"] = nonce
	content["Signature"] = signature
	_, err = client.JSONRPCRequest("KeyInitRepository.FlushKeyInit", content)
	if err != nil {
		return err
	}
	/*
		rep, ok := reply["KeyInit"].(string)
		if !ok {
			return log.Errorf("cryptengine: could not fetch key init for '%s'", sigKeyHash)
		}
		_, err = uid.NewJSONKeyInit([]byte(rep))
		if err != nil {
			return err
		}
	*/
	return nil
}
コード例 #5
0
ファイル: uid.go プロジェクト: JonathanLogan/mute
func (ce *CryptEngine) registerOrUpdate(
	pseudonym, token, command, verb string,
) error {
	// map pseudonym
	id, domain, err := identity.MapPlus(pseudonym)
	if err != nil {
		return err
	}
	// TODO: check token?
	// get UID from keyDB
	msg, messageReply, err := ce.keyDB.GetPrivateUID(id, false)
	if err != nil {
		return err
	}
	if messageReply != nil {
		return log.Errorf("cryptengine: UID has already been %s", verb)
	}
	// get JSON-RPC client and capabilities
	client, caps, err := ce.cache.Get(domain, ce.keydPort, ce.keydHost,
		ce.homedir, "KeyRepository."+command)
	if err != nil {
		return err
	}
	// register/update UID with key server
	content := make(map[string]interface{})
	content["UIDMessage"] = msg
	content["Token"] = token
	reply, err := client.JSONRPCRequest("KeyRepository."+command, content)
	if err != nil {
		return err
	}
	rep, ok := reply["UIDMessageReply"].(map[string]interface{})
	if !ok {
		return log.Errorf("cryptengine: %s reply has the wrong type", command)
	}

	// marshal the unstructured UIDMessageReply into a JSON byte array
	jsn, err := json.Marshal(rep)
	if err != nil {
		return err
	}
	// unmarshal the JSON byte array back into a UIDMessageReply
	msgReply, err := uid.NewJSONReply(string(jsn))
	if err != nil {
		return err
	}

	// store reply first to have proof, if the key server is cheating
	if err := ce.keyDB.AddPrivateUIDReply(msg, msgReply); err != nil {
		return err
	}

	// verify reply
	// TODO: keyserver can return more than one SIGPUBKEY
	if err := msgReply.VerifySrvSig(msg, caps.SIGPUBKEYS[0]); err != nil {
		return err
	}

	log.Infof("nym '%s' %s successfully", id, verb)
	return nil
}
コード例 #6
0
ファイル: uid.go プロジェクト: JonathanLogan/mute
func (ce *CtrlEngine) uidNew(
	c *cli.Context,
	minDelay, maxDelay int32,
	host string,
) error {
	// make sure the ID is well-formed
	unmapped := c.String("id")
	id, domain, err := identity.MapPlus(unmapped)
	if err != nil {
		return err
	}

	// sync corresponding hashchain
	if id != "keyserver" {
		if err := ce.upkeepHashchain(c, domain, c.String("host")); err != nil {
			return err
		}
	}

	// check that ID has not been registered already by the same user
	unmappedID, _, err := ce.msgDB.GetNym(id)
	if err != nil {
		return err
	}
	if unmappedID != "" {
		return log.Error(ErrUserIDOwned)
	}

	// check that ID has not been registered already by other user
	err = mutecryptHashchainSearch(c, id, c.String("host"), ce.passphrase)
	if err == nil {
		return log.Error(ErrUserIDTaken)
	}

	// get token from wallet
	token, err := wallet.GetToken(ce.client, def.AccdUsage, def.AccdOwner)
	if err != nil {
		return err
	}

	// register account for  UID
	_, privkey, err := ed25519.GenerateKey(cipher.RandReader)
	if err != nil {
		return log.Error(err)
	}
	server, err := mixclient.PayAccount(privkey, token.Token, "", def.CACert)
	if err != nil {
		ce.client.UnlockToken(token.Hash)
		return log.Error(err)
	}
	ce.client.DelToken(token.Hash)

	// generate secret for account
	var secret [64]byte
	if _, err := io.ReadFull(cipher.RandReader, secret[:]); err != nil {
		return err
	}

	// get mixaddress and nymaddress for KeyInit message
	expire := times.ThirtyDaysLater() // TODO: make this settable
	singleUse := false                // TODO correct?
	var pubkey [ed25519.PublicKeySize]byte
	copy(pubkey[:], privkey[32:])
	mixaddress, nymaddress, err := util.NewNymAddress(domain, secret[:], expire,
		singleUse, minDelay, maxDelay, id, &pubkey, server, def.CACert)
	if err != nil {
		return err
	}

	// generate UID
	err = mutecryptNewUID(c, ce.passphrase, id, domain, host, mixaddress,
		nymaddress, ce.client)
	if err != nil {
		return err
	}

	// save name mapping
	if err := ce.msgDB.AddNym(id, unmapped, c.String("full-name")); err != nil {
		return err
	}

	// register account for UID
	err = ce.msgDB.AddAccount(id, "", privkey, server, &secret,
		minDelay, maxDelay)
	if err != nil {
		return err
	}

	// set active UID, if this was the first UID
	active, err := ce.msgDB.GetValue(msgdb.ActiveUID)
	if err != nil {
		return err
	}
	if active == "" {
		if err := ce.msgDB.AddValue(msgdb.ActiveUID, unmapped); err != nil {
			return err
		}
	}
	return nil
}
コード例 #7
0
ファイル: keyinit.go プロジェクト: JonathanLogan/mute
func (ce *CryptEngine) addKeyInit(pseudonym, mixaddress, nymaddress, token string) error {
	// map pseudonym
	id, domain, err := identity.MapPlus(pseudonym)
	if err != nil {
		return err
	}
	// TODO: check token?
	// generate KeyInit
	msg, _, err := ce.keyDB.GetPrivateUID(id, true)
	if err != nil {
		return err
	}
	// TODO: fix parameter!
	ki, pubKeyHash, privateKey, err := msg.KeyInit(0,
		uint64(times.NinetyDaysLater()), 0, true, domain, mixaddress,
		nymaddress, cipher.RandReader)
	if err != nil {
		return err
	}
	var (
		kis          []*uid.KeyInit
		pubKeyHashes []string
		privateKeys  []string
		tokens       []string
	)
	kis = append(kis, ki)
	pubKeyHashes = append(pubKeyHashes, pubKeyHash)
	privateKeys = append(privateKeys, privateKey)
	tokens = append(tokens, token)
	// get JSON-RPC client and capabilities
	client, caps, err := ce.cache.Get(domain, ce.keydPort, ce.keydHost,
		ce.homedir, "KeyInitRepository.AddKeyInit")
	if err != nil {
		return err
	}
	// call server
	content := make(map[string]interface{})
	content["SigPubKey"] = msg.UIDContent.SIGKEY.PUBKEY
	content["KeyInits"] = kis
	content["Tokens"] = tokens
	reply, err := client.JSONRPCRequest("KeyInitRepository.AddKeyInit", content)
	if err != nil {
		return err
	}
	// verify server signatures
	sigs, ok := reply["Signatures"].([]interface{})
	if !ok {
		return log.Errorf("cryptengine: could not add key inits for '%s'", msg.UIDContent.IDENTITY)
	}
	if len(kis) != len(sigs) {
		return log.Error("cryptengine: number of returned signatures does not equal number of sent key init messages")
	}
	for i, ki := range kis {
		sig, ok := sigs[i].(string)
		if !ok {
			return log.Error("cryptengine: signature is not a string")
		}
		// TODO: keyserver can return more than one SIGPUBKEY
		if err := ki.VerifySrvSig(sig, caps.SIGPUBKEYS[0]); err != nil {
			return err
		}
	}
	// store server key init messages and server signatures
	for i, ki := range kis {
		sig := sigs[i].(string) // cast has been checked already above
		if err := ce.keyDB.AddPrivateKeyInit(ki, pubKeyHashes[i], msg.SigPubKey(), privateKeys[i], sig); err != nil {
			return err
		}
	}
	return nil
}
コード例 #8
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)
}
コード例 #9
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)
}