Hello there.

I'm a front-end developer passionate about cutting-edge technologies and impacting everyday lives. It's nice to meet you!

Trip Report: SEA-SJC, First Class on Delta CRJ-700

Lately, I’ve gotten myself into reading a lot of “trip reports”. I love traveling, but I don’t (yet?) have the ability to do so much. Well-written trip reports out there really have an ability to make a reader feel like they’re part of the experience. I vaguely remember trying to write one for my trip to San Francisco a few years ago. I’ve only taken maybe 2 long-haul flights in my life before then, and I was so excited to capture that experience and to share it with others. But alas, I left it somewhere in a draft folder that I can’t seem to find anymore, and never published it.

My work as a software engineer has me going from Seattle (where I’m based in) to the Bay Area quite often. Normally, I would have been taking advantage of the Elevate Gold status that I got from transferring points from my AmEx cards, and fly SEA-SFO in Virgin America (and I’ll be constantly comparing this trip with my previous trips on Virgin in this post). However, seeing that SPG was running a promotion with the Crossover Rewards program (where you can earn SPG points for flying with Delta) and offering 500 extra SPG points for trips with Delta, I was very tempted to give Delta a try. I love the purple mood lighting and all, but getting on a tiny regional jet could be interesting, too.

And so I decided to take the the flight DL 4766, which leaves Seattle at 4:15pm and arrives at San Jose at 6:24pm. In my opinion, the timing works a little bit better compared to VX 755, leaving SEA at 5:10pm and arriving at SFO at 7:10pm. The traffic is so much worse an hour later in the day in Seattle, and not being able to get to dinner at a reasonable time in the Bay Area is also a problem.

Picture taken from SJC during daybreak, on my return journey.

I arrived at the airport about an hour before. SEA, being one of my favorite airports in the States, was fairly empty. After getting through security, I started heading towards the Centurion Studio. My flight was leaving from gate B7, which is across the corridor from the Studio. I guess I could have checked out the Delta Sky Club, but that’s all the way in the satellite S building, and I decided that it’s probably not worth it.


I’ve been to only a few lounges in my life; and while this isn’t anywhere as luxurious like the Cathay Pacific Pier in Hong Kong, the Studio still makes a great place to relax and wait for a flight. The staff is friendly, and the snacks selection is… adequate. Really there isn’t much more I would be asking for before a regional flight.



I started to head for the gate 30 minutes before the departure time. The boarding process began promptly and I started making my way onto this tiny regional jet.


As a lone traveler onboard this 1-2 configuration first class cabin, I took seat 1A, one of the 3 single seats. Even as one of the first passengers to board, I had troubles finding a space in the overhead bins to put my duffle bag and backpack. Fortunately, the flight attendant offered to put my bag in one of the closets. There was a small bottle of water placed on every seat before we boarded, which is a nice touch.

The plane is an aged one – there’s no denying. The lack of in-flight entertainment is a little bit annoying, though I shouldn’t be complaining for such a short flight (That said, last time I flew to the Bay Area I was able to rewatch almost half of of Silicon Valley season 2 – what a great way to pass the time). The seat itself feels very wide – perhaps wider than Virgin’s first class product, but the legroom is a little limited for a first class seat. My legs could rest comfortably, but there was no room to stretch them out.


Shortly after all the first class passengers boarded, the attendant started offering welcome drinks. “Anything to drink?” Without any greetings like a “good evening”, or even a “hello”, she went straight to the important questions. “Um… ginger ale please,” I replied. I didn’t have a chance to confirm that she acknowledged my request before she already turned her back repeating the same questions to the passengers near me. Maybe she’s just swamped with pre-departure processes, or maybe the much-more-frequent business flyers on this route prefer the no-BS attitude. Meh, I thought.

Service & Food

The flight took off on time, and the in-flight services began promptly. The flight attendant started walking down the aisle asking for beverage choices, and whether you’d like a chicken wrap for dinner or not. Took me by a little surprise – I didn’t think a 4pm departure on a short flight would warrant a full meal. (Virgin America, if I recall correctly, doesn’t offer meal service on this route) “I’ll take it”, I answered, “with a Diet Coke please.”

