PullMsgResult ProduceInitiate(ref Msg msg) { int metadataLength = BasicPropertiesLength; byte[] metadataPlaintext = new byte[metadataLength]; AddBasicProperties(metadataPlaintext); msg.InitPool(113 + 128 + metadataLength + Curve25519XSalsa20Poly1305.TagLength); Span <byte> vouchNonce = stackalloc byte[Curve25519XSalsa20Poly1305.NonceLength]; Span <byte> vouchPlaintext = stackalloc byte[64]; Span <byte> vouchBox = stackalloc byte[Curve25519XSalsa20Poly1305.TagLength + 64]; // Create vouch = Box [C',S](C->S') m_cnPublicKey.CopyTo(vouchPlaintext); m_serverKey.CopyTo(vouchPlaintext.Slice(32)); VouchNoncePrefix.CopyTo(vouchNonce); using var rng = RandomNumberGenerator.Create(); #if NETSTANDARD2_1 rng.GetBytes(vouchNonce.Slice(8)); #else byte[] temp = new byte[16]; rng.GetBytes(temp); temp.CopyTo(vouchNonce.Slice(8)); #endif using var box = new Curve25519XSalsa20Poly1305(m_secretKey, m_cnServerKey); box.Encrypt(vouchBox, vouchPlaintext, vouchNonce); Span <byte> initiate = msg; Span <byte> initiateNonce = stackalloc byte[Curve25519XSalsa20Poly1305.NonceLength]; Span <byte> initiatePlaintext = new byte[128 + metadataLength]; Span <byte> initiateBox = initiate.Slice(113); // Create Box [C + vouch + metadata](C'->S') m_publicKey.CopyTo(initiatePlaintext); vouchNonce.Slice(8).CopyTo(initiatePlaintext.Slice(32)); vouchBox.CopyTo(initiatePlaintext.Slice(48)); metadataPlaintext.CopyTo(initiatePlaintext.Slice(48 + 80)); Array.Clear(metadataPlaintext, 0, metadataPlaintext.Length); InitiatieNoncePrefix.CopyTo(initiateNonce); NetworkOrderBitsConverter.PutUInt64(m_nonce, initiateNonce.Slice(16)); using var box2 = new Curve25519XSalsa20Poly1305(m_cnSecretKey, m_cnServerKey); box2.Encrypt(initiateBox, initiatePlaintext, initiateNonce); InitiateLiteral.CopyTo(initiate); // Cookie provided by the server in the WELCOME command m_cnCookie.CopyTo(initiate.Slice(9, 96)); // Short nonce, prefixed by "CurveZMQINITIATE" initiateNonce.Slice(16).CopyTo(initiate.Slice(105)); m_nonce++; return(PullMsgResult.Ok); }
PushMsgResult ProcessInitiate(ref Msg msg) { if (!CheckBasicCommandStructure(ref msg)) { return(PushMsgResult.Error); } Span <byte> initiate = msg; if (!IsCommand("INITIATE", ref msg)) { return(PushMsgResult.Error); } if (initiate.Length < 257) { return(PushMsgResult.Error); } Span <byte> cookieNonce = stackalloc byte[Curve25519XSalsa20Poly1305.NonceLength]; Span <byte> cookiePlaintext = stackalloc byte[64]; Span <byte> cookieBox = initiate.Slice(25, 80); CookieNoncePrefix.CopyTo(cookieNonce); initiate.Slice(9, 16).CopyTo(cookieNonce.Slice(8)); using var secretBox = new XSalsa20Poly1305(m_cookieKey); bool decrypted = secretBox.TryDecrypt(cookiePlaintext, cookieBox, cookieNonce); if (!decrypted) { return(PushMsgResult.Error); } // Check cookie plain text is as expected [C' + s'] if (!SpanUtility.Equals(m_cnClientKey, cookiePlaintext.Slice(0, 32)) || !SpanUtility.Equals(m_cnSecretKey, cookiePlaintext.Slice(32, 32))) { return(PushMsgResult.Error); } Span <byte> initiateNonce = stackalloc byte[Curve25519XSalsa20Poly1305.NonceLength]; byte[] initiatePlaintext = new byte[msg.Size - 113]; var initiateBox = initiate.Slice(113); InitiatieNoncePrefix.CopyTo(initiateNonce); initiate.Slice(105, 8).CopyTo(initiateNonce.Slice(16)); m_peerNonce = NetworkOrderBitsConverter.ToUInt64(initiate, 105); using var box = new Curve25519XSalsa20Poly1305(m_cnSecretKey, m_cnClientKey); bool decrypt = box.TryDecrypt(initiatePlaintext, initiateBox, initiateNonce); if (!decrypt) { return(PushMsgResult.Error); } Span <byte> vouchNonce = stackalloc byte[Curve25519XSalsa20Poly1305.NonceLength]; Span <byte> vouchPlaintext = stackalloc byte[64]; Span <byte> vouchBox = new Span <byte>(initiatePlaintext, 48, 80); var clientKey = new Span <byte>(initiatePlaintext, 0, 32); VouchNoncePrefix.CopyTo(vouchNonce); new Span <byte>(initiatePlaintext, 32, 16).CopyTo(vouchNonce.Slice(8)); using var box2 = new Curve25519XSalsa20Poly1305(m_cnSecretKey, clientKey); decrypt = box2.TryDecrypt(vouchPlaintext, vouchBox, vouchNonce); if (!decrypt) { return(PushMsgResult.Error); } // What we decrypted must be the client's short-term public key if (!SpanUtility.Equals(vouchPlaintext.Slice(0, 32), m_cnClientKey)) { return(PushMsgResult.Error); } // Create the session box m_box = new Curve25519XSalsa20Poly1305(m_cnSecretKey, m_cnClientKey); // This supports the Stonehouse pattern (encryption without authentication). m_state = State.SendingReady; if (!ParseMetadata(new Span <byte>(initiatePlaintext, 128, initiatePlaintext.Length - 128 - 16))) { return(PushMsgResult.Error); } vouchPlaintext.Clear(); Array.Clear(initiatePlaintext, 0, initiatePlaintext.Length); return(PushMsgResult.Ok); }