Example #1
0
        private (ArraySegment <byte> initializationNonce, ArraySegment <byte> remoteEcdhForInit) ReceiveInitializationRequest(State state, byte[] data)
        {
            if (!(state is ServerState serverState))
            {
                throw new InvalidOperationException("Only the server can receive an init request.");
            }

            // nonce(16), pubkey(32), ecdh(32), pading(...), signature(64), mac(12)
            var messageSize         = data.Length;
            var macOffset           = messageSize - MacSize;
            var initializationNonce = new ArraySegment <byte>(data, 0, InitializationNonceSize);

            CheckNonce(initializationNonce);

            // decrypt the message
            var cipher           = new AesCtrMode(AesFactory.GetAes(true, Configuration.ApplicationKey), initializationNonce);
            var decryptedPayload = cipher.Process(data, InitializationNonceSize, macOffset - InitializationNonceSize);

            Array.Copy(decryptedPayload, 0, data, InitializationNonceSize, decryptedPayload.Length);

            var clientPublicKeyOffset = InitializationNonceSize;
            var remoteEcdhOffset      = InitializationNonceSize + EcNumSize;
            var clientPublicKey       = new ArraySegment <byte>(data, clientPublicKeyOffset, EcNumSize);
            var remoteEcdhForInit     = new ArraySegment <byte>(data, remoteEcdhOffset, EcNumSize);
            var signedMessage         = new ArraySegment <byte>(data, 0, macOffset);

            if (serverState.ClientPublicKey != null)
            {
                if (!serverState.ClientPublicKey.Matches(clientPublicKey))
                {
                    throw new InvalidOperationException("The server was initialized before with a different public key");
                }
                else if (serverState.ClientInitializationNonce != null && initializationNonce.Matches(serverState.ClientInitializationNonce))
                {
                    throw new InvalidOperationException("The server was initialized before with the same nonce");
                }
                else
                {
                    // the client wants to reinitialize. Reset state.
                    serverState.RootKey               = null;
                    serverState.FirstSendHeaderKey    = null;
                    serverState.FirstReceiveHeaderKey = null;
                    serverState.LocalEcdhRatchetStep0 = null;
                    serverState.LocalEcdhRatchetStep1 = null;
                    serverState.Ratchets.Clear();
                }
            }

            serverState.ClientInitializationNonce = initializationNonce.ToArray();

            IVerifier verifier = VerifierFactory.Create(clientPublicKey);

            if (!verifier.VerifySignedMessage(Digest, signedMessage))
            {
                throw new InvalidOperationException("The signature was invalid");
            }

            serverState.ClientPublicKey = clientPublicKey.ToArray();
            return(initializationNonce, remoteEcdhForInit);
        }
Example #2
0
        private void ReceiveInitializationResponse(State state, byte[] data)
        {
            if (!(state is ClientState clientState))
            {
                throw new InvalidOperationException("Only the client can receive an init response.");
            }

            var messageSize    = data.Length;
            var macOffset      = messageSize - MacSize;
            var headerIvOffset = macOffset - HeaderIVSize;
            var headerSize     = InitializationNonceSize + EcNumSize;
            var payloadSize    = messageSize - headerSize - MacSize;

            // decrypt header
            var cipher          = new AesCtrMode(AesFactory.GetAes(true, Configuration.ApplicationKey), data, headerIvOffset, HeaderIVSize);
            var decryptedHeader = cipher.Process(data, 0, headerSize);

            Array.Copy(decryptedHeader, 0, data, 0, headerSize);

            // new nonce(16), ecdh pubkey(32), <nonce(16), server pubkey(32),
            // new ecdh pubkey(32) x2, signature(64)>, mac(12)
            var nonce            = new ArraySegment <byte>(data, 0, InitializationNonceSize);
            var rootEcdhKey      = new ArraySegment <byte>(data, InitializationNonceSize, EcNumSize);
            var encryptedPayload = new ArraySegment <byte>(data, headerSize, payloadSize);

            CheckNonce(nonce);

            // decrypt payload
            IKeyAgreement rootEcdh   = clientState.LocalEcdhForInit;
            var           rootPreKey = rootEcdh.DeriveKey(rootEcdhKey);

            rootPreKey = Digest.ComputeDigest(rootPreKey);
            cipher     = new AesCtrMode(AesFactory.GetAes(true, rootPreKey), nonce);
            var decryptedPayload = cipher.Process(encryptedPayload);

            Array.Copy(decryptedPayload, 0, data, headerSize, payloadSize);

            // extract some goodies
            var oldNonce           = new ArraySegment <byte>(data, headerSize, InitializationNonceSize);
            var serverPubKey       = new ArraySegment <byte>(data, headerSize + InitializationNonceSize, EcNumSize);
            var remoteRatchetEcdh0 = new ArraySegment <byte>(data, headerSize + InitializationNonceSize + EcNumSize, EcNumSize);
            var remoteRatchetEcdh1 = new ArraySegment <byte>(data, headerSize + InitializationNonceSize + EcNumSize * 2, EcNumSize);

            // make sure the nonce sent back by the server (which is encrypted and signed)
            // matches the nonce we sent previously
            if (!oldNonce.Matches(clientState.InitializationNonce))
            {
                throw new InvalidOperationException("Nonce did not match");
            }

            // verify that the signature matches
            IVerifier verifier = VerifierFactory.Create(serverPubKey);

            if (!verifier.VerifySignedMessage(Digest, new ArraySegment <byte>(data, 0, payloadSize + headerSize)))
            {
                throw new InvalidOperationException("The signature was invalid");
            }

            // keep the server public key around
            clientState.ServerPublicKey = serverPubKey.ToArray();

            // store the new nonce we got from the server
            clientState.InitializationNonce = nonce.ToArray();
            Log.Verbose($"storing iniitlizaionta nonce: {Log.ShowBytes(nonce)}");

            // we now have enough information to construct our double ratchet
            IKeyAgreement localStep0EcdhRatchet = KeyAgreementFactory.GenerateNew();
            IKeyAgreement localStep1EcdhRatchet = KeyAgreementFactory.GenerateNew();

            // initialize client root key and ecdh ratchet
            var genKeys          = KeyDerivation.GenerateKeys(rootPreKey, clientState.InitializationNonce, 3, 32);
            var rootKey          = genKeys[0];
            var receiveHeaderKey = genKeys[1];
            var sendHeaderKey    = genKeys[2];

            clientState.Ratchets.Add(EcdhRatchetStep.InitializeClient(KeyDerivation, Digest, rootKey,
                                                                      remoteRatchetEcdh0, remoteRatchetEcdh1, localStep0EcdhRatchet,
                                                                      receiveHeaderKey, sendHeaderKey,
                                                                      localStep1EcdhRatchet));

            clientState.LocalEcdhForInit = null;
        }