Shortly after taking everybody’s orders, she started serving drinks, and brought me a tray with the chicken wrap. Along with the chicken wrap was a few celery and carrot sticks with a small bowl of hummus. Sadly, there isn’t an oven equipped on this tiny jet; otherwise, I’d have preferred a hot meal. (To be fair, neither does American offer a hot meal on a flight like this even when flying a 737).


My first impression was: what a beautiful tray it is! The plate is placed neatly on top (alongside with way too many advertisement for the catering company, but I can overlook that), and it has a premium-but-not-over-the-top feel to it. I really do appreciate the design. The wrap was surprisingly good, I have to say; I was half-expecting a dry and plain sandwich wrap, but it turned out to be fairly juicy and flavorful. I’ll take this over American’s half-effort chicken salad any day.

Service was overall attentive, with the attendant offering to refill drinks constantly. The attendant does seem to be in a better mood after the takeoff, and was a little more friendly to the passengers. I guess the earlier perceived rudeness was in fact just due to a busy pre-departure schedule.

We landed in SJC on time. The skies were clear (a welcome sight coming from Seattle in the winter); even from several thousand feet in the air, you can almost spot where all the tech giants’ campuses are.

Final thoughts

The tiny jet turned out to be a pleasure to fly in. The 1-2 configuration can be a tough contender to Virgin’s superior First Class seat, and having a hearty meal just might be able to put Delta on top.

(Wow, this trip report took almost a year and a half to finish. I intend to be have a much quicker turnaround with my future trip reports, I promise!)

Getting ready for Europe!

I started planning this trip since January, and I can’t wait to finally depart. In just a few weeks, I get to join my sister in celebrating her graduation from Leicester University. My entire family will be in Europe as well to attend her graduation ceremony, as well as doing some sightseeing in nearby European countries. It’s gonna be great.

Not to take anything away from Rebecca’s amazing achievement (really, no words can describe how proud I am for her), another reason for my excitement is that this marks the first trip I’m taking with the miles I got almost solely from a hobby called “churning”. Yup, after 2 years of collecting credit cards (and points/miles), I’m finally reaping the fruits. All long-distance flights in this trip are redeemed with points; I’ve considered spending points on the European regional flights, too, but it didn’t make too much sense since actual ticket prices in Europe are so low.

An Average of 4.63 cpp

While people generally have different ideas of what a “good” redemption looks like, I’ve always thought that any redemptions below 3 cents per point (cpp) are not worth it. The idea behind is that, the churning hobby fuels experiences that I otherwise would not consider shelling out real money for. Redeeming points for things with lower cpp value means the opportunity cost – not being able to try out experiences that can’t be bought with cash – is a lot higher.

Here is a list of my redemptions for this trip. Actual ticket prices are found by using Google Flights, selecting the cheapest ticket on the same airline around the time range (checking both one-way and round trip prices, dividing appropriately).

  • Virgin Atlantic LAX – LHR in J: 70,000 Delta miles; actual $2400, ~3.42 cpp
  • Singapore Airlines FRA – JFK in J: 68,000 Singapore KrisFlyer miles + $310 fees; actual $3000, ~3.96 cpp
  • Cathay Pacific JFK – YVR in F: 40,000 AsiaMiles; actual $3200, ~7.97 cpp
  • Delta YVR – SEA in Y: 5,000 Delta Miles; actual $200, ~4 cpp

The highlight here is probably the Cathay Pacific flight. At 7.97 cpp, It’s truly one of Cathay Pacific’s little known sweet spots. Only other routes that guarantee their famous international First Class product are long-haul and costs at least 100,000 Asia Miles (e.g., LAX – HKG).

Many Firsts

