握手过程中,非对称密钥的应用

栏目: 编程工具 · 发布时间: 6年前

内容简介:在cjdns中,使用到了两组非对称密钥对。本文主要分析,在cjdns源码中,是如何使用稳定的公私钥进行身份验证的,又是如何在节点握手过程中完成临时公私钥生成,和临时公钥交换的。文章将从普通节点向接入点连接开始分析,遵循节点之间建立连接的过程,逐步分析两对非对称密钥在连接建立过程当中的产生和作用。

在cjdns中,使用到了两组非对称密钥对。

  • 一组稳定的公私钥,主要用于握手阶段的身份验证等操作。
    这一组密钥也就是我们在conf中所配置的privateKey和publicKey。同时,publicKey也会出现在那些将我们设置为接入点的节点的conf文件中,作为connectTo中的publicKey字段。
  • 一组临时的公私钥,用于握手完成后,正式通讯时加密数据。
    每一次会话都会生成临时的公私钥,这对公私钥才是真正用于加密数据的。

本文主要分析,在cjdns源码中,是如何使用稳定的公私钥进行身份验证的,又是如何在节点握手过程中完成临时公私钥生成,和临时公钥交换的。

文章将从普通节点向接入点连接开始分析,遵循节点之间建立连接的过程,逐步分析两对非对称密钥在连接建立过程当中的产生和作用。

普通点和接入点

当我们要和接入点建立连接时,我们是知道接入点的publicKey的。这个值会和password一起写在conf文件中。

"connectTo":{
                    "xx.xxx.xx.xxx:30199": {
                        "password": "xxxxxxxxxxxxxxxxxxxxxxxxxxxxxx",
                        "publicKey":"xxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxx.k"
                    }
				}

发出第一个包:普通点向接入点发起连接

本文不会分析配置从conf文件中读出的过程,直接进入到InterfaceConntroller.c中,有关和接入点建立链接的过程。

直接进入InterfaceController_bootstrapPeer方法,只分析相关代码

int InterfaceController_bootstrapPeer(struct InterfaceController* ifc,
                                      int interfaceNumber,
                                      uint8_t* herPublicKey,
                                      const struct Sockaddr* lladdrParm,
                                      String* password,
                                      String* login,
                                      String* user,
                                      struct Allocator* alloc)
{
    ......
    struct Peer* ep = Allocator_calloc(epAlloc, sizeof(struct Peer), 1);
    ......
    ep->caSession = CryptoAuth_newSession(ic->ca, epAlloc, herPublicKey, false, "outer");
    ......
	sendPing(ep);

    return 0;
}

先看一下CryptoAuth_newSession方法,在CryptoAuth.c中

CryptoAuth_newSession

struct CryptoAuth_Session* CryptoAuth_newSession(struct CryptoAuth* ca,
                                                 struct Allocator* alloc,
                                                 const uint8_t herPublicKey[32],
                                                 const bool requireAuth,
                                                 char* displayName)
{
    ......
    struct CryptoAuth_Session_pvt* session =
        Allocator_calloc(alloc, sizeof(struct CryptoAuth_Session_pvt), 1);
    ......
    Assert_true(herPublicKey);
    Bits_memcpy(session->pub.herPublicKey, herPublicKey, 32);
    uint8_t calculatedIp6[16];
    AddressCalc_addressForPublicKey(calculatedIp6, herPublicKey);
    Bits_memcpy(session->pub.herIp6, calculatedIp6, 16);

    return &session->pub;
}

只分析和公私钥对有关的部分。

主要操作是将conf中,接入点的publicKey,通过CryptoAuth_newSession方法,设置到代表接入点的ep的caSession的pub.herPublicKey,并且计算出对应的ip6,设置到session->pub.herIp6中。

sendPing

sendPing的具体调用流程不在这里详细分析,只截取和公私钥有关的部分。

在sendPing的过程中,会经历两次加密。事实上,每次发包过程,都会经历两次加密。

Assert_true(!CryptoAuth_encrypt(sess->pub.caSession, msg));
Assert_true(!CryptoAuth_encrypt(ep->caSession, msg));

这两次加密调用的都是同一个加密函数,但是传入的第一个参数不同。第一次调用时,会传入目标点的caSession。第二次调用时,会传入peer的caSession。

在连接接入点的过程中,两次加密都使用了自己和接入点之间的公私钥对,但两个caSession是不同的。

CryptoAuth_encrypt

直接看一下加密函数,CryptoAuth.c中的CryptoAuth_encrypt。两次加密使用的都是这个函数。

    struct CryptoAuth_Session_pvt* session =
        Identity_check((struct CryptoAuth_Session_pvt*) sessionPub);

    ......

    if (session->nextNonce <= CryptoAuth_State_RECEIVED_KEY) {
        if (session->nextNonce < CryptoAuth_State_RECEIVED_KEY) {
            encryptHandshake(msg, session, 0);
            return 0;
        }
        ......
    }

    ......
    return 0;
}

这是我们和接入点之间的第一个包,两个caSession的session->nextNonce = CryptoAuth_State_INIT = 0.所以,无论是第一次加密还是第二次加密,都会进入到encryptHandshake中

encryptHandshake

static void encryptHandshake(struct Message* message,
                             struct CryptoAuth_Session_pvt* session,
                             int setupMessage)
{
	......
    struct CryptoHeader* header = (struct CryptoHeader*) message->bytes;
    ......

    // set the permanent key
    Bits_memcpy(header->publicKey, session->context->pub.publicKey, 32);

    ......
    // Set the session state
    header->nonce = Endian_hostToBigEndian32(session->nextNonce);

    if (session->nextNonce == CryptoAuth_State_INIT ||
        session->nextNonce == CryptoAuth_State_RECEIVED_HELLO)
    {
        // If we're sending a hello or a key
        // Here we make up a temp keypair
        Random_bytes(session->context->rand, session->ourTempPrivKey, 32);
        crypto_scalarmult_curve25519_base(session->ourTempPubKey, session->ourTempPrivKey);
    }

    Bits_memcpy(header->encryptedTempKey, session->ourTempPubKey, 32);

    ......

    uint8_t sharedSecret[32];
    if (session->nextNonce < CryptoAuth_State_RECEIVED_HELLO) {
        getSharedSecret(sharedSecret,
                        session->context->privateKey,
                        session->pub.herPublicKey,
                        passwordHash,
                        session->context->logger);

        session->isInitiator = true;

        Assert_true(session->nextNonce <= CryptoAuth_State_SENT_HELLO);
        session->nextNonce = CryptoAuth_State_SENT_HELLO;
    }
    ......

    encryptRndNonce(header->handshakeNonce, message, sharedSecret);

    ......
}

主要做了一下几个操作:

1. 把自己的稳定的publicKey放入握手message中

......
   struct CryptoHeader* header = (struct CryptoHeader*) message->bytes;
   ......

   // set the permanent key
   Bits_memcpy(header->publicKey, session->context->pub.publicKey, 32);

2. 把自己的nextNonce放入握手message中的nonce字段

此时的session->nextNonce为CryptoAuth_State_INIT,也就是0

// Set the session state
header->nonce = Endian_hostToBigEndian32(session->nextNonce);

3. 生成握手过程中使用的临时公私钥对,并将临时公钥放入握手message中

