private bool IsValidPenny(Penny penny) { bool result = true; //start by testing the cache if (((BalloonStore)Process).CachedPennies.Any(p => penny.Id == p.Id)) { reply.Note = "Invalid Penny, I've seen this one before"; result = false; } else { //now verify the signature is valid RSAParameters receiverRSAKeyInfo = new RSAParameters(); receiverRSAKeyInfo.Modulus = ((BalloonStore)Process).PennyBankPublicKey.Modulus; receiverRSAKeyInfo.Exponent = ((BalloonStore)Process).PennyBankPublicKey.Exponent; RSACryptoServiceProvider receiverRSA = new RSACryptoServiceProvider(); receiverRSA.ImportParameters(receiverRSAKeyInfo); SHA1Managed hasher = new SHA1Managed(); byte[] bytes = penny.DataBytes(); byte[] hash = hasher.ComputeHash(bytes); RSAPKCS1SignatureDeformatter rsaSignComparer = new RSAPKCS1SignatureDeformatter(receiverRSA); rsaSignComparer.SetHashAlgorithm("SHA1"); bool verified = rsaSignComparer.VerifySignature(hash, penny.DigitalSignature); if (!verified) { reply.Note = "Invalid Penny, the signature doesn't check out"; result = false; } else { //finally ask the PennyBank to do the final validation ValidatePennyConversation conv = ((BalloonStore)Process).CommSubsystem.ConversationFactory.CreateFromConversationType<ValidatePennyConversation>(); conv.TargetEndPoint = ((BalloonStore)Process).PennyBankEndPoint; conv.Parent = this; conv.Pennies = new Penny[] { penny }; conv.Execute(); if (!ValidatedByPennyBank) { reply.Note = "Invalid Penny, PennyBank said no"; result = false; } } } return result; }
public void BalloonStore_TestBuyBalloonConversation() { Communicator mockRegistry = new Communicator() { MinPort = 13000, MaxPort = 13050 }; mockRegistry.Start(); PublicEndPoint mockEp = new PublicEndPoint(String.Format("127.0.0.1:{0}", mockRegistry.Port)); balloonStore.MyProcessInfo.Status = ProcessInfo.StatusCode.Initializing; balloonStore.RegistryEndPoint = mockEp; balloonStore.GetConversation().Launch(); // launch a bogus conversation in order to get the balloon store endpoint Envelope env = mockRegistry.Receive(1000); IPEndPoint balloonStoreEp = env.IPEndPoint; Penny penny = new Penny() { Id = 1 }; // test not in game balloonStore.MyProcessInfo.Status = ProcessInfo.StatusCode.PlayingGame; env = new Envelope() { IPEndPoint = balloonStoreEp, Message = new BuyBalloonRequest() { ConvId = new MessageNumber() { Pid = 1, Seq = 1 }, MsgId = new MessageNumber() { Pid = 1, Seq = 1 }, Penny = penny } }; mockRegistry.Send(env); BalloonReply reply = mockRegistry.Receive(2000).ActualMessage as BalloonReply; Assert.That(reply.Balloon, Is.Null); Assert.That(reply.Success, Is.False); Assert.That(reply.Note, Is.StringContaining("You are not in the Game")); // test no balloons balloonStore.Players.Add(new GameProcessData() { ProcessId = 1 }); env = new Envelope() { IPEndPoint = balloonStoreEp, Message = new BuyBalloonRequest() { ConvId = new MessageNumber() { Pid = 1, Seq = 1 }, MsgId = new MessageNumber() { Pid = 1, Seq = 1 }, Penny = penny } }; mockRegistry.Send(env); reply = mockRegistry.Receive(2000).ActualMessage as BalloonReply; Assert.That(reply.Balloon, Is.Null); Assert.That(reply.Success, Is.False); Assert.That(reply.Note, Is.StringContaining("No balloons left in inventory")); // test cached penny balloonStore.CachedPennies.Add(penny); balloonStore.CreateBalloons(); env = new Envelope() { IPEndPoint = balloonStoreEp, Message = new BuyBalloonRequest() { ConvId = new MessageNumber() { Pid = 1, Seq = 1 }, MsgId = new MessageNumber() { Pid = 1, Seq = 1 }, Penny = penny } }; mockRegistry.Send(env); reply = mockRegistry.Receive(2000).ActualMessage as BalloonReply; Assert.That(reply.Balloon, Is.Null); Assert.That(reply.Success, Is.False); Assert.That(reply.Note, Is.StringContaining("Invalid Penny, I've seen this one before")); balloonStore.CachedPennies.Remove(penny); // test invalid signature RSACryptoServiceProvider rsa = new RSACryptoServiceProvider(); RSAParameters parameters = rsa.ExportParameters(false); balloonStore.PennyBankPublicKey = new PublicKey() { Exponent = parameters.Exponent, Modulus = parameters.Modulus }; penny.DigitalSignature = new byte[] { 1, 2, 3 }; env = new Envelope() { IPEndPoint = balloonStoreEp, Message = new BuyBalloonRequest() { ConvId = new MessageNumber() { Pid = 1, Seq = 1 }, MsgId = new MessageNumber() { Pid = 1, Seq = 1 }, Penny = penny } }; mockRegistry.Send(env); reply = mockRegistry.Receive(2000).ActualMessage as BalloonReply; Assert.That(reply.Balloon, Is.Null); Assert.That(reply.Success, Is.False); Assert.That(reply.Note, Is.StringContaining("Invalid Penny, the signature doesn't check out")); // test valid signature, but failed ValidatePennyConversation balloonStore.PennyBankEndPoint = balloonStore.RegistryEndPoint; RSAPKCS1SignatureFormatter rsaSigner = new RSAPKCS1SignatureFormatter(rsa); rsaSigner.SetHashAlgorithm("SHA1"); SHA1Managed hasher = new SHA1Managed(); byte[] bytes = penny.DataBytes(); byte[] hash = hasher.ComputeHash(bytes); penny.DigitalSignature = rsaSigner.CreateSignature(hash); env = new Envelope() { IPEndPoint = balloonStoreEp, Message = new BuyBalloonRequest() { ConvId = new MessageNumber() { Pid = 1, Seq = 1 }, MsgId = new MessageNumber() { Pid = 1, Seq = 1 }, Penny = penny } }; mockRegistry.Send(env); // balloon store would have started a PennyValidation conversation PennyValidation request = mockRegistry.Receive(2000).ActualMessage as PennyValidation; Assert.That(request.MarkAsUsedIfValid, Is.True); Assert.That(request.Pennies[0].Id, Is.EqualTo(penny.Id)); env = new Envelope() { IPEndPoint = balloonStoreEp, Message = new Reply() { ConvId = request.ConvId, MsgId = new MessageNumber() { Pid = 1, Seq = 1 }, Success = false } }; reply = mockRegistry.Receive(2000).ActualMessage as BalloonReply; Assert.That(reply.Balloon, Is.Null); Assert.That(reply.Success, Is.False); Assert.That(reply.Note, Is.StringContaining("Invalid Penny, PennyBank said no")); // test successful conversation env = new Envelope() { IPEndPoint = balloonStoreEp, Message = new BuyBalloonRequest() { ConvId = new MessageNumber() { Pid = 1, Seq = 1 }, MsgId = new MessageNumber() { Pid = 1, Seq = 1 }, Penny = penny } }; mockRegistry.Send(env); // balloon store would have started a PennyValidation conversation request = mockRegistry.Receive(500).ActualMessage as PennyValidation; Assert.That(request.MarkAsUsedIfValid, Is.True); Assert.That(request.Pennies[0].Id, Is.EqualTo(penny.Id)); env = new Envelope() { IPEndPoint = balloonStoreEp, Message = new Reply() { ConvId = request.ConvId, MsgId = new MessageNumber() { Pid = 1, Seq = 1 }, Success = true } }; mockRegistry.Send(env); reply = mockRegistry.Receive(2000).ActualMessage as BalloonReply; Assert.That(reply.Balloon, Is.Not.Null); Assert.That(reply.Success, Is.True); Assert.That(reply.Note, Is.StringContaining("Nice doing business")); Assert.That(balloonStore.CachedPennies.Any(p => p.Id == penny.Id), Is.True); mockRegistry.Stop(); }