// SessionAnchor returns the decrypted and verified session anchor for KeyInit. func (ki *KeyInit) SessionAnchor(sigPubKey string) (*SessionAnchor, error) { // SIGKEYHASH corresponds to the SIGKEY of the Identity pubKey, err := base64.Decode(sigPubKey) if err != nil { return nil, err } keyHash := cipher.SHA512(pubKey) if ki.Contents.SIGKEYHASH != base64.Encode(cipher.SHA512(keyHash)) { log.Error(ErrWrongSigKeyHash) return nil, ErrWrongSigKeyHash } // verify that SESSIONANCHORHASH matches decrypted SESSIONANCHOR enc, err := base64.Decode(ki.Contents.SESSIONANCHOR) if err != nil { return nil, err } txt := cipher.AES256CTRDecrypt(keyHash[:32], enc) var sa SessionAnchor if err := json.Unmarshal(txt, &sa); err != nil { return nil, log.Error(err) } if ki.Contents.SESSIONANCHORHASH != base64.Encode(cipher.SHA512(sa.json())) { log.Error(ErrSessionAnchor) return nil, ErrSessionAnchor } return &sa, nil }
func TestSigKeyHash(t *testing.T) { msg, err := Create("*****@*****.**", false, "", "", Strict, hashchain.TestEntry, cipher.RandReader) if err != nil { t.Fatal(err) } if msg.Identity() != "*****@*****.**" { t.Error("wrong identity") } sigKeyHash, err := msg.SigKeyHash() if err != nil { t.Fatal(err) } sigPubKey, err := base64.Decode(msg.SigPubKey()) if err != nil { t.Fatal(err) } if sigKeyHash != base64.Encode(cipher.SHA512(cipher.SHA512(sigPubKey))) { t.Fatal("SIGKEYHASHs differ") } privKey := msg.PrivateEncKey() if err := msg.SetPrivateEncKey(privKey); err != nil { t.Fatal(err) } if privKey != msg.PrivateEncKey() { t.Error("private keys differ") } }
// SigKeyHash returns the SIGKEYHASH which corresponds to the sigPubKey. func SigKeyHash(sigPubKey string) (string, error) { keyHash, err := base64.Decode(sigPubKey) if err != nil { return "", err } return base64.Encode(cipher.SHA512(keyHash)), nil }
// CalcKey computes the session key from senderIdentityHash, // recipientIdentityHash, senderSessionHash, and recipientSessionHash. func CalcKey( senderIdentityHash string, recipientIdentityHash string, senderSessionHash string, recipientSessionHash string, ) string { key := senderIdentityHash + recipientIdentityHash key += senderSessionHash + recipientSessionHash return base64.Encode(cipher.SHA512([]byte(key))) }
// KeyInit returns a new KeyInit message for the given UID message. It also // returns the pubKeyHash and privateKey for convenient further use. // msgcount must increase for each message of the same type and user. // notafter is the unixtime after which the key(s) should not be used anymore. // notbefore is the unixtime before which the key(s) should not be used yet. // fallback determines if the key may serve as a fallback key. // repoURI is URI of the corresponding KeyInit repository. // Necessary randomness is read from rand. func (msg *Message) KeyInit( msgcount, notafter, notbefore uint64, fallback bool, repoURI, mixaddress, nymaddress string, rand io.Reader, ) (ki *KeyInit, pubKeyHash, privateKey string, err error) { var keyInit KeyInit // time checks if notbefore >= notafter { log.Error(ErrInvalidTimes) return nil, "", "", ErrInvalidTimes } if notafter < uint64(times.Now()) { log.Error(ErrExpired) return nil, "", "", ErrExpired } if notafter > uint64(times.Now())+MaxNotAfter { log.Error(ErrFuture) return nil, "", "", ErrFuture } // init keyInit.Contents.VERSION = ProtocolVersion keyInit.Contents.MSGCOUNT = msgcount keyInit.Contents.NOTAFTER = notafter keyInit.Contents.NOTBEFORE = notbefore keyInit.Contents.FALLBACK = fallback keyHash, err := base64.Decode(msg.UIDContent.SIGKEY.HASH) if err != nil { return nil, "", "", err } keyInit.Contents.SIGKEYHASH = base64.Encode(cipher.SHA512(keyHash)) // make sure REPOURIS is set to the first REPOURI of UIDContent.REPOURIS // TODO: support different KeyInit repository configurations if repoURI != msg.UIDContent.REPOURIS[0] { return nil, "", "", log.Error("uri: repoURI differs from msg.UIDContent.REPOURIS[0]") } keyInit.Contents.REPOURI = repoURI // create SessionAnchor sa, sah, pubKeyHash, privateKey, err := msg.sessionAnchor(keyHash, mixaddress, nymaddress, rand) if err != nil { return nil, "", "", err } keyInit.Contents.SESSIONANCHOR = sa keyInit.Contents.SESSIONANCHORHASH = sah // sign KeyInit: the content doesn't have to be hashed, because Ed25519 is // already taking care of that. sig := msg.UIDContent.SIGKEY.ed25519Key.Sign(keyInit.Contents.json()) keyInit.SIGNATURE = base64.Encode(sig) ki = &keyInit return }
func decrypt(sender, recipient *uid.Message, r io.Reader, recipientTemp *uid.KeyEntry, privateKey string, sign bool, chkMsg bool) error { // decrypt var res bytes.Buffer identities := []*uid.Message{recipient} input := base64.NewDecoder(r) version, preHeader, err := ReadFirstOuterHeader(input) if err != nil { return err } if version != Version { return errors.New("wrong version") } ms := memstore.New() if err := recipientTemp.SetPrivateKey(privateKey); err != nil { return err } ms.AddPrivateKeyEntry(recipientTemp) args := &DecryptArgs{ Writer: &res, Identities: identities, PreHeader: preHeader, Reader: input, Rand: cipher.RandReader, KeyStore: ms, } _, sig, err := Decrypt(args) if err != nil { return err } // do not compare messages when fuzzing, because messages have to be different! if chkMsg && res.String() != msgs.Message1 { return errors.New("messages differ") } if sign { contentHash := cipher.SHA512(res.Bytes()) decSig, err := base64.Decode(sig) if err != nil { return err } if len(decSig) != ed25519.SignatureSize { return errors.New("signature has wrong length") } var sigBuf [ed25519.SignatureSize]byte copy(sigBuf[:], decSig) if !ed25519.Verify(sender.PublicSigKey32(), contentHash, &sigBuf) { return errors.New("signature verification failed") } } return nil }
func TestSignatureSize(t *testing.T) { t.Parallel() _, privKey, err := ed25519.GenerateKey(cipher.RandReader) if err != nil { t.Fatal(err) } sig := ed25519.Sign(privKey, cipher.SHA512([]byte("test"))) ih := newInnerHeader(signatureType, false, sig[:]) var buf bytes.Buffer if err := ih.write(&buf); err != nil { t.Fatal(err) } oh := newOuterHeader(encryptedPacket, 4, buf.Bytes()) if oh.size() != signatureSize { t.Errorf("oh.size() = %d != %d", oh.size(), signatureSize) } }
func (msg *Message) sessionAnchor( key []byte, mixaddress, nymaddress string, rand io.Reader, ) (sessionAnchor, sessionAnchorHash, pubKeyHash, privateKey string, err error) { var sa SessionAnchor sa.MIXADDRESS = mixaddress sa.NYMADDRESS = nymaddress sa.PFKEYS = make([]KeyEntry, 1) if err := sa.PFKEYS[0].InitDHKey(rand); err != nil { return "", "", "", "", err } jsn := sa.json() hash := cipher.SHA512(jsn) // SESSIONANCHOR = AES256_CTR(key=UIDMessage.UIDContent.SIGKEY.HASH, SessionAnchor) enc := base64.Encode(cipher.AES256CTREncrypt(key[:32], jsn, rand)) return enc, base64.Encode(hash), sa.PFKEYS[0].HASH, base64.Encode(sa.PFKEYS[0].PrivateKey32()[:]), nil }
func TestSessionState(t *testing.T) { ms := New() ss := &session.State{ SenderSessionCount: 1, SenderMessageCount: 2, } sessionStateKey := base64.Encode(cipher.SHA512([]byte("sessionstatekey"))) err := ms.SetSessionState(sessionStateKey, ss) if err != nil { t.Fatal(err) } sss, err := ms.GetSessionState(sessionStateKey) if err != nil { t.Fatal(err) } if ss != sss { t.Error("session states differ") } }
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) } }
func TestSessionStates(t *testing.T) { tmpdir, keyDB, err := createDB() if err != nil { t.Fatal(err) } defer os.RemoveAll(tmpdir) defer keyDB.Close() var ( rt uid.KeyEntry ssp uid.KeyEntry nssp uid.KeyEntry nrsps uid.KeyEntry ) if err := rt.InitDHKey(cipher.RandReader); err != nil { t.Fatal(err) } if err := ssp.InitDHKey(cipher.RandReader); err != nil { t.Fatal(err) } if err := nssp.InitDHKey(cipher.RandReader); err != nil { t.Fatal(err) } if err := nrsps.InitDHKey(cipher.RandReader); err != nil { t.Fatal(err) } sessionStateKey1 := base64.Encode(cipher.SHA512([]byte("key1"))) sessionStateKey2 := base64.Encode(cipher.SHA512([]byte("key2"))) ss1 := &session.State{ SenderSessionCount: 1, SenderMessageCount: 2, MaxRecipientCount: 3, RecipientTemp: rt, SenderSessionPub: ssp, NextSenderSessionPub: &nssp, NextRecipientSessionPubSeen: &nrsps, NymAddress: "NYMADDRESS", KeyInitSession: true, } ss2 := &session.State{ RecipientTemp: rt, SenderSessionPub: ssp, NymAddress: "NYMADDRESS", } if err := keyDB.SetSessionState(sessionStateKey1, ss1); err != nil { t.Fatal(err) } if err := keyDB.SetSessionState(sessionStateKey2, ss2); err != nil { t.Fatal(err) } ss1db, err := keyDB.GetSessionState(sessionStateKey1) if err != nil { t.Fatal(err) } ss2db, err := keyDB.GetSessionState(sessionStateKey2) if err != nil { t.Fatal(err) } if !session.StateEqual(ss1, ss1db) { t.Error("ss1 and ss1db differ") } if !session.StateEqual(ss2, ss2db) { t.Error("ss2 and ss2db differ") } if err := keyDB.SetSessionState(sessionStateKey1, ss2); err != nil { t.Fatal(err) } ss1db, err = keyDB.GetSessionState(sessionStateKey1) if err != nil { t.Fatal(err) } if !session.StateEqual(ss2, ss1db) { t.Error("ss2 and ss1db differ") } }
// 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 }
// CalcStateKey computes the session state key from senderIdentityPub and // recipientIdentityPub. func CalcStateKey(senderIdentityPub, recipientIdentityPub *[32]byte) string { key := append(senderIdentityPub[:], recipientIdentityPub[:]...) return base64.Encode(cipher.SHA512(key)) }
func TestSessionStore(t *testing.T) { ms := New() sendKey, err := genMessageKey() if err != nil { t.Fatal(err) } recvKey, err := genMessageKey() if err != nil { t.Fatal(err) } sessionKey := base64.Encode(cipher.SHA512([]byte("sessionkey"))) rootKeyHash := cipher.SHA512([]byte("rootkey")) if ms.HasSession(sessionKey) { t.Error("HasSession() should fail") } err = ms.StoreSession(sessionKey, base64.Encode(rootKeyHash), base64.Encode(cipher.SHA512([]byte("chainkey"))), []string{base64.Encode(sendKey[:])}, []string{base64.Encode(recvKey[:])}) if err != nil { t.Fatal(err) } if !ms.HasSession(sessionKey) { t.Error("HasSession() should succeed") } if ms.SessionKey() != sessionKey { t.Error("wrong SessionKey() result") } // test root key hash h, err := ms.GetRootKeyHash(sessionKey) if err != nil { t.Fatal(err) } if !bytes.Equal(h[:], rootKeyHash[:]) { t.Error("root key hashes are not equal") } // test sender key key, err := ms.GetMessageKey(sessionKey, true, 0) if err != nil { t.Fatal(err) } if !bytes.Equal(key[:], sendKey[:]) { t.Error("send key differs") } err = ms.DelMessageKey(sessionKey, true, 0) if err != nil { t.Fatal(err) } _, err = ms.GetMessageKey(sessionKey, true, 0) if err != session.ErrMessageKeyUsed { t.Error("should fail with session.ErrMessageKeyUsed") } // test receiver key key, err = ms.GetMessageKey(sessionKey, false, 0) if err != nil { t.Fatal(err) } if !bytes.Equal(key[:], recvKey[:]) { t.Error("recv key differs") } err = ms.DelMessageKey(sessionKey, false, 0) if err != nil { t.Fatal(err) } _, err = ms.GetMessageKey(sessionKey, false, 0) if err != session.ErrMessageKeyUsed { t.Error("should fail with session.ErrMessageKeyUsed") } }
func TestGenerateMessageKeys(t *testing.T) { defer log.Flush() rk, err := base64.Decode("CH9NjvU/usWcT0vNgiiUHNt9UFgWKneEPRgN0HIvlP0=") if err != nil { t.Fatal(err) } ssp, err := base64.Decode("XRzQmXbf1TRTMVOpU9354Vx8mR32im0gK3IzzVPI/JE=") if err != nil { t.Fatal(err) } rp, err := base64.Decode("y2mzWFL3I16rkNPeMFleX/6a8Ynx93L8oirS4uSYTPo=") if err != nil { t.Fatal(err) } var rootKey [32]byte var senderSessionPub [32]byte var recipientPub [32]byte copy(rootKey[:], rk) copy(senderSessionPub[:], ssp) copy(recipientPub[:], rp) a := "*****@*****.**" b := "*****@*****.**" ms1 := memstore.New() senderSessionKey := session.CalcKey("sender", "recipient", base64.Encode(cipher.SHA512(senderSessionPub[:])), base64.Encode(cipher.SHA512(recipientPub[:]))) recipientSessionKey := session.CalcKey("recipient", "sender", base64.Encode(cipher.SHA512(recipientPub[:])), base64.Encode(cipher.SHA512(senderSessionPub[:]))) err = generateMessageKeys(a, b, "sender", "recipient", &rootKey, false, &senderSessionPub, &recipientPub, NumOfFutureKeys, ms1) if err != nil { t.Fatal(err) } rootKeyHash, err := ms1.GetRootKeyHash(senderSessionKey) if err != nil { t.Fatal(err) } b64 := base64.Encode(rootKeyHash[:]) if b64 != "KJgsEto4kssCEBJAgGJTt2fJ6/FJqMupevapOwtkdgjF0z0VNI8Zzv15hwRVfZPGrGtgc5AGaeZiyao2wZLE2Q==" { t.Errorf("wrong rootKeyHash: %s", b64) } key, err := ms1.GetMessageKey(senderSessionKey, true, 0) if err != nil { t.Fatal(err) } b64 = base64.Encode(key[:]) if b64 != "1EaJ70EOJ1tEYEksbmv1FOgmG+SB0A0LMcx4gp687NdrEmeb/T04GYneFw9hAenUGsgkOjGtySLIL36xqQlgmw==" { t.Errorf("wrong message key (sender, 0): %s", b64) } key, err = ms1.GetMessageKey(senderSessionKey, false, 0) if err != nil { t.Fatal(err) } b64 = base64.Encode(key[:]) if b64 != "o5K0K5cKuWEAMX6g1Cbv2yrcddg2eoB7PhJjtECO1IQsVbNkTf/FqiW4X2/Tmy6XbXhEoysdYPJL4bokoINvsA==" { t.Errorf("wrong message key (recipient, 0): %s", b64) } key, err = ms1.GetMessageKey(senderSessionKey, true, 49) if err != nil { t.Fatal(err) } b64 = base64.Encode(key[:]) if b64 != "r1TwPGq7WF5ysN2ZFyX4ZmnnNxMzH3hAAOfWew8mIND7BqFPSY01H/A7U48awcOwFd9pCnVXd5yc5W0TYvON/Q==" { t.Errorf("wrong message key (sender, 49): %s", b64) } key, err = ms1.GetMessageKey(senderSessionKey, false, 49) if err != nil { t.Fatal(err) } b64 = base64.Encode(key[:]) if b64 != "d45Eic1g95nDrSncvo4FML/zha9lHtnDO/9kDyARQP3AgguhXD1bjw+/ep8MkI91qjAlnmHcsxVOAEEMbecmaQ==" { t.Errorf("wrong message key (recipient, 49): %s", b64) } // generate additional keys from chainKey chainKey, err := ms1.GetChainKey(senderSessionKey) if err != nil { t.Fatal(err) } err = generateMessageKeys(a, b, "sender", "recipient", chainKey, false, &senderSessionPub, &recipientPub, NumOfFutureKeys, ms1) if err != nil { t.Fatal(err) } // generate all keys at the same time ms2 := memstore.New() copy(rootKey[:], rk) err = generateMessageKeys(a, b, "sender", "recipient", &rootKey, true, &senderSessionPub, &recipientPub, 2*NumOfFutureKeys, ms2) if err != nil { t.Fatal(err) } n1, err := ms1.NumMessageKeys(senderSessionKey) if err != nil { t.Fatal(err) } n2, err := ms2.NumMessageKeys(recipientSessionKey) if err != nil { t.Fatal(err) } if n1 != n2 { t.Error("number of message keys differ") } // compare keys k1, err := ms1.GetMessageKey(senderSessionKey, true, NumOfFutureKeys-1) if err != nil { t.Fatal(err) } k2, err := ms2.GetMessageKey(recipientSessionKey, false, NumOfFutureKeys-1) if err != nil { t.Fatal(err) } if !bytes.Equal(k1[:], k2[:]) { t.Error("keys differ") } k1, err = ms1.GetMessageKey(senderSessionKey, true, 2*NumOfFutureKeys-1) if err != nil { t.Fatal(err) } k2, err = ms2.GetMessageKey(recipientSessionKey, false, 2*NumOfFutureKeys-1) if err != nil { t.Fatal(err) } if !bytes.Equal(k1[:], k2[:]) { t.Error("keys differ") } }
// generateMessageKeys generates the next numOfKeys many session keys from // from rootKey for given senderIdentity and recipientIdentity. // If recipientKeys is true the generated sender and reciever keys are stored in // reverse order. // It uses senderSessionPub and recipientPub in the process and calls // keyStore.StoresSession and keyStore.SetSessionState to store the result. func generateMessageKeys( senderIdentity, recipientIdentity string, senderIdentityPubkeyHash, recipientIdentityPubkeyHash string, rootKey *[32]byte, recipientKeys bool, senderSessionPub, recipientPub *[32]byte, numOfKeys uint64, keyStore session.Store, ) error { var ( identities string send []string recv []string ) // identity_fix = HASH(SORT(SenderNym, RecipientNym)) if senderIdentity < recipientIdentity { identities = senderIdentity + recipientIdentity } else { identities = recipientIdentity + senderIdentity } identityFix := cipher.SHA512([]byte(identities)) recipientPubHash := cipher.SHA512(recipientPub[:]) senderSessionPubHash := cipher.SHA512(senderSessionPub[:]) chainKey := rootKey[:] for i := uint64(0); i < numOfKeys; i++ { // messagekey_send[i] = HMAC_HASH(chainkey, "MESSAGE" | HASH(RecipientPub) | identity_fix) buffer := append([]byte("MESSAGE"), recipientPubHash...) buffer = append(buffer, identityFix...) send = append(send, base64.Encode(cipher.HMAC(chainKey, buffer))) // messagekey_recv[i] = HMAC_HASH(chainkey, "MESSAGE" | HASH(SenderSessionPub) | identity_fix) buffer = append([]byte("MESSAGE"), senderSessionPubHash...) buffer = append(buffer, identityFix...) recv = append(recv, base64.Encode(cipher.HMAC(chainKey, buffer))) // chainkey = HMAC_HASH(chainkey, "CHAIN" ) chainKey = cipher.HMAC(chainKey, []byte("CHAIN"))[:32] } // calculate root key hash rootKeyHash := base64.Encode(cipher.SHA512(rootKey[:])) bzero.Bytes(rootKey[:]) // reverse key material, if necessary if recipientKeys { send, recv = recv, send } // store session var sessionKey string if recipientKeys { key := recipientIdentityPubkeyHash key += senderIdentityPubkeyHash key += base64.Encode(cipher.SHA512(recipientPub[:])) key += base64.Encode(cipher.SHA512(senderSessionPub[:])) sessionKey = base64.Encode(cipher.SHA512([]byte(key))) } else { key := senderIdentityPubkeyHash key += recipientIdentityPubkeyHash key += base64.Encode(cipher.SHA512(senderSessionPub[:])) key += base64.Encode(cipher.SHA512(recipientPub[:])) sessionKey = base64.Encode(cipher.SHA512([]byte(key))) } err := keyStore.StoreSession(sessionKey, rootKeyHash, base64.Encode(chainKey), send, recv) if err != nil { return err } return nil }
// 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 }