if (session->nextNonce == CryptoAuth_State_INIT ||
    session->nextNonce == CryptoAuth_State_RECEIVED_HELLO)
{
    // If we're sending a hello or a key
    // Here we make up a temp keypair
    Random_bytes(session->context->rand, session->ourTempPrivKey, 32);
    crypto_scalarmult_curve25519_base(session->ourTempPubKey, session->ourTempPrivKey);
}

Bits_memcpy(header->encryptedTempKey, session->ourTempPubKey, 32);

4. 生成握手过程中使用的加密密钥sharedSecret,修改nextNonce为SENT_HELLO = 1

uint8_t sharedSecret[32];
    if (session->nextNonce < CryptoAuth_State_RECEIVED_HELLO) {
        getSharedSecret(sharedSecret,
                        session->context->privateKey,
                        session->pub.herPublicKey,
                        passwordHash,
                        session->context->logger);

        session->isInitiator = true;

        Assert_true(session->nextNonce <= CryptoAuth_State_SENT_HELLO);
        session->nextNonce = CryptoAuth_State_SENT_HELLO;
    } else {
        ......
    }

注意看这里的getSharedSecret的参数,是自己的稳定私钥session->context->privateKey,对方的稳定公钥session->pub.herPublicKey,passwordHash。

static inline void getSharedSecret(uint8_t outputSecret[32],
                                   uint8_t myPrivateKey[32],
                                   uint8_t herPublicKey[32],
                                   uint8_t passwordHash[32],
                                   struct Log* logger)
{
    if (passwordHash == NULL) {
        crypto_box_curve25519xsalsa20poly1305_beforenm(outputSecret, herPublicKey, myPrivateKey);
    } else {
        union {
            struct {
                uint8_t key[32];
                uint8_t passwd[32];
            } components;
            uint8_t bytes[64];
        } buff;

        crypto_scalarmult_curve25519(buff.components.key, myPrivateKey, herPublicKey);
        Bits_memcpy(buff.components.passwd, passwordHash, 32);
        crypto_hash_sha256(outputSecret, buff.bytes, 64);
    }
    ......
}

根据passwordHash是否存在,也就是conf中是否有password字段,分为两种不同的方法来计算outputSecret。具体计算方法不需要分析。只需要知道,根据非对称加密的原理,在正确的成对使用双方的公私钥对,且password相同的情况下,一方加密的内容是一定会被对方解密的。在本次收发包中,使用的是双方的稳定公私钥对,只要接收方在解密握手包时,也使用双方的稳定公私钥对,message一定会被正确的解开。

5. 使用sharedSecret加密握手message

encryptRndNonce(header->handshakeNonce, message, sharedSecret);

总结

在普通点向接入点发出第一个包后,普通点上会建立一个接入点作为peer的caSession,和一个接入点作为目标点的caSession。

  1. 对于第一个caSession,他是代表邻居节点的struct Peer* ep的成员。这个ep会加入到ici->peerMap中,map名为EndpointsBySockaddr。所有对于它的维护都在InterfaceController.c中进行,代表的是对于邻居节点的状态维护。当第一个包发送完成后,这个caSessio中包括

    • 邻居节点的稳定公钥herPublicKey
    • 自己的稳定公钥publicKey
    • 自己的稳定私钥privateKey
    • 自己的临时公钥ourTempPubKey
    • 自己的临时私钥ourTempPrivKey、
    • 密钥sharedSecret
    • nextNonce值为CryptoAuth_State_SENT_HELLO = 1。

      计算sharedSecret使用到

    • 自己的稳定私钥privateKey
    • 邻居节点的稳定公钥herTempPubKey

      在发出去的握手包中包括

    • 值为CryptoAuth_State_INIT = 0的nonce字段
    • 自己的稳定公钥publicKey
    • 自己的临时公钥ourTempPubKey
    • 使用sharedSecret加密过的message内容
  2. 对于第二个caSession,他是代表与其他节点的会话的struct SessionManager_Session_pvt* sess的成员。这个sess会加入到sm->ifaceMap中,map名为OfSessionsByIp6。所有对于它的维护都在SessionManager.c中进行,代表的是对于与其他节点的会话的状态维护。当第一个包发送完成后,这个caSessio中包括

    • 目标节点的稳定公钥herPublicKey
    • 自己的稳定公钥publicKey
    • 自己的稳定私钥privateKey
    • 自己的临时公钥ourTempPubKey
    • 自己的临时私钥ourTempPrivKey
    • 密钥sharedSecret
    • nextNonce值为CryptoAuth_State_SENT_HELLO = 1

      计算sharedSecret使用到

    • 自己的稳定私钥privateKey
    • 目标节点的临时公钥herTempPubKey

      在发出去的握手包中包括

    • 值为CryptoAuth_State_INIT = 0的nonce字段
    • 自己的稳定公钥publicKey
    • 自己的临时公钥ourTempPubKey
    • 使用sharedSecret加密过的message内容

收到第一个包:接入点对握手包的解析处理

当接入点收到握手包后,会对这个包进行解密。解密过程同样有两次解密操作。

  1. 首先解密针对peer节点的加密,使用自己和peer节点之间的公私钥对进行解密。调用点在InterfaceController.c中,有两处:

    • handleIncomingFromWire函数中 if (CryptoAuth_decrypt(ep->caSession, msg))
    • handleUnexpectedIncoming函数中 if (CryptoAuth_decrypt(ep->caSession, msg))
      这两处调用都在收到数据包后的处理逻辑线上,根据不同情况,调用到其中的一个。
  2. 然后解密针对目标节点的加密,使用自己和目标节点之间的公私钥对进行解密。调用点在SessionManager.c中的incomingFromSwitchIf中, enum CryptoAuth_DecryptErr ret = CryptoAuth_decrypt(session->pub.caSession, msg); .

    这三个解密调用的都是同一个解密函数,但是传入的第一个参数不同。第一次调用时,会传入peer节点的caSession。第二次调用时,会传入目标节点的caSession。

    在接入点处理握手包的过程中,两次解密都使用了向他连接的普通点和它之间的公私钥对,但两个caSession是不同的。

根据收到包后的处理逻辑,这个包首先会进入到InterfaceController.c的handleUnexpectedIncoming函数中

handleIncomingFromWire

static Iface_DEFUN handleIncomingFromWire(struct Message* msg, struct Iface* addrIf)
{
    ......
    int epIndex = Map_EndpointsBySockaddr_indexForKey(&lladdr, &ici->peerMap);
    if (epIndex == -1) {
        return handleUnexpectedIncoming(msg, ici);
    }

    struct Peer* ep = Identity_check((struct Peer*) ici->peerMap.values[epIndex]);
    ......
    if (CryptoAuth_decrypt(ep->caSession, msg)) {
        return NULL;
    }
    ......
    return receivedPostCryptoAuth(msg, ep, ici->ic);
}

首先试图在ici->peerMap中查找是否有这个点。

接下来针对查找结果有一个判断逻辑,

* 如果没有记录,进入handleUnexpectedIncoming;
* 如果有,取出对应的Peer,并调用CryptoAuth_decrypt。最后调用receivedPostCryptoAuth。

因为这是接入点收到普通点的第一个包,所以,不会有关于普通点的记录。我们直接进入进入handleUnexpectedIncoming

handleUnexpectedIncoming

