scapy/crypto/cert.py
author Arnaud Ebalard <arno@natisbad.org>
Mon Nov 02 22:09:11 2009 +0100 (2009-11-02)
changeset 1153 42db888aaf7b
child 1199 3f7190d02f83
permissions -rwxr-xr-x
Added cert.py: X.509 Cert and Key module based on OpenSSL binary
arno@1153
     1
## This file is part of Scapy
arno@1153
     2
## See http://www.secdev.org/projects/scapy for more informations
arno@1153
     3
## Copyright (C) Arnaud Ebalard <arno@natisbad.org>
arno@1153
     4
## This program is published under a GPLv2 license
arno@1153
     5
arno@1153
     6
import os, sys, math, socket, struct, sha, hmac, string, time
arno@1153
     7
import random, popen2, tempfile
arno@1153
     8
from scapy.utils import strxor
arno@1153
     9
try:
arno@1153
    10
    HAS_HASHLIB=True
arno@1153
    11
    import hashlib
arno@1153
    12
except:
arno@1153
    13
    HAS_HASHLIB=False
arno@1153
    14
arno@1153
    15
from Crypto.PublicKey import *
arno@1153
    16
from Crypto.Cipher import *
arno@1153
    17
from Crypto.Hash import *
arno@1153
    18
arno@1153
    19
# Maximum allowed size in bytes for a certificate file, to avoid
arno@1153
    20
# loading huge file when importing a cert
arno@1153
    21
MAX_KEY_SIZE=50*1024
arno@1153
    22
MAX_CERT_SIZE=50*1024
arno@1153
    23
MAX_CRL_SIZE=10*1024*1024   # some are that big
arno@1153
    24
arno@1153
    25
#####################################################################
arno@1153
    26
# Some helpers
arno@1153
    27
#####################################################################
arno@1153
    28
arno@1153
    29
def warning(m):
arno@1153
    30
    print "WARNING: %s" % m
arno@1153
    31
arno@1153
    32
def randstring(l):
arno@1153
    33
    """
arno@1153
    34
    Returns a random string of length l (l >= 0)
arno@1153
    35
    """
arno@1153
    36
    tmp = map(lambda x: struct.pack("B", random.randrange(0, 256, 1)), [""]*l)
arno@1153
    37
    return "".join(tmp)
arno@1153
    38
arno@1153
    39
def zerofree_randstring(l):
arno@1153
    40
    """
arno@1153
    41
    Returns a random string of length l (l >= 0) without zero in it. 
arno@1153
    42
    """
arno@1153
    43
    tmp = map(lambda x: struct.pack("B", random.randrange(1, 256, 1)), [""]*l)
arno@1153
    44
    return "".join(tmp)
arno@1153
    45
arno@1153
    46
def strand(s1, s2):
arno@1153
    47
    """
arno@1153
    48
    Returns the binary AND of the 2 provided strings s1 and s2. s1 and s2
arno@1153
    49
    must be of same length.
arno@1153
    50
    """
arno@1153
    51
    return "".join(map(lambda x,y:chr(ord(x)&ord(y)), s1, s2))
arno@1153
    52
arno@1153
    53
# OS2IP function defined in RFC 3447 for octet string to integer conversion
arno@1153
    54
def pkcs_os2ip(x):
arno@1153
    55
    """
arno@1153
    56
    Accepts a byte string as input parameter and return the associated long
arno@1153
    57
    value:
arno@1153
    58
arno@1153
    59
    Input : x        octet string to be converted
arno@1153
    60
arno@1153
    61
    Output: x        corresponding nonnegative integer
arno@1153
    62
arno@1153
    63
    Reverse function is pkcs_i2osp()
arno@1153
    64
    """
arno@1153
    65
    return RSA.number.bytes_to_long(x) 
arno@1153
    66
arno@1153
    67
# IP2OS function defined in RFC 3447 for octet string to integer conversion
arno@1153
    68
def pkcs_i2osp(x,xLen):
arno@1153
    69
    """
arno@1153
    70
    Converts a long (the first parameter) to the associated byte string
arno@1153
    71
    representation of length l (second parameter). Basically, the length
arno@1153
    72
    parameters allow the function to perform the associated padding.
arno@1153
    73
arno@1153
    74
    Input : x        nonnegative integer to be converted
arno@1153
    75
            xLen     intended length of the resulting octet string
arno@1153
    76
arno@1153
    77
    Output: x        corresponding nonnegative integer
arno@1153
    78
arno@1153
    79
    Reverse function is pkcs_os2ip().
arno@1153
    80
    """
arno@1153
    81
    z = RSA.number.long_to_bytes(x)
arno@1153
    82
    padlen = max(0, xLen-len(z))
arno@1153
    83
    return '\x00'*padlen + z
arno@1153
    84
arno@1153
    85
# for every hash function a tuple is provided, giving access to 
arno@1153
    86
# - hash output length in byte
arno@1153
    87
# - associated hash function that take data to be hashed as parameter
arno@1153
    88
#   XXX I do not provide update() at the moment.
arno@1153
    89
# - DER encoding of the leading bits of digestInfo (the hash value
arno@1153
    90
#   will be concatenated to create the complete digestInfo).
arno@1153
    91
# 
arno@1153
    92
# Notes:
arno@1153
    93
# - MD4 asn.1 value should be verified. Also, as stated in 
arno@1153
    94
#   PKCS#1 v2.1, MD4 should not be used.
arno@1153
    95
# - hashlib is available from http://code.krypto.org/python/hashlib/
arno@1153
    96
# - 'tls' one is the concatenation of both md5 and sha1 hashes used
arno@1153
    97
#   by SSL/TLS when signing/verifying things
arno@1153
    98
_hashFuncParams = {
arno@1153
    99
    "md2"    : (16, 
arno@1153
   100
                lambda x: MD2.new(x).digest(), 
arno@1153
   101
                '\x30\x20\x30\x0c\x06\x08\x2a\x86\x48\x86\xf7\x0d\x02\x02\x05\x00\x04\x10'),
arno@1153
   102
    "md4"    : (16, 
arno@1153
   103
                lambda x: MD4.new(x).digest(), 
arno@1153
   104
                '\x30\x20\x30\x0c\x06\x08\x2a\x86\x48\x86\xf7\x0d\x02\x04\x05\x00\x04\x10'), # is that right ?
arno@1153
   105
    "md5"    : (16, 
arno@1153
   106
                lambda x: MD5.new(x).digest(), 
arno@1153
   107
                '\x30\x20\x30\x0c\x06\x08\x2a\x86\x48\x86\xf7\x0d\x02\x05\x05\x00\x04\x10'),
arno@1153
   108
    "sha1"   : (20,
arno@1153
   109
                lambda x: SHA.new(x).digest(), 
arno@1153
   110
                '\x30\x21\x30\x09\x06\x05\x2b\x0e\x03\x02\x1a\x05\x00\x04\x14'),
arno@1153
   111
    "tls"    : (36,
arno@1153
   112
                lambda x: MD5.new(x).digest() + SHA.new(x).digest(),
arno@1153
   113
                '') }
arno@1153
   114
arno@1153
   115
if HAS_HASHLIB:
arno@1153
   116
    _hashFuncParams["sha224"] = (28, 
arno@1153
   117
                lambda x: hashlib.sha224(x).digest(),
arno@1153
   118
                '\x30\x2d\x30\x0d\x06\x09\x60\x86\x48\x01\x65\x03\x04\x02\x04\x05\x00\x04\x1c')
arno@1153
   119
    _hashFuncParams["sha256"] = (32, 
arno@1153
   120
                lambda x: hashlib.sha256(x).digest(), 
arno@1153
   121
                '\x30\x31\x30\x0d\x06\x09\x60\x86\x48\x01\x65\x03\x04\x02\x01\x05\x00\x04\x20')
arno@1153
   122
    _hashFuncParams["sha384"] = (48, 
arno@1153
   123
                lambda x: hashlib.sha384(x).digest(),
arno@1153
   124
               '\x30\x41\x30\x0d\x06\x09\x60\x86\x48\x01\x65\x03\x04\x02\x02\x05\x00\x04\x30')
arno@1153
   125
    _hashFuncParams["sha512"] = (64, 
arno@1153
   126
               lambda x: hashlib.sha512(x).digest(),
arno@1153
   127
               '\x30\x51\x30\x0d\x06\x09\x60\x86\x48\x01\x65\x03\x04\x02\x03\x05\x00\x04\x40')
arno@1153
   128
else:
arno@1153
   129
    warning("hashlib support is not available. Consider installing it")
arno@1153
   130
    warning("if you need sha224, sha256, sha384 and sha512 algs.")
arno@1153
   131
    
arno@1153
   132
def pkcs_mgf1(mgfSeed, maskLen, h):
arno@1153
   133
    """
arno@1153
   134
    Implements generic MGF1 Mask Generation function as described in
arno@1153
   135
    Appendix B.2.1 of RFC 3447. The hash function is passed by name.
arno@1153
   136
    valid values are 'md2', 'md4', 'md5', 'sha1', 'tls, 'sha256',
arno@1153
   137
    'sha384' and 'sha512'. Returns None on error.
arno@1153
   138
arno@1153
   139
    Input:
arno@1153
   140
       mgfSeed: seed from which mask is generated, an octet string
arno@1153
   141
       maskLen: intended length in octets of the mask, at most 2^32 * hLen
arno@1153
   142
                hLen (see below)
arno@1153
   143
       h      : hash function name (in 'md2', 'md4', 'md5', 'sha1', 'tls',
arno@1153
   144
                'sha256', 'sha384'). hLen denotes the length in octets of
arno@1153
   145
                the hash function output.
arno@1153
   146
arno@1153
   147
    Output:
arno@1153
   148
       an octet string of length maskLen
arno@1153
   149
    """
arno@1153
   150
arno@1153
   151
    # steps are those of Appendix B.2.1
arno@1153
   152
    if not _hashFuncParams.has_key(h):
arno@1153
   153
        warning("pkcs_mgf1: invalid hash (%s) provided")
arno@1153
   154
        return None
arno@1153
   155
    hLen = _hashFuncParams[h][0]
arno@1153
   156
    hFunc = _hashFuncParams[h][1]
arno@1153
   157
    if maskLen > 2**32 * hLen:                               # 1)
arno@1153
   158
        warning("pkcs_mgf1: maskLen > 2**32 * hLen")         
arno@1153
   159
        return None
arno@1153
   160
    T = ""                                                   # 2)
arno@1153
   161
    maxCounter = math.ceil(float(maskLen) / float(hLen))     # 3)
arno@1153
   162
    counter = 0
arno@1153
   163
    while counter < maxCounter:
arno@1153
   164
        C = pkcs_i2osp(counter, 4)
arno@1153
   165
        T += hFunc(mgfSeed + C)
arno@1153
   166
        counter += 1
arno@1153
   167
    return T[:maskLen]
arno@1153
   168
arno@1153
   169
arno@1153
   170
def pkcs_emsa_pss_encode(M, emBits, h, mgf, sLen): 
arno@1153
   171
    """
arno@1153
   172
    Implements EMSA-PSS-ENCODE() function described in Sect. 9.1.1 of RFC 3447
arno@1153
   173
arno@1153
   174
    Input:
arno@1153
   175
       M     : message to be encoded, an octet string
arno@1153
   176
       emBits: maximal bit length of the integer resulting of pkcs_os2ip(EM),
arno@1153
   177
               where EM is the encoded message, output of the function.
arno@1153
   178
       h     : hash function name (in 'md2', 'md4', 'md5', 'sha1', 'tls',
arno@1153
   179
               'sha256', 'sha384'). hLen denotes the length in octets of
arno@1153
   180
               the hash function output. 
arno@1153
   181
       mgf   : the mask generation function f : seed, maskLen -> mask
arno@1153
   182
       sLen  : intended length in octets of the salt
arno@1153
   183
arno@1153
   184
    Output:
arno@1153
   185
       encoded message, an octet string of length emLen = ceil(emBits/8)
arno@1153
   186
arno@1153
   187
    On error, None is returned.
arno@1153
   188
    """
arno@1153
   189
arno@1153
   190
    # 1) is not done
arno@1153
   191
    hLen = _hashFuncParams[h][0]                             # 2)
arno@1153
   192
    hFunc = _hashFuncParams[h][1]
arno@1153
   193
    mHash = hFunc(M)
arno@1153
   194
    emLen = int(math.ceil(emBits/8.))
arno@1153
   195
    if emLen < hLen + sLen + 2:                              # 3)
arno@1153
   196
        warning("encoding error (emLen < hLen + sLen + 2)")
arno@1153
   197
        return None
arno@1153
   198
    salt = randstring(sLen)                                  # 4)
arno@1153
   199
    MPrime = '\x00'*8 + mHash + salt                         # 5)
arno@1153
   200
    H = hFunc(MPrime)                                        # 6)
arno@1153
   201
    PS = '\x00'*(emLen - sLen - hLen - 2)                    # 7)
arno@1153
   202
    DB = PS + '\x01' + salt                                  # 8)
arno@1153
   203
    dbMask = mgf(H, emLen - hLen - 1)                        # 9)
arno@1153
   204
    maskedDB = strxor(DB, dbMask)                            # 10)
arno@1153
   205
    l = (8*emLen - emBits)/8                                 # 11)
arno@1153
   206
    rem = 8*emLen - emBits - 8*l # additionnal bits
arno@1153
   207
    andMask = l*'\x00'
arno@1153
   208
    if rem:
arno@1153
   209
        j = chr(reduce(lambda x,y: x+y, map(lambda x: 1<<x, range(8-rem))))
arno@1153
   210
        andMask += j
arno@1153
   211
        l += 1
arno@1153
   212
    maskedDB = strand(maskedDB[:l], andMask) + maskedDB[l:]
arno@1153
   213
    EM = maskedDB + H + '\xbc'                               # 12)
arno@1153
   214
    return EM                                                # 13)
arno@1153
   215
arno@1153
   216
arno@1153
   217
def pkcs_emsa_pss_verify(M, EM, emBits, h, mgf, sLen):
arno@1153
   218
    """
arno@1153
   219
    Implements EMSA-PSS-VERIFY() function described in Sect. 9.1.2 of RFC 3447
arno@1153
   220
arno@1153
   221
    Input:
arno@1153
   222
       M     : message to be encoded, an octet string
arno@1153
   223
       EM    : encoded message, an octet string of length emLen = ceil(emBits/8)
arno@1153
   224
       emBits: maximal bit length of the integer resulting of pkcs_os2ip(EM)
arno@1153
   225
       h     : hash function name (in 'md2', 'md4', 'md5', 'sha1', 'tls',
arno@1153
   226
               'sha256', 'sha384'). hLen denotes the length in octets of
arno@1153
   227
               the hash function output.
arno@1153
   228
       mgf   : the mask generation function f : seed, maskLen -> mask
arno@1153
   229
       sLen  : intended length in octets of the salt
arno@1153
   230
arno@1153
   231
    Output:
arno@1153
   232
       True if the verification is ok, False otherwise.
arno@1153
   233
    """
arno@1153
   234
    
arno@1153
   235
    # 1) is not done
arno@1153
   236
    hLen = _hashFuncParams[h][0]                             # 2)
arno@1153
   237
    hFunc = _hashFuncParams[h][1]
arno@1153
   238
    mHash = hFunc(M)
arno@1153
   239
    emLen = int(math.ceil(emBits/8.))                        # 3)
arno@1153
   240
    if emLen < hLen + sLen + 2:
arno@1153
   241
        return False
arno@1153
   242
    if EM[-1] != '\xbc':                                     # 4)
arno@1153
   243
        return False
arno@1153
   244
    l = emLen - hLen - 1                                     # 5)
arno@1153
   245
    maskedDB = EM[:l]
arno@1153
   246
    H = EM[l:l+hLen]
arno@1153
   247
    l = (8*emLen - emBits)/8                                 # 6)
arno@1153
   248
    rem = 8*emLen - emBits - 8*l # additionnal bits
arno@1153
   249
    andMask = l*'\xff'
arno@1153
   250
    if rem:
arno@1153
   251
        val = reduce(lambda x,y: x+y, map(lambda x: 1<<x, range(8-rem)))
arno@1153
   252
        j = chr(~val & 0xff)
arno@1153
   253
        andMask += j
arno@1153
   254
        l += 1
arno@1153
   255
    if strand(maskedDB[:l], andMask) != '\x00'*l:
arno@1153
   256
        return False
arno@1153
   257
    dbMask = mgf(H, emLen - hLen - 1)                        # 7)
arno@1153
   258
    DB = strxor(maskedDB, dbMask)                            # 8)
arno@1153
   259
    l = (8*emLen - emBits)/8                                 # 9)
arno@1153
   260
    rem = 8*emLen - emBits - 8*l # additionnal bits
arno@1153
   261
    andMask = l*'\x00'
arno@1153
   262
    if rem:
arno@1153
   263
        j = chr(reduce(lambda x,y: x+y, map(lambda x: 1<<x, range(8-rem))))
arno@1153
   264
        andMask += j
arno@1153
   265
        l += 1
arno@1153
   266
    DB = strand(DB[:l], andMask) + DB[l:]
arno@1153
   267
    l = emLen - hLen - sLen - 1                              # 10)
arno@1153
   268
    if DB[:l] != '\x00'*(l-1) + '\x01':
arno@1153
   269
        return False
arno@1153
   270
    salt = DB[-sLen:]                                        # 11)
arno@1153
   271
    MPrime = '\x00'*8 + mHash + salt                         # 12)
arno@1153
   272
    HPrime = hFunc(MPrime)                                   # 13)
arno@1153
   273
    return H == HPrime                                       # 14)
arno@1153
   274
arno@1153
   275