I’m also looking forward to many first experiences on this trip. Other than the SEA – LAX and YVR – SEA connection flights, I’ve never taken any airline-class combo like the ones I’m about to take before. In addition, I’ll get to ride an intercity train from Stockholm to Gothenburg that is managed by MTR, the company behind Hong Kong’s subway system. How awesome is that?!

With my new toy, a Sony a6500, we’ll be sure to take lots of pictures to document my journey!

Securing data and communication with RSA across Android app and Python server

I took my Algorithms class in USC with Prof. Leonard Adleman. Unsurprisingly, one of the biggest topics that we focused on in that class was RSA encryption. “Generate a pair of keys – you can encrypt by taking this exponent and mod that, decrypt by taking that exponent and mod this.” It all sounded too simple.

A few semesters later, I find myself needing to use a public-key encryption scheme. I’m trying to implement Spotify support for the playlist app I’ve been working on, QCast, for which we need to ask the user to input their Spotify credentials. Since a Spotify account involves some really sensitive details, and we might want to store the credentials locally on device (so that we can offer to automatically login for user), we thought we’d be a little smarter than just having plaintext username and passwords around. Vinnie argues: “https is enough!”; I’d generally agree for communication (though a quick Google search seems to suggest otherwise), but definitely not for storage.

In fact, for that matter, not just any encryption – it has to be asymmetric so that even if the key to encrypt is public and known to everyone, the encrypted data cannot be decrypted.

So I thought I’d implement RSA, thinking it can’t be that hard… right? What, import RSA, RSA.encrypt(key, message)? Hah, so I naively thought. Turns out, there are different ways of generating the keys, different ways of encrypting and decrypting. For the cherry on top, Java and Python use different ways of naming and setting up things. As a result, even though the final solutions that we have isn’t too complicated, we wasted a ton of time researching for the right way to do things. I hope to save ourselves and other people the troubles for doing all the research again in the future with a short guide.


  • Generate a pair of RSA keys
  • Encrypt using the public key in Android with the javax.crypto package (though it can be easily adapted to any Java project).
  • Decrypt using the private key in Python with PyCrypto*.

* M2Crypto is the hip alternative, but is not supported by Google App Engine.

Generating Keys

To generate, we use openssl, a widely-used security library.

First, let’s generate a private key.

This command will do the trick, outputting the generated key to the file private_key.pem. 2048 is the length of the key generated; the length is actually very important – turns out, you cannot encrypt a string that is longer than the size of the key.

From this private key, we can generate a public key.

This command takes in the private key from the file private_key.pem, generates a public key (with the -pubout flag) using the format der, and writes it to the file public_key.der. We encode the public key in der format and not pem format, because Java likes the base64-decoded version of the key (that’s pretty much the only difference, anyway – a good StackOverflow answer here on the different key formats).

Python, on the other hand is much more robust and can support the pem encoding just fine.

In your Android app, create a folder named raw inside [modulename]/src/main/res/, and put the public key there. It’s okay if the public key is “leaked” (hence the name public) – the encrypted data cannot be decrypted with the public key. In your python app, I would create a folder called ‘resources’ in root and put the private key there.

Encrypting Messages in Java

(Special thanks to David for his help in this part)

Before we start encrypting, we first need to read from the public key file, and generate a usable PublicKey object.

(The comment above X509EncodedKeySpec ... highlights my frustration – it is rather hard to get a clear answer to the different formats and encodings of keys, and I had to settle with a lot of trial and error.)

So now that we have the key ready to be used, we can set up the “encryption engine” and feed the key to it.

This part seems more straight froward (except with the weird syntax that Cipher uses). One line, in particular, deserves a little more attention, though.

Cipher.getInstance("RSA") – There are actually multiple ways of using RSA, including different kind of “padding” – it works kind of like a salt in a hash. This StackExchange post really helped me understand this concept:

The operation at the core of RSA is a modular exponentiation: given input m, compute m^e modulo n. Although in general this is a one-way permutation of integers modulo n, it does not fulfill all the characteristics needed for generic asymmetric encryption:

  • If e is small and m is small, then m^e could be smaller than n, at which point the modular exponentiation is no longer modular, and can be reverted efficiently.
  • The operation is deterministic, which allows for exhaustive search on the message: the attacker encrypts possible messages until a match is found with the actual encrypted message.
  • The modular exponentiation is malleable: given the “encryption” of m1 and m2, a simple multiplication yields the encryption of m1m2. This is akin to homomorphic encryption, which can be a good property, or not, depending on the context.

For these reasons, the integer m which is subject to RSA must not be the data to encrypt alone, but should be the result of a transform which ensures that m is “not small”, contains some random bytes, and deters malleability.

So while getInstance("RSA") can compute the RSA encryption, the string parameter can actually be overloaded with different options.

  1. Encryption algorithm to use – in our case, RSA.
  2. “Transformation mode” – doesn’t seem to have an effect on RSA encryption, so we’ll just set that to NONE. (I’d love to be corrected)
  3. Padding standard. Some brief research tells me that the one should be used these days seems to be OAEP Padding, with some hash function (usually SHA or MD5, by default SHA1) and mask generation function (by default MGF1).

Therefore, you might want to replace that string parameter with "RSA/NONE/OAEPWithSHA1AndMGF1Padding". (Why they decide to have parameters to be this really baffles me.)

The base64-encoded string is then stored in the variable encrypted, ready to be sent to the server for decryption.

Decrypting Messages in Python

Over on the server-side, we need to do something similar. Namely, read from the key file, set it up with the correct padding configuration, and decrypt. Fortunately, the code to do this is a lot simpler on Python.

First, make sure your dependencies are set correctly. In app.yaml of your Google App Engine project, make sure you specify you want to use the pycrypto project that is included with Google App Engine: (or, (sudo) pip install pycrypto for other projects)

Setting up in Python is actually extremely easy. All the modules we need come from the Cipher package. actually takes in multiple parameters; but since we decided to use the default OAEP settings (SHA1 and MGF1), nothing needs to be set explicitly. However, if you do change it, you might find this reference guide useful. For example, to use with the Java configuration of "RSA/NONE/OAEPWithSHA-512AndMGF1Padding", you would want to change to, SHA512).

Similarly, if you decide not to use OAEP Padding, then is completely unnecessary. Just use what RSA.importKey returns and that’ll do the trick.

Afterwards, we can decrypt very easily:

Ta da! The decrypted string is now in the decrypted variable.


As mentioned above, the amount of data we can use with RSA depends on the length of the key that we chose. If we generated our private key with length 2048 bits, the message cannot be more than 256 bytes, minus padding size.

The most common solutions I’ve found for encrypting long strings is a hybrid solution, combining both a symmetric encryption scheme (like AES) and RSA:

  1. Generate symmetric key, K
  2. Encrypt data with K
  3. Encrypt K, which we can guarantee length for, with RSA using the public key
  4. Send encrypted data, and the encrypted key to server
  5. Server decrypts the encrypted key with RSA using the private key, getting original K
  6. Decrypt with AES using K


The motivation for writing was that there didn’t seem to be a simple guide for cryptography beginners who want to implement some security in their app without having to dive into jargons that are way too complicated. I hope this guide served that purpose.

Please help me correct anything incorrectly explained in the post – I’m no expert and would definitely love to learn more.

(We are taking QCast to iOS too, so there might just be a post with encryption in iOS soon. )

The new

I liked my old Peetahzee’s Blog quite a lot. It was my first major web design project, and it took quite a few iterations to get it looking right. It looked good, well, in 2008 standards at least. Just to give some context – border-radius wasn’t yet a thing back then.

I gave it a facelift in 2011 – call it v1.5 if you will – when I entered college and realized that portfolio websites matter to recruiters. was no longer just a personal blog anymore, but more so a professional representation of me as a developer. I put some CSS tricks on (oh how I loved inset box-shadow), got a new portfolio site going, and hoped that it would last for a few more years.

Much blue. So color. Very roundedcorners. Wow.

Much blue. So color. Very roundedcorners. Wow.