static Iface_DEFUN handleUnexpectedIncoming(struct Message* msg,
                                            struct InterfaceController_Iface_pvt* ici)
{
    ......
    struct Peer* ep = Allocator_calloc(epAlloc, sizeof(struct Peer), 1);
    ......
    ep->caSession = CryptoAuth_newSession(ic->ca, epAlloc, ch->publicKey, true, "outer");
    if (CryptoAuth_decrypt(ep->caSession, msg)) {
        // If the first message is a dud, drop all state for this peer.
        // probably some random crap that wandered in the socket.
        Allocator_free(epAlloc);
        return NULL;
    }
    ......
    return receivedPostCryptoAuth(msg, ep, ic);
}

1. 创建Peer,并调用CryptoAuth_newSession

重温一下CryptoAuth_newSession

struct CryptoAuth_Session* CryptoAuth_newSession(struct CryptoAuth* ca,
                                                 struct Allocator* alloc,
                                                 const uint8_t herPublicKey[32],
                                                 const bool requireAuth,
                                                 char* displayName)
{
    .......
    Assert_true(herPublicKey);
    Bits_memcpy(session->pub.herPublicKey, herPublicKey, 32);
    uint8_t calculatedIp6[16];
    AddressCalc_addressForPublicKey(calculatedIp6, herPublicKey);
    Bits_memcpy(session->pub.herIp6, calculatedIp6, 16);

    return &session->pub;
}

将收到的包中,普通点的publicKey,通过CryptoAuth_newSession方法,设置到代表普通点的ep的caSession的pub.herPublicKey,并且计算出对应的ip6,设置到session->pub.herIp6中。

2. 调用CryptoAuth_decrypt,解密握手message

enum CryptoAuth_DecryptErr CryptoAuth_decrypt(struct CryptoAuth_Session* sessionPub,
                                              struct Message* msg)
{
    ......
    if (!session->established) {
        if (nonce >= Nonce_FIRST_TRAFFIC_PACKET) {
            ......
        }
        ......
        return decryptHandshake(session, nonce, msg, header);

    }
    ......
}

3. 调用decryptHandshake

先简单说明下几个变量:

  • nonce:从收到的包里取出,是发包者那边的nextNonce,值为CryptoAuth_State_INIT = 0
  • nextNonce:一个局部变量,随情况变化,最后会被设置到session->nextNonce
  • session->nextNonce:session中的nextNonce,真正标识这个session的状态,目前为CryptoAuth_State_INIT = 0

对照一下Nonce和CryptoAuth_State

enum Nonce {
    Nonce_HELLO = 0,
    Nonce_REPEAT_HELLO = 1,
    Nonce_KEY = 2,
    Nonce_REPEAT_KEY = 3,
    Nonce_FIRST_TRAFFIC_PACKET = 4
};
enum CryptoAuth_State {
    // New CryptoAuth session, has not sent or received anything
    CryptoAuth_State_INIT = 0,

    // Sent a hello message, waiting for reply
    CryptoAuth_State_SENT_HELLO = 1,

    // Received a hello message, have not yet sent a reply
    CryptoAuth_State_RECEIVED_HELLO = 2,

    // Received a hello message, sent a key message, waiting for the session to complete
    CryptoAuth_State_SENT_KEY = 3,

    // Sent a hello message, received a key message, may or may not have sent some data traffic
    // but no data traffic has yet been received
    CryptoAuth_State_RECEIVED_KEY = 4,

    // Received data traffic, session is in run state
    CryptoAuth_State_ESTABLISHED = 100
};
static enum CryptoAuth_DecryptErr decryptHandshake(struct CryptoAuth_Session_pvt* session,
                                                   const uint32_t nonce,
                                                   struct Message* message,
                                                   struct CryptoHeader* header)
{
    ......
    if (nonce < Nonce_KEY) { // HELLO or REPEAT_HELLO
        ......
        getSharedSecret(sharedSecret,
                        session->context->privateKey,
                        session->pub.herPublicKey,
                        passwordHash,
                        session->context->logger);
        nextNonce = CryptoAuth_State_RECEIVED_HELLO;
    }
    ......
    // Decrypt her temp public key and the message.
    if (decryptRndNonce(header->handshakeNonce, message, sharedSecret)) {
        // just in case
        Bits_memset(header, 0, CryptoHeader_SIZE);
        cryptoAuthDebug(session, "DROP message with nonce [%d], decryption failed", nonce);
        return CryptoAuth_DecryptErr_HANDSHAKE_DECRYPT_FAILED;
    }

    if (Bits_isZero(header->encryptedTempKey, 32)) {
        // we need to reject 0 public keys outright because they will be confused with "unknown"
        cryptoAuthDebug0(session, "DROP message with zero as temp public key");
        return CryptoAuth_DecryptErr_WISEGUY;
    }

    // Post-decryption checking
    if (nonce == Nonce_HELLO) {
        // A new hello packet
        if (!Bits_memcmp(session->herTempPubKey, header->encryptedTempKey, 32)) {
            // possible replay attack or duped packet
            cryptoAuthDebug0(session, "DROP dupe hello packet with same temp key");
            return CryptoAuth_DecryptErr_INVALID_PACKET;
        }
    }
    ......

    if (nextNonce == CryptoAuth_State_RECEIVED_KEY) {
       ......
    } else if (nextNonce == CryptoAuth_State_RECEIVED_HELLO) {
        Assert_true(nonce == Nonce_HELLO || nonce == Nonce_REPEAT_HELLO);
        if (Bits_memcmp(session->herTempPubKey, header->encryptedTempKey, 32)) {
            // fresh new hello packet, we should reset the session.
            switch (session->nextNonce) {
                case CryptoAuth_State_SENT_HELLO: {
                   	......
                }
                case CryptoAuth_State_INIT: {
                    Bits_memcpy(session->herTempPubKey, header->encryptedTempKey, 32);
                    break;
                }
                default: {
                    ......
                }
            }
        } else {
        	......
        }
    } else {
        ......
    }
    ......
    session->nextNonce = nextNonce;
    ......
}

这个函数主要做了一下操作:

3.1. 计算SharedSecret

getSharedSecret(sharedSecret,
                session->context->privateKey,
                session->pub.herPublicKey,
                passwordHash,
                session->context->logger);
nextNonce = CryptoAuth_State_RECEIVED_HELLO;

关于这个函数的具体实现,在加密部分已经分析过,得出结论,因为双方使用的都是稳定的公私钥对,所以,计算出的SharedSecret是可以解密握手包的。

3.2. 解密message和一些字段的合法性检查

// Decrypt her temp public key and the message.
if (decryptRndNonce(header->handshakeNonce, message, sharedSecret)) {
    // just in case
    Bits_memset(header, 0, CryptoHeader_SIZE);
    cryptoAuthDebug(session, "DROP message with nonce [%d], decryption failed", nonce);
    return CryptoAuth_DecryptErr_HANDSHAKE_DECRYPT_FAILED;
}

if (Bits_isZero(header->encryptedTempKey, 32)) {
    // we need to reject 0 public keys outright because they will be confused with "unknown"
    cryptoAuthDebug0(session, "DROP message with zero as temp public key");
    return CryptoAuth_DecryptErr_WISEGUY;
}

// Post-decryption checking
if (nonce == Nonce_HELLO) {
    // A new hello packet
    if (!Bits_memcmp(session->herTempPubKey, header->encryptedTempKey, 32)) {
        // possible replay attack or duped packet
        cryptoAuthDebug0(session, "DROP dupe hello packet with same temp key");
        return CryptoAuth_DecryptErr_INVALID_PACKET;
    }
}