arno@1153
   276
def pkcs_emsa_pkcs1_v1_5_encode(M, emLen, h): # section 9.2 of RFC 3447
arno@1153
   277
    """
arno@1153
   278
    Implements EMSA-PKCS1-V1_5-ENCODE() function described in Sect.
arno@1153
   279
    9.2 of RFC 3447.
arno@1153
   280
arno@1153
   281
    Input:
arno@1153
   282
       M    : message to be encode, an octet string
arno@1153
   283
       emLen: intended length in octets of the encoded message, at least
arno@1153
   284
              tLen + 11, where tLen is the octet length of the DER encoding
arno@1153
   285
              T of a certain value computed during the encoding operation.
arno@1153
   286
       h    : hash function name (in 'md2', 'md4', 'md5', 'sha1', 'tls',
arno@1153
   287
              'sha256', 'sha384'). hLen denotes the length in octets of
arno@1153
   288
              the hash function output.
arno@1153
   289
arno@1153
   290
    Output:
arno@1153
   291
       encoded message, an octet string of length emLen
arno@1153
   292
arno@1153
   293
    On error, None is returned.
arno@1153
   294
    """
arno@1153
   295
    hLen = _hashFuncParams[h][0]                             # 1)
arno@1153
   296
    hFunc = _hashFuncParams[h][1]
arno@1153
   297
    H = hFunc(M)
arno@1153
   298
    hLeadingDigestInfo = _hashFuncParams[h][2]               # 2)
arno@1153
   299
    T = hLeadingDigestInfo + H
arno@1153
   300
    tLen = len(T)
arno@1153
   301
    if emLen < tLen + 11:                                    # 3)
arno@1153
   302
        warning("pkcs_emsa_pkcs1_v1_5_encode: intended encoded message length too short")
arno@1153
   303
        return None
arno@1153
   304
    PS = '\xff'*(emLen - tLen - 3)                           # 4)
arno@1153
   305
    EM = '\x00' + '\x01' + PS + '\x00' + T                   # 5)
arno@1153
   306
    return EM                                                # 6)
arno@1153
   307
arno@1153
   308
arno@1153
   309
# XXX should add other pgf1 instance in a better fashion.
arno@1153
   310
arno@1153
   311
def create_ca_file(anchor_list, filename):
arno@1153
   312
    """
arno@1153
   313
    Concatenate all the certificates (PEM format for the export) in
arno@1153
   314
    'anchor_list' and write the result to file 'filename'. On success
arno@1153
   315
    'filename' is returned, None otherwise.
arno@1153
   316
arno@1153
   317
    If you are used to OpenSSL tools, this function builds a CAfile
arno@1153
   318
    that can be used for certificate and CRL check.
arno@1153
   319
arno@1153
   320
    Also see create_temporary_ca_file().
arno@1153
   321
    """
arno@1153
   322
    try:
arno@1153
   323
        f = open(filename, "w")
arno@1153
   324
        for a in anchor_list:
arno@1153
   325
            s = a.output(fmt="PEM")
arno@1153
   326
            f.write(s)
arno@1153
   327
        f.close()
arno@1153
   328
    except:
arno@1153
   329
        return None
arno@1153
   330
    return filename
arno@1153
   331
arno@1153
   332
def create_temporary_ca_file(anchor_list):
arno@1153
   333
    """
arno@1153
   334
    Concatenate all the certificates (PEM format for the export) in
arno@1153
   335
    'anchor_list' and write the result to file to a temporary file
arno@1153
   336
    using mkstemp() from tempfile module. On success 'filename' is
arno@1153
   337
    returned, None otherwise.
arno@1153
   338
arno@1153
   339
    If you are used to OpenSSL tools, this function builds a CAfile
arno@1153
   340
    that can be used for certificate and CRL check.
arno@1153
   341
arno@1153
   342
    Also see create_temporary_ca_file().
arno@1153
   343
    """
arno@1153
   344
    try:
arno@1153
   345
        f, fname = tempfile.mkstemp()
arno@1153
   346
        for a in anchor_list:
arno@1153
   347
            s = a.output(fmt="PEM")
arno@1153
   348
            l = os.write(f, s)
arno@1153
   349
        os.close(f)
arno@1153
   350
    except:
arno@1153
   351
        return None
arno@1153
   352
    return fname
arno@1153
   353
arno@1153
   354
def create_temporary_ca_path(anchor_list, folder):
arno@1153
   355
    """
arno@1153
   356
    Create a CA path folder as defined in OpenSSL terminology, by
arno@1153
   357
    storing all certificates in 'anchor_list' list in PEM format
arno@1153
   358
    under provided 'folder' and then creating the associated links
arno@1153
   359
    using the hash as usually done by c_rehash.
arno@1153
   360
arno@1153
   361
    Note that you can also include CRL in 'anchor_list'. In that
arno@1153
   362
    case, they will also be stored under 'folder' and associated
arno@1153
   363
    links will be created.
arno@1153
   364
arno@1153
   365
    In folder, the files are created with names of the form
arno@1153
   366
    0...ZZ.pem. If you provide an empty list, folder will be created
arno@1153
   367
    if it does not already exist, but that's all.
arno@1153
   368
arno@1153
   369
    The number of certificates written to folder is returned on
arno@1153
   370
    success, None on error.
arno@1153
   371
    """
arno@1153
   372
    # We should probably avoid writing duplicate anchors and also
arno@1153
   373
    # check if they are all certs.
arno@1153
   374
    try:
arno@1153
   375
        if not os.path.isdir(folder):
arno@1153
   376
            os.makedirs(folder)
arno@1153
   377
    except:
arno@1153
   378
        return None
arno@1153
   379
    
arno@1153
   380
    l = len(anchor_list)
arno@1153
   381
    if l == 0:
arno@1153
   382
        return None
arno@1153
   383
    fmtstr = "%%0%sd.pem" % math.ceil(math.log(l, 10))
arno@1153
   384
    i = 0
arno@1153
   385
    try:
arno@1153
   386
        for a in anchor_list:
arno@1153
   387
            fname = os.path.join(folder, fmtstr % i)
arno@1153
   388
            f = open(fname, "w")
arno@1153
   389
            s = a.output(fmt="PEM")
arno@1153
   390
            f.write(s)
arno@1153
   391
            f.close()
arno@1153
   392
            i += 1
arno@1153
   393
    except:
arno@1153
   394
        return None
arno@1153
   395
arno@1153
   396
    r,w=popen2.popen2("c_rehash %s" % folder)
arno@1153
   397
    r.close(); w.close()
arno@1153
   398
arno@1153
   399
    return l
arno@1153
   400
arno@1153
   401
arno@1153
   402
#####################################################################
arno@1153
   403
# Public Key Cryptography related stuff
arno@1153
   404
#####################################################################
arno@1153
   405
arno@1153
   406
class OSSLHelper:
arno@1153
   407
    def _apply_ossl_cmd(self, osslcmd, rawdata):
arno@1153
   408
	r,w=popen2.popen2(osslcmd)
arno@1153
   409
	w.write(rawdata)
arno@1153
   410
	w.close()
arno@1153
   411
	res = r.read()
arno@1153
   412
	r.close()
arno@1153
   413
	return res
arno@1153
   414
arno@1153
   415
class _EncryptAndVerify:
arno@1153
   416
    ### Below are encryption methods
arno@1153
   417
arno@1153
   418
    def _rsaep(self, m):
arno@1153
   419
        """
arno@1153
   420
        Internal method providing raw RSA encryption, i.e. simple modular
arno@1153
   421
        exponentiation of the given message representative 'm', a long
arno@1153
   422
        between 0 and n-1.
arno@1153
   423
arno@1153
   424
        This is the encryption primitive RSAEP described in PKCS#1 v2.1,
arno@1153
   425
        i.e. RFC 3447 Sect. 5.1.1.
arno@1153
   426
arno@1153
   427
        Input:
arno@1153
   428
           m: message representative, a long between 0 and n-1, where
arno@1153
   429
              n is the key modulus.
arno@1153
   430
arno@1153
   431
        Output:
arno@1153
   432
           ciphertext representative, a long between 0 and n-1
arno@1153
   433
arno@1153
   434
        Not intended to be used directly. Please, see encrypt() method.
arno@1153
   435
        """
arno@1153
   436
arno@1153
   437
        n = self.modulus
arno@1153
   438
        if type(m) is int:
arno@1153
   439
            m = long(m)
arno@1153
   440
        if type(m) is not long or m > n-1:
arno@1153
   441
            warning("Key._rsaep() expects a long between 0 and n-1")
arno@1153
   442
            return None
arno@1153
   443
arno@1153
   444
        return self.key.encrypt(m, "")[0]
arno@1153
   445
arno@1153
   446
arno@1153
   447
    def _rsaes_pkcs1_v1_5_encrypt(self, M):
arno@1153
   448
        """
arno@1153
   449
        Implements RSAES-PKCS1-V1_5-ENCRYPT() function described in section
arno@1153
   450
        7.2.1 of RFC 3447.
arno@1153
   451
arno@1153
   452
        Input:
arno@1153
   453
           M: message to be encrypted, an octet string of length mLen, where
arno@1153
   454
              mLen <= k - 11 (k denotes the length in octets of the key modulus)
arno@1153
   455
arno@1153
   456
        Output:
arno@1153
   457
           ciphertext, an octet string of length k
arno@1153
   458
arno@1153
   459
        On error, None is returned.
arno@1153
   460
        """
arno@1153
   461
arno@1153
   462
        # 1) Length checking
arno@1153
   463
        mLen = len(M)
arno@1153
   464
        k = self.modulusLen / 8
arno@1153
   465
        if mLen > k - 11:
arno@1153
   466
            warning("Key._rsaes_pkcs1_v1_5_encrypt(): message too "
arno@1153
   467
                    "long (%d > %d - 11)" % (mLen, k))
arno@1153
   468
            return None
arno@1153
   469
arno@1153
   470
        # 2) EME-PKCS1-v1_5 encoding
arno@1153
   471
        PS = zerofree_randstring(k - mLen - 3)      # 2.a)
arno@1153
   472
        EM = '\x00' + '\x02' + PS + '\x00' + M      # 2.b)
arno@1153
   473
arno@1153
   474
        # 3) RSA encryption
arno@1153
   475
        m = pkcs_os2ip(EM)                          # 3.a)
arno@1153
   476
        c = self._rsaep(m)                          # 3.b)
arno@1153
   477
        C = pkcs_i2osp(c, k)                        # 3.c)
arno@1153
   478
arno@1153
   479
        return C                                    # 4)
arno@1153
   480
arno@1153
   481
arno@1153
   482
    def _rsaes_oaep_encrypt(self, M, h=None, mgf=None, L=None):
arno@1153
   483
        """
arno@1153
   484
        Internal method providing RSAES-OAEP-ENCRYPT as defined in Sect.
arno@1153
   485
        7.1.1 of RFC 3447. Not intended to be used directly. Please, see
arno@1153
   486
        encrypt() method for type "OAEP".
arno@1153
   487
arno@1153
   488
arno@1153
   489
        Input:
arno@1153
   490
           M  : message to be encrypted, an octet string of length mLen
arno@1153
   491
                where mLen <= k - 2*hLen - 2 (k denotes the length in octets
arno@1153
   492
                of the RSA modulus and hLen the length in octets of the hash
arno@1153
   493
                function output)
arno@1153
   494
           h  : hash function name (in 'md2', 'md4', 'md5', 'sha1', 'tls',
arno@1153
   495
                'sha256', 'sha384'). hLen denotes the length in octets of
arno@1153
   496
                the hash function output. 'sha1' is used by default if not
arno@1153
   497
                provided.
arno@1153
   498
           mgf: the mask generation function f : seed, maskLen -> mask
arno@1153
   499
           L  : optional label to be associated with the message; the default
arno@1153
   500
                value for L, if not provided is the empty string
arno@1153
   501
arno@1153
   502
        Output:
arno@1153
   503
           ciphertext, an octet string of length k
arno@1153
   504
arno@1153
   505
        On error, None is returned.
arno@1153
   506
        """
arno@1153
   507
        # The steps below are the one described in Sect. 7.1.1 of RFC 3447.
arno@1153
   508
        # 1) Length Checking
arno@1153
   509
                                                    # 1.a) is not done
arno@1153
   510
        mLen = len(M)
arno@1153
   511
        if h is None:
arno@1153
   512
            h = "sha1"
arno@1153
   513
        if not _hashFuncParams.has_key(h):
arno@1153
   514
            warning("Key._rsaes_oaep_encrypt(): unknown hash function %s.", h)
arno@1153
   515
            return None
arno@1153
   516
        hLen = _hashFuncParams[h][0]
arno@1153
   517
        hFun = _hashFuncParams[h][1]
arno@1153
   518
        k = self.modulusLen / 8
arno@1153
   519
        if mLen > k - 2*hLen - 2:                   # 1.b)
arno@1153
   520
            warning("Key._rsaes_oaep_encrypt(): message too long.")
arno@1153
   521
            return None
arno@1153
   522
        
arno@1153
   523
        # 2) EME-OAEP encoding
arno@1153
   524
        if L is None:                               # 2.a)
arno@1153
   525
            L = ""
arno@1153
   526
        lHash = hFun(L)
arno@1153
   527
        PS = '\x00'*(k - mLen - 2*hLen - 2)         # 2.b)
arno@1153
   528
        DB = lHash + PS + '\x01' + M                # 2.c)
arno@1153
   529
        seed = randstring(hLen)                     # 2.d)
arno@1153
   530
        if mgf is None:                             # 2.e)
arno@1153
   531
            mgf = lambda x,y: pkcs_mgf1(x,y,h)
arno@1153
   532
        dbMask = mgf(seed, k - hLen - 1)
arno@1153
   533
        maskedDB = strxor(DB, dbMask)               # 2.f)
arno@1153
   534
        seedMask = mgf(maskedDB, hLen)              # 2.g)
arno@1153
   535
        maskedSeed = strxor(seed, seedMask)         # 2.h)
arno@1153
   536
        EM = '\x00' + maskedSeed + maskedDB         # 2.i)
arno@1153
   537
arno@1153
   538
        # 3) RSA Encryption
arno@1153
   539
        m = pkcs_os2ip(EM)                          # 3.a)
arno@1153
   540
        c = self._rsaep(m)                          # 3.b)
arno@1153
   541
        C = pkcs_i2osp(c, k)                        # 3.c)
arno@1153
   542
arno@1153
   543
        return C                                    # 4)
arno@1153
   544
arno@1153
   545
arno@1153
   546
    def encrypt(self, m, t=None, h=None, mgf=None, L=None):
arno@1153
   547
        """
arno@1153
   548
        Encrypt message 'm' using 't' encryption scheme where 't' can be:
arno@1153
   549
arno@1153
   550
        - None: the message 'm' is directly applied the RSAEP encryption
arno@1153
   551
                primitive, as described in PKCS#1 v2.1, i.e. RFC 3447
arno@1153
   552
                Sect 5.1.1. Simply put, the message undergo a modular
arno@1153
   553
                exponentiation using the public key. Additionnal method
arno@1153
   554
                parameters are just ignored.
arno@1153
   555
arno@1153
   556
        - 'pkcs': the message 'm' is applied RSAES-PKCS1-V1_5-ENCRYPT encryption
arno@1153
   557
                scheme as described in section 7.2.1 of RFC 3447. In that
arno@1153
   558
                context, other parameters ('h', 'mgf', 'l') are not used.
arno@1153
   559
arno@1153
   560
        - 'oaep': the message 'm' is applied the RSAES-OAEP-ENCRYPT encryption
arno@1153
   561
                scheme, as described in PKCS#1 v2.1, i.e. RFC 3447 Sect
arno@1153
   562
                7.1.1. In that context,
arno@1153
   563
arno@1153
   564
                o 'h' parameter provides the name of the hash method to use.
arno@1153
   565
                  Possible values are "md2", "md4", "md5", "sha1", "tls",
arno@1153
   566
                  "sha224", "sha256", "sha384" and "sha512". if none is provided,
arno@1153
   567
                  sha1 is used.
arno@1153
   568
arno@1153
   569
                o 'mgf' is the mask generation function. By default, mgf
arno@1153
   570
                  is derived from the provided hash function using the
arno@1153
   571
                  generic MGF1 (see pkcs_mgf1() for details).
arno@1153
   572
arno@1153
   573
                o 'L' is the optional label to be associated with the
arno@1153
   574
                  message. If not provided, the default value is used, i.e
arno@1153
   575
                  the empty string. No check is done on the input limitation
arno@1153
   576
                  of the hash function regarding the size of 'L' (for
arno@1153
   577
                  instance, 2^61 - 1 for SHA-1). You have been warned.
arno@1153
   578
        """
arno@1153
   579
arno@1153
   580
        if t is None: # Raw encryption
arno@1153
   581
            m = pkcs_os2ip(m)
arno@1153
   582
            c = self._rsaep(m)
arno@1153
   583
            return pkcs_i2osp(c, self.modulusLen/8)
arno@1153
   584
        
arno@1153
   585
        elif t == "pkcs":
arno@1153
   586
            return self._rsaes_pkcs1_v1_5_encrypt(m)
arno@1153
   587
        
arno@1153
   588
        elif t == "oaep":
arno@1153
   589
            return self._rsaes_oaep_encrypt(m, h, mgf, L)
arno@1153
   590
arno@1153
   591
        else:
arno@1153
   592
            warning("Key.encrypt(): Unknown encryption type (%s) provided" % t)
arno@1153
   593
            return None
arno@1153
   594
arno@1153
   595
    ### Below are verification related methods
arno@1153
   596
arno@1153
   597
    def _rsavp1(self, s):
