#!/usr/bin/python import sys import os import getopt import nss.nss as nss verbose = 0 def setup_contexts(mechanism, key, iv): # Get a PK11 slot based on the cipher slot = nss.get_best_slot(mechanism) # If key was supplied use it, otherwise generate one if key: if verbose: print "using supplied key data" print "key:\n%s" % (key) key_si = nss.SecItem(nss.read_hex(key)) sym_key = nss.import_sym_key(slot, mechanism, nss.PK11_OriginUnwrap, nss.CKA_ENCRYPT, key_si) else: if verbose: print "generating key data" sym_key = slot.key_gen(mechanism, None, slot.get_best_key_length(mechanism)) # If initialization vector was supplied use it, otherwise set it to None if iv: if verbose: print "supplied iv:\n%s" % (iv) iv_data = nss.read_hex(iv) iv_si = nss.SecItem(iv_data) iv_param = nss.param_from_iv(mechanism, iv_si) else: iv_length = nss.get_iv_length(mechanism) if iv_length > 0: iv_data = nss.generate_random(iv_length) iv_si = nss.SecItem(iv_data) iv_param = nss.param_from_iv(mechanism, iv_si) if verbose: print "generated %d byte initialization vector: %s" % \ (iv_length, nss.data_to_hex(iv_data, separator=":")) else: iv_param = None # Create an encoding context encoding_ctx = nss.create_context_by_sym_key(mechanism, nss.CKA_ENCRYPT, sym_key, iv_param) # Create a decoding context decoding_ctx = nss.create_context_by_sym_key(mechanism, nss.CKA_DECRYPT, sym_key, iv_param) return encoding_ctx, decoding_ctx def simple_test(encoding_ctx, decoding_ctx, plain_text): result = 0 if verbose: print "Plain Text:\n%s" % (plain_text) # Encode the plain text by feeding it to cipher_op getting cipher text back. # Append the final bit of cipher text by calling digest_final cipher_text = encoding_ctx.cipher_op(plain_text) cipher_text += encoding_ctx.digest_final() if verbose: print "Cipher Text:\n%s" % (nss.data_to_hex(cipher_text, separator=":")) # Decode the cipher text by feeding it to cipher_op getting plain text back. # Append the final bit of plain text by calling digest_final decoded_text = decoding_ctx.cipher_op(cipher_text) decoded_text += decoding_ctx.digest_final() if verbose: print "Decoded Text:\n%s" % (decoded_text) # Validate the encryption/decryption by comparing the decoded text with # the original plain text, they should match. if decoded_text != plain_text: result = 1 print "FAILED! decoded_text != plain_text" if cipher_text == plain_text: result = 1 print "FAILED! cipher_text == plain_text" return result def file_test(encoding_ctx, decoding_ctx, in_filename, chunk_size): result = 0 encrypted_filename = os.path.basename(in_filename) + ".encrypted" decrypted_filename = os.path.basename(in_filename) + ".decrypted" in_file = open(in_filename, "r") encrypted_file = open(encrypted_filename, "w") if verbose: print "Encrypting file \"%s\" to \"%s\"" % (in_filename, encrypted_filename) # Encode the data read from a file in chunks while True: # Read a chunk of data until EOF, encrypt it and write the encrypted data in_data = in_file.read(chunk_size) if len(in_data) == 0: # EOF break encrypted_data = encoding_ctx.cipher_op(in_data) encrypted_file.write(encrypted_data) # Done encoding the input, get the final encoded data, write it, close files encrypted_data = encoding_ctx.digest_final() encrypted_file.write(encrypted_data) in_file.close() encrypted_file.close() # Decode the encoded file in a similar fashion if verbose: print "Decrypting file \"%s\" to \"%s\"" % (encrypted_filename, decrypted_filename) encrypted_file = open(encrypted_filename, "r") decrypted_file = open(decrypted_filename, "w") while True: # Read a chunk of data until EOF, encrypt it and write the encrypted data in_data = encrypted_file.read(chunk_size) if len(in_data) == 0: # EOF break decrypted_data = decoding_ctx.cipher_op(in_data) decrypted_file.write(decrypted_data) # Done encoding the input, get the final encoded data, write it, close files decrypted_data = decoding_ctx.digest_final() decrypted_file.write(decrypted_data) encrypted_file.close() decrypted_file.close() # Validate the encryption/decryption by comparing the decoded text with # the original plain text, they should match. in_data = open(in_filename).read() encrypted_data = open(encrypted_filename).read() decrypted_data = open(decrypted_filename).read() if decrypted_data != in_data: result = 1 print "FAILED! decrypted_data != in_data" if encrypted_data == in_data: result = 1 print "FAILED! encrypted_data == in_data" # clean up os.unlink(encrypted_filename) os.unlink(decrypted_filename) return result def usage(): print '''\ digest_test [-v -h] filename filename file to be used as test data -v --verbose turn on verbose output -h --help print usage -s --size number of octets processed in one iteration -m --mech encryption mechanism name (e.g. CKM_*) name is case insensitive, CKM_ prefix is optional -t --text plain text -k --key key (in hexadecimal format) -i --iv initialization vector (in hexadecimal format) ''' def main(): global verbose mechanism = nss.CKM_DES_CBC_PAD plain_text = "Encrypt me!" key = "e8:a7:7c:e2:05:63:6a:31" iv = "e4:bb:3b:d3:c3:71:2e:58" in_filename = None chunk_size = 128 try: opts, args = getopt.getopt(sys.argv[1:], "hvs:m:t:k:i:", ["help", "verbose", "size=", "mechanism=", "text=", "key=", "iv="]) except getopt.GetoptError, err: print str(err) # will print something like "option -a not recognized" usage() sys.exit(2) verbose = False for o, a in opts: if o in ("-s", "--size"): chunk_size = int(a) elif o in ("-m", "--mech"): try: mechanism = nss.key_mechanism_type_from_name(a) except Exception, e: print "error with mech argument (%s)" % (e) sys.exit(2) elif o in ("-t", "--text"): plain_text = a elif o in ("-k", "--key"): key = a elif o in ("-i", "--iv"): iv = a elif o in ("-v", "--verbose"): verbose += 1 elif o in ("-h", "--help"): usage() sys.exit(0) else: assert False, "unhandled option" if (len(args) > 1): print "expected single file name" usage() sys.exit(2) elif (len(args) == 1): in_filename = args[0] nss.nss_init_nodb() result = 0 encoding_ctx, decoding_ctx = setup_contexts(mechanism, key, iv) result += simple_test(encoding_ctx, decoding_ctx, plain_text) if in_filename: # In theory we should be able to reuse a context by calling finalize() # on it, however at the time of this writing it only works for # digest contexts, not encryption/decryption contexts # so as a workaround we just create the contexts again # #encoding_ctx.finalize() #decoding_ctx.finalize() encoding_ctx, decoding_ctx = setup_contexts(mechanism, key, iv) result += file_test(encoding_ctx, decoding_ctx, in_filename, chunk_size) if result == 0: print "SUCCESS" else: print "FAILED %d tests" % (result) sys.exit(result) if __name__ == "__main__": main()