不做具体分析

3.3. 将message中对方的临时公钥保存到herTempPubKey中

case CryptoAuth_State_INIT: {
    Bits_memcpy(session->herTempPubKey, header->encryptedTempKey, 32);
    break;
}

3.4 设置session->nextNonce

设为CryptoAuth_State_RECEIVED_HELLO = 2

session->nextNonce = nextNonce;

4. 调用receivedPostCryptoAuth

static Iface_DEFUN receivedPostCryptoAuth(struct Message* msg,
                                          struct Peer* ep,
                                          struct InterfaceController_pvt* ic)
{
    ......
    if (ep->state < InterfaceController_PeerState_ESTABLISHED) {
        ......
        if (caState == CryptoAuth_State_ESTABLISHED) {
            ......
        } else {
            ......
            if (msg->length < 8 || msg->bytes[7] != 1) {
                ......
            } else {
                ......
                if ((ep->pingCount + 1) % 7) {
                    sendPing(ep);
                }
            }
        }
    }
    ......
    return Iface_next(&ep->switchIf, msg);
}

4.1 sendPing

这是针对收到握手包之后发回的回应包,具体操作放在后面分析。

4.2 Iface_next(&ep->switchIf, msg)

代码执行到这里,接入点作为peer的解密已经完成了。接下来将会去寻找这个包的下一跳,而这个包的目标节点也是接入点,所以这个包最终会交给SessionManager来处理。由incomingFromSwitchIf函数来解密,并维护caSession状态,很相似,不再具体分析了。

总结

当接入点收到普通点发出第一个包后,接入点上会建立一个普通点点作为peer的caSession,和一个普通点作为目标点的caSession。

  1. 对于第一个caSession,他是代表邻居节点的struct Peer* ep的成员。这个ep会加入到ici->peerMap中,map名为EndpointsBySockaddr。所有对于它的维护都在InterfaceController.c中进行,代表的是对于邻居节点的状态维护。当这个包接收过程完成后,这个caSessio中包括

    • 邻居节点的稳定公钥herPublicKey
    • 邻居节点的临时公钥herTempPubKey
    • 自己的稳定公钥publicKey
    • 自己的稳定私钥privateKey
    • nextNonce值为CryptoAuth_State_RECEIVED_HELLO = 2

      计算sharedSecret使用到

    • 自己的稳定私钥privateKey
    • 邻居节点的临时公钥herTempPubKey
  2. 对于第二个caSession,他是代表与其他节点的会话的struct SessionManager_Session_pvt* sess的成员。这个sess会加入到sm->ifaceMap中,map名为OfSessionsByIp6。所有对于它的维护都在SessionManager.c中进行,代表的是对于与其他节点的会话的状态维护。当第一个包的接收过程处理完成后,这个caSessio中包括

    • 目标节点的稳定公钥herPublicKey
    • 目标节点的临时公钥herTempPublicKey
    • 自己的稳定公钥publicKey
    • 自己的稳定私钥privateKey
    • nextNonce值为CryptoAuth_State_RECEIVED_HELLO = 2

      计算sharedSecret使用到

    • 自己的稳定私钥privateKey
    • 目标节点的临时公钥herTempPubKey

发出第二个包:接入点的回包

接入点的回包操作,就是上面的sendPing。依然不详细分析具体调用流程,只关注对于加密部分的函数的调用,直接进入到CryptoAuth_encrypt方法。

CryptoAuth_encrypt

int CryptoAuth_encrypt(struct CryptoAuth_Session* sessionPub, struct Message* msg)
{
    ......
    if (session->nextNonce <= CryptoAuth_State_RECEIVED_KEY) {
        if (session->nextNonce < CryptoAuth_State_RECEIVED_KEY) {
            encryptHandshake(msg, session, 0);
            return 0;
        } else {
            ......
        }
    }
}

encryptHandshake

static void encryptHandshake(struct Message* message,
                             struct CryptoAuth_Session_pvt* session,
                             int setupMessage)
{
    ......
    // set the permanent key
    Bits_memcpy(header->publicKey, session->context->pub.publicKey, 32);

    // Set the session state
    header->nonce = Endian_hostToBigEndian32(session->nextNonce);

    if (session->nextNonce == CryptoAuth_State_INIT ||
        session->nextNonce == CryptoAuth_State_RECEIVED_HELLO)
    {
        // If we're sending a hello or a key
        // Here we make up a temp keypair
        Random_bytes(session->context->rand, session->ourTempPrivKey, 32);
        crypto_scalarmult_curve25519_base(session->ourTempPubKey, session->ourTempPrivKey);
		......
    }

    Bits_memcpy(header->encryptedTempKey, session->ourTempPubKey, 32);
    ......
    uint8_t sharedSecret[32];
    if (session->nextNonce < CryptoAuth_State_RECEIVED_HELLO) {
        ......
    } else {
        // Handshake2
        // herTempPubKey was set by decryptHandshake()
        Assert_ifParanoid(!Bits_isZero(session->herTempPubKey, 32));
        getSharedSecret(sharedSecret,
                        session->context->privateKey,
                        session->herTempPubKey,
                        passwordHash,
                        session->context->logger);

        Assert_true(session->nextNonce <= CryptoAuth_State_SENT_KEY);
        session->nextNonce = CryptoAuth_State_SENT_KEY;
        ......
    }

    ......
    encryptRndNonce(header->handshakeNonce, message, sharedSecret);
    ......
}

这个函数的主要操作:

1. 将自己的稳定公钥放到message中

// set the permanent key
   Bits_memcpy(header->publicKey, session->context->pub.publicKey, 32);

2. 将自己的nextNonce放到message中

// Set the session state
   header->nonce = Endian_hostToBigEndian32(session->nextNonce);

此时,nextNonce值为CryptoAuth_State_RECEIVED_HELLO = 2

3. 生成握手过程中使用的临时公私钥对,并将临时公钥放入握手message中

if (session->nextNonce == CryptoAuth_State_INIT ||
    session->nextNonce == CryptoAuth_State_RECEIVED_HELLO)
{
    // If we're sending a hello or a key
    // Here we make up a temp keypair
    Random_bytes(session->context->rand, session->ourTempPrivKey, 32);
    crypto_scalarmult_curve25519_base(session->ourTempPubKey, session->ourTempPrivKey);
}

Bits_memcpy(header->encryptedTempKey, session->ourTempPubKey, 32);

4. 生成握手过程中使用的加密密钥sharedSecret,修改nextNonce为SENT_KEY = 3

if (session->nextNonce < CryptoAuth_State_RECEIVED_HELLO) {
        ......
    } else {
        // Handshake2
        // herTempPubKey was set by decryptHandshake()
        Assert_ifParanoid(!Bits_isZero(session->herTempPubKey, 32));
        getSharedSecret(sharedSecret,
                        session->context->privateKey,
                        session->herTempPubKey,
                        passwordHash,
                        session->context->logger);

        Assert_true(session->nextNonce <= CryptoAuth_State_SENT_KEY);
        session->nextNonce = CryptoAuth_State_SENT_KEY;
        ......
    }

要注意,这里getSharedSecret使用的是自己的稳定私钥和对方的临时公钥。我们要关注解密时使用的公私钥对是否匹配。

5. 使用sharedSecret加密握手message

encryptRndNonce(header->handshakeNonce, message, sharedSecret);

总结