arno@1153
   598
        """
arno@1153
   599
        Internal method providing raw RSA verification, i.e. simple modular
arno@1153
   600
        exponentiation of the given signature representative 'c', an integer
arno@1153
   601
        between 0 and n-1.
arno@1153
   602
arno@1153
   603
        This is the signature verification primitive RSAVP1 described in
arno@1153
   604
        PKCS#1 v2.1, i.e. RFC 3447 Sect. 5.2.2.
arno@1153
   605
arno@1153
   606
        Input:
arno@1153
   607
          s: signature representative, an integer between 0 and n-1,
arno@1153
   608
             where n is the key modulus.
arno@1153
   609
arno@1153
   610
        Output:
arno@1153
   611
           message representative, an integer between 0 and n-1
arno@1153
   612
arno@1153
   613
        Not intended to be used directly. Please, see verify() method.
arno@1153
   614
        """
arno@1153
   615
        return self._rsaep(s)
arno@1153
   616
arno@1153
   617
    def _rsassa_pss_verify(self, M, S, h=None, mgf=None, sLen=None):
arno@1153
   618
        """
arno@1153
   619
        Implements RSASSA-PSS-VERIFY() function described in Sect 8.1.2
arno@1153
   620
        of RFC 3447
arno@1153
   621
arno@1153
   622
        Input:
arno@1153
   623
           M: message whose signature is to be verified
arno@1153
   624
           S: signature to be verified, an octet string of length k, where k
arno@1153
   625
              is the length in octets of the RSA modulus n.
arno@1153
   626
arno@1153
   627
        Output:
arno@1153
   628
           True is the signature is valid. False otherwise.
arno@1153
   629
        """
arno@1153
   630
arno@1153
   631
        # Set default parameters if not provided
arno@1153
   632
        if h is None: # By default, sha1
arno@1153
   633
            h = "sha1"
arno@1153
   634
        if not _hashFuncParams.has_key(h):
arno@1153
   635
            warning("Key._rsassa_pss_verify(): unknown hash function "
arno@1153
   636
                    "provided (%s)" % h)
arno@1153
   637
            return False
arno@1153
   638
        if mgf is None: # use mgf1 with underlying hash function
arno@1153
   639
            mgf = lambda x,y: pkcs_mgf1(x, y, h)
arno@1153
   640
        if sLen is None: # use Hash output length (A.2.3 of RFC 3447)
arno@1153
   641
            hLen = _hashFuncParams[h][0]
arno@1153
   642
            sLen = hLen
arno@1153
   643
arno@1153
   644
        # 1) Length checking
arno@1153
   645
        modBits = self.modulusLen
arno@1153
   646
        k = modBits / 8
arno@1153
   647
        if len(S) != k:
arno@1153
   648
            return False
arno@1153
   649
arno@1153
   650
        # 2) RSA verification
arno@1153
   651
        s = pkcs_os2ip(S)                           # 2.a)
arno@1153
   652
        m = self._rsavp1(s)                         # 2.b)
arno@1153
   653
        emLen = math.ceil((modBits - 1) / 8.)       # 2.c)
arno@1153
   654
        EM = pkcs_i2osp(m, emLen) 
arno@1153
   655
arno@1153
   656
        # 3) EMSA-PSS verification
arno@1153
   657
        Result = pkcs_emsa_pss_verify(M, EM, modBits - 1, h, mgf, sLen)
arno@1153
   658
arno@1153
   659
        return Result                               # 4)
arno@1153
   660
arno@1153
   661
arno@1153
   662
    def _rsassa_pkcs1_v1_5_verify(self, M, S, h):
arno@1153
   663
        """
arno@1153
   664
        Implements RSASSA-PKCS1-v1_5-VERIFY() function as described in
arno@1153
   665
        Sect. 8.2.2 of RFC 3447.
arno@1153
   666
arno@1153
   667
        Input:
arno@1153
   668
           M: message whose signature is to be verified, an octet string
arno@1153
   669
           S: signature to be verified, an octet string of length k, where
arno@1153
   670
              k is the length in octets of the RSA modulus n
arno@1153
   671
           h: hash function name (in 'md2', 'md4', 'md5', 'sha1', 'tls',
arno@1153
   672
                'sha256', 'sha384').
arno@1153
   673
           
arno@1153
   674
        Output:
arno@1153
   675
           True if the signature is valid. False otherwise.
arno@1153
   676
        """
arno@1153
   677
arno@1153
   678
        # 1) Length checking
arno@1153
   679
        k = self.modulusLen / 8
arno@1153
   680
        if len(S) != k:
arno@1153
   681
            warning("invalid signature (len(S) != k)")
arno@1153
   682
            return False
arno@1153
   683
arno@1153
   684
        # 2) RSA verification
arno@1153
   685
        s = pkcs_os2ip(S)                           # 2.a)
arno@1153
   686
        m = self._rsavp1(s)                         # 2.b)
arno@1153
   687
        EM = pkcs_i2osp(m, k)                       # 2.c)
arno@1153
   688
arno@1153
   689
        # 3) EMSA-PKCS1-v1_5 encoding
arno@1153
   690
        EMPrime = pkcs_emsa_pkcs1_v1_5_encode(M, k, h)
arno@1153
   691
        if EMPrime is None:
arno@1153
   692
            warning("Key._rsassa_pkcs1_v1_5_verify(): unable to encode.")
arno@1153
   693
            return False
arno@1153
   694
arno@1153
   695
        # 4) Comparison
arno@1153
   696
        return EM == EMPrime
arno@1153
   697
arno@1153
   698
arno@1153
   699
    def verify(self, M, S, t=None, h=None, mgf=None, sLen=None):
arno@1153
   700
        """
arno@1153
   701
        Verify alleged signature 'S' is indeed the signature of message 'M' using
arno@1153
   702
        't' signature scheme where 't' can be:
arno@1153
   703
arno@1153
   704
        - None: the alleged signature 'S' is directly applied the RSAVP1 signature
arno@1153
   705
                primitive, as described in PKCS#1 v2.1, i.e. RFC 3447 Sect
arno@1153
   706
                5.2.1. Simply put, the provided signature is applied a moular
arno@1153
   707
                exponentiation using the public key. Then, a comparison of the
arno@1153
   708
                result is done against 'M'. On match, True is returned.
arno@1153
   709
                Additionnal method parameters are just ignored.
arno@1153
   710
arno@1153
   711
        - 'pkcs': the alleged signature 'S' and message 'M' are applied
arno@1153
   712
                RSASSA-PKCS1-v1_5-VERIFY signature verification scheme as
arno@1153
   713
                described in Sect. 8.2.2 of RFC 3447. In that context,
arno@1153
   714
                the hash function name is passed using 'h'. Possible values are
arno@1153
   715
                "md2", "md4", "md5", "sha1", "tls", "sha224", "sha256", "sha384"
arno@1153
   716
                and "sha512". If none is provided, sha1 is used. Other additionnal
arno@1153
   717
                parameters are ignored.
arno@1153
   718
arno@1153
   719
        - 'pss': the alleged signature 'S' and message 'M' are applied
arno@1153
   720
                RSASSA-PSS-VERIFY signature scheme as described in Sect. 8.1.2.
arno@1153
   721
                of RFC 3447. In that context,
arno@1153
   722
arno@1153
   723
                o 'h' parameter provides the name of the hash method to use.
arno@1153
   724
                   Possible values are "md2", "md4", "md5", "sha1", "tls", "sha224",
arno@1153
   725
                   "sha256", "sha384" and "sha512". if none is provided, sha1
arno@1153
   726
                   is used. 
arno@1153
   727
arno@1153
   728
                o 'mgf' is the mask generation function. By default, mgf
arno@1153
   729
                   is derived from the provided hash function using the
arno@1153
   730
                   generic MGF1 (see pkcs_mgf1() for details).
arno@1153
   731
arno@1153
   732
                o 'sLen' is the length in octet of the salt. You can overload the
arno@1153
   733
                  default value (the octet length of the hash value for provided
arno@1153
   734
                  algorithm) by providing another one with that parameter.
arno@1153
   735
        """
arno@1153
   736
        if t is None: # RSAVP1
arno@1153
   737
            S = pkcs_os2ip(S)
arno@1153
   738
            n = self.modulus
arno@1153
   739
            if S > n-1:
arno@1153
   740
                warning("Signature to be verified is too long for key modulus")
arno@1153
   741
                return False
arno@1153
   742
            m = self._rsavp1(S)
arno@1153
   743
            if m is None:
arno@1153
   744
                return False
arno@1153
   745
            l = int(math.ceil(math.log(m, 2) / 8.)) # Hack
arno@1153
   746
            m = pkcs_i2osp(m, l)
arno@1153
   747
            return M == m
arno@1153
   748
arno@1153
   749
        elif t == "pkcs": # RSASSA-PKCS1-v1_5-VERIFY
arno@1153
   750
            if h is None:
arno@1153
   751
                h = "sha1"
arno@1153
   752
            return self._rsassa_pkcs1_v1_5_verify(M, S, h)
arno@1153
   753
arno@1153
   754
        elif t == "pss": # RSASSA-PSS-VERIFY
arno@1153
   755
            return self._rsassa_pss_verify(M, S, h, mgf, sLen)
arno@1153
   756
arno@1153
   757
        else:
arno@1153
   758
            warning("Key.verify(): Unknown signature type (%s) provided" % t)
arno@1153
   759
            return None
arno@1153
   760
    
arno@1153
   761
class _DecryptAndSignMethods(OSSLHelper):
arno@1153
   762
    ### Below are decryption related methods. Encryption ones are inherited
arno@1153
   763
    ### from PubKey
arno@1153
   764
arno@1153
   765
    def _rsadp(self, c):
arno@1153
   766
        """
arno@1153
   767
        Internal method providing raw RSA decryption, i.e. simple modular
arno@1153
   768
        exponentiation of the given ciphertext representative 'c', a long
arno@1153
   769
        between 0 and n-1.
arno@1153
   770
arno@1153
   771
        This is the decryption primitive RSADP described in PKCS#1 v2.1,
arno@1153
   772
        i.e. RFC 3447 Sect. 5.1.2.
arno@1153
   773
arno@1153
   774
        Input:
arno@1153
   775
           c: ciphertest representative, a long between 0 and n-1, where
arno@1153
   776
              n is the key modulus.
arno@1153
   777
arno@1153
   778
        Output:
arno@1153
   779
           ciphertext representative, a long between 0 and n-1
arno@1153
   780
arno@1153
   781
        Not intended to be used directly. Please, see encrypt() method.
arno@1153
   782
        """
arno@1153
   783
arno@1153
   784
        n = self.modulus
arno@1153
   785
        if type(c) is int:
arno@1153
   786
            c = long(c)        
arno@1153
   787
        if type(c) is not long or c > n-1:
arno@1153
   788
            warning("Key._rsaep() expects a long between 0 and n-1")
arno@1153
   789
            return None
arno@1153
   790
arno@1153
   791
        return self.key.decrypt(c)    
arno@1153
   792
arno@1153
   793
arno@1153
   794
    def _rsaes_pkcs1_v1_5_decrypt(self, C):
arno@1153
   795
        """
arno@1153
   796
        Implements RSAES-PKCS1-V1_5-DECRYPT() function described in section
arno@1153
   797
        7.2.2 of RFC 3447.
arno@1153
   798
arno@1153
   799
        Input:
arno@1153
   800
           C: ciphertext to be decrypted, an octet string of length k, where
arno@1153
   801
              k is the length in octets of the RSA modulus n.
arno@1153
   802
arno@1153
   803
        Output:
arno@1153
   804
           an octet string of length k at most k - 11
arno@1153
   805
arno@1153
   806
        on error, None is returned.
arno@1153
   807
        """
arno@1153
   808
        
arno@1153
   809
        # 1) Length checking
arno@1153
   810
        cLen = len(C)
arno@1153
   811
        k = self.modulusLen / 8
arno@1153
   812
        if cLen != k or k < 11:
arno@1153
   813
            warning("Key._rsaes_pkcs1_v1_5_decrypt() decryption error "
arno@1153
   814
                    "(cLen != k or k < 11)")
arno@1153
   815
            return None
arno@1153
   816
arno@1153
   817
        # 2) RSA decryption
arno@1153
   818
        c = pkcs_os2ip(C)                           # 2.a)
arno@1153
   819
        m = self._rsadp(c)                          # 2.b)
arno@1153
   820
        EM = pkcs_i2osp(m, k)                       # 2.c)
arno@1153
   821
arno@1153
   822
        # 3) EME-PKCS1-v1_5 decoding
arno@1153
   823
arno@1153
   824
        # I am aware of the note at the end of 7.2.2 regarding error
arno@1153
   825
        # conditions reporting but the one provided below are for _local_
arno@1153
   826
        # debugging purposes. --arno
arno@1153
   827
        
arno@1153
   828
        if EM[0] != '\x00':
arno@1153
   829
            warning("Key._rsaes_pkcs1_v1_5_decrypt(): decryption error "
arno@1153
   830
                    "(first byte is not 0x00)")
arno@1153
   831
            return None
arno@1153
   832
arno@1153
   833
        if EM[1] != '\x02':
arno@1153
   834
            warning("Key._rsaes_pkcs1_v1_5_decrypt(): decryption error "
arno@1153
   835
                    "(second byte is not 0x02)")
arno@1153
   836
            return None
arno@1153
   837
arno@1153
   838
        tmp = EM[2:].split('\x00', 1)
arno@1153
   839
        if len(tmp) != 2:
arno@1153
   840
            warning("Key._rsaes_pkcs1_v1_5_decrypt(): decryption error "
arno@1153
   841
                    "(no 0x00 to separate PS from M)")
arno@1153
   842
            return None
arno@1153
   843
arno@1153
   844
        PS, M = tmp
arno@1153
   845
        if len(PS) < 8:
arno@1153
   846
            warning("Key._rsaes_pkcs1_v1_5_decrypt(): decryption error "
arno@1153
   847
                    "(PS is less than 8 byte long)")
arno@1153
   848
            return None
arno@1153
   849
arno@1153
   850
        return M                                    # 4)
arno@1153
   851
arno@1153
   852
arno@1153
   853
    def _rsaes_oaep_decrypt(self, C, h=None, mgf=None, L=None):
arno@1153
   854
        """
arno@1153
   855
        Internal method providing RSAES-OAEP-DECRYPT as defined in Sect.
arno@1153
   856
        7.1.2 of RFC 3447. Not intended to be used directly. Please, see
arno@1153
   857
        encrypt() method for type "OAEP".
arno@1153
   858
arno@1153
   859
arno@1153
   860
        Input:
arno@1153
   861
           C  : ciphertext to be decrypted, an octet string of length k, where
arno@1153
   862
                k = 2*hLen + 2 (k denotes the length in octets of the RSA modulus
arno@1153
   863
                and hLen the length in octets of the hash function output)
arno@1153
   864
           h  : hash function name (in 'md2', 'md4', 'md5', 'sha1', 'tls',
arno@1153
   865
                'sha256', 'sha384'). 'sha1' is used if none is provided.
arno@1153
   866
           mgf: the mask generation function f : seed, maskLen -> mask
arno@1153
   867
           L  : optional label whose association with the message is to be
arno@1153
   868
                verified; the default value for L, if not provided is the empty
arno@1153
   869
                string.
arno@1153
   870
arno@1153
   871
        Output:
arno@1153
   872
           message, an octet string of length k mLen, where mLen <= k - 2*hLen - 2
arno@1153
   873
arno@1153
   874
        On error, None is returned.
arno@1153
   875
        """
arno@1153
   876
        # The steps below are the one described in Sect. 7.1.2 of RFC 3447.
arno@1153
   877
arno@1153
   878
        # 1) Length Checking
arno@1153
   879
                                                    # 1.a) is not done
arno@1153
   880
        if h is None:
arno@1153
   881
            h = "sha1"
arno@1153
   882
        if not _hashFuncParams.has_key(h):
arno@1153
   883
            warning("Key._rsaes_oaep_decrypt(): unknown hash function %s.", h)
arno@1153
   884
            return None
arno@1153
   885
        hLen = _hashFuncParams[h][0]
arno@1153
   886
        hFun = _hashFuncParams[h][1]
arno@1153
   887
        k = self.modulusLen / 8
arno@1153
   888
        cLen = len(C)
arno@1153
   889
        if cLen != k:                               # 1.b)
arno@1153
   890
            warning("Key._rsaes_oaep_decrypt(): decryption error. "
arno@1153
   891
                    "(cLen != k)")
arno@1153
   892
            return None
arno@1153
   893
        if k < 2*hLen + 2:
arno@1153
   894
            warning("Key._rsaes_oaep_decrypt(): decryption error. "
arno@1153
   895
                    "(k < 2*hLen + 2)")
arno@1153
   896
            return None
arno@1153
   897
arno@1153
   898
        # 2) RSA decryption
arno@1153
   899
        c = pkcs_os2ip(C)                           # 2.a)
arno@1153
   900
        m = self._rsadp(c)                          # 2.b)
arno@1153
   901
        EM = pkcs_i2osp(m, k)                       # 2.c)
arno@1153
   902
arno@1153
   903
        # 3) EME-OAEP decoding
arno@1153
   904
        if L is None:                               # 3.a)
arno@1153
   905
            L = ""
arno@1153
   906
        lHash = hFun(L)
arno@1153
   907
        Y = EM[:1]                                  # 3.b)
arno@1153
   908
        if Y != '\x00':
arno@1153
   909
            warning("Key._rsaes_oaep_decrypt(): decryption error. "
arno@1153
   910
                    "(Y is not zero)")
arno@1153
   911
            return None
arno@1153
   912
        maskedSeed = EM[1:1+hLen]
arno@1153
   913
        maskedDB = EM[1+hLen:]
