Advertisements
//
archives

General

This category contains 6 posts

Amazon SimpleDB for Windows Phone Part 4 — TVMClient

Today I was made aware that in my series on Amazon SimpleDB for Windows Phone, I had mentioned that part 4 would contain some code samples and talk about encryption. I was confused for a second because I knew there was no part 4 and I thought the series was done. The series is not done. This is part 4 where we’ll show how to decrypt the credentials sent from the TVM. This one goes out to reader @jayborseth.

One thing to note is that since I started the series, an AWS SDK for Windows Phone has been released in beta form. The example I’m about to show does not use this SDK (and I don’t think the SDK has any work with the TVM).

As a refresher, the Token Vending Machine allows for an application to obtain temporary credentials for access to AWS services. In the anonymous way of obtaining credentials, there are 3 steps:

 

Step 1 – Register Device

Here is my code to register the device:

public void RegisterDevice(string deviceId, string tvmKey, Action callback)
{
    IDictionary parameters = new Dictionary();

    parameters["uid"] = deviceId;
    parameters["key"] = tvmKey;

    string queryString = GetParametersAsString(parameters);

    var uri = new Uri(string.Format("{0}/registerdevice?{1}", TvmUrl, queryString));

    var hwr = WebRequest.Create(uri) as HttpWebRequest;
    if (hwr != null)
    {
        hwr.Method = "GET";
    }

    TemporaryTokenPackage tokenPackage = new TemporaryTokenPackage() { DeviceId = deviceId, Request = hwr };

    AsyncCallback responseHandler = (async) =>
    {
        TemporaryTokenPackage temporaryTokenPackage = (TemporaryTokenPackage)async.AsyncState;
        HttpWebRequest request = temporaryTokenPackage.Request;

        HttpWebResponse response = null;
        try
        {
            response = (HttpWebResponse)request.EndGetResponse(async);
            callback(true, null);

        }
        catch (WebException we)
        {
            if (((HttpWebResponse)we.Response).StatusDescription == "Conflict")
            {
                callback(true, null);
            }
            else
            {
                callback(false, we);
            }

        }
    };

    hwr.BeginGetResponse(responseHandler, tokenPackage);
}

Couple of things. Here is my TemporaryTokenPackage which I used to pass pieces of data around the async handlers:

public class TemporaryTokenPackage
{
    public HttpWebRequest Request { get; set; }
    public string DeviceId { get; set; }
}

In the RegisterDevice method, we setup a typical request having a device ID and a tvm key. The tvm key is a Guid string without dashes. So,

tvmKey = Guid.NewGuid().ToString().Replace(“-“, “”);

You set the key and tell the TVM what key you are going to use using this call. The TVM will eventually use that key to encrypt the credentials.

The /registerdevice HTTP call doesn’t do much. If it returns, then you’ve successfully registered the device. If you get any error, you did not register the device and something is wrong. Try again.

Step 2 – Ask for Temporary Credentials

This is the part of the process where you ask the TVM for temporary credentials. If you already have a set of temporary credentials and they haven’t expired, you don’t need to ask for another set. Otherwise, get out your knee pads and beg. Here is how I did it:

public void GetTemporaryCredentials(string deviceId, string tvmKey, Action callback)
{
    string timeStamp = GetFormattedTimestampIso8601(0);
    string signature = Hmac2556Sign(timeStamp, tvmKey);

    IDictionary parameters = new Dictionary();
    parameters["uid"] = deviceId;
    parameters["timestamp"] = timeStamp;
    parameters["signature"] = signature;

    string queryString = GetParametersAsString(parameters);
    var uri = new Uri(string.Format("{0}/gettoken?{1}", TvmUrl, queryString));

    var hwr = WebRequest.Create(uri) as HttpWebRequest;
    AsyncCallback responseHandler = async =>
    {
        var request = (HttpWebRequest)async.AsyncState;

        HttpWebResponse response = null;
        try
        {
            response = (HttpWebResponse) request.EndGetResponse(async);

        }
        catch (WebException we)
        {
            callback(null, we);
        }

        if (IsGoodResponse(response))
        {
            Stream stream = response.GetResponseStream();
            var sr = new StreamReader(stream);

            var responseText = sr.ReadToEnd();
            sr.Close();

            var dataToDecrypt = Convert.FromBase64String(responseText);
            var plainText = Decrypt(dataToDecrypt, tvmKey);

            var temporaryCredentials = AwsTemporaryCredentialFactory.Create(plainText);

            callback(temporaryCredentials, null);

            return;

        }
        else
        {
            string responseText = "noresponse";

            if (response != null)
            {
                responseText = response.StatusCode.ToString();
            }

            callback(null, new WebException("Bad web response, StatusCode=" + responseText));

        }
    };

    hwr.BeginGetResponse(responseHandler, hwr);
}