这是握手过程中的第二个包,由接入点发出。这个包发出后,接入点上关于普通点的两个caSession都发生了变化。

  1. 对于代表邻居节点的caSession,目前的状态如下,其中黑体为本次发包过程中的改变

    • 邻居节点的稳定公钥herPublicKey
    • 邻居节点的临时公钥herTempPubKey
    • 自己的稳定公钥publicKey
    • 自己的稳定私钥privateKey
    • 自己的临时公钥ourTempPubKey
    • 自己的临时私钥ourTempPrivKey
    • 密钥sharedSecret
    • nextNonce值为CryptoAuth_State_SENT_KEY = 3

      计算sharedSecret使用到

    • 自己的稳定私钥privateKey
    • 邻居节点的临时公钥herTempPubKey

      在发出去的握手包中包括

    • 值为CryptoAuth_State_RECEIVED_HELLO = 2的nonce字段
    • 自己的稳定公钥publicKey
    • 自己的临时公钥ourTempPubKey
    • 使用sharedSecret加密过的message内容
  2. 对于代表目标节点的caSession,目前的状态如下,其中黑体为本次发包过程中的改变

    • 目标节点的稳定公钥herPublicKey
    • 目标节点的临时公钥herTempPubKey
    • 自己的稳定公钥publicKey
    • 自己的稳定私钥privateKey
    • 自己的临时公钥ourTempPubKey
    • 自己的临时私钥ourTempPrivKey
    • 密钥sharedSecret
    • nextNonce值为CryptoAuth_State_SENT_KEY = 3

      计算sharedSecret使用到

    • 自己的稳定私钥privateKey
    • 目标节点的临时公钥herTempPubKey

      在发出去的握手包中包括

    • 值为CryptoAuth_State_RECEIVED_HELLO = 2的nonce字段
    • 自己的稳定公钥publicKey
    • 自己的临时公钥ourTempPubKey
    • 使用sharedSecret加密过的message内容

收到第二个包:普通点收到接入点的回包

当普通点收到接入点的回包后,会对这个包进行解密。解密过程同样有两次解密操作。

  1. 首先解密针对peer节点的加密,使用自己和peer节点之间的公私钥对进行解密。调用点在InterfaceController.c中,有两处:

    • handleIncomingFromWire函数中 if (CryptoAuth_decrypt(ep->caSession, msg))
    • handleUnexpectedIncoming函数中 if (CryptoAuth_decrypt(ep->caSession, msg))
      这两处调用都在收到数据包后的处理逻辑线上,根据不同情况,调用到其中的一个。
  2. 然后解密针对目标节点的加密,使用自己和目标节点之间的公私钥对进行解密。调用点在SessionManager.c中的incomingFromSwitchIf中, enum CryptoAuth_DecryptErr ret = CryptoAuth_decrypt(session->pub.caSession, msg); .

    这三个解密调用的都是同一个解密函数,但是传入的第一个参数不同。第一次调用时,会传入peer节点的caSession。第二次调用时,会传入目标节点的caSession。

    在接入点处理握手包的过程中,两次解密都使用了向他连接的普通点和它之间的公私钥对,但两个caSession是不同的。

根据收到包后的处理逻辑,这个包首先会进入到InterfaceController.c的handleUnexpectedIncoming函数中

handleIncomingFromWire

static Iface_DEFUN handleIncomingFromWire(struct Message* msg, struct Iface* addrIf)
{
    ......
    int epIndex = Map_EndpointsBySockaddr_indexForKey(&lladdr, &ici->peerMap);
    ......

    struct Peer* ep = Identity_check((struct Peer*) ici->peerMap.values[epIndex]);
    ......
    if (CryptoAuth_decrypt(ep->caSession, msg)) {
        return NULL;
    }
    ......
    return receivedPostCryptoAuth(msg, ep, ici->ic);
}

1. 从ici->peerMap中找到ep

int epIndex = Map_EndpointsBySockaddr_indexForKey(&lladdr, &ici->peerMap);
   ......

   struct Peer* ep = Identity_check((struct Peer*) ici->peerMap.values[epIndex]);

2. 调用CryptoAuth_decrypt进行解密

enum CryptoAuth_DecryptErr CryptoAuth_decrypt(struct CryptoAuth_Session* sessionPub,
                                              struct Message* msg)
{
    ......
    uint32_t nonce = Endian_bigEndianToHost32(header->nonce);

    if (!session->established) {
        if (nonce >= Nonce_FIRST_TRAFFIC_PACKET) {
            ......
        }
		......
        return decryptHandshake(session, nonce, msg, header);

    }
    ......
}

3. decryptHandshake

先给出几个关键参数的值:

  • nonce:从收到的包里取出,是发包者那边的nextNonce,值为CryptoAuth_State_RECEIVED_HELLO = 2
  • nextNonce:一个局部变量,随情况变化,最后会被设置到session->nextNonce
  • session->nextNonce:session中的nextNonce,真正标识这个session的状态。当前值为CryptoAuth_State_SENT_HELLO = 1

对照一下Nonce和CryptoAuth_State

enum Nonce {
    Nonce_HELLO = 0,
    Nonce_REPEAT_HELLO = 1,
    Nonce_KEY = 2,
    Nonce_REPEAT_KEY = 3,
    Nonce_FIRST_TRAFFIC_PACKET = 4
};
enum CryptoAuth_State {
    // New CryptoAuth session, has not sent or received anything
    CryptoAuth_State_INIT = 0,

    // Sent a hello message, waiting for reply
    CryptoAuth_State_SENT_HELLO = 1,

    // Received a hello message, have not yet sent a reply
    CryptoAuth_State_RECEIVED_HELLO = 2,

    // Received a hello message, sent a key message, waiting for the session to complete
    CryptoAuth_State_SENT_KEY = 3,

    // Sent a hello message, received a key message, may or may not have sent some data traffic
    // but no data traffic has yet been received
    CryptoAuth_State_RECEIVED_KEY = 4,

    // Received data traffic, session is in run state
    CryptoAuth_State_ESTABLISHED = 100
};
static enum CryptoAuth_DecryptErr decryptHandshake(struct CryptoAuth_Session_pvt* session,
                                                   const uint32_t nonce,
                                                   struct Message* message,
                                                   struct CryptoHeader* header)
{
    ......
    if (nonce < Nonce_KEY) { // HELLO or REPEAT_HELLO
        ......
    } else {
        ......
        // We sent the hello, this is a key
        getSharedSecret(sharedSecret,
                        session->ourTempPrivKey,
                        session->pub.herPublicKey,
                        passwordHash,
                        session->context->logger);
        nextNonce = CryptoAuth_State_RECEIVED_KEY;
    }

    ......
    // Decrypt her temp public key and the message.
    if (decryptRndNonce(header->handshakeNonce, message, sharedSecret)) {
        // just in case
        Bits_memset(header, 0, CryptoHeader_SIZE);
        cryptoAuthDebug(session, "DROP message with nonce [%d], decryption failed", nonce);
        return CryptoAuth_DecryptErr_HANDSHAKE_DECRYPT_FAILED;
    }

    ......
    if (nextNonce == CryptoAuth_State_RECEIVED_KEY) {
        Assert_true(nonce == Nonce_KEY || nonce == Nonce_REPEAT_KEY);
        switch (session->nextNonce) {
            ......
            case CryptoAuth_State_SENT_HELLO: {
                Bits_memcpy(session->herTempPubKey, header->encryptedTempKey, 32);
                break;
            }
            ......
        }
    }
    ......
    session->nextNonce = nextNonce;
    ......
}

