SecItem.type SecItem.len, SecItem.data PK11SymKey.key_data, PK11SymKey.key_length, PK11SymKey.slot create_context_by_sym_key param_from_iv generate_new_param get_iv_length get_block_size get_pad_mechanism * SecItem's now support indexing and slicing on their data * Clean up parsing and parameter validation of variable arg functions git-svn-id: svn://10.0.0.236/trunk@258428 18797224-902f-48f8-a5cc-f745e15eee43
242 lines
8.0 KiB
Python
Executable File
242 lines
8.0 KiB
Python
Executable File
#!/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()
|