The other day I decided that I would add Zenfolio support to my photo tagging site, smark.us. (I’m sill working on that, btw.) However, I was unable to find any Zenfolio Python libraries, so i wrote one. Since Zenfolio supports JSON-RPC, it was quite easy, however I found their authentication documentation to be a little vague on how to generate a valid response to a provided challenge. After poking at it for a bit, I got it working. To help other folks save time, here is a code snippet that you can use to generate a valid response to a challenge issued by GetChallenge(). Note that this is only for JSON-RPC. Other protocols such as SOAP use different encodings.
def GenerateAuthenticateProof(auth_challenge, password):
"""Generate proof for calling Zenfolio's Authenticate() method via JSON-RPC.
Args:
auth_challenge: Dict like object containing the result of a call to Zenfolio's
GetChallenge(). It must contain key values PasswordSalt and Challenge.
Both of which should represent lists of integers.
password: User password as a string
Returns:
A list of integer values that should be serialized and passed as the proof
argument when calling Zenfolio's Authenticate method."""
# Convert the numeric salt and challenge arrays into binary strings.
salt = ''.join([chr(x) for x in auth_challenge['PasswordSalt']])
challenge = ''.join([chr(x) for x in auth_challenge['Challenge']])
# Calculate a hash of the binary salt and the password as utf-8
combo = salt + password.encode('utf-8')
h2 = hashlib.sha256(combo)
# Calculate the proof digest
combo = challenge + h2.digest()
h1 = hashlib.sha256(combo)
proof = h1.digest()
# Return the proof digest as a list of integer values.
return list(struct.unpack('B'*32, proof))
