blob: 11a2d2fbe4feb990b86555e4153a92461267351a [file] [log] [blame]
#
# Licensed to the Apache Software Foundation (ASF) under one
# or more contributor license agreements. See the NOTICE file
# distributed with this work for additional information
# regarding copyright ownership. The ASF licenses this file
# to you under the Apache License, Version 2.0 (the
# "License"); you may not use this file except in compliance
# with the License. You may obtain a copy of the License at
#
# http://www.apache.org/licenses/LICENSE-2.0
#
# Unless required by applicable law or agreed to in writing,
# software distributed under the License is distributed on an
# "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
# KIND, either express or implied. See the License for the
# specific language governing permissions and limitations
# under the License.
#
from hmac import HMAC
from binascii import b2a_hex
from sasl import Sasl
import os
import base64
class SCRAM_base(Sasl):
def __init__(self, algorithm, user, password, name, sasl_options=None):
Sasl.__init__(self, user, password, name, sasl_options)
self.algorithm = algorithm
self.client_nonce = b2a_hex(os.urandom(16))
self.server_signature = None
def initialResponse(self):
name = self.user.replace("=","=3D").replace(",","=2C")
self.client_first_message = "n=" + name + ",r=" + self.client_nonce
return "n,," + self.client_first_message
def response(self, challenge):
if(self.server_signature):
self.evaluateOutcome(challenge)
return ""
else:
serverChallenge, salt, iterations = challenge.split(",")
self.server_nonce = serverChallenge[2:]
if self.server_nonce.find(self.client_nonce) != 0:
raise SaslException("Server nonce does not start with client nonce")
self.salt = base64.b64decode(salt[2:])
iterations = int(iterations[2:])
hmac = HMAC(key=self.password.replace("=","=3D").replace(",","=2C"),digestmod=self.algorithm)
hmac.update(self.salt)
hmac.update("\x00\x00\x00\x01")
saltedPassword = hmac.digest()
previous = saltedPassword
for i in range(1,iterations):
hmac = HMAC(key=self.password.replace("=","=3D").replace(",","=2C"),digestmod=self.algorithm)
hmac.update(previous)
previous = hmac.digest()
saltedPassword = ''.join(chr(ord(a) ^ ord(b)) for a,b in zip(saltedPassword,previous))
clientFinalMessageWithoutProof = "c=" + base64.b64encode("n,,") + ",r=" + self.server_nonce
authMessage = self.client_first_message + "," + challenge + "," + clientFinalMessageWithoutProof
clientKey = HMAC(key=saltedPassword,msg="Client Key",digestmod=self.algorithm).digest()
hashFunc = self.algorithm()
hashFunc.update(clientKey)
storedKey = hashFunc.digest()
clientSignature = HMAC(key=storedKey, msg=authMessage, digestmod=self.algorithm).digest()
clientProof = ''.join(chr(ord(a) ^ ord(b)) for a,b in zip(clientKey,clientSignature))
serverKey = HMAC(key=saltedPassword,msg="Server Key",digestmod=self.algorithm).digest()
self.server_signature = HMAC(key=serverKey,msg=authMessage,digestmod=self.algorithm).digest()
return clientFinalMessageWithoutProof + ",p=" + base64.b64encode(clientProof)
def evaluateOutcome(self, challenge):
serverVerification = challenge.split(",")[0]
serverSignature = base64.b64decode(serverVerification[2:])
if serverSignature != self.server_signature:
raise SaslException("Server verification failed")
return