arno@1153
   914
        if mgf is None:
arno@1153
   915
            mgf = lambda x,y: pkcs_mgf1(x, y, h)
arno@1153
   916
        seedMask = mgf(maskedDB, hLen)              # 3.c)
arno@1153
   917
        seed = strxor(maskedSeed, seedMask)         # 3.d)
arno@1153
   918
        dbMask = mgf(seed, k - hLen - 1)            # 3.e)
arno@1153
   919
        DB = strxor(maskedDB, dbMask)               # 3.f)
arno@1153
   920
arno@1153
   921
        # I am aware of the note at the end of 7.1.2 regarding error
arno@1153
   922
        # conditions reporting but the one provided below are for _local_
arno@1153
   923
        # debugging purposes. --arno
arno@1153
   924
arno@1153
   925
        lHashPrime = DB[:hLen]                      # 3.g)
arno@1153
   926
        tmp = DB[hLen:].split('\x01', 1)
arno@1153
   927
        if len(tmp) != 2:
arno@1153
   928
            warning("Key._rsaes_oaep_decrypt(): decryption error. "
arno@1153
   929
                    "(0x01 separator not found)")
arno@1153
   930
            return None
arno@1153
   931
        PS, M = tmp
arno@1153
   932
        if PS != '\x00'*len(PS):
arno@1153
   933
            warning("Key._rsaes_oaep_decrypt(): decryption error. "
arno@1153
   934
                    "(invalid padding string)")
arno@1153
   935
            return None
arno@1153
   936
        if lHash != lHashPrime:
arno@1153
   937
            warning("Key._rsaes_oaep_decrypt(): decryption error. "
arno@1153
   938
                    "(invalid hash)")
arno@1153
   939
            return None            
arno@1153
   940
        return M                                    # 4)
arno@1153
   941
arno@1153
   942
arno@1153
   943
    def decrypt(self, C, t=None, h=None, mgf=None, L=None):
arno@1153
   944
        """
arno@1153
   945
        Decrypt ciphertext 'C' using 't' decryption scheme where 't' can be:
arno@1153
   946
arno@1153
   947
        - None: the ciphertext 'C' is directly applied the RSADP decryption
arno@1153
   948
                primitive, as described in PKCS#1 v2.1, i.e. RFC 3447
arno@1153
   949
                Sect 5.1.2. Simply, put the message undergo a modular
arno@1153
   950
                exponentiation using the private key. Additionnal method
arno@1153
   951
                parameters are just ignored.
arno@1153
   952
arno@1153
   953
        - 'pkcs': the ciphertext 'C' is applied RSAES-PKCS1-V1_5-DECRYPT
arno@1153
   954
                decryption scheme as described in section 7.2.2 of RFC 3447.
arno@1153
   955
                In that context, other parameters ('h', 'mgf', 'l') are not
arno@1153
   956
                used.
arno@1153
   957
arno@1153
   958
        - 'oaep': the ciphertext 'C' is applied the RSAES-OAEP-DECRYPT decryption
arno@1153
   959
                scheme, as described in PKCS#1 v2.1, i.e. RFC 3447 Sect
arno@1153
   960
                7.1.2. In that context,
arno@1153
   961
arno@1153
   962
                o 'h' parameter provides the name of the hash method to use.
arno@1153
   963
                  Possible values are "md2", "md4", "md5", "sha1", "tls",
arno@1153
   964
                  "sha224", "sha256", "sha384" and "sha512". if none is provided,
arno@1153
   965
                  sha1 is used by default.
arno@1153
   966
arno@1153
   967
                o 'mgf' is the mask generation function. By default, mgf
arno@1153
   968
                  is derived from the provided hash function using the
arno@1153
   969
                  generic MGF1 (see pkcs_mgf1() for details).
arno@1153
   970
arno@1153
   971
                o 'L' is the optional label to be associated with the
arno@1153
   972
                  message. If not provided, the default value is used, i.e
arno@1153
   973
                  the empty string. No check is done on the input limitation
arno@1153
   974
                  of the hash function regarding the size of 'L' (for
arno@1153
   975
                  instance, 2^61 - 1 for SHA-1). You have been warned.        
arno@1153
   976
        """
arno@1153
   977
        if t is None:
arno@1153
   978
            C = pkcs_os2ip(C)
arno@1153
   979
            c = self._rsadp(C)
arno@1153
   980
            l = int(math.ceil(math.log(c, 2) / 8.)) # Hack
arno@1153
   981
            return pkcs_i2osp(c, l)
arno@1153
   982
arno@1153
   983
        elif t == "pkcs":
arno@1153
   984
            return self._rsaes_pkcs1_v1_5_decrypt(C)
arno@1153
   985
arno@1153
   986
        elif t == "oaep":
arno@1153
   987
            return self._rsaes_oaep_decrypt(C, h, mgf, L)
arno@1153
   988
arno@1153
   989
        else:
arno@1153
   990
            warning("Key.decrypt(): Unknown decryption type (%s) provided" % t)
arno@1153
   991
            return None
arno@1153
   992
arno@1153
   993
    ### Below are signature related methods. Verification ones are inherited from
arno@1153
   994
    ### PubKey
arno@1153
   995
arno@1153
   996
    def _rsasp1(self, m):
arno@1153
   997
        """
arno@1153
   998
        Internal method providing raw RSA signature, i.e. simple modular
arno@1153
   999
        exponentiation of the given message representative 'm', an integer
arno@1153
  1000
        between 0 and n-1.
arno@1153
  1001
arno@1153
  1002
        This is the signature primitive RSASP1 described in PKCS#1 v2.1,
arno@1153
  1003
        i.e. RFC 3447 Sect. 5.2.1.
arno@1153
  1004
arno@1153
  1005
        Input:
arno@1153
  1006
           m: message representative, an integer between 0 and n-1, where
arno@1153
  1007
              n is the key modulus.
arno@1153
  1008
arno@1153
  1009
        Output:
arno@1153
  1010
           signature representative, an integer between 0 and n-1
arno@1153
  1011
arno@1153
  1012
        Not intended to be used directly. Please, see sign() method.
arno@1153
  1013
        """
arno@1153
  1014
        return self._rsadp(m)
arno@1153
  1015
arno@1153
  1016
arno@1153
  1017
    def _rsassa_pss_sign(self, M, h=None, mgf=None, sLen=None):
arno@1153
  1018
        """
arno@1153
  1019
        Implements RSASSA-PSS-SIGN() function described in Sect. 8.1.1 of
arno@1153
  1020
        RFC 3447.
arno@1153
  1021
arno@1153
  1022
        Input:
arno@1153
  1023
           M: message to be signed, an octet string
arno@1153
  1024
arno@1153
  1025
        Output:
arno@1153
  1026
           signature, an octet string of length k, where k is the length in
arno@1153
  1027
           octets of the RSA modulus n.
arno@1153
  1028
arno@1153
  1029
        On error, None is returned.
arno@1153
  1030
        """
arno@1153
  1031
arno@1153
  1032
        # Set default parameters if not provided
arno@1153
  1033
        if h is None: # By default, sha1
arno@1153
  1034
            h = "sha1"
arno@1153
  1035
        if not _hashFuncParams.has_key(h):
arno@1153
  1036
            warning("Key._rsassa_pss_sign(): unknown hash function "
arno@1153
  1037
                    "provided (%s)" % h)
arno@1153
  1038
            return None
arno@1153
  1039
        if mgf is None: # use mgf1 with underlying hash function
arno@1153
  1040
            mgf = lambda x,y: pkcs_mgf1(x, y, h)
arno@1153
  1041
        if sLen is None: # use Hash output length (A.2.3 of RFC 3447)
arno@1153
  1042
            hLen = _hashFuncParams[h][0]
arno@1153
  1043
            sLen = hLen
arno@1153
  1044
arno@1153
  1045
        # 1) EMSA-PSS encoding
arno@1153
  1046
        modBits = self.modulusLen
arno@1153
  1047
        k = modBits / 8
arno@1153
  1048
        EM = pkcs_emsa_pss_encode(M, modBits - 1, h, mgf, sLen)
arno@1153
  1049
        if EM is None:
arno@1153
  1050
            warning("Key._rsassa_pss_sign(): unable to encode")
arno@1153
  1051
            return None
arno@1153
  1052
arno@1153
  1053
        # 2) RSA signature
arno@1153
  1054
        m = pkcs_os2ip(EM)                          # 2.a)
arno@1153
  1055
        s = self._rsasp1(m)                         # 2.b)
arno@1153
  1056
        S = pkcs_i2osp(s, k)                        # 2.c)
arno@1153
  1057
arno@1153
  1058
        return S                                    # 3)
arno@1153
  1059
arno@1153
  1060
arno@1153
  1061
    def _rsassa_pkcs1_v1_5_sign(self, M, h):
arno@1153
  1062
        """
arno@1153
  1063
        Implements RSASSA-PKCS1-v1_5-SIGN() function as described in
arno@1153
  1064
        Sect. 8.2.1 of RFC 3447.
arno@1153
  1065
arno@1153
  1066
        Input:
arno@1153
  1067
           M: message to be signed, an octet string
arno@1153
  1068
           h: hash function name (in 'md2', 'md4', 'md5', 'sha1', 'tls'
arno@1153
  1069
                'sha256', 'sha384').
arno@1153
  1070
           
arno@1153
  1071
        Output:
arno@1153
  1072
           the signature, an octet string.
arno@1153
  1073
        """
arno@1153
  1074
        
arno@1153
  1075
        # 1) EMSA-PKCS1-v1_5 encoding
arno@1153
  1076
        k = self.modulusLen / 8
arno@1153
  1077
        EM = pkcs_emsa_pkcs1_v1_5_encode(M, k, h)
arno@1153
  1078
        if EM is None:
arno@1153
  1079
            warning("Key._rsassa_pkcs1_v1_5_sign(): unable to encode")
arno@1153
  1080
            return None
arno@1153
  1081
arno@1153
  1082
        # 2) RSA signature
arno@1153
  1083
        m = pkcs_os2ip(EM)                          # 2.a)
arno@1153
  1084
        s = self._rsasp1(m)                         # 2.b)
arno@1153
  1085
        S = pkcs_i2osp(s, k)                        # 2.c)
arno@1153
  1086
arno@1153
  1087
        return S                                    # 3)
arno@1153
  1088
arno@1153
  1089
arno@1153
  1090
    def sign(self, M, t=None, h=None, mgf=None, sLen=None):
arno@1153
  1091
        """
arno@1153
  1092
        Sign message 'M' using 't' signature scheme where 't' can be:
arno@1153
  1093
arno@1153
  1094
        - None: the message 'M' is directly applied the RSASP1 signature
arno@1153
  1095
                primitive, as described in PKCS#1 v2.1, i.e. RFC 3447 Sect
arno@1153
  1096
                5.2.1. Simply put, the message undergo a modular exponentiation
arno@1153
  1097
                using the private key. Additionnal method parameters are just
arno@1153
  1098
                ignored.
arno@1153
  1099
arno@1153
  1100
        - 'pkcs': the message 'M' is applied RSASSA-PKCS1-v1_5-SIGN signature
arno@1153
  1101
                scheme as described in Sect. 8.2.1 of RFC 3447. In that context,
arno@1153
  1102
                the hash function name is passed using 'h'. Possible values are
arno@1153
  1103
                "md2", "md4", "md5", "sha1", "tls", "sha224", "sha256", "sha384"
arno@1153
  1104
                and "sha512". If none is provided, sha1 is used. Other additionnal 
arno@1153
  1105
                parameters are ignored.
arno@1153
  1106
arno@1153
  1107
        - 'pss' : the message 'M' is applied RSASSA-PSS-SIGN signature scheme as
arno@1153
  1108
                described in Sect. 8.1.1. of RFC 3447. In that context,
arno@1153
  1109
arno@1153
  1110
                o 'h' parameter provides the name of the hash method to use.
arno@1153
  1111
                   Possible values are "md2", "md4", "md5", "sha1", "tls", "sha224",
arno@1153
  1112
                   "sha256", "sha384" and "sha512". if none is provided, sha1
arno@1153
  1113
                   is used. 
arno@1153
  1114
arno@1153
  1115
                o 'mgf' is the mask generation function. By default, mgf
arno@1153
  1116
                   is derived from the provided hash function using the
arno@1153
  1117
                   generic MGF1 (see pkcs_mgf1() for details).
arno@1153
  1118
arno@1153
  1119
                o 'sLen' is the length in octet of the salt. You can overload the
arno@1153
  1120
                  default value (the octet length of the hash value for provided
arno@1153
  1121
                  algorithm) by providing another one with that parameter.
arno@1153
  1122
        """
arno@1153
  1123
arno@1153
  1124
        if t is None: # RSASP1
arno@1153
  1125
            M = pkcs_os2ip(M)
arno@1153
  1126
            n = self.modulus
arno@1153
  1127
            if M > n-1:
arno@1153
  1128
                warning("Message to be signed is too long for key modulus")
arno@1153
  1129
                return None
arno@1153
  1130
            s = self._rsasp1(M)
arno@1153
  1131
            if s is None:
arno@1153
  1132
                return None
arno@1153
  1133
            return pkcs_i2osp(s, self.modulusLen/8)
arno@1153
  1134
        
arno@1153
  1135
        elif t == "pkcs": # RSASSA-PKCS1-v1_5-SIGN
arno@1153
  1136
            if h is None:
arno@1153
  1137
                h = "sha1"
arno@1153
  1138
            return self._rsassa_pkcs1_v1_5_sign(M, h)
arno@1153
  1139
        
arno@1153
  1140
        elif t == "pss": # RSASSA-PSS-SIGN
arno@1153
  1141
            return self._rsassa_pss_sign(M, h, mgf, sLen)
arno@1153
  1142
arno@1153
  1143
        else:
arno@1153
  1144
            warning("Key.sign(): Unknown signature type (%s) provided" % t)
arno@1153
  1145
            return None
arno@1153
  1146
arno@1153
  1147
arno@1153
  1148
arno@1153
  1149
arno@1153
  1150
class PubKey(OSSLHelper, _EncryptAndVerify):
arno@1153
  1151
    # Below are the fields we recognize in the -text output of openssl
arno@1153
  1152
    # and from which we extract information. We expect them in that
arno@1153
  1153
    # order. Number of spaces does matter.
arno@1153
  1154
    possible_fields = [ "Modulus (",
arno@1153
  1155
                        "Exponent:" ]
arno@1153
  1156
    possible_fields_count = len(possible_fields)
arno@1153
  1157
    
arno@1153
  1158
    def __init__(self, keypath):
arno@1153
  1159
        error_msg = "Unable to import key."
arno@1153
  1160
arno@1153
  1161
        # XXX Temporary hack to use PubKey inside Cert
arno@1153
  1162
        if type(keypath) is tuple:
arno@1153
  1163
            e, m, mLen = keypath
arno@1153
  1164
            self.modulus = m
arno@1153
  1165
            self.modulusLen = mLen
arno@1153
  1166
            self.pubExp = e
arno@1153
  1167
            return
arno@1153
  1168
arno@1153
  1169
        fields_dict = {}
arno@1153
  1170
        for k in self.possible_fields:
arno@1153
  1171
            fields_dict[k] = None
arno@1153
  1172
arno@1153
  1173
        self.keypath = None
arno@1153
  1174
        rawkey = None
arno@1153
  1175
arno@1153
  1176
        if (not '\x00' in keypath) and os.path.isfile(keypath): # file
arno@1153
  1177
            self.keypath = keypath
arno@1153
  1178
            key_size = os.path.getsize(keypath)
arno@1153
  1179
            if key_size > MAX_KEY_SIZE:
arno@1153
  1180
                raise Exception(error_msg)
arno@1153
  1181
            try:
arno@1153
  1182
                f = open(keypath)
arno@1153
  1183
                rawkey = f.read()
arno@1153
  1184
                f.close()
arno@1153
  1185
            except:
arno@1153
  1186
    		raise Exception(error_msg)     
arno@1153
  1187
        else:
arno@1153
  1188
            rawkey = keypath
arno@1153
  1189
arno@1153
  1190
	if rawkey is None:
arno@1153
  1191
	    raise Exception(error_msg)
arno@1153
  1192
arno@1153
  1193
	self.rawkey = rawkey
arno@1153
  1194
arno@1153
  1195
        # Let's try to get file format : PEM or DER.
arno@1153
  1196
        fmtstr = 'openssl rsa -text -pubin -inform %s -noout '
arno@1153
  1197
        convertstr = 'openssl rsa -pubin -inform %s -outform %s 2>/dev/null'
arno@1153
  1198
        key_header = "-----BEGIN PUBLIC KEY-----"
arno@1153
  1199
        key_footer = "-----END PUBLIC KEY-----"
arno@1153
  1200
        l = rawkey.split(key_header, 1)
arno@1153
  1201
        if len(l) == 2: # looks like PEM
arno@1153
  1202
            tmp = l[1]
arno@1153
  1203
            l = tmp.split(key_footer, 1)
arno@1153
  1204
            if len(l) == 2:
arno@1153
  1205
                tmp = l[0]
arno@1153
  1206
                rawkey = "%s%s%s\n" % (key_header, tmp, key_footer)
arno@1153
  1207
            else:
arno@1153
  1208
                raise Exception(error_msg)
arno@1153
  1209
            r,w,e = popen2.popen3(fmtstr % "PEM")
arno@1153
  1210
            w.write(rawkey)
arno@1153
  1211
            w.close()
arno@1153
  1212
            textkey = r.read()
arno@1153
  1213
            r.close()
arno@1153
  1214
            res = e.read()
arno@1153
  1215
            e.close()
arno@1153
  1216
            if res == '':