Step 3 – Decrypt Your New Creds

This is where, as they say on MTV Cribs, the magic happens. Note that at the start of this method, we’re calculating the signature. This is what we talked about in Part 1. The bulk of this method is setting up the async HTTP call and dealing with the response. If we have a good HTTP response, we know that the TVM sent us something secret and we need to get it out. After encrypting the temporary credentials, the TVM will Base64 encode the result. So we need to decode that data using the .NET framework methods and we simply call Convert.FromBase64String which gives us a lovely byte array. Oh byte array.

This byte array is the raw encrypted credentials and we need to decrypt it. I pass into the Decrypt method the binary data to decrypt and the same tvm key that we used to register the device. Here’s the Decrypt method:

public string Decrypt(byte[] dataToDecrypt, string secretKey)
{
    var secretKeyToUse = DecodeHex(secretKey.ToCharArray());
    var cipher = CipherUtilities.GetCipher("AES/CBC/PKCS7Padding");

    cipher.Init(false, new KeyParameter(secretKeyToUse));

    int size =  cipher.GetOutputSize(dataToDecrypt.Length);
    var results = new byte[size];

    int olen = cipher.ProcessBytes(dataToDecrypt, 0, dataToDecrypt.Length, results, 0);
    cipher.DoFinal(results, olen);

    var result = Encoding.UTF8.GetString(results, 0, results.Length);

    return result;
}

///
<summary> /// Used to decode a plain text key into a key that can be used to decrypt. The data being passed in is assumed to be a
/// series of hex digits and this converts those 2-digit hex bytes into a single byte array.
/// </summary>
/// This is adapated from the org.apache.commons.codec.binary.Hex java source code at http://kickjava.com/src/org/apache/commons/codec/binary/Hex.java.htm
///
///An array of characters containing hex digits
/// A byte array containing the decoded hex data in binary format.
public static byte[] DecodeHex(char[] data)
{
    int len = data.Length;

    if ((len & 0x01) != 0)
    {
        throw new DataLengthException("Odd number of characters.");
    }

    var outresult = new byte[len >> 1];

    // two characters form the hex value.
    for (int i = 0, j = 0; j < len; i++)
    {
        var f = Convert.ToInt32(data[j++].ToString(), 16) << 4;
        f = f | Convert.ToInt32(data[j++].ToString(), 16);

        outresult[i] = (byte)(f & 0xFF);
    }

    return outresult;
}

Unfortunately, the key as we have it in memory is not what the TVM used to encrypt with. We need the DecodeHex method to rearrange the bits until we get a key that is the same format as was used to encrypt. Using this decoded key, we’re using the Bouncy Castle CipherUtilities to do the actual decryption. Assuming this works, we’ll end up with a UTF8 encoded string of decrypted data. But wait! There’s more!

Just having this string doesn’t give us everything. We need to split this packed string into a useable set of credentials. I used a factory to unpack this string into its constituent pieces of data (note that this is nearly identical to how the Andoid SDK sample did it):

public class AwsTemporaryCredentialFactory
{
    private const string AccessKeyName = "accessKey";
    private const string SecretKeyName = "secretKey";
    private const string SecurityTokenKeyName = "securityToken";
    private const string ExpirationDateKeyName = "expirationDate";

    ///
<summary> /// Used to create a set of temporary security credentials from the response provided by the
 /// Token Vending Machine.
 /// </summary>
    ///The response from the Token Vending Machine
    /// A set of temporary AWS credentials
    public static AwsTemporaryCredentials Create(string credentialString)
    {
        AwsTemporaryCredentials credentials = new AwsTemporaryCredentials
                                                  {
                                                      AccessKey = ExtractElement(credentialString, AccessKeyName),
                                                      SecretKey = ExtractElement(credentialString, SecretKeyName),
                                                      SecurityToken =
                                                          ExtractElement(credentialString, SecurityTokenKeyName),
                                                      ExpirationDate =
                                                          AwsTemporaryCredentials.GetExpirationTimeFromMilliseconds(
                                                              ExtractElement(credentialString,
                                                                             ExpirationDateKeyName))
                                                  };

        return credentials;

    }

