// stage0
        private ActionResult PostStage0(Covenant.Models.Grunts.GruntEncryptedMessage gruntStage0Response)
        {
            // Check if this Grunt ID is already active
            API.Models.Grunt savedGrunt = CovenantClient.ApiGruntsByIdGet(gruntStage0Response.Id);
            if (savedGrunt == null)
            {
                // Always return NotFound, don't give away unnecessary info
                return(NotFound());
            }
            if (!gruntStage0Response.VerifyHMAC(Convert.FromBase64String(savedGrunt.GruntSharedSecretPassword)))
            {
                // Always return NotFound, don't give away unnecessary info
                return(NotFound());
            }
            Covenant.Models.Grunts.Grunt realGrunt = null;
            if (savedGrunt.Status != GruntStatus.Uninitialized)
            {
                savedGrunt.Status = GruntStatus.Stage0;
                // We create a new Grunt if this one is not uninitialized
                API.Models.Grunt tempModel = new API.Models.Grunt
                {
                    Status                    = savedGrunt.Status,
                    ListenerId                = savedGrunt.ListenerId,
                    CovenantIPAddress         = savedGrunt.CovenantIPAddress,
                    GruntSharedSecretPassword = savedGrunt.GruntSharedSecretPassword,
                    Delay                  = savedGrunt.Delay, Jitter = savedGrunt.Jitter,
                    ConnectAttempts        = savedGrunt.ConnectAttempts,
                    DotNetFrameworkVersion = savedGrunt.DotNetFrameworkVersion
                };
                API.Models.Grunt tempGrunt = CovenantClient.ApiGruntsPost(tempModel);
                realGrunt = Covenant.Models.Grunts.Grunt.Create(tempGrunt);
            }
            else
            {
                savedGrunt.Status = GruntStatus.Stage0;
                API.Models.Grunt tempGrunt = CovenantClient.ApiGruntsPut(savedGrunt);
                realGrunt = Covenant.Models.Grunts.Grunt.Create(tempGrunt);
            }

            // EncryptedMessage is the RSA Public Key
            realGrunt.GruntRSAPublicKey = Convert.ToBase64String(Encrypt.Utilities.AesDecrypt(
                                                                     gruntStage0Response,
                                                                     Convert.FromBase64String(realGrunt.GruntSharedSecretPassword)
                                                                     ));
            // Generate negotiated session key
            Aes newAesKey = Aes.Create();

            newAesKey.GenerateKey();
            realGrunt.GruntNegotiatedSessionKey = Convert.ToBase64String(newAesKey.Key);
            CovenantClient.ApiGruntsPut(realGrunt.ToModel());

            byte[] rsaEncryptedBytes = realGrunt.RSAEncrypt(Convert.FromBase64String(realGrunt.GruntNegotiatedSessionKey));

            Covenant.Models.Grunts.GruntEncryptedMessage message = Covenant.Models.Grunts.GruntEncryptedMessage.Create(
                realGrunt,
                rsaEncryptedBytes,
                Convert.FromBase64String(realGrunt.GruntSharedSecretPassword)
                );
            string Stage0Response = message.Id + "," + message.Name + "," + message.IV + "," + message.EncryptedMessage + "," + message.HMAC;
            // Stage0Response: "Id,Name,Base64(IV),Base64(AES(RSA(SessionKey))),Base64(HMAC)"
            // Transform response
            string transformed = this.Profile.Transform(Common.CovenantEncoding.GetBytes(Stage0Response));
            // Format transformed response
            string response = String.Format(this.Profile.HttpPostResponse, transformed);

            return(Ok(response));
        }