arno@1153
  1217
                self.format = "PEM"
arno@1153
  1218
                self.pemkey = rawkey
arno@1153
  1219
                self.textkey = textkey
arno@1153
  1220
                cmd = convertstr % ("PEM", "DER")
arno@1153
  1221
                self.derkey = self._apply_ossl_cmd(cmd, rawkey)
arno@1153
  1222
            else:
arno@1153
  1223
                raise Exception(error_msg)
arno@1153
  1224
        else: # not PEM, try DER
arno@1153
  1225
            r,w,e = popen2.popen3(fmtstr % "DER")            
arno@1153
  1226
            w.write(rawkey)
arno@1153
  1227
            w.close()
arno@1153
  1228
            textkey = r.read()
arno@1153
  1229
            r.close()
arno@1153
  1230
            res = e.read()
arno@1153
  1231
	    if res == '':
arno@1153
  1232
		self.format = "DER"
arno@1153
  1233
                self.derkey = rawkey
arno@1153
  1234
                self.textkey = textkey
arno@1153
  1235
                cmd = convertstr % ("DER", "PEM")
arno@1153
  1236
                self.pemkey = self._apply_ossl_cmd(cmd, rawkey)
arno@1153
  1237
                cmd = convertstr % ("DER", "DER")
arno@1153
  1238
                self.derkey = self._apply_ossl_cmd(cmd, rawkey)                
arno@1153
  1239
	    else:
arno@1153
  1240
                try: # Perhaps it is a cert
arno@1153
  1241
                    c = Cert(keypath)
arno@1153
  1242
                except:
arno@1153
  1243
                    raise Exception(error_msg)
arno@1153
  1244
                # TODO:
arno@1153
  1245
                # Reconstruct a key (der and pem) and provide:
arno@1153
  1246
                # self.format
arno@1153
  1247
                # self.derkey
arno@1153
  1248
                # self.pemkey
arno@1153
  1249
                # self.textkey
arno@1153
  1250
                # self.keypath
arno@1153
  1251
arno@1153
  1252
        self.osslcmdbase = 'openssl rsa -pubin -inform %s ' % self.format
arno@1153
  1253
arno@1153
  1254
        self.keypath = keypath
arno@1153
  1255
arno@1153
  1256
        # Parse the -text output of openssl to make things available
arno@1153
  1257
        l = self.textkey.split('\n', 1)
arno@1153
  1258
        if len(l) != 2:
arno@1153
  1259
            raise Exception(error_msg)
arno@1153
  1260
        cur, tmp = l
arno@1153
  1261
        i = 0
arno@1153
  1262
        k = self.possible_fields[i] # Modulus (
arno@1153
  1263
        cur = cur[len(k):] + '\n'
arno@1153
  1264
        while k:
arno@1153
  1265
            l = tmp.split('\n', 1)
arno@1153
  1266
            if len(l) != 2: # Over
arno@1153
  1267
                fields_dict[k] = cur
arno@1153
  1268
                break
arno@1153
  1269
            l, tmp = l
arno@1153
  1270
arno@1153
  1271
            newkey = 0
arno@1153
  1272
            # skip fields we have already seen, this is the purpose of 'i'
arno@1153
  1273
            for j in range(i, self.possible_fields_count):
arno@1153
  1274
                f = self.possible_fields[j]
arno@1153
  1275
                if l.startswith(f):
arno@1153
  1276
                    fields_dict[k] = cur
arno@1153
  1277
                    cur = l[len(f):] + '\n'
arno@1153
  1278
                    k = f
arno@1153
  1279
                    newkey = 1
arno@1153
  1280
                    i = j+1
arno@1153
  1281
                    break
arno@1153
  1282
            if newkey == 1:
arno@1153
  1283
                continue
arno@1153
  1284
            cur += l + '\n'
arno@1153
  1285
arno@1153
  1286
        # modulus and modulus length
arno@1153
  1287
        v = fields_dict["Modulus ("]
arno@1153
  1288
        self.modulusLen = None
arno@1153
  1289
        if v:
arno@1153
  1290
            v, rem = v.split(' bit):', 1)
arno@1153
  1291
            self.modulusLen = int(v)
arno@1153
  1292
            rem = rem.replace('\n','').replace(' ','').replace(':','')
arno@1153
  1293
            self.modulus = long(rem, 16)
arno@1153
  1294
        if self.modulus is None:
arno@1153
  1295
            raise Exception(error_msg)
arno@1153
  1296
        
arno@1153
  1297
        # public exponent
arno@1153
  1298
        v = fields_dict["Exponent:"]
arno@1153
  1299
        self.pubExp = None
arno@1153
  1300
        if v:
arno@1153
  1301
            self.pubExp = long(v.split('(', 1)[0])
arno@1153
  1302
        if self.pubExp is None:
arno@1153
  1303
            raise Exception(error_msg)
arno@1153
  1304
arno@1153
  1305
        self.key = RSA.construct((self.modulus, self.pubExp, ))
arno@1153
  1306
arno@1153
  1307
    def __str__(self):
arno@1153
  1308
        return self.derkey
arno@1153
  1309
arno@1153
  1310
arno@1153
  1311
class Key(OSSLHelper, _DecryptAndSignMethods, _EncryptAndVerify):
arno@1153
  1312
    # Below are the fields we recognize in the -text output of openssl
arno@1153
  1313
    # and from which we extract information. We expect them in that
arno@1153
  1314
    # order. Number of spaces does matter.
arno@1153
  1315
    possible_fields = [ "Private-Key: (",
arno@1153
  1316
                        "modulus:",
arno@1153
  1317
                        "publicExponent:",
arno@1153
  1318
                        "privateExponent:",
arno@1153
  1319
                        "prime1:",
arno@1153
  1320
                        "prime2:",
arno@1153
  1321
                        "exponent1:",
arno@1153
  1322
                        "exponent2:",
arno@1153
  1323
                        "coefficient:" ]
arno@1153
  1324
    possible_fields_count = len(possible_fields)
arno@1153
  1325
    
arno@1153
  1326
    def __init__(self, keypath):
arno@1153
  1327
        error_msg = "Unable to import key."
arno@1153
  1328
arno@1153
  1329
        fields_dict = {}
arno@1153
  1330
        for k in self.possible_fields:
arno@1153
  1331
            fields_dict[k] = None
arno@1153
  1332
arno@1153
  1333
        self.keypath = None
arno@1153
  1334
        rawkey = None
arno@1153
  1335
arno@1153
  1336
        if (not '\x00' in keypath) and os.path.isfile(keypath):
arno@1153
  1337
            self.keypath = keypath
arno@1153
  1338
            key_size = os.path.getsize(keypath)
arno@1153
  1339
            if key_size > MAX_KEY_SIZE:
arno@1153
  1340
                raise Exception(error_msg)
arno@1153
  1341
            try:
arno@1153
  1342
                f = open(keypath)
arno@1153
  1343
                rawkey = f.read()
arno@1153
  1344
                f.close()
arno@1153
  1345
            except:
arno@1153
  1346
    		raise Exception(error_msg)     
arno@1153
  1347
        else:
arno@1153
  1348
            rawkey = keypath
arno@1153
  1349
arno@1153
  1350
	if rawkey is None:
arno@1153
  1351
	    raise Exception(error_msg)
arno@1153
  1352
arno@1153
  1353
	self.rawkey = rawkey
arno@1153
  1354
arno@1153
  1355
        # Let's try to get file format : PEM or DER.
arno@1153
  1356
        fmtstr = 'openssl rsa -text -inform %s -noout '
arno@1153
  1357
        convertstr = 'openssl rsa -inform %s -outform %s 2>/dev/null'
arno@1153
  1358
        key_header = "-----BEGIN RSA PRIVATE KEY-----"
arno@1153
  1359
        key_footer = "-----END RSA PRIVATE KEY-----"
arno@1153
  1360
        l = rawkey.split(key_header, 1)
arno@1153
  1361
        if len(l) == 2: # looks like PEM
arno@1153
  1362
            tmp = l[1]
arno@1153
  1363
            l = tmp.split(key_footer, 1)
arno@1153
  1364
            if len(l) == 2:
arno@1153
  1365
                tmp = l[0]
arno@1153
  1366
                rawkey = "%s%s%s\n" % (key_header, tmp, key_footer)
arno@1153
  1367
            else:
arno@1153
  1368
                raise Exception(error_msg)
arno@1153
  1369
            r,w,e = popen2.popen3(fmtstr % "PEM")
arno@1153
  1370
            w.write(rawkey)
arno@1153
  1371
            w.close()
arno@1153
  1372
            textkey = r.read()
arno@1153
  1373
            r.close()
arno@1153
  1374
            res = e.read()
arno@1153
  1375
            e.close()
arno@1153
  1376
            if res == '':
arno@1153
  1377
                self.format = "PEM"
arno@1153
  1378
                self.pemkey = rawkey
arno@1153
  1379
                self.textkey = textkey
arno@1153
  1380
                cmd = convertstr % ("PEM", "DER")
arno@1153
  1381
                self.derkey = self._apply_ossl_cmd(cmd, rawkey)
arno@1153
  1382
            else:
arno@1153
  1383
                raise Exception(error_msg)
arno@1153
  1384
        else: # not PEM, try DER
arno@1153
  1385
            r,w,e = popen2.popen3(fmtstr % "DER")            
arno@1153
  1386
            w.write(rawkey)
arno@1153
  1387
            w.close()
arno@1153
  1388
            textkey = r.read()
arno@1153
  1389
            r.close()
arno@1153
  1390
            res = e.read()
arno@1153
  1391
	    if res == '':
arno@1153
  1392
		self.format = "DER"
arno@1153
  1393
                self.derkey = rawkey
arno@1153
  1394
                self.textkey = textkey
arno@1153
  1395
                cmd = convertstr % ("DER", "PEM")
arno@1153
  1396
                self.pemkey = self._apply_ossl_cmd(cmd, rawkey)
arno@1153
  1397
                cmd = convertstr % ("DER", "DER")
arno@1153
  1398
                self.derkey = self._apply_ossl_cmd(cmd, rawkey)
arno@1153
  1399
	    else:
arno@1153
  1400
		raise Exception(error_msg)     
arno@1153
  1401
arno@1153
  1402
        self.osslcmdbase = 'openssl rsa -inform %s ' % self.format
arno@1153
  1403
arno@1153
  1404
        r,w,e = popen2.popen3('openssl asn1parse -inform DER ')
arno@1153
  1405
        w.write(self.derkey)
arno@1153
  1406
        w.close()
arno@1153
  1407
        self.asn1parsekey = r.read()
arno@1153
  1408
        r.close()
arno@1153
  1409
        res = e.read()
arno@1153
  1410
        e.close()
arno@1153
  1411
        if res != '':
arno@1153
  1412
            raise Exception(error_msg)
arno@1153
  1413
arno@1153
  1414
        self.keypath = keypath
arno@1153
  1415
arno@1153
  1416
        # Parse the -text output of openssl to make things available
arno@1153
  1417
        l = self.textkey.split('\n', 1)
arno@1153
  1418
        if len(l) != 2:
arno@1153
  1419
            raise Exception(error_msg)
arno@1153
  1420
        cur, tmp = l
arno@1153
  1421
        i = 0
arno@1153
  1422
        k = self.possible_fields[i] # Private-Key: (
arno@1153
  1423
        cur = cur[len(k):] + '\n'
arno@1153
  1424
        while k:
arno@1153
  1425
            l = tmp.split('\n', 1)
arno@1153
  1426
            if len(l) != 2: # Over
arno@1153
  1427
                fields_dict[k] = cur
arno@1153
  1428
                break
arno@1153
  1429
            l, tmp = l
arno@1153
  1430
arno@1153
  1431
            newkey = 0
arno@1153
  1432
            # skip fields we have already seen, this is the purpose of 'i'
arno@1153
  1433
            for j in range(i, self.possible_fields_count):
arno@1153
  1434
                f = self.possible_fields[j]
arno@1153
  1435
                if l.startswith(f):
arno@1153
  1436
                    fields_dict[k] = cur
arno@1153
  1437
                    cur = l[len(f):] + '\n'
arno@1153
  1438
                    k = f
arno@1153
  1439
                    newkey = 1
arno@1153
  1440
                    i = j+1
arno@1153
  1441
                    break
arno@1153
  1442
            if newkey == 1:
arno@1153
  1443
                continue
arno@1153
  1444
            cur += l + '\n'
arno@1153
  1445
arno@1153
  1446
        # modulus length
arno@1153
  1447
        v = fields_dict["Private-Key: ("]
arno@1153
  1448
        self.modulusLen = None
arno@1153
  1449
        if v:
arno@1153
  1450
            self.modulusLen = int(v.split(' bit', 1)[0])
arno@1153
  1451
        if self.modulusLen is None:
arno@1153
  1452
            raise Exception(error_msg)
arno@1153
  1453
        
arno@1153
  1454
        # public exponent
arno@1153
  1455
        v = fields_dict["publicExponent:"]
arno@1153
  1456
        self.pubExp = None
arno@1153
  1457
        if v:
arno@1153
  1458
            self.pubExp = long(v.split('(', 1)[0])
arno@1153
  1459
        if self.pubExp is None:
arno@1153
  1460
            raise Exception(error_msg)
arno@1153
  1461
arno@1153
  1462
        tmp = {}
arno@1153
  1463
        for k in ["modulus:", "privateExponent:", "prime1:", "prime2:",
arno@1153
  1464
                  "exponent1:", "exponent2:", "coefficient:"]:
arno@1153
  1465
            v = fields_dict[k]
arno@1153
  1466
            if v:
arno@1153
  1467
                s = v.replace('\n', '').replace(' ', '').replace(':', '')
arno@1153
  1468
                tmp[k] = long(s, 16)
arno@1153
  1469
            else:
arno@1153
  1470
                raise Exception(error_msg)
arno@1153
  1471
arno@1153
  1472
        self.modulus     = tmp["modulus:"]
arno@1153
  1473
        self.privExp     = tmp["privateExponent:"]
arno@1153
  1474
        self.prime1      = tmp["prime1:"]
arno@1153
  1475
        self.prime2      = tmp["prime2:"] 
arno@1153
  1476
        self.exponent1   = tmp["exponent1:"]
arno@1153
  1477
        self.exponent2   = tmp["exponent2:"]
arno@1153
  1478
        self.coefficient = tmp["coefficient:"]
arno@1153
  1479
arno@1153
  1480
        self.key = RSA.construct((self.modulus, self.pubExp, self.privExp))
arno@1153
  1481
arno@1153
  1482
    def __str__(self):
arno@1153
  1483
        return self.derkey
arno@1153
  1484
arno@1153
  1485
arno@1153
  1486
# We inherit from PubKey to get access to all encryption and verification
arno@1153
  1487
# methods. To have that working, we simply need Cert to provide 
arno@1153
  1488
# modulusLen and key attribute.
arno@1153
  1489
# XXX Yes, it is a hack.
arno@1153
  1490
class Cert(OSSLHelper, _EncryptAndVerify):
arno@1153
  1491
    # Below are the fields we recognize in the -text output of openssl
arno@1153
  1492
    # and from which we extract information. We expect them in that
arno@1153
  1493
    # order. Number of spaces does matter.
arno@1153
  1494
    possible_fields = [ "        Version:",
arno@1153
  1495
                        "        Serial Number:",
arno@1153
  1496
                        "        Signature Algorithm:",
arno@1153
  1497
                        "        Issuer:",
arno@1153
  1498
                        "            Not Before:",
arno@1153
  1499
                        "            Not After :",
arno@1153
  1500
                        "        Subject:",
arno@1153
  1501
                        "            Public Key Algorithm:",
arno@1153
  1502
                        "                Modulus (",
arno@1153
  1503
                        "                Exponent:",
arno@1153
  1504
                        "            X509v3 Subject Key Identifier:",
arno@1153
  1505
                        "            X509v3 Authority Key Identifier:",
arno@1153
  1506
                        "                keyid:",
arno@1153
  1507
                        "                DirName:",
arno@1153
  1508
                        "                serial:",
arno@1153
  1509
                        "            X509v3 Basic Constraints:",
arno@1153
  1510
                        "            X509v3 Key Usage:",
arno@1153
  1511
                        "            X509v3 Extended Key Usage:",
arno@1153
  1512
                        "            X509v3 CRL Distribution Points:",
arno@1153
  1513
                        "            Authority Information Access:",
arno@1153
  1514
                        "    Signature Algorithm:" ]
arno@1153
  1515
    possible_fields_count = len(possible_fields)
arno@1153
  1516
    
arno@1153
  1517
    def __init__(self, certpath):
arno@1153
  1518
        error_msg = "Unable to import certificate."
arno@1153
  1519
arno@1153
  1520
        fields_dict = {}
arno@1153
  1521
        for k in self.possible_fields:
arno@1153
  1522
            fields_dict[k] = None
arno@1153
  1523
arno@1153
  1524
        self.certpath = None
arno@1153
  1525
        rawcert = None
arno@1153
  1526
arno@1153
  1527
        if (not '\x00' in certpath) and os.path.isfile(certpath): # file
arno@1153
  1528
            self.certpath = certpath
arno@1153
  1529
            cert_size = os.path.getsize(certpath)
arno@1153
  1530
            if cert_size > MAX_CERT_SIZE:
arno@1153
  1531
                raise Exception(error_msg)
arno@1153
  1532
            try:
arno@1153
  1533
                f = open(certpath)
arno@1153
  1534
                rawcert = f.read()
arno@1153
  1535
                f.close()
arno@1153
  1536
            except:
arno@1153
  1537
    		raise Exception(error_msg)     
arno@1153
  1538
        else:
arno@1153
  1539
            rawcert = certpath
arno@1153
  1540
            