3.1 计算SharedSecret

getSharedSecret(sharedSecret,
                session->ourTempPrivKey,
                session->pub.herPublicKey,
                passwordHash,
                session->context->logger);
nextNonce = CryptoAuth_State_RECEIVED_KEY;

关于这个函数的具体实现,在加密部分已经分析过。在接入点发包时,使用的是接入点的稳定私钥和普通点的临时公钥。而在这里,使用的是自己的临时私钥和接入点稳定公钥。公私钥对匹配,所以,计算出的SharedSecret是可以解密握手包的。

3.2. 解密message

// Decrypt her temp public key and the message.
if (decryptRndNonce(header->handshakeNonce, message, sharedSecret)) {
    // just in case
    Bits_memset(header, 0, CryptoHeader_SIZE);
    cryptoAuthDebug(session, "DROP message with nonce [%d], decryption failed", nonce);
    return CryptoAuth_DecryptErr_HANDSHAKE_DECRYPT_FAILED;
}

不做具体分析

3.3. 将message中对方的临时公钥保存到herTempPubKey中

if (nextNonce == CryptoAuth_State_RECEIVED_KEY) {
       Assert_true(nonce == Nonce_KEY || nonce == Nonce_REPEAT_KEY);
       switch (session->nextNonce) {
           ......
           case CryptoAuth_State_SENT_HELLO: {
               Bits_memcpy(session->herTempPubKey, header->encryptedTempKey, 32);
               break;
           }
           ......
       }
   }

3.4 设置session->nextNonce

设为CryptoAuth_State_RECEIVED_KEY = 4

session->nextNonce = nextNonce;

4. 调用receivedPostCryptoAuth

static Iface_DEFUN receivedPostCryptoAuth(struct Message* msg,
                                          struct Peer* ep,
                                          struct InterfaceController_pvt* ic)
{
    ......
    if (ep->state < InterfaceController_PeerState_ESTABLISHED) {
   		......
        if (caState == CryptoAuth_State_ESTABLISHED) {
            ......
        } else {
            ......
            if (msg->length < 8 || msg->bytes[7] != 1) {
                ......
            } else {
                ......
                if ((ep->pingCount + 1) % 7) {
                    sendPing(ep);
                }
            }
        }
    }
    ......
    return Iface_next(&ep->switchIf, msg);
}

Iface_next(&ep->switchIf, msg);会将message送去SessionManager.c,进行针对目标节点的解密操作,不做详细分析。

后面将直接进入回包分析,sendPing。

总结

网络中的第二个包,是普通点收到接入点发出的回包,普通点会根据回包的内容,维护两个caSession的状态。

  1. 对于代表邻居节点的caSession,目前的状态如下,其中黑体为本次收包过程中的改变

    • 邻居节点的稳定公钥herPublicKey
    • 邻居节点的临时公钥herTempPubKey
    • 自己的稳定公钥publicKey
    • 自己的稳定私钥privateKey
    • 自己的临时公钥ourTempPubKey
    • 自己的临时私钥ourTempPrivKey
    • nextNonce值为CryptoAuth_State_RECEIVED_KEY = 4。

      计算sharedSecret使用到

    • 自己的临时私钥ourTempPrivKey
    • 邻居节点的稳定公钥herPublicKey
  2. 对于代表目标节点的caSession,目前的状态如下,其中黑体为本次收包过程中的改变

    • 目标节点的稳定公钥herPublicKey
    • 目标节点的临时公钥herTempPubKey
    • 自己的稳定公钥publicKey
    • 自己的稳定私钥privateKey
    • 自己的临时公钥ourTempPubKey
    • 自己的临时私钥ourTempPrivKey
    • nextNonce值为CryptoAuth_State_RECEIVED_KEY = 4

      计算sharedSecret使用到

    • 自己的临时私钥ourTempPrivKey
    • 目标节点的稳定公钥herPublicKey

发出第三个包:普通点向接入点回包

普通点的回包操作,就是上面的sendPing。依然不详细分析具体调用流程,只关注对于加密部分的函数的调用,直接进入到CryptoAuth_encrypt方法。

CryptoAuth_encrypt

目前session->nextNonce为CryptoAuth_State_RECEIVED_KEY = 4

int CryptoAuth_encrypt(struct CryptoAuth_Session* sessionPub, struct Message* msg)
{
    ......
    if (session->nextNonce <= CryptoAuth_State_RECEIVED_KEY) {
        if (session->nextNonce < CryptoAuth_State_RECEIVED_KEY) {
            ......
        } else {
            ......
            getSharedSecret(session->sharedSecret,
                            session->ourTempPrivKey,
                            session->herTempPubKey,
                            NULL,
                            session->context->logger);
        }
    }

    encrypt(session->nextNonce, msg, session->sharedSecret, session->isInitiator);

    Message_push32(msg, session->nextNonce, NULL);
    session->nextNonce++;
    return 0;
}

getSharedSecret

getSharedSecret(session->sharedSecret,
                session->ourTempPrivKey,
                session->herTempPubKey,
                NULL,
                session->context->logger);

这里使用的是自己的临时私钥和接入点的临时公钥来计算SharedSecret

encrypt加密message

static inline void encrypt(uint32_t nonce,
                           struct Message* msg,
                           uint8_t secret[32],
                           bool isInitiator)
{
    union {
        uint32_t ints[2];
        uint8_t bytes[24];
    } nonceAs = { .ints = {0, 0} };
    nonceAs.ints[isInitiator] = Endian_hostToLittleEndian32(nonce);

    encryptRndNonce(nonceAs.bytes, msg, secret);
}
static inline void encryptRndNonce(uint8_t nonce[24],
                                   struct Message* msg,
                                   uint8_t secret[32])
{
    Assert_true(msg->padding >= 32);
    uint8_t* startAt = msg->bytes - 32;
    // This function trashes 16 bytes of the padding so we will put it back
    uint8_t paddingSpace[16];
    Bits_memcpy(paddingSpace, startAt, 16);
    Bits_memset(startAt, 0, 32);
    if (!Defined(NSA_APPROVED)) {
        crypto_box_curve25519xsalsa20poly1305_afternm(
            startAt, startAt, msg->length + 32, nonce, secret);
    }

    Bits_memcpy(startAt, paddingSpace, 16);
    Message_shift(msg, 16, NULL);
}

将session的next->nextNonce放入message中

此时session->nextNonce为CryptoAuth_State_RECEIVED_KEY = 4

Message_push32(msg, session->nextNonce, NULL);

维护session->nextNonce

session->nextNonce++;

此时session->nextNonce值为5

总结

