Friday, 2 July 2010

How I landed a high-paying new job

I am happy to announce that my business card now reads "Senior Web Security Engineer"!

It's been about a month since my interview now, and my new employer (kept anonymous for oblivious reasons) have kindly given me permission to blog about it given that I anonymized it all. Since a few of you are probably out hunting for new jobs at the moment - and who wouldn't in this economy - keep reading for some pointers on how to pass a job interview.

The resumé and application

These are straightforward, so I won't go into detail. Just remember to put the prospective employer's name all over the application, proofread twice, and use glossy paper for the cover letter. Bonus points for delivering in person. If applicable, flirt heavily with whomever is receiving the application - getting someone on your good side can only help you.

I was lucky here, as the HR lady was obviously impressed by my shark tooth ear studs. She even called me up the same afternoon! After answering a few simple math questions to show I wasn't completely useless (this is called a "phone screening", but I think "phony screening" would be a funnier name), I was invited to...

The interview

I had a couple of days to prepare for this, so I had time to hit the sun parlor a few times and even found the time to get my teeth bleached. I also memorized the current "hot words" in cryptology in the evenings, and reimplemented the "rot thirteen" cipher a million times until I could write the C# code blindfolded, doing my best to stay sharp.

The first part of the interview was supposed to be with me, my prospective manager Bob (not his real name of course - it's just an alibi), and the HR lead Cyril (likewise not his real name). However, since Bob got unexpectedly ill that day and couldn't come, it was just me and Cyril. Cyril had little to no programming knowledge, but Bob had been thoughtful enough to fax him with a list of techincal questions. The list was quite long, and I spent about half an hour talking about the different cryptology disciples; cryptography, ciphering, stenography, cracking, then a bit about security mechanisms like web code obfuscation, reverse engineering, litigation and firewalls. To top it off, I went on quite a bit about my experience as a team leader and my thoughts on keeping a team efficient. All in all I did pretty good, and Cyril was very impressed by my broad knowledge. He gave me the green flag, and after a pleasant free lunch I was on to the last part...

The programming test

Oh my World this was unexpectedly harder than I thought it would be. Here I was expecting to be given a variant of the fizz test, but instead I was tasked to implement a complete cipher program from scratch in only two hours! Fortunately for you, my company has replaced that specific test with a new one now, so I'm allowed to post the entire thing here!

You have TWO HOURS for this excercise. After completion, you must print out your
solution, sign your name in the top right corner of every page, get a stamp of
authenticity from HR, and deliver it to your manager.

You will work with a well-known encryption procedure. This is called a substitution
cipher, in which each letter of the original message (the "plaintext") is replaced
by a different letter to generate the coded message (the "ciphertext").

To simplify matters further, we will restrict our plaintext to use only the upper
case letters A to Z, together with the space and newline characters; furthermore,
space and newline characters will be left unchanged by the encryption.

In each case, the replacement letter will be a fixed number of letters later in the
alphabet compared to the original letter (and "wrapping around" back to A again
after Z, as necessary). The number of letters to count forward will be called the
encryption key.

Thus, with a key of 3, we would replace A by D, B by E and so on, with, finally, W
being replaced by Z, X by A, Y by B and Z by C.

This simple kind of cipher is sometimes called a "Caesar Cipher" after Julius
Caesar, who is said to have used it for secure battlefield messages.

You are required to develop a program which will input a series of lines of
characters, and encode them with a Caesar cipher; as each line of plaintext is
input, the corresponding line of ciphertext should be output. It is up to you to
devise a mechanism whereby the user can indicate that all lines have been input,
and to implement the program in such a way that it will then terminate.

The key for the encoding should be "hard coded" into your program (as opposed to be
being read in at run time). Thus, changing the key will involve modifying the
source program and recompiling.

You may find it handy to be able to run your program on an input file (and
producing an output file) rather than working only with the keyboard and screen.
Use command line redirection to achieve this.

A command line gui? What is this, the 1970-ies? (A side note: This was the main reason I later persuaded them to replace the test with a new one.)

And the algorithm was pretty muddily explained. Replace X, Y corresponding with A, B and whatnot? That couldn't possibly be any less unclear, couldn't it not? Not giving up hope, I read the text carefully over and over again until finally it hit me: This is just a less general version of my Rot Thirteen cipher! I was in luck. After quickly typing in the memorized code, I had about an hour to figure out how to get the blooming thing to work in a terminal and add some polish. I got time to add some XML in there and even add a few design patterns. I was, and still am, pretty pleased with the end result:

using System;

namespace ConsoleApplication1 {
    // Factory pattern
    class CesarFactory {
        private int key;

        public CesarFactory(int key) {
            this.key = key;
        }

        public ICipher Instantiate() {
            return new CesarAdapter();
        }
    }

    // Interface pattern
    interface ICipher {
        string Encipher(string plaintext);
    }

    // Adapter pattern
    class CesarAdapter : ICipher {
        string ICipher.Encipher(string plaintext) {
            return Cesar.Encode(plaintext);
        }
    }

    // Cesar cipher
    class Cesar {
        public static string Encode(string input) {
            char[] chars = input.ToCharArray();
            for (int i = 0; i < input.Length; i++) {
                int lowerRotation = Rotate(chars[i] - 'a', 3, 26);
                int upperRotation = Rotate(chars[i] - 'A', 3, 26);
                if (chars[i] > 'a') chars[i] =
                    (char)('a' + lowerRotation);
                else if (chars[i] > 'A') chars[i] =
                    (char)('A' + upperRotation);
            }
            return new string(chars);
        }

        private static int Rotate(int c, int delta, int max) {
            c += delta;
            while (c > max) c -= max;
            return c;
        }
    }

    // Entry point.
    class Program {
        // Command line gui.
        static void Main(string[] args) {
            // Create Cesar factory
            CesarFactory factory = new CesarFactory(3);
            // Xml header
            Console.Out.Write("<XML version=\"1.0\"><Cesar>");
            // Read as long as there is something in the input buffer.
            while (Console.BufferHeight > 0) {
                // Read from Console.
                int x = Console.Read();
                // Encode input as string.
                string plaintext = new string(new char[]{(char)x});
                // Get cipher.
                ICipher cesar = factory.Instantiate();
                // Encipher plaintext.
                string ciphertext = cesar.Encipher(plaintext);
                // Output ciphertext to Console.
                Console.Write(ciphertext);
            }
            // Xml footer
            Console.Out.Write("</XML></Cesar>");
        }
    }
}

At the 1 hour 56 minute mark I sent my submission to the printer, got it stamped by HR, signed it, and had it faxed home to Bob for review. Exhausted but hopeful, I was now free to go and relax while the review took its course.

A few days later, the phone call I had been waiting so anxiously for finally came. I was accepted as Senior Web Security Engineer for a big bank, earning over £60k a year!

All it took was dedication, commitment, and a sense of purpose. And let's not be modest anymore (job interview tip here!) - a heavy dose of talent. How awesome is that?

28 comments:

  1. You mismatched the xml tags in the end.

    ReplyDelete
  2. @Anonymous: Thanks for the point-out! However, it is trivial to write a program that can parse this XML, so it is not a big issue.

    ReplyDelete
  3. I can't tell if you're trolling or not... "Shark tooth ear studs" and "time to hit the sun parlor a few times and even found the time to get my teeth bleached."? What is this Jersey Shore?

    Also, Caesar Cipher is so common; it's the first thing taught in any cryptography class....

    That technical interview should be wildly easy for anyone even being considered for "Senior Web Security Engineer"

    ReplyDelete
  4. @anonymous#2: I think it is sad that caring about your exterior is such a taboo in engineering disciplines.

    Even if you don't specifically care how you look, it is important to realize that when you're job hunting, you, your application and your resumé are usually passing through several layers of HR people. These HR people have no basis to jugde your true technical competence; they jugde you by a general impression. And, like it or not, your physical appearance is a big part of that impression.

    Also, the techincal interview was all in all pretty easy; it was just tougher than I have come to expect. ;-)

    ReplyDelete
  5. You apparently don't understand how your own blog works; you tagged this with one giant tag instead of a bunch of individual ones, and spelled several of the words wrong ("cesar"? "cryphology"?)

    ReplyDelete
  6. module Main where

    caeserify :: Int -> String -> String
    caeserify n input = map xlate input
        where
          -- convert a character from one encoding to the other
          xlate :: Char -> Char
          xlate c = toEnum $ xlate' (fromEnum c)

          -- xlate' adds n to the character code if it is alphabetic
          xlate' :: Int -> Int
          xlate' c | c >= c_A && c <= c_Z = (c - c_A + n) `mod` 26 + c_A
          xlate' c | otherwise                       = c

          (c_A, c_Z) = (fromEnum 'A', fromEnum 'Z')

    -- run the Caeser cypher against standard input
    main = interact $ caeserify 3

    ReplyDelete
  7. @Daniel That's terrible -- it doesn't use enough design patterns *or* output malformed XML

    ReplyDelete
  8. module Main where

    import System.IO
    import qualified Data.Map as Map

    key :: Int
    key = 3

    main :: IO ()
    main = interact rotn

    rotn :: String -> String
    rotn = map replace
     where
      chars = ['A'..'Z']
      submap = Map.fromList . zip chars . take 26 . drop key . cycle $ chars
      replace c = case Map.lookup c submap of
       Just c' -> c'
       Nothing -> c

    ReplyDelete
  9. lol this is sad

    ReplyDelete
  10. @Rodrigo: nice code! Also, you could use findWithDefault c instead of the case/lookup expression, save a couple lines.

    ReplyDelete
  11. Albert: the "XML" you output isn't valid XML at all. It would be trivial to write a parser for it, but no XML parser would take it:


    $ xmllint cesar.xml
    cesar.xml:3: parser error : Opening and ending tag mismatch: Cesar line 1 and XML
    </XML></Cesar>
    ^
    cesar.xml:3: parser error : Opening and ending tag mismatch: XML line 1 and Cesar
    </XML></Cesar>

    You meant to do this:

    <?xml version="1.0">
    <caesar>...</caesar>

    ReplyDelete
  12. Ugh, I left out the ? at the end of the processing instruction. Should say: <?xml version="1.0"?>

    ReplyDelete
  13. @anonymous#3: Thanks for pointing out the tag problem! Serves me right for posting late at night - stupid mistakes happen. :-/

    @Daniel: Interesting code! Is that F#?

    ReplyDelete
  14. Nope, it's Haskell. Same with Rodrigo.

    ReplyDelete
  15. $R=-4;while(<>){s/[^ \nA-Z]//g;s/[A-Z]/chr(ord('A')+((ord($&)+$R-ord('A'))%26))/ge;print}

    ReplyDelete
  16. @Daniel Thank you, I forgot about findWithDefault. I guess I need to re-read the Haskell API :)

    ReplyDelete
  17. $ tr 'A-Z' 'D-ZA-C' <input >output

    ReplyDelete
  18. That's for a key of 3, of course.

    ReplyDelete
  19. Not quite as good as Bill's code which uses the venerable UnixTranslateUtilityAdaptor design pattern, but still:

    #include

    #define OFFSET 3

    main() {
    char ch;

    while(scanf("%c", &ch) != EOF) {
    if(ch >= 'A' && ch <= 'Z') {
    ch += OFFSET;
    if(ch > 'Z') ch -= 'Z' - 'A' + 1;
    printf("%c", ch);
    }
    }
    }

    ReplyDelete
  20. What bank do you work at now? Can you say? I want to keep my money the fuck out of there.

    ReplyDelete
  21. @Calvin Spealman said...

    What bank do you work at now? Can you say? I want to keep my money the fuck out of there.

    Unfortunately, people like this work at all major banks.

    ReplyDelete
  22. why can't you faggots learn to use % instead of branches and shit.

    fucking c = (c + key)%26;

    I don't get why % is so hard for most programmers to learn how to use.

    ReplyDelete
  23. @Calvin: I can assure you, security at is far above the national standard. Also, you appear to be from the US, while I work in UK. :)

    @Anonymous: Please, keep a civil language. We are all professionals here. The modulo operator, or % as you call it, does a full division internally. Division uses significantly more cycles than branching.

    ReplyDelete
  24. In my shell: "tr a-z c-za-b".
    But that took me five hours to write it. And it does not do a fuck of xml. Maybe I should read the manual page of sed. Fucking GNU.

    ReplyDelete
  25. Good thing they didn't ding you for misspelling "Caesar" over and over in your source code.

    ReplyDelete
  26. Based on the code you posted, I personally would probably not hire you. Patterns are not hard, and showing off that you read a book about them does not impress me. Knowing when it is appropriate to use a pattern is a valuable skill, a skill that your 60%-useless-boilerplate code does not indicate that you have.

    ReplyDelete
  27. input = 'PLAINTEXT'
    d = dict( (a, b) for a, b in zip('ABCDEFGHIJKLMNOPQRSTUVWXYZ', 'DEFGHIJKLMNOPQRSTUVWXYZABC') )
    print ''.join(map(lambda e:d[e], input))

    ReplyDelete
  28. Run like hell!! No "senior web security expert" would ever write their own security algo. And as an interview exercise? WTF. I would rather my bank hire experts based on knowledge of XSS, phishing detection, password security, firewalls, etc.

    ReplyDelete