// 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 }
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 }
// 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 }
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 }
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 }
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 }
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 }
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) }