    ///
<summary> /// Used to extract a piece of data from a json string.
 /// </summary>
    /// This is a C# port of the Java version written by Amazon.com
    ///The raw string to exctract the element from.
    ///the name of the piece of data to extract.
    /// The value of the exctracted element.
    private static String ExtractElement(String json, String element)
    {
        bool hasElement = (json.IndexOf(element) != -1);
        if (hasElement)
        {
            int elementIndex = json.IndexOf(element);
            int startIndex = json.IndexOf("\"", elementIndex);
            int endIndex = json.IndexOf("\"", startIndex + 1);

            return json.Substring(startIndex + 1, endIndex - (startIndex + 1));
        }

        return null;
    }
}

This retrieves a AwsTemporaryCredentials object which I’ve defined as:

public class AwsTemporaryCredentials
{
    public string AccessKey { get; set; }
    public string SecretKey { get; set; }
    public string SecurityToken { get; set; }
    public DateTime ExpirationDate { get; set; }

    ///
<summary> /// Handles converting milliseconds since 01-01-1970 into a useable DateTime.
 /// </summary>
    ///The number of milliseconds since 01-01-1970 as a string
    /// A DateTime that is 01-01-1970 plus the number of milliseconds
    public static DateTime GetExpirationTimeFromMilliseconds(string milliseconds)
    {
        long longMillseconds;
        Int64.TryParse(milliseconds, out longMillseconds);

        return GetExpirationTimeFromMilliseconds(longMillseconds);

    }

    ///
<summary> /// Handles converting milliseconds since 01-01-1970 into a useable DateTime.
 /// </summary>
    ///The number of milliseconds since 01-01-1970
    /// A DateTime that is 01-01-1970 plus the number of milliseconds
    public static DateTime GetExpirationTimeFromMilliseconds(long milliseconds)
    {
        var ticks = milliseconds*TimeSpan.TicksPerMillisecond;
        var correctedDate = new DateTime(1970, 1, 1).Add(new TimeSpan(ticks));
        return correctedDate;
    }

}

So there you go. 3 steps necessary to obtain temporary credentials from the Token Vending Machine (using the anonymous method). Once we have a set of temporary credentials, we can use those credentials just as we would with a “normal” set of credentials to utilize Amazon AWS services. Tired yet?

Questions? Comments? Confused? Drop a comment below if you need any help. Don’t forget to follow me on Twitter @kenstone.

Advertisements

Wrapping Up 2011

2011 was an indirect year for me. As usual, my initial direction skewed, but I found some goodness along the way. I ended up doing things I didn’t think I’d be doing and stopped doing some activities I thought I’d never stop. 2010 had been the year of relaxing and I told myself 2011 would be the year full of doing and it certainly lived up to that declaration.

Some achievements:

  • Joined Windows Phone developer program
  • Published 2 quality apps
  • Got my first eagle in golf *
  • Finished weight loss at 35 pounds
  • Attended Tech-Ed 2011 in Atlanta
  • Had my wisdom teeth pulled
  • Gained 10 pounds back
  • Designed, screen printed, and started selling my first t-shirt
  • Joined the Kindle Fire developer program
  • Started this blog
  • Stopped playing golf altogether
  • Read 2 books on drawing
  • Officially switched departments at work (technically early 2012, close enough)
  • Learned how to recognize a dangling participle

* Technically, it wasn’t really an eagle. I played two balls off the tee.

Favorite books of 2011

  • The Drunkard’s Walk: How Randomness Rules Our Lives by Leonard Mlodinow
    This helped me realize that so much in life is random and that the old saying of “If at first you don’t succeed, try, try again.” really has mathematical truth to it.
  • Vector Basic Training by Von Glitschka
    While my Photoshop skills were tolerable, I’ve been intimidated by Adobe Illustrator for some time. This book helped me get my feet wet and gain some confidence.

In all, my family is healthy and doing great, I’ve got a job I love, and I’m looking forward to the next year. Stay tuned to this blog in the coming year for more posts on Windows Phone and Windows 8 development, t-shirt fun, and who knows what else. Follow me on twitter (@kenstone) for more bits and pieces of my awesomeness. Also, follow my new blog, Trash My Stuff, where I’ll cronicle the sale and disposal of many of my unnecessary possessions.

image credit: flickr user · · · — — — · · ·

