summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorViktor Haag <ViktorHaag@users.noreply.github.com>2017-11-14 07:44:44 -0800
committerOmer Katz <omer.drow@gmail.com>2017-11-14 17:44:44 +0200
commitcfb82feb03fcd60b3b66ac09bf1b478cd5f11b7d (patch)
treec7c9a66b98d34d4e398cd92e8291d65b4b844d85
parentfa0b63cfaced831d8b916c5a125128f582acf044 (diff)
downloadoauthlib-cfb82feb03fcd60b3b66ac09bf1b478cd5f11b7d.tar.gz
Add support for HMAC-SHA256 (builds on PR#388) (#498)
* Add support for HMAC-SHA256 * Add explicit declaration of HMAC-SHA1 and point HMAC at it To avoid confusion, HMAC constant name should explicitly state which SHA variant is used, but for backwards compatibility, SIGNATURE_HMAC is still needed * add support for HMAC-SHA256 including tests and comments * constructor tests verify client built with correct signer method
-rw-r--r--oauthlib/oauth1/__init__.py2
-rw-r--r--oauthlib/oauth1/rfc5849/__init__.py11
-rw-r--r--oauthlib/oauth1/rfc5849/signature.py57
-rw-r--r--tests/oauth1/rfc5849/test_client.py40
4 files changed, 103 insertions, 7 deletions
diff --git a/oauthlib/oauth1/__init__.py b/oauthlib/oauth1/__init__.py
index f9dff74..dc908d4 100644
--- a/oauthlib/oauth1/__init__.py
+++ b/oauthlib/oauth1/__init__.py
@@ -9,7 +9,7 @@ and Server classes.
from __future__ import absolute_import, unicode_literals
from .rfc5849 import Client
-from .rfc5849 import SIGNATURE_HMAC, SIGNATURE_RSA, SIGNATURE_PLAINTEXT
+from .rfc5849 import SIGNATURE_HMAC, SIGNATURE_HMAC_SHA1, SIGNATURE_HMAC_SHA256, SIGNATURE_RSA, SIGNATURE_PLAINTEXT
from .rfc5849 import SIGNATURE_TYPE_AUTH_HEADER, SIGNATURE_TYPE_QUERY
from .rfc5849 import SIGNATURE_TYPE_BODY
from .rfc5849.request_validator import RequestValidator
diff --git a/oauthlib/oauth1/rfc5849/__init__.py b/oauthlib/oauth1/rfc5849/__init__.py
index 06902e2..f9113ab 100644
--- a/oauthlib/oauth1/rfc5849/__init__.py
+++ b/oauthlib/oauth1/rfc5849/__init__.py
@@ -27,10 +27,12 @@ from oauthlib.common import Request, urlencode, generate_nonce
from oauthlib.common import generate_timestamp, to_unicode
from . import parameters, signature
-SIGNATURE_HMAC = "HMAC-SHA1"
+SIGNATURE_HMAC_SHA1 = "HMAC-SHA1"
+SIGNATURE_HMAC_SHA256 = "HMAC-SHA256"
+SIGNATURE_HMAC = SIGNATURE_HMAC_SHA1
SIGNATURE_RSA = "RSA-SHA1"
SIGNATURE_PLAINTEXT = "PLAINTEXT"
-SIGNATURE_METHODS = (SIGNATURE_HMAC, SIGNATURE_RSA, SIGNATURE_PLAINTEXT)
+SIGNATURE_METHODS = (SIGNATURE_HMAC_SHA1, SIGNATURE_HMAC_SHA256, SIGNATURE_RSA, SIGNATURE_PLAINTEXT)
SIGNATURE_TYPE_AUTH_HEADER = 'AUTH_HEADER'
SIGNATURE_TYPE_QUERY = 'QUERY'
@@ -43,7 +45,8 @@ class Client(object):
"""A client used to sign OAuth 1.0 RFC 5849 requests."""
SIGNATURE_METHODS = {
- SIGNATURE_HMAC: signature.sign_hmac_sha1_with_client,
+ SIGNATURE_HMAC_SHA1: signature.sign_hmac_sha1_with_client,
+ SIGNATURE_HMAC_SHA256: signature.sign_hmac_sha256_with_client,
SIGNATURE_RSA: signature.sign_rsa_sha1_with_client,
SIGNATURE_PLAINTEXT: signature.sign_plaintext_with_client
}
@@ -57,7 +60,7 @@ class Client(object):
resource_owner_key=None,
resource_owner_secret=None,
callback_uri=None,
- signature_method=SIGNATURE_HMAC,
+ signature_method=SIGNATURE_HMAC_SHA1,
signature_type=SIGNATURE_TYPE_AUTH_HEADER,
rsa_key=None, verifier=None, realm=None,
encoding='utf-8', decoding=None,
diff --git a/oauthlib/oauth1/rfc5849/signature.py b/oauthlib/oauth1/rfc5849/signature.py
index 10d057f..30001ef 100644
--- a/oauthlib/oauth1/rfc5849/signature.py
+++ b/oauthlib/oauth1/rfc5849/signature.py
@@ -469,6 +469,63 @@ def sign_hmac_sha1(base_string, client_secret, resource_owner_secret):
# .. _`RFC2045, Section 6.8`: http://tools.ietf.org/html/rfc2045#section-6.8
return binascii.b2a_base64(signature.digest())[:-1].decode('utf-8')
+
+def sign_hmac_sha256_with_client(base_string, client):
+ return sign_hmac_sha256(base_string,
+ client.client_secret,
+ client.resource_owner_secret
+ )
+
+
+def sign_hmac_sha256(base_string, client_secret, resource_owner_secret):
+ """**HMAC-SHA256**
+
+ The "HMAC-SHA256" signature method uses the HMAC-SHA256 signature
+ algorithm as defined in `RFC4634`_::
+
+ digest = HMAC-SHA256 (key, text)
+
+ Per `section 3.4.2`_ of the spec.
+
+ .. _`RFC4634`: http://tools.ietf.org/html/rfc4634
+ .. _`section 3.4.2`: http://tools.ietf.org/html/rfc5849#section-3.4.2
+ """
+
+ # The HMAC-SHA256 function variables are used in following way:
+
+ # text is set to the value of the signature base string from
+ # `Section 3.4.1.1`_.
+ #
+ # .. _`Section 3.4.1.1`: http://tools.ietf.org/html/rfc5849#section-3.4.1.1
+ text = base_string
+
+ # key is set to the concatenated values of:
+ # 1. The client shared-secret, after being encoded (`Section 3.6`_).
+ #
+ # .. _`Section 3.6`: http://tools.ietf.org/html/rfc5849#section-3.6
+ key = utils.escape(client_secret or '')
+
+ # 2. An "&" character (ASCII code 38), which MUST be included
+ # even when either secret is empty.
+ key += '&'
+
+ # 3. The token shared-secret, after being encoded (`Section 3.6`_).
+ #
+ # .. _`Section 3.6`: http://tools.ietf.org/html/rfc5849#section-3.6
+ key += utils.escape(resource_owner_secret or '')
+
+ # FIXME: HMAC does not support unicode!
+ key_utf8 = key.encode('utf-8')
+ text_utf8 = text.encode('utf-8')
+ signature = hmac.new(key_utf8, text_utf8, hashlib.sha256)
+
+ # digest is used to set the value of the "oauth_signature" protocol
+ # parameter, after the result octet string is base64-encoded
+ # per `RFC2045, Section 6.8`.
+ #
+ # .. _`RFC2045, Section 6.8`: http://tools.ietf.org/html/rfc2045#section-6.8
+ return binascii.b2a_base64(signature.digest())[:-1].decode('utf-8')
+
_jwtrs1 = None
#jwt has some nice pycrypto/cryptography abstractions
diff --git a/tests/oauth1/rfc5849/test_client.py b/tests/oauth1/rfc5849/test_client.py
index dcb4c3d..777efc2 100644
--- a/tests/oauth1/rfc5849/test_client.py
+++ b/tests/oauth1/rfc5849/test_client.py
@@ -2,7 +2,8 @@
from __future__ import absolute_import, unicode_literals
from oauthlib.common import Request
-from oauthlib.oauth1 import (SIGNATURE_PLAINTEXT, SIGNATURE_RSA,
+from oauthlib.oauth1 import (SIGNATURE_PLAINTEXT, SIGNATURE_HMAC_SHA1,
+ SIGNATURE_HMAC_SHA256, SIGNATURE_RSA,
SIGNATURE_TYPE_BODY, SIGNATURE_TYPE_QUERY)
from oauthlib.oauth1.rfc5849 import Client, bytes_type
@@ -62,13 +63,48 @@ class ClientConstructorTests(TestCase):
self.assertIsInstance(k, bytes_type)
self.assertIsInstance(v, bytes_type)
+ def test_hmac_sha1(self):
+ client = Client('client_key')
+ # instance is using the correct signer method
+ self.assertEqual(Client.SIGNATURE_METHODS[SIGNATURE_HMAC_SHA1],
+ client.SIGNATURE_METHODS[client.signature_method])
+
+ def test_hmac_sha256(self):
+ client = Client('client_key', signature_method=SIGNATURE_HMAC_SHA256)
+ # instance is using the correct signer method
+ self.assertEqual(Client.SIGNATURE_METHODS[SIGNATURE_HMAC_SHA256],
+ client.SIGNATURE_METHODS[client.signature_method])
+
def test_rsa(self):
client = Client('client_key', signature_method=SIGNATURE_RSA)
- self.assertIsNone(client.rsa_key) # don't need an RSA key to instantiate
+ # instance is using the correct signer method
+ self.assertEqual(Client.SIGNATURE_METHODS[SIGNATURE_RSA],
+ client.SIGNATURE_METHODS[client.signature_method])
+ # don't need an RSA key to instantiate
+ self.assertIsNone(client.rsa_key)
class SignatureMethodTest(TestCase):
+ def test_hmac_sha1_method(self):
+ client = Client('client_key', timestamp='1234567890', nonce='abc')
+ u, h, b = client.sign('http://example.com')
+ correct = ('OAuth oauth_nonce="abc", oauth_timestamp="1234567890", '
+ 'oauth_version="1.0", oauth_signature_method="HMAC-SHA1", '
+ 'oauth_consumer_key="client_key", '
+ 'oauth_signature="hH5BWYVqo7QI4EmPBUUe9owRUUQ%3D"')
+ self.assertEqual(h['Authorization'], correct)
+
+ def test_hmac_sha256_method(self):
+ client = Client('client_key', signature_method=SIGNATURE_HMAC_SHA256,
+ timestamp='1234567890', nonce='abc')
+ u, h, b = client.sign('http://example.com')
+ correct = ('OAuth oauth_nonce="abc", oauth_timestamp="1234567890", '
+ 'oauth_version="1.0", oauth_signature_method="HMAC-SHA256", '
+ 'oauth_consumer_key="client_key", '
+ 'oauth_signature="JzgJWBxX664OiMW3WE4MEjtYwOjI%2FpaUWHqtdHe68Es%3D"')
+ self.assertEqual(h['Authorization'], correct)
+
def test_rsa_method(self):
private_key = (
"-----BEGIN RSA PRIVATE KEY-----\nMIICXgIBAAKBgQDk1/bxy"