arno@1153
  1541
	if rawcert is None:
arno@1153
  1542
	    raise Exception(error_msg)
arno@1153
  1543
arno@1153
  1544
	self.rawcert = rawcert
arno@1153
  1545
arno@1153
  1546
        # Let's try to get file format : PEM or DER.
arno@1153
  1547
        fmtstr = 'openssl x509 -text -inform %s -noout '
arno@1153
  1548
        convertstr = 'openssl x509 -inform %s -outform %s '
arno@1153
  1549
        cert_header = "-----BEGIN CERTIFICATE-----"
arno@1153
  1550
        cert_footer = "-----END CERTIFICATE-----"
arno@1153
  1551
        l = rawcert.split(cert_header, 1)
arno@1153
  1552
        if len(l) == 2: # looks like PEM
arno@1153
  1553
            tmp = l[1]
arno@1153
  1554
            l = tmp.split(cert_footer, 1)
arno@1153
  1555
            if len(l) == 2:
arno@1153
  1556
                tmp = l[0]
arno@1153
  1557
                rawcert = "%s%s%s\n" % (cert_header, tmp, cert_footer)
arno@1153
  1558
            else:
arno@1153
  1559
                raise Exception(error_msg)
arno@1153
  1560
            r,w,e = popen2.popen3(fmtstr % "PEM")
arno@1153
  1561
            w.write(rawcert)
arno@1153
  1562
            w.close()
arno@1153
  1563
            textcert = r.read()
arno@1153
  1564
            r.close()
arno@1153
  1565
            res = e.read()
arno@1153
  1566
            e.close()
arno@1153
  1567
            if res == '':
arno@1153
  1568
                self.format = "PEM"
arno@1153
  1569
                self.pemcert = rawcert
arno@1153
  1570
                self.textcert = textcert
arno@1153
  1571
                cmd = convertstr % ("PEM", "DER")
arno@1153
  1572
                self.dercert = self._apply_ossl_cmd(cmd, rawcert)
arno@1153
  1573
            else:
arno@1153
  1574
                raise Exception(error_msg)
arno@1153
  1575
        else: # not PEM, try DER
arno@1153
  1576
            r,w,e = popen2.popen3(fmtstr % "DER")            
arno@1153
  1577
            w.write(rawcert)
arno@1153
  1578
            w.close()
arno@1153
  1579
            textcert = r.read()
arno@1153
  1580
            r.close()
arno@1153
  1581
            res = e.read()
arno@1153
  1582
	    if res == '':
arno@1153
  1583
		self.format = "DER"
arno@1153
  1584
                self.dercert = rawcert
arno@1153
  1585
                self.textcert = textcert
arno@1153
  1586
                cmd = convertstr % ("DER", "PEM")
arno@1153
  1587
                self.pemcert = self._apply_ossl_cmd(cmd, rawcert)
arno@1153
  1588
                cmd = convertstr % ("DER", "DER")                
arno@1153
  1589
                self.dercert = self._apply_ossl_cmd(cmd, rawcert)
arno@1153
  1590
	    else:
arno@1153
  1591
		raise Exception(error_msg)
arno@1153
  1592
arno@1153
  1593
        self.osslcmdbase = 'openssl x509 -inform %s ' % self.format
arno@1153
  1594
                                                  
arno@1153
  1595
        r,w,e = popen2.popen3('openssl asn1parse -inform DER ')
arno@1153
  1596
        w.write(self.dercert)
arno@1153
  1597
        w.close()
arno@1153
  1598
        self.asn1parsecert = r.read()
arno@1153
  1599
        r.close()
arno@1153
  1600
        res = e.read()
arno@1153
  1601
        e.close()
arno@1153
  1602
        if res != '':
arno@1153
  1603
            raise Exception(error_msg)
arno@1153
  1604
        
arno@1153
  1605
        # Grab _raw_ X509v3 Authority Key Identifier, if any.
arno@1153
  1606
        tmp = self.asn1parsecert.split(":X509v3 Authority Key Identifier", 1)
arno@1153
  1607
        self.authorityKeyID = None
arno@1153
  1608
        if len(tmp) == 2:
arno@1153
  1609
            tmp = tmp[1]
arno@1153
  1610
            tmp = tmp.split("[HEX DUMP]:", 1)[1]
arno@1153
  1611
            self.authorityKeyID=tmp.split('\n',1)[0]
arno@1153
  1612
arno@1153
  1613
        # Grab _raw_ X509v3 Subject Key Identifier, if any.
arno@1153
  1614
        tmp = self.asn1parsecert.split(":X509v3 Subject Key Identifier", 1)
arno@1153
  1615
        self.subjectKeyID = None
arno@1153
  1616
        if len(tmp) == 2:
arno@1153
  1617
            tmp = tmp[1]
arno@1153
  1618
            tmp = tmp.split("[HEX DUMP]:", 1)[1]
arno@1153
  1619
            self.subjectKeyID=tmp.split('\n',1)[0]            
arno@1153
  1620
arno@1153
  1621
        # Get tbsCertificate using the worst hack. output of asn1parse
arno@1153
  1622
        # looks like that:
arno@1153
  1623
        #
arno@1153
  1624
        # 0:d=0  hl=4 l=1298 cons: SEQUENCE          
arno@1153
  1625
        # 4:d=1  hl=4 l=1018 cons: SEQUENCE          
arno@1153
  1626
        # ...
arno@1153
  1627
        #
arno@1153
  1628
        l1,l2 = self.asn1parsecert.split('\n', 2)[:2]
arno@1153
  1629
        hl1 = int(l1.split("hl=",1)[1].split("l=",1)[0])
arno@1153
  1630
        rem = l2.split("hl=",1)[1]
arno@1153
  1631
        hl2, rem = rem.split("l=",1)
arno@1153
  1632
        hl2 = int(hl2)
arno@1153
  1633
        l = int(rem.split("cons",1)[0])
arno@1153
  1634
        self.tbsCertificate = self.dercert[hl1:hl1+hl2+l]
arno@1153
  1635
arno@1153
  1636
        # Parse the -text output of openssl to make things available
arno@1153
  1637
        tmp = self.textcert.split('\n', 2)[2]
arno@1153
  1638
        l = tmp.split('\n', 1)
arno@1153
  1639
        if len(l) != 2:
arno@1153
  1640
            raise Exception(error_msg)
arno@1153
  1641
        cur, tmp = l
arno@1153
  1642
        i = 0
arno@1153
  1643
        k = self.possible_fields[i] # Version:
arno@1153
  1644
        cur = cur[len(k):] + '\n'
arno@1153
  1645
        while k:
arno@1153
  1646
            l = tmp.split('\n', 1)
arno@1153
  1647
            if len(l) != 2: # Over
arno@1153
  1648
                fields_dict[k] = cur
arno@1153
  1649
                break
arno@1153
  1650
            l, tmp = l
arno@1153
  1651
arno@1153
  1652
            newkey = 0
arno@1153
  1653
            # skip fields we have already seen, this is the purpose of 'i'
arno@1153
  1654
            for j in range(i, self.possible_fields_count):
arno@1153
  1655
                f = self.possible_fields[j]
arno@1153
  1656
                if l.startswith(f):
arno@1153
  1657
                    fields_dict[k] = cur
arno@1153
  1658
                    cur = l[len(f):] + '\n'
arno@1153
  1659
                    k = f
arno@1153
  1660
                    newkey = 1
arno@1153
  1661
                    i = j+1
arno@1153
  1662
                    break
arno@1153
  1663
            if newkey == 1:
arno@1153
  1664
                continue
arno@1153
  1665
            cur += l + '\n'
arno@1153
  1666
arno@1153
  1667
        # version
arno@1153
  1668
        v = fields_dict["        Version:"]
arno@1153
  1669
        self.version = None
arno@1153
  1670
        if v:
arno@1153
  1671
            self.version = int(v[1:2])
arno@1153
  1672
        if self.version is None:
arno@1153
  1673
            raise Exception(error_msg)
arno@1153
  1674
arno@1153
  1675
        # serial number
arno@1153
  1676
        v = fields_dict["        Serial Number:"]
arno@1153
  1677
        self.serial = None
arno@1153
  1678
        if v:
arno@1153
  1679
            v = v.replace('\n', '').strip()
arno@1153
  1680
            if "0x" in v:
arno@1153
  1681
                v = v.split("0x", 1)[1].split(')', 1)[0]
arno@1153
  1682
            v = v.replace(':', '').upper()
arno@1153
  1683
            if len(v) % 2:
arno@1153
  1684
                v = '0' + v
arno@1153
  1685
            self.serial = v
arno@1153
  1686
        if self.serial is None:
arno@1153
  1687
            raise Exception(error_msg)
arno@1153
  1688
arno@1153
  1689
        # Signature Algorithm        
arno@1153
  1690
        v = fields_dict["        Signature Algorithm:"]
arno@1153
  1691
        self.sigAlg = None
arno@1153
  1692
        if v:
arno@1153
  1693
            v = v.split('\n',1)[0]
arno@1153
  1694
            v = v.strip()
arno@1153
  1695
            self.sigAlg = v
arno@1153
  1696
        if self.sigAlg is None:
arno@1153
  1697
            raise Exception(error_msg)
arno@1153
  1698
        
arno@1153
  1699
        # issuer
arno@1153
  1700
        v = fields_dict["        Issuer:"]
arno@1153
  1701
        self.issuer = None
arno@1153
  1702
        if v:
arno@1153
  1703
            v = v.split('\n',1)[0]
arno@1153
  1704
            v = v.strip()
arno@1153
  1705
            self.issuer = v
arno@1153
  1706
        if self.issuer is None:
arno@1153
  1707
            raise Exception(error_msg)
arno@1153
  1708
arno@1153
  1709
        # not before
arno@1153
  1710
        v = fields_dict["            Not Before:"]
arno@1153
  1711
        self.notBefore_str = None
arno@1153
  1712
        if v:
arno@1153
  1713
            v = v.split('\n',1)[0]
arno@1153
  1714
            v = v.strip()
arno@1153
  1715
            self.notBefore_str = v
arno@1153
  1716
        if self.notBefore_str is None:
arno@1153
  1717
            raise Exception(error_msg)
arno@1153
  1718
        self.notBefore = time.strptime(self.notBefore_str,
arno@1153
  1719
                                       "%b %d %H:%M:%S %Y %Z")
arno@1153
  1720
        self.notBefore_str_simple = time.strftime("%x", self.notBefore)
arno@1153
  1721
        
arno@1153
  1722
        # not after
arno@1153
  1723
        v = fields_dict["            Not After :"]
arno@1153
  1724
        self.notAfter_str = None
arno@1153
  1725
        if v:
arno@1153
  1726
            v = v.split('\n',1)[0]
arno@1153
  1727
            v = v.strip()
arno@1153
  1728
            self.notAfter_str = v
arno@1153
  1729
        if self.notAfter_str is None:
arno@1153
  1730
            raise Exception(error_msg)
arno@1153
  1731
        self.notAfter = time.strptime(self.notAfter_str,
arno@1153
  1732
                                      "%b %d %H:%M:%S %Y %Z")
arno@1153
  1733
        self.notAfter_str_simple = time.strftime("%x", self.notAfter)
arno@1153
  1734
        
arno@1153
  1735
        # subject
arno@1153
  1736
        v = fields_dict["        Subject:"]
arno@1153
  1737
        self.subject = None
arno@1153
  1738
        if v:
arno@1153
  1739
            v = v.split('\n',1)[0]
arno@1153
  1740
            v = v.strip()
arno@1153
  1741
            self.subject = v
arno@1153
  1742
        if self.subject is None:
arno@1153
  1743
            raise Exception(error_msg)
arno@1153
  1744
        
arno@1153
  1745
        # Public Key Algorithm
arno@1153
  1746
        v = fields_dict["            Public Key Algorithm:"]
arno@1153
  1747
        self.pubKeyAlg = None
arno@1153
  1748
        if v:
arno@1153
  1749
            v = v.split('\n',1)[0]
arno@1153
  1750
            v = v.strip()
arno@1153
  1751
            self.pubKeyAlg = v
arno@1153
  1752
        if self.pubKeyAlg is None:
arno@1153
  1753
            raise Exception(error_msg)
arno@1153
  1754
        
arno@1153
  1755
        # Modulus
arno@1153
  1756
        v = fields_dict["                Modulus ("]
arno@1153
  1757
        self.modulus = None
arno@1153
  1758
        if v:
arno@1153
  1759
            v,t = v.split(' bit):',1)
arno@1153
  1760
            self.modulusLen = int(v)
arno@1153
  1761
            t = t.replace(' ', '').replace('\n', ''). replace(':', '')
arno@1153
  1762
            self.modulus_hexdump = t
arno@1153
  1763
            self.modulus = long(t, 16)
arno@1153
  1764
        if self.modulus is None:
arno@1153
  1765
            raise Exception(error_msg)
arno@1153
  1766
arno@1153
  1767
        # Exponent
arno@1153
  1768
        v = fields_dict["                Exponent:"]
arno@1153
  1769
        self.exponent = None
arno@1153
  1770
        if v:
arno@1153
  1771
            v = v.split('(',1)[0]
arno@1153
  1772
            self.exponent = long(v)
arno@1153
  1773
        if self.exponent is None:
arno@1153
  1774
            raise Exception(error_msg)
arno@1153
  1775
arno@1153
  1776
        # Public Key instance
arno@1153
  1777
        self.key = RSA.construct((self.modulus, self.exponent, ))
arno@1153
  1778
        
arno@1153
  1779
        # Subject Key Identifier
arno@1153
  1780
arno@1153
  1781
        # Authority Key Identifier: keyid, dirname and serial
arno@1153
  1782
        self.authorityKeyID_keyid   = None
arno@1153
  1783
        self.authorityKeyID_dirname = None
arno@1153
  1784
        self.authorityKeyID_serial  = None
arno@1153
  1785
        if self.authorityKeyID: # (hex version already done using asn1parse)
arno@1153
  1786
            v = fields_dict["                keyid:"]
arno@1153
  1787
            if v:
arno@1153
  1788
                v = v.split('\n',1)[0]
arno@1153
  1789
                v = v.strip().replace(':', '')
arno@1153
  1790
                self.authorityKeyID_keyid = v
arno@1153
  1791
            v = fields_dict["                DirName:"]
arno@1153
  1792
            if v:
arno@1153
  1793
                v = v.split('\n',1)[0]
arno@1153
  1794
                self.authorityKeyID_dirname = v
arno@1153
  1795
            v = fields_dict["                serial:"]
arno@1153
  1796
            if v:
arno@1153
  1797
                v = v.split('\n',1)[0]
arno@1153
  1798
                v = v.strip().replace(':', '')
arno@1153
  1799
                self.authorityKeyID_serial = v                
arno@1153
  1800
arno@1153
  1801
        # Basic constraints
arno@1153
  1802
        self.basicConstraintsCritical = False
arno@1153
  1803
        self.basicConstraints=None
arno@1153
  1804
        v = fields_dict["            X509v3 Basic Constraints:"]
arno@1153
  1805
        if v:
arno@1153
  1806
            self.basicConstraints = {}
arno@1153
  1807
            v,t = v.split('\n',2)[:2]
arno@1153
  1808
            if "critical" in v:
arno@1153
  1809
                self.basicConstraintsCritical = True
arno@1153
  1810
            if "CA:" in t:
arno@1153
  1811
                self.basicConstraints["CA"] = t.split('CA:')[1][:4] == "TRUE"
arno@1153
  1812
            if "pathlen:" in t:
arno@1153
  1813
                self.basicConstraints["pathlen"] = int(t.split('pathlen:')[1])
arno@1153
  1814
arno@1153
  1815
        # X509v3 Key Usage
arno@1153
  1816
        self.keyUsage = []
arno@1153
  1817
        v = fields_dict["            X509v3 Key Usage:"]
arno@1153
  1818
        if v:	
arno@1153
  1819
            # man 5 x509v3_config
arno@1153
  1820
            ku_mapping = {"Digital Signature": "digitalSignature",
arno@1153
  1821
                          "Non Repudiation": "nonRepudiation",
arno@1153
  1822
                          "Key Encipherment": "keyEncipherment",
arno@1153
  1823
                          "Data Encipherment": "dataEncipherment",
arno@1153
  1824
                          "Key Agreement": "keyAgreement",
arno@1153
  1825
                          "Certificate Sign": "keyCertSign",
arno@1153
  1826
                          "CRL Sign": "cRLSign",
arno@1153
  1827
                          "Encipher Only": "encipherOnly",
arno@1153
  1828
                          "Decipher Only": "decipherOnly"}
arno@1153
  1829
            v = v.split('\n',2)[1]
arno@1153
  1830
            l = map(lambda x: x.strip(), v.split(','))
arno@1153
  1831
            while l:
arno@1153
  1832
                c = l.pop()
arno@1153
  1833
                if ku_mapping.has_key(c):
arno@1153
  1834
                    self.keyUsage.append(ku_mapping[c])
arno@1153
  1835
                else:
arno@1153
  1836
                    self.keyUsage.append(c) # Add it anyway
arno@1153
  1837
                    print "Found unknown X509v3 Key Usage: '%s'" % c
arno@1153
  1838
                    print "Report it to arno (at) natisbad.org for addition"
arno@1153
  1839
arno@1153
  1840
        # X509v3 Extended Key Usage
arno@1153
  1841
        self.extKeyUsage = []
arno@1153
  1842
        v = fields_dict["            X509v3 Extended Key Usage:"]
arno@1153
  1843
        if v:	
arno@1153
  1844
            # man 5 x509v3_config:
arno@1153
  1845
            eku_mapping = {"TLS Web Server Authentication": "serverAuth",
arno@1153
  1846
                           "TLS Web Client Authentication": "clientAuth",
arno@1153
  1847
                           "Code Signing": "codeSigning",
arno@1153
  1848
                           "E-mail Protection": "emailProtection",
arno@1153
  1849
                           "Time Stamping": "timeStamping",
arno@1153
  1850
                           "Microsoft Individual Code Signing": "msCodeInd",
arno@1153
  1851
                           "Microsoft Commercial Code Signing": "msCodeCom",
arno@1153
  1852
                           "Microsoft Trust List Signing": "msCTLSign",
arno@1153
  1853
                           "Microsoft Encrypted File System": "msEFS",
arno@1153
  1854
                           "Microsoft Server Gated Crypto": "msSGC",
arno@1153
  1855
                           "Netscape Server Gated Crypto": "nsSGC",
arno@1153
  1856
                           "IPSec End System": "iPsecEndSystem",
arno@1153
  1857
                           "IPSec Tunnel": "iPsecTunnel",
arno@1153
  1858
                           "IPSec User": "iPsecUser"}
arno@1153
  1859
            v = v.split('\n',2)[1]
arno@1153
  1860
            l = map(lambda x: x.strip(), v.split(','))
arno@1153
  1861
            while l:
arno@1153
  1862
                c = l.pop()
arno@1153
  1863
                if eku_mapping.has_key(c):
arno@1153
  1864
                    self.extKeyUsage.append(eku_mapping[c])
arno@1153
  1865
                else:
arno@1153
  1866
                    self.extKeyUsage.append(c) # Add it anyway
arno@1153
  1867
                    print "Found unknown X509v3 Extended Key Usage: '%s'" % c
arno@1153
  1868
                    print "Report it to arno (at) natisbad.org for addition"
arno@1153
  1869
arno@1153
  1870
        # CRL Distribution points
arno@1153
  1871
        self.cRLDistributionPoints = []
arno@1153
  1872
        v = fields_dict["            X509v3 CRL Distribution Points:"]
arno@1153
  1873
        if v:
arno@1153
  1874
            v = v.split("\n\n", 1)[0]
arno@1153
  1875
            v = v.split("URI:")[1:]
arno@1153
  1876
            self.CRLDistributionPoints = map(lambda x: x.strip(), v)
arno@1153
  1877
            
arno@1153
  1878
        # Authority Information Access: list of tuples ("method", "location")
arno@1153
  1879
        self.authorityInfoAccess = []
arno@1153
  1880
        v = fields_dict["            Authority Information Access:"]
arno@1153
  1881
        if v:
arno@1153
  1882
            v = v.split("\n\n", 1)[0]
arno@1153
  1883
            v = v.split("\n")[1:]
arno@1153
  1884
            for e in v:
arno@1153
  1885
                method, location = map(lambda x: x.strip(), e.split(" - ", 1))
arno@1153
  1886
                self.authorityInfoAccess.append((method, location))
arno@1153
  1887
arno@1153
  1888
        # signature field
arno@1153
  1889
        v = fields_dict["    Signature Algorithm:" ]
arno@1153
  1890
        self.sig = None
arno@1153
  1891
        if v:
arno@1153
  1892
            v = v.split('\n',1)[1]
arno@1153
  1893
            v = v.replace(' ', '').replace('\n', '')
arno@1153
  1894
            self.sig = "".join(map(lambda x: chr(int(x, 16)), v.split(':')))
arno@1153
  1895
            self.sigLen = len(self.sig)
arno@1153
  1896
        if self.sig is None:
arno@1153
  1897
            raise Exception(error_msg)
arno@1153
  1898
arno@1153
  1899
    def isIssuerCert(self, other):
arno@1153
  1900
        """
arno@1153
  1901
        True if 'other' issued 'self', i.e.:
arno@1153
  1902
          - self.issuer == other.subject
arno@1153
  1903
          - self is signed by other
arno@1153
  1904
        """
arno@1153
  1905
        # XXX should be done on raw values, instead of their textual repr
arno@1153
  1906
        if self.issuer != other.subject:
arno@1153
  1907
            return False
arno@1153
  1908
arno@1153
  1909
        # Sanity check regarding modulus length and the
arno@1153
  1910
        # signature length
arno@1153
  1911
        keyLen = (other.modulusLen + 7)/8
arno@1153
  1912
        if keyLen != self.sigLen:
arno@1153
  1913
            return False
arno@1153
  1914
arno@1153
  1915
        unenc = other.encrypt(self.sig) # public key encryption, i.e. decrypt
arno@1153
  1916
arno@1153
  1917
        # XXX Check block type (00 or 01 and type of padding)
arno@1153
  1918
        unenc = unenc[1:]
arno@1153
  1919
        if not '\x00' in unenc:
arno@1153
  1920
            return False
arno@1153
  1921
        pos = unenc.index('\x00')
arno@1153
  1922
        unenc = unenc[pos+1:]
arno@1153
  1923
arno@1153
  1924
        found = None
arno@1153
  1925
        for k in _hashFuncParams.keys():
arno@1153
  1926
            if self.sigAlg.startswith(k):
arno@1153
  1927
                found = k
arno@1153
  1928
                break
arno@1153
  1929
        if not found:
arno@1153
  1930
            return False
arno@1153
  1931
        hlen, hfunc, digestInfo =  _hashFuncParams[k]
arno@1153
  1932
        
arno@1153
  1933
        if len(unenc) != (hlen+len(digestInfo)):
arno@1153
  1934
            return False
arno@1153
  1935
arno@1153
  1936
        if not unenc.startswith(digestInfo):
arno@1153
  1937
            return False
arno@1153
  1938
arno@1153
  1939
        h = unenc[-hlen:]
arno@1153
  1940
        myh = hfunc(self.tbsCertificate)
arno@1153
  1941
arno@1153
  1942
        return h == myh
arno@1153
  1943
arno@1153
  1944
    def chain(self, certlist):
arno@1153
  1945
        """
arno@1153
  1946
        Construct the chain of certificates leading from 'self' to the
arno@1153
  1947
        self signed root using the certificates in 'certlist'. If the
arno@1153
  1948
        list does not provide all the required certs to go to the root
arno@1153
  1949
        the function returns a incomplete chain starting with the
arno@1153
  1950
        certificate. This fact can be tested by tchecking if the last
arno@1153
  1951
        certificate of the returned chain is self signed (if c is the
arno@1153
  1952
        result, c[-1].isSelfSigned())
arno@1153
  1953
        """
arno@1153
  1954
        d = {}
arno@1153
  1955
        for c in certlist:
arno@1153
  1956
            # XXX we should check if we have duplicate
arno@1153
  1957
            d[c.subject] = c
arno@1153
  1958
        res = [self]
arno@1153
  1959
        cur = self
arno@1153
  1960
        while not cur.isSelfSigned():
arno@1153
  1961
            if d.has_key(cur.issuer):
arno@1153
  1962
                possible_issuer = d[cur.issuer]
arno@1153
  1963
                if cur.isIssuerCert(possible_issuer):
arno@1153
  1964
                    res.append(possible_issuer)
arno@1153
  1965
                    cur = possible_issuer
arno@1153
  1966
                else:
arno@1153
  1967
                    break
arno@1153
  1968
        return res
arno@1153
  1969
arno@1153
  1970
    def remainingDays(self, now=None):
arno@1153
  1971
        """
arno@1153
  1972
        Based on the value of notBefore field, returns the number of
arno@1153
  1973
        days the certificate will still be valid. The date used for the
arno@1153
  1974
        comparison is the current and local date, as returned by 
arno@1153
  1975
        time.localtime(), except if 'now' argument is provided another
arno@1153
  1976
        one. 'now' argument can be given as either a time tuple or a string
arno@1153
  1977
        representing the date. Accepted format for the string version
arno@1153
  1978
        are:
arno@1153
  1979
        
arno@1153
  1980
         - '%b %d %H:%M:%S %Y %Z' e.g. 'Jan 30 07:38:59 2008 GMT'
arno@1153
  1981
         - '%m/%d/%y' e.g. '01/30/08' (less precise)
arno@1153
  1982
arno@1153
  1983
        If the certificate is no more valid at the date considered, then,
arno@1153
  1984
        a negative value is returned representing the number of days
arno@1153
  1985
        since it has expired.
arno@1153
  1986
        
arno@1153
  1987
        The number of days is returned as a float to deal with the unlikely
arno@1153
  1988
        case of certificates that are still just valid.
arno@1153
  1989
        """
arno@1153
  1990
        if now is None:
arno@1153
  1991
            now = time.localtime()
arno@1153
  1992
        elif type(now) is str:
arno@1153
  1993
            try:
arno@1153
  1994
                if '/' in now:
arno@1153
  1995
                    now = time.strptime(now, '%m/%d/%y')
arno@1153
  1996
                else:
arno@1153
  1997
                    now = time.strptime(now, '%b %d %H:%M:%S %Y %Z')
arno@1153
  1998
            except:
arno@1153
  1999
                warning("Bad time string provided '%s'. Using current time" % now)
arno@1153
  2000
                now = time.localtime()
arno@1153
  2001
arno@1153
  2002
        now = time.mktime(now)
arno@1153
  2003
        nft = time.mktime(self.notAfter)
arno@1153
  2004
        diff = (nft - now)/(24.*3600)
arno@1153
  2005
        return diff
arno@1153
  2006
arno@1153
  2007
arno@1153
  2008
    # return SHA-1 hash of cert embedded public key
arno@1153
  2009
    # !! At the moment, the trailing 0 is in the hashed string if any
arno@1153
  2010
    def keyHash(self):
arno@1153
  2011
	m = self.modulus_hexdump
arno@1153
  2012
        res = []
arno@1153
  2013
        i = 0
arno@1153
  2014
        l = len(m)
arno@1153
  2015
        while i<l: # get a string version of modulus
arno@1153
  2016
            res.append(struct.pack("B", int(m[i:i+2], 16)))
arno@1153
  2017
            i += 2
arno@1153
  2018
        return sha.new("".join(res)).digest()    
arno@1153
  2019
arno@1153
  2020
    def output(self, fmt="DER"):
arno@1153
  2021
        if fmt == "DER":
arno@1153
  2022
            return self.dercert
arno@1153
  2023
        elif fmt == "PEM":
arno@1153
  2024
            return self.pemcert
arno@1153
  2025
        elif fmt == "TXT":
arno@1153
  2026
            return self.textcert
arno@1153
  2027
arno@1153
  2028
    def export(self, filename, fmt="DER"):
arno@1153
  2029
        """
arno@1153
  2030
        Export certificate in 'fmt' format (PEM, DER or TXT) to file 'filename'
arno@1153
  2031
        """
arno@1153
  2032
        f = open(filename, "wb")
arno@1153
  2033
        f.write(self.output(fmt))
arno@1153
  2034
        f.close()
arno@1153
  2035
arno@1153
  2036
    def isSelfSigned(self):
arno@1153
  2037
        """
arno@1153
  2038
        Return True if the certificate is self signed:
arno@1153
  2039
          - issuer and subject are the same
arno@1153
  2040
          - the signature of the certificate is valid.
arno@1153
  2041
        """
arno@1153
  2042
        if self.issuer == self.subject:
arno@1153
  2043
            return self.isIssuerCert(self)
arno@1153
  2044
        return False
arno@1153
  2045
arno@1153
  2046
    # Print main informations stored in certificate
arno@1153
  2047
    def show(self):
arno@1153
  2048
        print "Serial: %s" % self.serial
arno@1153
  2049
        print "Issuer: " + self.issuer
arno@1153
  2050
        print "Subject: " + self.subject
arno@1153
  2051
        print "Validity: %s to %s" % (self.notBefore_str_simple,
arno@1153
  2052
                                      self.notAfter_str_simple)
arno@1153
  2053
arno@1153
  2054
    def __repr__(self):
arno@1153
  2055
        return "[X.509 Cert. Subject:%s, Issuer:%s]" % (self.subject, self.issuer)
arno@1153
  2056
arno@1153
  2057
    def __str__(self):
arno@1153
  2058
        return self.dercert
arno@1153
  2059
arno@1153
  2060
    def verifychain(self, anchors, untrusted=None):
arno@1153
  2061
        """
arno@1153
  2062
        Perform verification of certificate chains for that certificate. The
arno@1153
  2063
        behavior of verifychain method is mapped (and also based) on openssl
arno@1153
  2064
        verify userland tool (man 1 verify).
arno@1153
  2065
        A list of anchors is required. untrusted parameter can be provided 
arno@1153
  2066
        a list of untrusted certificates that can be used to reconstruct the
arno@1153
  2067
        chain.
arno@1153
  2068
arno@1153
  2069
        If you have a lot of certificates to verify against the same
arno@1153
  2070
        list of anchor, consider constructing this list as a cafile
arno@1153
  2071
        and use .verifychain_from_cafile() instead.
arno@1153
  2072
        """
arno@1153
  2073
        cafile = create_temporary_ca_file(anchors)
arno@1153
  2074
        if not cafile:
arno@1153
  2075
            return False
arno@1153
  2076
        untrusted_file = None
arno@1153
  2077
        if untrusted:
arno@1153
  2078
            untrusted_file = create_temporary_ca_file(untrusted) # hack
arno@1153
  2079
            if not untrusted_file:
arno@1153
  2080
                os.unlink(cafile)
arno@1153
  2081
                return False
arno@1153
  2082
        res = self.verifychain_from_cafile(cafile, 
arno@1153
  2083
                                           untrusted_file=untrusted_file)
arno@1153
  2084
        os.unlink(cafile)
arno@1153
  2085
        if untrusted_file:
arno@1153
  2086
            os.unlink(untrusted_file)
arno@1153
  2087
        return res
arno@1153
  2088
arno@1153
  2089
    def verifychain_from_cafile(self, cafile, untrusted_file=None):
arno@1153
  2090
        """
arno@1153
  2091
        Does the same job as .verifychain() but using the list of anchors
arno@1153
  2092
        from the cafile. This is useful (because more efficient) if
arno@1153
  2093
        you have a lot of certificates to verify do it that way: it
arno@1153
  2094
        avoids the creation of a cafile from anchors at each call.
arno@1153
  2095
arno@1153
  2096
        As for .verifychain(), a list of untrusted certificates can be
arno@1153
  2097
        passed (as a file, this time)
arno@1153
  2098
        """
arno@1153
  2099
        u = ""
arno@1153
  2100
        if untrusted_file:
arno@1153
  2101
            u = "-untrusted %s" % untrusted_file
arno@1153
  2102
        try:
arno@1153
  2103
            cmd = "openssl verify -CAfile %s %s " % (cafile, u)
arno@1153
  2104
            pemcert = self.output(fmt="PEM")
arno@1153
  2105
            cmdres = self._apply_ossl_cmd(cmd, pemcert)
arno@1153
  2106
        except:
arno@1153
  2107
            return False
arno@1153
  2108
        return cmdres.endswith("\nOK\n") or cmdres.endswith(": OK\n")
arno@1153
  2109
arno@1153
  2110
    def verifychain_from_capath(self, capath, untrusted_file=None):
arno@1153
  2111
        """
arno@1153
  2112
        Does the same job as .verifychain_from_cafile() but using the list
arno@1153
  2113
        of anchors in capath directory. The directory should contain
arno@1153
  2114
        certificates files in PEM format with associated links as
arno@1153
  2115
        created using c_rehash utility (man c_rehash).
arno@1153
  2116
arno@1153
  2117
        As for .verifychain_from_cafile(), a list of untrusted certificates
arno@1153
  2118
        can be passed as a file (concatenation of the certificates in
arno@1153
  2119
        PEM format)
arno@1153
  2120
        """
arno@1153
  2121
        u = ""
arno@1153
  2122
        if untrusted_file:
arno@1153
  2123
            u = "-untrusted %s" % untrusted_file
arno@1153
  2124
        try:
arno@1153
  2125
            cmd = "openssl verify -CApath %s %s " % (capath, u)
arno@1153
  2126
            pemcert = self.output(fmt="PEM")
arno@1153
  2127
            cmdres = self._apply_ossl_cmd(cmd, pemcert)
arno@1153
  2128
        except:
arno@1153
  2129
            return False
arno@1153
  2130
        return cmdres.endswith("\nOK\n") or cmdres.endswith(": OK\n")
arno@1153
  2131
arno@1153
  2132
    def is_revoked(self, crl_list):
arno@1153
  2133
        """
arno@1153
  2134
        Given a list of trusted CRL (their signature has already been
arno@1153
  2135
        verified with trusted anchors), this function returns True if
arno@1153
  2136
        the certificate is marked as revoked by one of those CRL.
arno@1153
  2137
arno@1153
  2138
        Note that if the Certificate was on hold in a previous CRL and
arno@1153
  2139
        is now valid again in a new CRL and bot are in the list, it
arno@1153
  2140
        will be considered revoked: this is because _all_ CRLs are 
arno@1153
  2141
        checked (not only the freshest) and revocation status is not
arno@1153
  2142
        handled.
arno@1153
  2143
arno@1153
  2144
        Also note that the check on the issuer is performed on the
arno@1153
  2145
        Authority Key Identifier if available in _both_ the CRL and the
arno@1153
  2146
        Cert. Otherwise, the issuers are simply compared.
arno@1153
  2147
        """
arno@1153
  2148
        for c in crl_list:
arno@1153
  2149
            if (self.authorityKeyID is not None and 
arno@1153
  2150
                c.authorityKeyID is not None and
arno@1153
  2151
                self.authorityKeyID == c.authorityKeyID):
arno@1153
  2152
                return self.serial in map(lambda x: x[0], c.revoked_cert_serials)
arno@1153
  2153
            elif (self.issuer == c.issuer):