网络中的第三个包,是普通点向接入点发出的包,此时两个caSession的状态。

  1. 对于代表邻居节点的caSession,目前的状态如下,其中黑体为本次发包后的改变

    • 邻居节点的稳定公钥herPublicKey
    • 邻居节点的临时公钥herTempPubKey
    • 自己的稳定公钥publicKey
    • 自己的稳定私钥privateKey
    • 自己的临时公钥ourTempPubKey
    • 自己的临时私钥ourTempPrivKey
    • nextNonce值为5

      计算sharedSecret使用到

    • 自己的临时私钥ourTempPrivKey
    • 邻居节点的临时公钥herTempPubKey

      在发出去的握手包中包括

    • 值为CryptoAuth_State_RECEIVED_KEY = 4的nonce字段
    • 使用sharedSecret加密过的message内容
  2. 对于代表目标节点的caSession,目前的状态如下,其中黑体为本次发包后的改变

    • 目标节点的稳定公钥herPublicKey
    • 目标节点的临时公钥herTempPubKey
    • 自己的稳定公钥publicKey
    • 自己的稳定私钥privateKey
    • 自己的临时公钥ourTempPubKey
    • 自己的临时私钥ourTempPrivKey
    • nextNonce值为5

      计算sharedSecret使用到

    • 自己的临时私钥ourTempPrivKey
    • 目标节点的临时公钥herTempPubKey

      在发出去的握手包中包括

    • 值为CryptoAuth_State_RECEIVED_KEY = 4的nonce字段
    • 使用sharedSecret加密过的message内容

收到第三个包:接入点收到普通点发来的包

直接进入handleIncomingFromWire

handleIncomingFromWire

static Iface_DEFUN handleIncomingFromWire(struct Message* msg, struct Iface* addrIf)
{
    ......
    int epIndex = Map_EndpointsBySockaddr_indexForKey(&lladdr, &ici->peerMap);
    ......
    struct Peer* ep = Identity_check((struct Peer*) ici->peerMap.values[epIndex]);
    ......
    if (CryptoAuth_decrypt(ep->caSession, msg)) {
        return NULL;
    }
    ......
    return receivedPostCryptoAuth(msg, ep, ici->ic);
}

1. CryptoAuth_decrypt

enum CryptoAuth_DecryptErr CryptoAuth_decrypt(struct CryptoAuth_Session* sessionPub,
                                              struct Message* msg)
{
    ......
    uint32_t nonce = Endian_bigEndianToHost32(header->nonce);//nonce值为4

    if (!session->established) {
        if (nonce >= Nonce_FIRST_TRAFFIC_PACKET) {//Nonce_FIRST_TRAFFIC_PACKET = 4
            ......
            getSharedSecret(secret,
                            session->ourTempPrivKey,
                            session->herTempPubKey,
                            NULL,
                            session->context->logger);

            enum CryptoAuth_DecryptErr ret = decryptMessage(session, nonce, msg, secret);
            if (!ret) {
                ......
                Bits_memcpy(session->sharedSecret, secret, 32);

                // Now we're in run mode, no more handshake packets will be accepted
                session->established = true;
                session->nextNonce += 3;
                ......
                return 0;
            }
            ......
        }
		......
    }
    ......
}

1.1 getSharedSecret

getSharedSecret(secret,
                session->ourTempPrivKey,
                session->herTempPubKey,
                NULL,
                session->context->logger);

这里,使用了接入点的临时私钥和普通点的临时公钥来计算SharedSecret

####### 1.2 调用decryptMessage解密message ######

static inline enum CryptoAuth_DecryptErr decryptMessage(struct CryptoAuth_Session_pvt* session,
                                                        uint32_t nonce,
                                                        struct Message* content,
                                                        uint8_t secret[32])
{
    // Decrypt with authentication and replay prevention.
    if (decrypt(nonce, content, secret, session->isInitiator)) {
        cryptoAuthDebug0(session, "DROP authenticated decryption failed");
        return CryptoAuth_DecryptErr_DECRYPT;
    }
    ......
    return 0;
}

1.3 保存会话密钥

Bits_memcpy(session->sharedSecret, secret, 32);

将使用接入点临时私钥和普通点临时公钥计算出的SharedSecret保存到session->sharedSecret中。

当握手过程完成后,节点间的交互都会使用这个SharedSecret来加密message。将这个值保存起来,方便后期的加密操作。

1.4 维护session状态

这段代码执行之前,session->nextNonce的值为CryptoAuth_State_SENT_KEY = 3

session->established = true;
session->nextNonce += 3;

此时session->nextNonce的值为6。session状态为established

2. 调用receivedPostCryptoAuth

static Iface_DEFUN receivedPostCryptoAuth(struct Message* msg,
                                          struct Peer* ep,
                                          struct InterfaceController_pvt* ic)
{
    ......
    int caState = CryptoAuth_getState(ep->caSession);

    if (ep->state < InterfaceController_PeerState_ESTABLISHED) {
        // EP states track CryptoAuth states...
        ep->state = caState;
        ......
        if (caState == CryptoAuth_State_ESTABLISHED) {
            moveEndpointIfNeeded(ep);
            //sendPeer(0xffffffff, PFChan_Core_PEER, ep);// version is not known at this point.
        }
        ......
    }
	......
	return Iface_next(&ep->switchIf, msg);
}

2.1 计算caState

enum CryptoAuth_State CryptoAuth_getState(struct CryptoAuth_Session* caSession)
{
    struct CryptoAuth_Session_pvt* session =
        Identity_check((struct CryptoAuth_Session_pvt*)caSession);

    if (session->nextNonce <= CryptoAuth_State_RECEIVED_KEY) {
        return session->nextNonce;
    }
    return (session->established) ? CryptoAuth_State_ESTABLISHED : CryptoAuth_State_RECEIVED_KEY;
}

之前都没有分析这个函数,原因是,在这个包之前,caState都和session->nextNonce一样。

而这一次,由于前面将session->established设为true,所以,这次,caState将会返回CryptoAuth_State_ESTABLISHED

2.2 一些状态维护操作

if (ep->state < InterfaceController_PeerState_ESTABLISHED) {
    // EP states track CryptoAuth states...
    ep->state = caState;
    ......
    if (caState == CryptoAuth_State_ESTABLISHED) {
        moveEndpointIfNeeded(ep);
        //sendPeer(0xffffffff, PFChan_Core_PEER, ep);// version is not known at this point.
    }
    ......
}

主要是修改了ep->state

2.3 将message送去下一站处理

return Iface_next(&ep->switchIf, msg);

Iface_next(&ep->switchIf, msg);会将message送去SessionManager.c,进行针对目标节点的解密操作,不做详细分析。

总结

网络中的第三个包,是接入点收到普通点发出的回包,接入点会根据回包的内容,维护两个caSession的状态。

  1. 对于代表邻居节点的caSession,目前的状态如下,其中黑体为本次收包过程中的改变

    • 邻居节点的稳定公钥herPublicKey
    • 邻居节点的临时公钥herTempPubKey
    • 自己的稳定公钥publicKey
    • 自己的稳定私钥privateKey
    • 自己的临时公钥ourTempPubKey
    • 自己的临时私钥ourTempPrivKey
    • nextNonce值为6
    • caState为established
    • ep->state为established

      计算sharedSecret使用到

    • 自己的临时私钥ourTempPrivKey
    • 邻居节点的临时公钥herTempPubKey
  2. 对于代表目标节点的caSession,目前的状态如下,其中黑体为本次收包过程中的改变

    • 目标节点的稳定公钥herPublicKey
    • 目标节点的临时公钥herTempPubKey
    • 自己的稳定公钥publicKey
    • 自己的稳定私钥privateKey
    • 自己的临时公钥ourTempPubKey
    • 自己的临时私钥ourTempPrivKey
    • nextNonce值为6
    • caState为established
    • ep->state为established

      计算sharedSecret使用到

    • 自己的临时私钥ourTempPrivKey
    • 目标节点的临时公钥herTempPubKey

到这里,接入点上关于普通点的两个caSession的状态都已经变成了established。握手过程结束,进入正常的会话过程。

