"""mems.microscope.token

Functions for creating and checking the authorization tokens passed to
remote servers.  Tokens are strings, generated from an initial string
S in a roundabout way:

v1=   S 
v2=   DSA_Signature(S), S
v3=   pickle.dumps( v2 )
v4=   zlib.compress( v3 )
token= base64.encode(v4)      (newlines are removed)
"""

# created 1999/12/02, AMK

__revision__ = "$Id: token.py,v 1.4 2002/02/15 20:44:41 akuchlin Exp $"

import whrandom, pickle, base64, zlib, string
import DSA

# Create object for our DSA key.  You'd have to replace this
# with your own generated key.
#
# XXX The key management here is cumbersome; we should really just
# always load a key from a config file.

y=6618447082395041567031332393302663060749748038512994535823414587940814689684439758182350489041359529307134109060356L
g=18470855821583393765756231399125172982419605071708111474329212237208699892808851227699852524147036384436605010520636L
p=19701003098197240406867617967975792798052090827429789001989646271460329857479895738620353959215891739186063365650181L
q=1305643899121611337449080241747266198844540003113L

try:
    f = open('/www/misc/key.private', 'r')
except IOError:
    # Construct a DSA object containing only the public key
    KEY = DSA.construct( (y,g,p,q) )
else:
    # Load pickled DSA key from file
    KEY = pickle.load(f) ; f.close()

def create_token(s):
    "Create an authentication token from a string of data"
    if not KEY.hasprivate():
        raise ValueError, "In-memory DSA key doesn't have private component"

    # Generate 150-bit value of K for the signing
    # XXX need to generate this in cryptographically secure way!
    K = ""
    for i in range( 16 ):
        K = K + chr( whrandom.randint(0,255) )
        
    signature = KEY.sign( s, K ) # Sign string
    token = signature,s                 # Token contains signature and string

    # Pickle the token and compress it
    pickled_token = pickle.dumps(token)
    compressed_token = zlib.compress(pickled_token)

    # Base-64 encode it so it can be included in a URL
    encoded_token = base64.encodestring(compressed_token)
    encoded_token = string.replace(encoded_token, '\n', "")
    return encoded_token


# Verify a token
def verify_token(token):
    """Verify whether a token is valid or not.
    If the signature fails, ValueError is raised.
    Otherwise, the signed value of the information string (the data
    that's actually signed) is returned."""
    
    token = base64.decodestring(token)  # Undo base-64 encoding
    token = zlib.decompress( token )    # Decompress it
    signature, s = pickle.loads(token)
    if not KEY.verify(s, signature):
        raise ValueError, "Signature failed to verify"
    else:
        return s

if __name__ == '__main__':
    token = "eJwViTEOAjEQA/u8go6rkO3sJps/pOMFESBxzXFF+D+hGY8129bdwixg4c7WVGrmWnMPrCNFzrWh6A+jq6dOoq1i0hKBqkF4DpYWUqEQ1UugggaUYE/zRLpfx3e+X8fcH2Pun+PyHHNc08lVlW4/Oa8gsQ=="
    assert verify_token( token )

    # Try creating a token
    S = 'authentication data'
    token = create_token( S )
    print token
    assert verify_token( token )