arno@1153
  2154
                return self.serial in map(lambda x: x[0], c.revoked_cert_serials)
arno@1153
  2155
        return False
arno@1153
  2156
arno@1153
  2157
def print_chain(l):
arno@1153
  2158
    llen = len(l) - 1
arno@1153
  2159
    if llen < 0:
arno@1153
  2160
        return ""
arno@1153
  2161
    c = l[llen]
arno@1153
  2162
    llen -= 1
arno@1153
  2163
    s = "_ "
arno@1153
  2164
    if not c.isSelfSigned():
arno@1153
  2165
        s = "_ ... [Missing Root]\n"
arno@1153
  2166
    else:
arno@1153
  2167
        s += "%s [Self Signed]\n" % c.subject
arno@1153
  2168
    i = 1
arno@1153
  2169
    while (llen != -1):
arno@1153
  2170
        c = l[llen]
arno@1153
  2171
        s += "%s\_ %s" % (" "*i, c.subject)
arno@1153
  2172
        if llen != 0:
arno@1153
  2173
            s += "\n"
arno@1153
  2174
        i += 2
arno@1153
  2175
        llen -= 1
arno@1153
  2176
    print s
arno@1153
  2177
arno@1153
  2178
# import popen2
arno@1153
  2179
# a=popen2.Popen3("openssl crl -text -inform DER -noout ", capturestderr=True)
arno@1153
  2180
# a.tochild.write(open("samples/klasa1.crl").read())
arno@1153
  2181
# a.tochild.close()
arno@1153
  2182
# a.poll()
arno@1153
  2183
arno@1153
  2184
class CRL(OSSLHelper):
arno@1153
  2185
    # Below are the fields we recognize in the -text output of openssl
arno@1153
  2186
    # and from which we extract information. We expect them in that
arno@1153
  2187
    # order. Number of spaces does matter.
arno@1153
  2188
    possible_fields = [ "        Version",
arno@1153
  2189
                        "        Signature Algorithm:",
arno@1153
  2190
                        "        Issuer:",
arno@1153
  2191
                        "        Last Update:",
arno@1153
  2192
                        "        Next Update:",
arno@1153
  2193
                        "        CRL extensions:",
arno@1153
  2194
                        "            X509v3 Issuer Alternative Name:",
arno@1153
  2195
                        "            X509v3 Authority Key Identifier:", 
arno@1153
  2196
                        "                keyid:",
arno@1153
  2197
                        "                DirName:",
arno@1153
  2198
                        "                serial:",
arno@1153
  2199
                        "            X509v3 CRL Number:", 
arno@1153
  2200
                        "Revoked Certificates:",
arno@1153
  2201
                        "No Revoked Certificates.",
arno@1153
  2202
                        "    Signature Algorithm:" ]
arno@1153
  2203
    possible_fields_count = len(possible_fields)
arno@1153
  2204
arno@1153
  2205
    def __init__(self, crlpath):
arno@1153
  2206
        error_msg = "Unable to import CRL."
arno@1153
  2207
arno@1153
  2208
        fields_dict = {}
arno@1153
  2209
        for k in self.possible_fields:
arno@1153
  2210
            fields_dict[k] = None
arno@1153
  2211
arno@1153
  2212
        self.crlpath = None
arno@1153
  2213
        rawcrl = None
arno@1153
  2214
arno@1153
  2215
        if (not '\x00' in crlpath) and os.path.isfile(crlpath):
arno@1153
  2216
            self.crlpath = crlpath
arno@1153
  2217
            cert_size = os.path.getsize(crlpath)
arno@1153
  2218
            if cert_size > MAX_CRL_SIZE:
arno@1153
  2219
                raise Exception(error_msg)
arno@1153
  2220
            try:
arno@1153
  2221
                f = open(crlpath)
arno@1153
  2222
                rawcrl = f.read()
arno@1153
  2223
                f.close()
arno@1153
  2224
            except:
arno@1153
  2225
    		raise Exception(error_msg)     
arno@1153
  2226
        else:
arno@1153
  2227
            rawcrl = crlpath
arno@1153
  2228
arno@1153
  2229
	if rawcrl is None:
arno@1153
  2230
	    raise Exception(error_msg)
arno@1153
  2231
arno@1153
  2232
	self.rawcrl = rawcrl
arno@1153
  2233
arno@1153
  2234
        # Let's try to get file format : PEM or DER.
arno@1153
  2235
        fmtstr = 'openssl crl -text -inform %s -noout '
arno@1153
  2236
        convertstr = 'openssl crl -inform %s -outform %s '
arno@1153
  2237
        crl_header = "-----BEGIN X509 CRL-----"
arno@1153
  2238
        crl_footer = "-----END X509 CRL-----"
arno@1153
  2239
        l = rawcrl.split(crl_header, 1)
arno@1153
  2240
        if len(l) == 2: # looks like PEM
arno@1153
  2241
            tmp = l[1]
arno@1153
  2242
            l = tmp.split(crl_footer, 1)
arno@1153
  2243
            if len(l) == 2:
arno@1153
  2244
                tmp = l[0]
arno@1153
  2245
                rawcrl = "%s%s%s\n" % (crl_header, tmp, crl_footer)
arno@1153
  2246
            else:
arno@1153
  2247
                raise Exception(error_msg)
arno@1153
  2248
            r,w,e = popen2.popen3(fmtstr % "PEM")
arno@1153
  2249
            w.write(rawcrl)
arno@1153
  2250
            w.close()
arno@1153
  2251
            textcrl = r.read()
arno@1153
  2252
            r.close()
arno@1153
  2253
            res = e.read()
arno@1153
  2254
            e.close()
arno@1153
  2255
            if res == '':
arno@1153
  2256
                self.format = "PEM"
arno@1153
  2257
                self.pemcrl = rawcrl
arno@1153
  2258
                self.textcrl = textcrl
arno@1153
  2259
                cmd = convertstr % ("PEM", "DER")
arno@1153
  2260
                self.dercrl = self._apply_ossl_cmd(cmd, rawcrl)
arno@1153
  2261
            else:
arno@1153
  2262
                raise Exception(error_msg)
arno@1153
  2263
        else: # not PEM, try DER
arno@1153
  2264
            r,w,e = popen2.popen3(fmtstr % "DER")            
arno@1153
  2265
            w.write(rawcrl)
arno@1153
  2266
            w.close()
arno@1153
  2267
            textcrl = r.read()
arno@1153
  2268
            r.close()
arno@1153
  2269
            res = e.read()
arno@1153
  2270
	    if res == '':
arno@1153
  2271
		self.format = "DER"
arno@1153
  2272
                self.dercrl = rawcrl
arno@1153
  2273
                self.textcrl = textcrl
arno@1153
  2274
                cmd = convertstr % ("DER", "PEM")
arno@1153
  2275
                self.pemcrl = self._apply_ossl_cmd(cmd, rawcrl)
arno@1153
  2276
                cmd = convertstr % ("DER", "DER")
arno@1153
  2277
                self.dercrl = self._apply_ossl_cmd(cmd, rawcrl)
arno@1153
  2278
	    else:
arno@1153
  2279
		raise Exception(error_msg)
arno@1153
  2280
arno@1153
  2281
        self.osslcmdbase = 'openssl crl -inform %s ' % self.format
arno@1153
  2282
arno@1153
  2283
        r,w,e = popen2.popen3('openssl asn1parse -inform DER ')
arno@1153
  2284
        w.write(self.dercrl)
arno@1153
  2285
        w.close()
arno@1153
  2286
        self.asn1parsecrl = r.read()
arno@1153
  2287
        r.close()
arno@1153
  2288
        res = e.read()
arno@1153
  2289
        e.close()
arno@1153
  2290
        if res != '':
arno@1153
  2291
            raise Exception(error_msg)
arno@1153
  2292
arno@1153
  2293
        # Grab _raw_ X509v3 Authority Key Identifier, if any.
arno@1153
  2294
        tmp = self.asn1parsecrl.split(":X509v3 Authority Key Identifier", 1)
arno@1153
  2295
        self.authorityKeyID = None
arno@1153
  2296
        if len(tmp) == 2:
arno@1153
  2297
            tmp = tmp[1]
arno@1153
  2298
            tmp = tmp.split("[HEX DUMP]:", 1)[1]
arno@1153
  2299
            self.authorityKeyID=tmp.split('\n',1)[0]
arno@1153
  2300
arno@1153
  2301
        # Parse the -text output of openssl to make things available
arno@1153
  2302
        tmp = self.textcrl.split('\n', 1)[1]
arno@1153
  2303
        l = tmp.split('\n', 1)
arno@1153
  2304
        if len(l) != 2:
arno@1153
  2305
            raise Exception(error_msg)
arno@1153
  2306
        cur, tmp = l
arno@1153
  2307
        i = 0
arno@1153
  2308
        k = self.possible_fields[i] # Version
arno@1153
  2309
        cur = cur[len(k):] + '\n'
arno@1153
  2310
        while k:
arno@1153
  2311
            l = tmp.split('\n', 1)
arno@1153
  2312
            if len(l) != 2: # Over
arno@1153
  2313
                fields_dict[k] = cur
arno@1153
  2314
                break
arno@1153
  2315
            l, tmp = l
arno@1153
  2316
arno@1153
  2317
            newkey = 0
arno@1153
  2318
            # skip fields we have already seen, this is the purpose of 'i'
arno@1153
  2319
            for j in range(i, self.possible_fields_count):
arno@1153
  2320
                f = self.possible_fields[j]
arno@1153
  2321
                if l.startswith(f):
arno@1153
  2322
                    fields_dict[k] = cur
arno@1153
  2323
                    cur = l[len(f):] + '\n'
arno@1153
  2324
                    k = f
arno@1153
  2325
                    newkey = 1
arno@1153
  2326
                    i = j+1
arno@1153
  2327
                    break
arno@1153
  2328
            if newkey == 1:
arno@1153
  2329
                continue
arno@1153
  2330
            cur += l + '\n'
arno@1153
  2331
arno@1153
  2332
        # version
arno@1153
  2333
        v = fields_dict["        Version"]
arno@1153
  2334
        self.version = None
arno@1153
  2335
        if v:
arno@1153
  2336
            self.version = int(v[1:2])
arno@1153
  2337
        if self.version is None:
arno@1153
  2338
            raise Exception(error_msg)
arno@1153
  2339
arno@1153
  2340
        # signature algorithm
arno@1153
  2341
        v = fields_dict["        Signature Algorithm:"]
arno@1153
  2342
        self.sigAlg = None
arno@1153
  2343
        if v:
arno@1153
  2344
            v = v.split('\n',1)[0]
arno@1153
  2345
            v = v.strip()
arno@1153
  2346
            self.sigAlg = v
arno@1153
  2347
        if self.sigAlg is None:
arno@1153
  2348
            raise Exception(error_msg)
arno@1153
  2349
arno@1153
  2350
        # issuer
arno@1153
  2351
        v = fields_dict["        Issuer:"]
arno@1153
  2352
        self.issuer = None
arno@1153
  2353
        if v:
arno@1153
  2354
            v = v.split('\n',1)[0]
arno@1153
  2355
            v = v.strip()
arno@1153
  2356
            self.issuer = v
arno@1153
  2357
        if self.issuer is None:
arno@1153
  2358
            raise Exception(error_msg)
arno@1153
  2359
arno@1153
  2360
        # last update
arno@1153
  2361
        v = fields_dict["        Last Update:"]
arno@1153
  2362
        self.lastUpdate_str = None
arno@1153
  2363
        if v:
arno@1153
  2364
            v = v.split('\n',1)[0]
arno@1153
  2365
            v = v.strip()
arno@1153
  2366
            self.lastUpdate_str = v
arno@1153
  2367
        if self.lastUpdate_str is None:
arno@1153
  2368
            raise Exception(error_msg)
arno@1153
  2369
        self.lastUpdate = time.strptime(self.lastUpdate_str,
arno@1153
  2370
                                       "%b %d %H:%M:%S %Y %Z")
arno@1153
  2371
        self.lastUpdate_str_simple = time.strftime("%x", self.lastUpdate)
arno@1153
  2372
arno@1153
  2373
        # next update
arno@1153
  2374
        v = fields_dict["        Next Update:"]
arno@1153
  2375
        self.nextUpdate_str = None
arno@1153
  2376
        if v:
arno@1153
  2377
            v = v.split('\n',1)[0]
arno@1153
  2378
            v = v.strip()
arno@1153
  2379
            self.nextUpdate_str = v
arno@1153
  2380
        if self.nextUpdate_str is None:
arno@1153
  2381
            raise Exception(error_msg)
arno@1153
  2382
        self.nextUpdate = time.strptime(self.nextUpdate_str,
arno@1153
  2383
                                       "%b %d %H:%M:%S %Y %Z")
arno@1153
  2384
        self.nextUpdate_str_simple = time.strftime("%x", self.nextUpdate)
arno@1153
  2385
        
arno@1153
  2386
        # XXX Do something for Issuer Alternative Name
arno@1153
  2387
arno@1153
  2388
        # Authority Key Identifier: keyid, dirname and serial
arno@1153
  2389
        self.authorityKeyID_keyid   = None
arno@1153
  2390
        self.authorityKeyID_dirname = None
arno@1153
  2391
        self.authorityKeyID_serial  = None
arno@1153
  2392
        if self.authorityKeyID: # (hex version already done using asn1parse)
arno@1153
  2393
            v = fields_dict["                keyid:"]
arno@1153
  2394
            if v:
arno@1153
  2395
                v = v.split('\n',1)[0]
arno@1153
  2396
                v = v.strip().replace(':', '')
arno@1153
  2397
                self.authorityKeyID_keyid = v
arno@1153
  2398
            v = fields_dict["                DirName:"]
arno@1153
  2399
            if v:
arno@1153
  2400
                v = v.split('\n',1)[0]
arno@1153
  2401
                self.authorityKeyID_dirname = v
arno@1153
  2402
            v = fields_dict["                serial:"]
arno@1153
  2403
            if v:
arno@1153
  2404
                v = v.split('\n',1)[0]
arno@1153
  2405
                v = v.strip().replace(':', '')
arno@1153
  2406
                self.authorityKeyID_serial = v
arno@1153
  2407
arno@1153
  2408
        # number
arno@1153
  2409
        v = fields_dict["            X509v3 CRL Number:"]
arno@1153
  2410
        self.number = None
arno@1153
  2411
        if v:
arno@1153
  2412
            v = v.split('\n',2)[1]
arno@1153
  2413
            v = v.strip()
arno@1153
  2414
            self.number = int(v)
arno@1153
  2415
arno@1153
  2416
        # Get the list of serial numbers of revoked certificates
arno@1153
  2417
        self.revoked_cert_serials = []
arno@1153
  2418
        v = fields_dict["Revoked Certificates:"]
arno@1153
  2419
        t = fields_dict["No Revoked Certificates."]
arno@1153
  2420
        if (t is None and v is not None):
arno@1153
  2421
            v = v.split("Serial Number: ")[1:]
arno@1153
  2422
            for r in v:
arno@1153
  2423
                s,d = r.split('\n', 1)
arno@1153
  2424
                s = s.split('\n', 1)[0]
arno@1153
  2425
                d = d.split("Revocation Date:", 1)[1]
arno@1153
  2426
                d = time.strptime(d.strip(), "%b %d %H:%M:%S %Y %Z")
arno@1153
  2427
                self.revoked_cert_serials.append((s,d))
arno@1153
  2428
arno@1153
  2429
        # signature field
arno@1153
  2430
        v = fields_dict["    Signature Algorithm:" ]
arno@1153
  2431
        self.sig = None
arno@1153
  2432
        if v:
arno@1153
  2433
            v = v.split('\n',1)[1]
arno@1153
  2434
            v = v.replace(' ', '').replace('\n', '')
arno@1153
  2435
            self.sig = "".join(map(lambda x: chr(int(x, 16)), v.split(':')))
arno@1153
  2436
            self.sigLen = len(self.sig)
arno@1153
  2437
        if self.sig is None:
arno@1153
  2438
            raise Exception(error_msg)
arno@1153
  2439
arno@1153
  2440
    def __str__(self):
arno@1153
  2441
        return self.dercrl
arno@1153
  2442
        
arno@1153
  2443
    # Print main informations stored in CRL
arno@1153
  2444
    def show(self):
arno@1153
  2445
        print "Version: %d" % self.version
arno@1153
  2446
        print "sigAlg: " + self.sigAlg
arno@1153
  2447
        print "Issuer: " + self.issuer
arno@1153
  2448
        print "lastUpdate: %s" % self.lastUpdate_str_simple
arno@1153
  2449
        print "nextUpdate: %s" % self.nextUpdate_str_simple
arno@1153
  2450
arno@1153
  2451
    def verify(self, anchors):
arno@1153
  2452
        """
arno@1153
  2453
        Return True if the CRL is signed by one of the provided
arno@1153
  2454
        anchors. False on error (invalid signature, missing anchorand, ...)
arno@1153
  2455
        """
arno@1153
  2456
        cafile = create_temporary_ca_file(anchors)
arno@1153
  2457
        if cafile is None:
arno@1153
  2458
            return False
arno@1153
  2459
        try:
arno@1153
  2460
            cmd = self.osslcmdbase + '-noout -CAfile %s 2>&1' % cafile
arno@1153
  2461
            cmdres = self._apply_ossl_cmd(cmd, self.rawcrl)
arno@1153
  2462
        except:
arno@1153
  2463
            os.unlink(cafile)
arno@1153
  2464
            return False
arno@1153
  2465
        os.unlink(cafile)
arno@1153
  2466
        return "verify OK" in cmdres
arno@1153
  2467
arno@1153
  2468
arno@1153
  2469
    
arno@1153
  2470