// 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 TestUIDMessage(t *testing.T) { id := strings.Repeat("lp", 32) + "@" + strings.Repeat("x", 185) + ".one" uid, err := uid.Create(id, true, "", "", uid.Strict, hashchain.TestEntry, cipher.RandReader) if err != nil { t.Fatal(err) } uid, err = uid.Update(cipher.RandReader) if err != nil { t.Fatal(err) } // in JSON integers have a variable width, set them to maximum value uid.UIDContent.MSGCOUNT = 18446744073709551615 uid.UIDContent.NOTAFTER = 18446744073709551615 uid.UIDContent.NOTBEFORE = 18446744073709551615 if len(uid.JSON()) != MaxUIDMessage { t.Error("len(uid.JSON()) != MaxUIDMessage") } }
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) }