Why I Made the Switch From iPad to Kindle Fire

Are you here because you’re wondering if the Kindle Fire is a good replacement for an iPad? Are you wondering if your kid will be disappointed if he gets a Fire for Christmas instead of an iPad? Read on to see how I went from iPad to Fire.

My Existing Setup

To know where I’m coming from, it’ll be good to know what I was working with and how I used it on a daily basis. I have an iPad 2 3G with 32 GB. The 3G is not enabled. I do not use iTunes for music. I have been a Zune Pass subscriber since its launch. I have the $15/month plan that gives you unlimited music and 10 free MP3 tracks a month (this plan is no longer available for new subscribers).

On the iPad, the applications I used most frequently are (in order of most to least often):

  • Flipboard
  • Email (Exchange and Gmail)
  • Web browsing
  • Words with Friends
  • WordPress (for checking blog statistics and comment moderation. Never for creation)
  • Huffington Post
  • CNBC
  • DC Comics
  • Netflix
  • Goodreads

These are apps I used frequently. Any app that was only used maybe once a week or less is not included here.

I already have a Kindle 2 that I use for all of my ebook reading. From a mindset point of view, I’m the kind of guy who likes to try out what’s new. Tuesday’s are one of my favorite days of the week because new music, dvds, and video games are released on Tuesday. Thus why I have a Zune pass. I get to check out all the new music legally without having to go broke. The iPad was never a laptop replacement for me and I seriously doubt is for many people. It’s a content consumption device, just like the Fire. And the Fire consumes it well.

The Kindle Fire

I preordered the Fire on the day it was announced. As a friend said, if I didn’t like it, I could just return it. When it first arrived, I was a little underwhelmed. Animations felt choppy, finger presses didn’t seem to register all the time while at other times seemed too sensitive. I tried reading a book and my eyes started to hurt. I was disheartened because I wanted this to be a great device.

So I told myself to put the iPad away and use the Kindle Fire. I stopped bringing the iPad to work and only toted the Fire and my Windows Phone with me (along with my laptop).

Email

One of the first things I did was setting up email. I knew that it didn’t support Exchange ActiveSync for my corporate email, so I setup my Gmail account. This was where the first annoyance came in. I told the app that I was a Gmail subscriber and it asked for my email address. The thing is, I am a Google Apps subscriber, so my email address does not end in @gmail.com. This apparently wasn’t what the Fire wanted to hear. Instead, I was subject to a standard IMAP setup process asking for IMAP servers, SMTP servers, etc. Even though a Google Apps Gmail account has all the same information as a standard Gmail account. Put simply, this process was no fun. All other devices I’ve used have had no problem with the nonstandard Gmail address. I’ll put this down as a bug and hope that it gets fixed.

Web Browsing

Truthfully, I didn’t notice any superior browsing performance with the Silk browser. In fact, I felt the device performed better after I turned Silk off. So that’s where I stand on it. Turn Silk off. Browsing felt as fast as it did on the iPad. A big advantage the iPad has is the smoothness of the interface. Everything flows very well. This is an area where the Fire hopefully can improve through software updates. Scrolling in particular felt a tad jerky. Initially I had problems with getting it to recognize certain taps with my fingers. Once I got used to the Fire over a few days, these odd tap issues disappeared. Did the device fix itself? No. Did I change? Most likely.

I discovered that my finger muscles were interacting with the device as they were with the iPad and once they learned a new skill, I had no issues. I’m sure it could be said that a device shouldn’t make a user have to change in order to use it. And there’s some truth to that. But I think it’s like when I drive my wife’s car. Initially the gas pedal seems really jumpy and sensitive. But after a few minutes/hours, I can drive it with none of those issues. That being said, there are software interface issues where the app developers need to provide more feedback about ongoing activity.

Reading Books

I’ve been a Kindle user for a few years now and I love e-ink. Really, I love it a lot. I have never liked reading articles on LCD screens. I still receive a physical copy of the Wall Street Journal every day because I like reading the paper. I do not read it online even though I could.

I never really read books on the iPad. I’ve always felt that the iPad was too big and unwieldy to use reading books at night in bed. Perhaps this is because I’m used to reading my Kindle 2 in bed. That device is a perfect size. It can be held in one hand while still being able to flip pages. The Kindle Fire has the form factor right. The size is perfect, although a little thinner would be nice. I much prefer the rubberized backing. The iPad’s aluminum backing can be cold and harsh early in the morning or late at night. But my first reading experience was met with pain.