But of course, in this industry, hardly anything ever lasts for more than a couple of years. I soon got jealous of all my friends’ fancy personal sites and thought, hey, I can do better with my own site. I was very happy with myself when I redesigned my new resume – it’s clean, has a very “refreshing” look, and (I believe) helps people focus on the right things through graphics and typography. I wonder what it would look on a website.

Based on that, there are a couple of things I wanted to do differently:

  • Less blue. It’s still my favorite color, but it seems that when the entire page is blue, it is quite distracting
  • Dominant “featured image” – an image to sum up the contents of the post
  • Focus of the page is on the content. Sidebar is much less important
  • Some place for a one-liner about myself
  • Always-on nav bar
  • Responsive design
  • Some sort of scrolling magic

Ah, about the scrolling magic – I didn’t want to just be like everybody else who gets a full-screen-image-parallax-thingy-majingy on there and call it a day. I thought about what I want the visitor to see on the instant they first visit the page, and what they’d want to see after spending some time. One obvious example would be my face. It can really help the visitor identify that it’s my blog, but it can also get in the way of the contents of the blog afterwards.

The entire redesign took me about two weeks from mockup to final implementation, and I’m quite pleased with the results. As a bonus, under this new design, I feel like I can have more flexibility when designing individual pages in the future (for example, the portfolio page.) I have some plans for that and can’t wait to get started on that.

Any comments or suggestions? I would love some feedback!

When a toddler is owning you…

Dang it im artistic.


Potato skins.

Openness: what I enjoyed most in Google

(Just got back from Hong Kong, and extremely jetlagged. Why am I up at 4 in the morning…)

As I step back onto the school grounds in a blazing 90`F LA weather, I couldn’t help but to think back that awesome summer I’ve had. The 10-week internship at Google flew by like a blink. I’ve made some great friends, met some amazing people in the company, and walked away learning a lot of new things I wouldn’t even have heard about otherwise.

It’s also time now to think about internships again. Lately, I’ve been picking up on reading in blogposts, articles, and journals about work experience at different companies. I seem to have found a trend in all these posts: innovation. This word appears everywhere. Don’t get me wrong – it’s an amazing vision and mission to have, one that I often share and enjoy working for; but when everybody has it on their profile pages, what makes them different?

In a lot of ways Google’s innovations exceed far beyond what others can even imagine: grand technologies like Google Glass and self-driving cars, and tiny details like empowering blind users through improving accessibility in their apps. But these weren’t what made me fall in love with working there; no, what did was the openness in its culture and empowerment of their employees – it’s an innovation in the workplace.

How so? Take this for an example. As a mere intern, I had access to almost the entire codebase of Google from every single project. Compare that to what I heard from my host, who has worked in Microsoft as a PM intern without any code access – not even those in his own project, this is pretty darn cool. With access to a codebase this big, there is no lack of resources for learning how to use certain technologies. I cannot think of a better way of encouraging Googlers to go outside their safety zones and learn from others.

Every Friday, employees and interns enjoy a very special treat from Sergey and Larry themselves in a company-wide all-hands meeting called TGIF. They’d present about a product Google has been working on, and then take questions from the audience – any question. When I was interning, there were questions ranging from “How is Larry doing?” (with regards to his illness) to “Why did we buy Meebo?” And with that, you can surely expect somebody from the exec team is going to give you a detailed and thorough answer, as if presenting the entire exec meeting to you. What’s more awesome is that the mic is even open for interns, and a lot of us have surely taken advantage of this opportunity. This has created an environment of trust, one that ensures employees of the most honest feedback from management and room for creativity.

This is extremely hard for any business to pull off. The amount of effort it requires the leadership team to be this open is extraordinary. But the effort isn’t unnoticed, as it has built an extremely unique and impressive culture all around the company. Undoubtedly, this, is what I enjoyed most in my time at Google.

(Disclaimer: I’m only speaking about what I’ve seen and heard during my internship, and in no way do I represent any of the companies mentioned here.)

Look, I have veggies on my plate!