views:

221

answers:

1

Hi, currently I'm interfacing the Twitter API using the OAuth protocol and writing the code in Python. As most of the users out there, I think the toughest part of the specs is dealing with signatures.

After wandering around the web in search for a solution, I decided to go for my custom code, so as to have a better understanding of what is going on.

For the sake of other users, I'm posting here a very simple and short implementation of the SHA1 signature specs in Python:

import hmac

from hashlib import sha1
from urllib import quote, urlencode
from base64 import b64encode
from urlparse import urlparse

def sign_request_sha1(url,method,data,secret=""):
  pu = urlparse(urlparse(url).geturl())

  normUrl = "%s://%s%s%s" % (
      pu.scheme,
      pu.hostname,
      "" if not pu.port or {"http":80,"https":443}[pu.scheme] == pu.port else ":%d" % pu.port,
      pu.path,
                            )

  names = data.keys()
  names.sort()

  sig = "%s&%s&%s" % (
          method.upper(),
          quote(normUrl,''),
          quote("&".join(["%s=%s" % (k,quote(data[k].encode('utf-8'),'')) for k in names]),''),
                     )

  key = "%s&%s" % (quote(CONSUMER_SECRET.encode('utf-8'),''),secret)

  return b64encode(hmac.new(key,sig,sha1).digest())

The input parameters to the function are:

  • url: the url you are going to call for the specific OAuth request.
  • method: this must be "GET" or "POST" depending on how you're going to issue your request.
  • data: a dictionary containing all the parameters of the requests, including any custom argument but excluding the "oauth_signature" one (for obvious reasons).
  • secret: a secret token you received in the initial phase of the protocol.

I tested it with Twitter and it seems to work but I'd like to receive some comments about mistakes, improvements and so on.

Lastly, here you find a piece of code calling the code for the initial "request token" phase:

from random import getrandbits
from base64 import b64encode
from time import time

def twitter_request_token(req,callback,errback):
  req_url="http://twitter.com:80/oauth/request_token"

  data = { \
    "oauth_consumer_key" : CONSUMER_KEY,
    "oauth_nonce" : b64encode("%0x" % getrandbits(256))[:32],
    "oauth_timestamp" : str(int(time())),
    "oauth_signature_method" : "HMAC-SHA1",
    "oauth_version" : "1.0",
    "oauth_callback" : "http://localhost:8080/",
         }

  data["oauth_signature"] = sign_request_sha1(req_url,"GET",data)

Thank you.

A: 

My knee-jerk reaction to this is If You're Typing The Letters A-E-S Into Your Code, You're Doing It Wrong. Or, as redditor khafra recently reminded us of the Sicilian's version:

Haha.. you fool! You fell victim to one of the classic blunders. The most famous is: Never get involved in a land war in Asia. But only slightly less famous is this: Never attempt to roll your own crypto when there's a well-tested library that'll do it better!

I mean, I get it. The first time I looked at it, oauth.py didn't impress me either. There's been a lot of work on it since and it's looking better, but there still appear to be no tests, so I don't know. Anyway, tests or no tests, it's been reviewed and used by more people than your code has.

But that's just me being uptight on the subject of crypto code reuse and doesn't really help you in figuring out the protocol machinery. It looks okay to me, but I haven't had my head in the OAuth spec too much lately.

Just use some more lines for that pu.port business; having a conditional if expression, an or expression, and the {}[] construct all in one line is really hard to read.

If you really want code review by people who are familiar with the protocol, you're probably better off asking the mailing list. And if you can offer them an alternate API that will make the code in their repository more appealing to new users, that'll be good for everyone.

keturn
Thank you for your answer. Here some points:- Generally speaking, I agree with you on not writing your own crypto library: too many caveats, too many details to get it right. In my case, we are not talking about OpenSSL, just a small piece of a protocol which USES crypto functions (namely MAC) and it's fairly short.- In no way my code is inteded as a replacement of the OAuth lib. Maybe, if it were more documented, it'd be easier to use and more wide spread.- About one liners: I think that the {}[] is far more readable than having the same code as an if split in two or more lines.
Cristiano Paris