My eyes really started to hurt. After speaking with a coworker, however, I realized that part of the problem is that the font size was too small. I’m not old, but I’m not young. I’ve noticed over the past few years having to increase the font size on my computer for web browsing. I had to do the same thing on the Fire. I incresed the font size twice and almost immediately, my eyes felt better. I also realized I had the brightness up way too high. I was, again, used to the iPad that changes the brightness depending on ambient light. That’s super convienent and is part of the price premium you pay for. So once i decreased the brightness, everything seemed a lot nicer. If all you want a Kindle for is reading, get the Kindle Touch. But if you want more, that’s where the Fire comes in.

Apps

If you ever hear someone say that the Fire isn’t a tablet, don’t listen to them. It absolutely is a tablet and the apps are what makes it so. Go back and look at the list of apps I used on the iPad. Now compare that to what’s available on the Fire. The only thing missing is Flipboard, WordPress, and CNBC. I replaced Flipboard with the cross-platform reading tool Pulse. I used Pulse on the iPad as my secondary reading tool, but it has now become my primary. WordPress is missing a native app, but as I was only using it for statistics, no big loss. CNBC. Sure that’s missing. Then again, I only used the CNBC app to track my stocks performance on a daily basis; easily replicated in a different app. There is no DC Comics app, per se, but the ComiXology app is free and serves the same purpose. I can view comics pane by pane and have all my comics at my fingertips, exactly like I want to.

The Amazon App Store also has a free paid app every day and I’ve used this to get some nice games and apps for free. While I feel bad that the developer isn’t getting paid for those apps, you should take advantage of them too.

Media

I am an Amazon Prime subscriber, so I have access to the free Instant Streaming videos on the device. And they play back very well, what few there are. There’s also the Amazon Cloud Player. Since I use Zune, my entire collection is not available to me on the Fire, but since I have the plan that lets me have 10 free MP3 tracks a month, I have a decent amount of favorite music in MP3 format. I put those tracks on the Amazon Cloud Player and they’re immediately available to you by pressing the Music tab. Which brings me to the issue of device storage.

One of the supposed dings against the device is that there is only 8 GB of non-expandable memory. What the people that make this ding forget is that, for the most part, you won’t need to put anything on the device. Your music would just go on the cloud player and stream right from there. As long as you have wifi, there’s no problem. Lets say you’re going to be on a plane or car ride for a number of hours. Just download what you want for that duration of time and you’re set. The same goes for movies. Just stream whatever you want from the cloud. If you’re using Netflix, there’s no difference. You’ve always needed network connectivity for Netflix. As an example, I still have 6 GB free.

Miscellaneous

Other dings against the Fire are its lack of camera, gps, and bluetooth. But lets be honest, the cameras on the iPad are not that good. Flickr statistics show that not many people use those cameras for pictures. The forward camera, ostensibly for FaceTime, is really garbage. GPS is only available on 3G iPads, not the WiFi only model, so they’re even there. And Bluetooth…well, I never used it.

Conclusion

People who already have an iPad probably won’t want to switch. That’s fine. But if you don’t already have one, the Fire is a superb alternative. Look at it this way. The Fire costs $199. The iPad 2 baseline is $499. Is the iPad 2.5 times better than the Fire? No. No way it is 2.5 times better. Will your kid who’s expecting an iPad for Christmas be disappointed with a Fire? Probably. But he’ll get over it and with the money you saved, you can get a Fire too!

Holy Lame Apple Product Announcement Batman!

You really did it to yourself, Apple. I suppose it’s not that surprising. You did really well time after time and we all just assumed you would do well again. But this time, there was (implicit) overpromise, under deliver. Maybe if you hadn’t announced most of this stuff at WWDC just months ago, we’d be impressed. Actually, you should have introduced this stuff at WWDC and just never done this announcement.

How Many Megahertz?

Why exactly do we suddenly care about the technical specs of the phone? Dual core? Fancy lens? My mom isn’t ever going to care about that. The 8 MP camera might interest her. So the iPhone 4S has the same chip as the iPad 2. Big deal. Whenever anyone asks me about my iPad 2, I never get asked, “Does that have the dual core A5 chip in it?” No, they want to feel how light it is, how thin it is and does it play Angry Birds. The iPhone 4S looks exactly like the iPhone 4 and that’s what makes it lame. Fine, you’ve got dual antennas now. Way to fix a problem a year later. You couldn’t just admit that you made a mistake there and redesigned it? No, you had to go and make it more complicated. Wasn’t it Einstein who said:

“Make everything as simple as possible, but not simpler.”  *

Pfft. Einstein. Who cares. Put two antennas in there!!! You know, you could just not make the band around the phone the antenna. Sometimes the road less travelled is less travelled for a reason. Seinfeld said that.

Wait. Apple is a Greeting Card Company Now?

There was an Onion article that spoofed how new Apple CEO Tim Cook wanted to focus on printers. Who knew how close to the truth that was. Since you didn’t really have much new in the software department to show us, we got to see your oh so awesome greeting card app. Now my friends and family can see just how thoughtful I am. I cared enough to click a button on my phone. Really? Where’s Seth Meyers? Also, that was super sweet when you introduced Google Latitude. I mean Glympse. I mean Find my Friends.

And thanks for those helpful statistics on Lion. 10% of your user base in 2 weeks and Windows 7 took a whole month?? Seriously, what intern did you have working on that slide deck? You know, Windows 8 had 10% of your Lion user base downloaded in less than 12 hours. And it’s not even released. Interesting stat? Nope. Neither was yours. Besides, Lion was a service pack. Not a new OS. Firefox much?

What Did I Like?

Actually, I liked the $99 AppleCare+ announcement. If it were for a MacBook. So I have to pay $99 just for the chance to spend ANOTHER $49 to get my phone fixed if I drop it? Amazing. You’re going to convince people to spend $199 on a 16 GB phone on contract, and then another 50% for the possibility of getting a refurb one when they inevitably drop it. It’s a phone. It’s not a collector’s item. If you drop it, just wait 12 months until Apple releases a new one. This is a page right out of Best Buy’s playbook.

Now You Too Can Be a 4 Year Old Girl

Oh sweet! A Mickey Mouse watch face for my iPod nano. Awesome. I’m gonna go get laid right now. Hold my fanny pack?

It’s too little and quite lame in comparison to the Windows Phone Mango rollout that just preceded you. Remember when everyone had a PlayStation?

* This is the classic paraphrase of Einstein. Someone said it simpler than he did. How meta.

Amazon Cloud Player on Windows Phone Mango

Wilco, yo.In preparation for the Kindle Fire, I uploaded what MP3s I do have to the Kindle Cloud Player. Playing these on IE9 in Windows Phone Mango does work. The UI is a little slow and for some reason the volume on mine was crazy quiet. External amplification was needed. It’s a good example of the background audio features of Windows Phone Mango. So who do I have to talk to in order to get that volume issue solved?

Not Tumblr

Here’s the truth: before I started this blog, I started a tumblr. The tumblr is nice and I’m still going to use it for certain things (maybe). It’s just that most folks there seem to be content curators, not necessarily content creators. When I started it, I had in mind being a content creator. But as the weeks went on, I felt more out-of-place with “long form” posts (and they’re not very long). I realized that what I really wanted what something more standalone where I could write extended posts about whatever I wanted. Inevitably, what I wanted to write about was not the specific topic I decided on for the tumblr.

The tumblr isn’t the first blog I’ve had either. I had one at least 4 years ago that I shut down because of trolls and lack of interest. Unfortunately, I am still related to one troll in particular, so I’m going to stay semi-anonymous as long as I can. For now, I expect this blog to be a little random until I find the voice. Here’s are topics I expect to see:

  • Programming (Windows Phone 7, Windows 8, .NET, etc.)
  • Graphic Design
  • Menswear
  • Golf
  • T-Shirts
  • Etc.

By definition, some of this will be narcissistic, and that’s okay. Hopefully, though, you’ll find a post that helps you do something, or at a minimum, interests you for a minute or two. I’m doing it for me and that’s what matters.

Its easy enough to just not create a blog and I’ve done that. There’s twitter and I do that too (see the sidebar). I’ve quit Facebook for the same reason I quit the original blog. But I need to work on my writing and I’m looking for a place to put some content so I can get it out of my head. I need a place to shill my wares, whether those wares are virtual or physical.

Despite the title of the blog, I hope this will be whimsical and easy-going. Dare I say delightful? That’s who I am underneath the veneers. So let’s see how it goes.

Advertisements

I'm Ken Stone. I'm an indie developer with a focus on .NET and Windows Phone development. I have a day job, but everything I say is mine alone.

TechEd 2012July 11th, 2012
Let the learning begin.