然而,普通点怎么办?普通点上关于接入点的两个caSession的状态都还没有established,nextNonce值都是5.如何触发普通点上的caSession也变成established?这就需要用到cjdns中的一个关于节点保活的机制。

当接入点上表示普通点的那个ep的state变为established之后,接入点将会每隔固定时间,向普通点发送sendPing来维护普通点的状态。于是,在这个维护机制下,接入点向普通点发送了sendPing。

第四个包:接入点发送ping包,触发普通点状态变化

关于sendPing。依然不详细分析具体调用流程,只关注对于加密部分的函数的调用,直接进入到CryptoAuth_encrypt方法。

CryptoAuth_encrypt

目前session->nextNonce为6

int CryptoAuth_encrypt(struct CryptoAuth_Session* sessionPub, struct Message* msg)
{
    ......
    encrypt(session->nextNonce, msg, session->sharedSecret, session->isInitiator);

    Message_push32(msg, session->nextNonce, NULL);
    session->nextNonce++;
    return 0;
}

这里主要做了三个操作:

  1. 调用encrypt加密message,使用的密钥是session->sharedSecret,这是使用接入点的临时私钥和普通点的临时公钥计算出来的
  2. 将nextNonce=6放入message中
  3. 修改nextNonce为7

收到第四个包:普通点收到接入点发来的ping包

普通点将在处理这个包的过程中完成握手阶段,进入稳定会话阶段。

handleIncomingFromWire

Iface_DEFUN handleIncomingFromWire(struct Message* msg, struct Iface* addrIf)
{
 ......
 int epIndex = Map_EndpointsBySockaddr_indexForKey(&lladdr, &ici->peerMap);
 ......
 struct Peer* ep = Identity_check((struct Peer*) ici->peerMap.values[epIndex]);
 ......
 if (CryptoAuth_decrypt(ep->caSession, msg)) {
 return NULL;
 }
 ......
 if (ep->state == InterfaceController_PeerState_ESTABLISHED &&
 CryptoAuth_getState(ep->caSession) != CryptoAuth_State_ESTABLISHED) {
 sendPeer(0xffffffff, PFChan_Core_PEER_GONE, ep);
 }
 return receivedPostCryptoAuth(msg, ep, ici->ic);
}

1. 从ici->peerMap中找到ep

int epIndex = Map_EndpointsBySockaddr_indexForKey(&lladdr, &ici->peerMap);
   ......

   struct Peer* ep = Identity_check((struct Peer*) ici->peerMap.values[epIndex]);

2. 调用CryptoAuth_decrypt进行解密

nonce:6

session->nextNonce = 5

enum CryptoAuth_DecryptErr CryptoAuth_decrypt(struct CryptoAuth_Session* sessionPub,
                                              struct Message* msg)
{
    ......
    uint32_t nonce = Endian_bigEndianToHost32(header->nonce);

    if (!session->established) {
        if (nonce >= Nonce_FIRST_TRAFFIC_PACKET) {
            ......
            getSharedSecret(secret,
                            session->ourTempPrivKey,
                            session->herTempPubKey,
                            NULL,
                            session->context->logger);

            enum CryptoAuth_DecryptErr ret = decryptMessage(session, nonce, msg, secret);
            if (!ret) {
                cryptoAuthDebug0(session, "Final handshake step succeeded");
                Bits_memcpy(session->sharedSecret, secret, 32);

                // Now we're in run mode, no more handshake packets will be accepted
                session->established = true;
                session->nextNonce += 3;
                ......
                return 0;
            }
            ......
        }

        ......
    }
    ......
}

2.1 计算SharedSecret

getSharedSecret(secret,
                session->ourTempPrivKey,
                session->herTempPubKey,
                NULL,
                session->context->logger);

使用了普通点的临时私钥和接入点的临时公钥来计算SharedSecret

2.2 解密message

enum CryptoAuth_DecryptErr ret = decryptMessage(session, nonce, msg, secret);

2.3 保存会话密钥

Bits_memcpy(session->sharedSecret, secret, 32);

将使用普通点临时私钥和接入点临时公钥计算出的SharedSecret保存到session->sharedSecret中。

当握手过程完成后,节点间的交互都会使用这个SharedSecret来加密message。将这个值保存起来,方便后期的加密操作。

2.4 维护session状态

这段代码执行之前,session->nextNonce的值为5

session->established = true;
session->nextNonce += 3;

此时session->nextNonce的值为8。session状态为established

3. receivedPostCryptoAuth

static Iface_DEFUN receivedPostCryptoAuth(struct Message* msg,
                                          struct Peer* ep,
                                          struct InterfaceController_pvt* ic)
{
    ......
    int caState = CryptoAuth_getState(ep->caSession);
    if (ep->state < InterfaceController_PeerState_ESTABLISHED) {
        // EP states track CryptoAuth states...
        ep->state = caState;
        ......
        if (caState == CryptoAuth_State_ESTABLISHED) {
            moveEndpointIfNeeded(ep);
            //sendPeer(0xffffffff, PFChan_Core_PEER, ep);// version is not known at this point.
        } else {
            ......
        }
    }
    ......
    return Iface_next(&ep->switchIf, msg);
}

和前面接入点收到包后的处理一样,不再详细分析。

caSession和ep->state都是CryptoAuth_State_ESTABLISHED

总结

网络中的第四个包,是接入点执行保活操作时,发往普通点的包。普通点根据包的内容,维护两个caSession的状态。

  1. 对于代表邻居节点的caSession,目前的状态如下,其中黑体为本次收包过程中的改变

    • 邻居节点的稳定公钥herPublicKey
    • 邻居节点的临时公钥herTempPubKey
    • 自己的稳定公钥publicKey
    • 自己的稳定私钥privateKey
    • 自己的临时公钥ourTempPubKey
    • 自己的临时私钥ourTempPrivKey
    • nextNonce值为8
    • caState为established
    • ep->state为established
  2. 对于代表目标节点的caSession,目前的状态如下,其中黑体为本次收包过程中的改变

    • 目标节点的稳定公钥herPublicKey
    • 目标节点的临时公钥herTempPubKey
    • 自己的稳定公钥publicKey
    • 自己的稳定私钥privateKey
    • 自己的临时公钥ourTempPubKey
    • 自己的临时私钥ourTempPrivKey
    • nextNonce值为8
    • caState为established
    • ep->state为established

到这里,普通点上关于接入点的两个caSession的状态都已经变成了established。握手过程结束,进入正常的会话过程。


以上就是本文的全部内容,希望对大家的学习有所帮助,也希望大家多多支持 码农网

查看所有标签

猜你喜欢:

本站部分资源来源于网络,本站转载出于传递更多信息之目的,版权归原作者或者来源机构所有,如转载稿涉及版权问题,请联系我们

Ajax Design Patterns

Ajax Design Patterns

Michael Mahemoff / O'Reilly Media / 2006-06-29 / USD 44.99

Ajax, or Asynchronous JavaScript and XML, exploded onto the scene in the spring of 2005 and remains the hottest story among web developers. With its rich combination of technologies, Ajax provides a s......一起来看看 《Ajax Design Patterns》 这本书的介绍吧!

图片转BASE64编码
图片转BASE64编码

在线图片转Base64编码工具

HTML 编码/解码
HTML 编码/解码

HTML 编码/解码

HSV CMYK 转换工具
HSV CMYK 转换工具

HSV CMYK互换工具