diff --git a/docs/README b/docs/README new file mode 100644 index 0000000..80e30e8 --- /dev/null +++ b/docs/README @@ -0,0 +1,17 @@ +***** ***** ***** ***** +*** Picseal *** +***** ***** ***** ***** + +Your Photo is your Crypto Key +Generate a new Crypto Key to share with every photo + + +[[ Requirements ]] +Python 3 +pycrypto==2.6.1 + + + +[[ Usage ]] + + diff --git a/docs/README_DSN b/docs/README_DSN new file mode 100644 index 0000000..8b01621 --- /dev/null +++ b/docs/README_DSN @@ -0,0 +1,37 @@ + + +[[ JPG file parse Pseudo Code ]] + +1) Verify File + a. read JPG file: find record markers, save file locations + b. verify no parsing issues + c. save file locations for metatdate + d. save file locations for image data +2) Generate Hash + a. hash all image data +3) Digital Signature + a. generate new public keys + b. sign hash data with priv key +4) Write new PicSeal Public file + a. write encrypted original metadata + b. write PicSeal metadata + i. write JSON + b. write image data +5) Write new PicSeal Private file + a. create thumbnail (smaller files) + b. write PicSeal metadata (JSON) + c. write image data + + +[[ PicSeal Metadata ]] +JSON Format + +{ + "format_ver": "50", + "pubkey_alg": "rsa", + "pubkey_size": 4096, + "pubkey_pem": "MIICIjANBgkqhkiG9w0BAQEFAAOCAg8AMIICCgKCAgEAnvdAyn5JuiRcoL8s+rAV\nMpoEUioB1NTV97EDG4z/R5pm89v86UkH0xeeB6OS2EacsPHmbIY6oY0IXKA+0EX6\nSZdVSya2vv36fy/CByfHgZsDUxpzMnRZLdYbITBwil7keCTOC4EfnYIMUDsvrx6s\nA+BSEbR1rUkIXMe7NJ2Qssj68lXKMOfhrJ9wUgPLVSTIiDytiX4Wd+yAuo9lUqUk\nxt7FwjEEBV5Nj0yKZp2sJZnqp+pL5dsLsYz9xKNQHonMkGuj+3IthenTkfuXm9a6\nx+Qm3B+6AN4qBd7Uz65tffS2e2OQCzSRVoqEaRUrQKvQcSfJv+w0lh4xoZs41CJE\nc8XmmJeaDqt/zYBQWWYJlvZfpq0oh3mKGmHRtNOnKQmMd+FRJj/5fUvG7WedcHt1\noAkUg1qKu/HBgPNTVN9PWaikM1fA1E8T1koCYN0ecP29Zo8SHwcL6g82ou+fGtae\nSrXW0bFKV1JbF+hF3nBzCw+xDPAXXpUGPTwSXYZa6Gvgfckk4qP17vfFaR9f+hiL\nmW8GUmgfupvbapcG189M+UB9nDUSj3557TJpqItdbH5m4FGNv9tcMRYSwIniVVxw\n+F5FuT7nhd8vC+simwyjlB3hteICya1c7tVo5rav/LBBzHYg9ywPJCdZKUvN3qRE\n4Txbp7DC99x/xZhGck2Cpj8CAwEAAQ==", + "image_sig": "6acda44a9e492ddcc0e6ddadbdbd2cc20fdbb06a9264f36268b03aff921332919edb541d9bcdafa2b958276fb553682e5b67d92a127ec8d5d89b29774db86c50", + "metadata_sig": "" +} + diff --git a/docs/picseal_metadata.json b/docs/picseal_metadata.json new file mode 100644 index 0000000..2c4941f --- /dev/null +++ b/docs/picseal_metadata.json @@ -0,0 +1,7 @@ +{ + "format_ver": "50", + "pubkey_alg": "rsa", + "pubkey_size": 4096, + "pubkey_pem": "MIICIjANBgkqhkiG9w0BAQEFAAOCAg8AMIICCgKCAgEAnvdAyn5JuiRcoL8s+rAV\nMpoEUioB1NTV97EDG4z/R5pm89v86UkH0xeeB6OS2EacsPHmbIY6oY0IXKA+0EX6\nSZdVSya2vv36fy/CByfHgZsDUxpzMnRZLdYbITBwil7keCTOC4EfnYIMUDsvrx6s\nA+BSEbR1rUkIXMe7NJ2Qssj68lXKMOfhrJ9wUgPLVSTIiDytiX4Wd+yAuo9lUqUk\nxt7FwjEEBV5Nj0yKZp2sJZnqp+pL5dsLsYz9xKNQHonMkGuj+3IthenTkfuXm9a6\nx+Qm3B+6AN4qBd7Uz65tffS2e2OQCzSRVoqEaRUrQKvQcSfJv+w0lh4xoZs41CJE\nc8XmmJeaDqt/zYBQWWYJlvZfpq0oh3mKGmHRtNOnKQmMd+FRJj/5fUvG7WedcHt1\noAkUg1qKu/HBgPNTVN9PWaikM1fA1E8T1koCYN0ecP29Zo8SHwcL6g82ou+fGtae\nSrXW0bFKV1JbF+hF3nBzCw+xDPAXXpUGPTwSXYZa6Gvgfckk4qP17vfFaR9f+hiL\nmW8GUmgfupvbapcG189M+UB9nDUSj3557TJpqItdbH5m4FGNv9tcMRYSwIniVVxw\n+F5FuT7nhd8vC+simwyjlB3hteICya1c7tVo5rav/LBBzHYg9ywPJCdZKUvN3qRE\n4Txbp7DC99x/xZhGck2Cpj8CAwEAAQ==", + "image_sig": "6acda44a9e492ddcc0e6ddadbdbd2cc20fdbb06a9264f36268b03aff921332919edb541d9bcdafa2b958276fb553682e5b67d92a127ec8d5d89b29774db86c50" +} \ No newline at end of file diff --git a/test/GExiv2_.py b/ex/GExiv2_.py similarity index 100% rename from test/GExiv2_.py rename to ex/GExiv2_.py diff --git a/test/exif_r.py b/ex/exif_r.py similarity index 100% rename from test/exif_r.py rename to ex/exif_r.py diff --git a/test/exif_w.py b/ex/exif_w.py similarity index 100% rename from test/exif_w.py rename to ex/exif_w.py diff --git a/test/sort.py b/ex/sort.py similarity index 100% rename from test/sort.py rename to ex/sort.py diff --git a/test/bmw_rim_640.jpg b/img/bmw_rim_640.jpg similarity index 100% rename from test/bmw_rim_640.jpg rename to img/bmw_rim_640.jpg diff --git a/test/bmw_rim_full.jpg b/img/bmw_rim_full.jpg similarity index 100% rename from test/bmw_rim_full.jpg rename to img/bmw_rim_full.jpg diff --git a/test/space.jpg b/img/space.jpg similarity index 100% rename from test/space.jpg rename to img/space.jpg diff --git a/test/space_test.jpg b/img/space_test.jpg similarity index 100% rename from test/space_test.jpg rename to img/space_test.jpg diff --git a/libs/img_exif.py b/libs/img_exif.py index 7addf28..80c28e5 100644 --- a/libs/img_exif.py +++ b/libs/img_exif.py @@ -1,6 +1,9 @@ # +# DEPRECATED # -# +# ditching this for my own custom code +# uses GExiv2, and the C to Python bindings. but pain in the ass to install and config +# very limited read/wriging...cannot create a new application comment block. writing my own import logging import json import gi diff --git a/libs/jpg_bin.py b/libs/jpg_bin.py index fefb658..ffeac8e 100644 --- a/libs/jpg_bin.py +++ b/libs/jpg_bin.py @@ -20,29 +20,34 @@ import re import struct import logging +from libs.jpg_fp import JpgFingerprint # class JpgBin: BUF_CHUNK_SIZE = 2048 - data_buf = None - data_idx = 0 - data_len = 0 - fh = None - hh = None markers = { 'SOS': 0xffd9 } - continue_process = True - - metadata_h = {} - img_bin_h = {} - - def __init__(self): - pass + self.data_buf = None + self.data_idx = 0 + self.data_len = 0 + self.fh = None + self.hh = None + + self.continue_process = True + + self.metadata_h = {} + self.img_bin_h = {} + + self.prev_fpos = 0 + self.prev_mhex = 0xdead + self.prev_mstr = "DUH!" + + self.jpg_fp = JpgFingerprint() # def __isJPG(self): @@ -61,16 +66,140 @@ class JpgBin: return False while(self.continue_process): - self.findAllMarker() + self.findAllMarkers() self.getMoreBytes() return True return False + # + def findAllMarkers(self): + (word_b,) = struct.unpack('>H', self.data_buf[self.data_idx:self.data_idx+2]) + hex_str = word_b.to_bytes(2, 'big').hex() + # RST 0xD(n) (n==0..7) + if (0xffd0 == word_b or 0xffd1 == word_b or 0xffd2 == word_b or 0xffd3 == word_b or 0xffd4 == word_b or 0xffd5 == word_b or 0xffd6 == word_b or 0xffd7 == word_b): + logging.info("[ {} : RST ]".format(hex_str)) + self.markerRST(word_b) + # Comments section + elif (0xfffe == word_b): + logging.info("[ {} : Comment ]".format(hex_str)) + self.markerComment(word_b) + # DQT + elif (0xffdb == word_b): + logging.info("[ {} : DQT ]".format(hex_str)) + self.markerDQT(word_b) + # DRI + elif (0xffdd == word_b): + logging.info("[ {} : DRI ]".format(hex_str)) + self.markerDRI(word_b) + # SOF0 - Start of Frame 0 + elif (0xffc0 == word_b): + logging.info("[ {} : SOF0 ]".format(hex_str)) + self.markerSOF0(word_b) + # SOF2 - Start of Frame 2 + elif (0xffc2 == word_b): + logging.info("[ {} : SOF2 ]".format(hex_str)) + self.markerSOF2(word_b) + # DHT + elif (0xffC4 == word_b): + logging.info("[ {} : DHT ]".format(hex_str)) + self.markerDHT(word_b) + # SOS + elif (0xffda == word_b): + logging.info("[ {} : SOS ]".format(hex_str)) + self.markerSOS(word_b) + # EOI + elif (0xffd9 == word_b): + logging.info("[ {} : EOI ]".format(hex_str)) + self.markerEOI(word_b) + # APP 0xE(n) - App Exif Data + # struct.pack(">H", intt).hex() + elif ( re.search(r'ffe.', hex_str ) ): + logging.info("[ {} : App Data ]".format(hex_str)) + self.markerAppData(word_b) + else: + self.data_idx += 1 + + # + # Image Metadata, Exif + # + # + def markerAppData(self, marker_hex): + fpos = self.fh.tell() + rec_len = self.calcSeekBytes() + self.jpg_fp.addImgMetadata(marker_hex, fpos, rec_len, "APP ") + + # + def markerComment(self, marker_hex): + fpos = self.fh.tell() + rec_len = self.calcSeekBytes() + self.jpg_fp.addImgMetadata(marker_hex, fpos, rec_len, "COM ") + + # + # Image Data + # + # + def markerSOS(self, marker_hex): + # rec_len = self.calcSeekBytes() + self.__addImgData(marker_hex, "SOS ") + + # + def markerRST(self, marker_hex): + self.__addImgData(marker_hex, "RST ") + + # + def markerDQT(self, marker_hex): + fpos = self.fh.tell() + rec_len = self.calcSeekBytes() + self.jpg_fp.addImgData(marker_hex, fpos, rec_len, "DQT ") + + # + def markerDRI(self, marker_hex): + fpos = self.fh.tell() + self.data_idx += 4 + self.jpg_fp.addImgData(marker_hex, fpos, 4, "DRI ") + + # + def markerSOF0(self, marker_hex): + fpos = self.fh.tell() + rec_len = self.calcSeekBytes() + self.jpg_fp.addImgData(marker_hex, fpos, rec_len, "SOF0") + + # + def markerSOF2(self, marker_hex): + fpos = self.fh.tell() + rec_len = self.calcSeekBytes() + self.jpg_fp.addImgData(marker_hex, fpos, rec_len, "SOF2") + + def markerDHT(self, marker_hex): + fpos = self.fh.tell() + rec_len = self.calcSeekBytes() + self.jpg_fp.addImgData(marker_hex, fpos, rec_len, "DHT ") + + # + def markerEOI(self, marker_hex): + fpos = self.fh.tell() + self.__addImgData(marker_hex, "EOI ") + self.jpg_fp.addImgData(marker_hex, fpos, 2, "EOI ") + + + # private helper function + def __addImgData(self, mhex, mstr): + fpos = self.fh.tell() + cur_fpos = (fpos - (self.data_len - self.data_idx)) + if (self.prev_fpos > 0): + rec_len = cur_fpos - self.prev_fpos + self.jpg_fp.addImgData(self.prev_mhex, self.prev_fpos, rec_len, self.prev_mstr) + + self.prev_mhex = mhex + self.prev_mstr = mstr + self.prev_fpos = cur_fpos + self.data_idx += 2 + # def genHash(self, file_h, hash_h): self.hh = hash_h - self.processFile(file_h) + #self.processFile(file_h) while(self.continue_process): if (self.findMarker(self.makers['SOS'])): @@ -106,6 +235,7 @@ class JpgBin: self.seekBytes(rec_diff) else: self.data_idx += rec_len + return rec_len # def seekBytes(self, num_bytes): @@ -114,107 +244,23 @@ class JpgBin: self.getMoreBytes(True) return pos + # + def printMarkerImg(self): + return self.jpg_fp.printImgMarkers() + + # + def printMarkerMeta(self): + return self.jpg_fp.printMDMarkers() + # def findMarker(self, marker): - pass # - def findAllMarker(self): - (word_b,) = struct.unpack('>H', self.data_buf[self.data_idx:self.data_idx+2]) - hex_str = word_b.to_bytes(2, 'big').hex() - # RST 0xD(n) (n==0..7) - if (0xffd0 == word_b or 0xffd1 == word_b or 0xffd2 == word_b or 0xffd3 == word_b or 0xffd4 == word_b or 0xffd5 == word_b or 0xffd6 == word_b or 0xffd7 == word_b): - logging.info("[ {} : RST ]".format(hex_str)) - self.markerRST() - # Comments section - elif (0xfffe == word_b): - logging.info("[ {} : Comment ]".format(hex_str)) - self.markerComment() - # DQT - elif (0xffdb == word_b): - logging.info("[ {} : DQT ]".format(hex_str)) - self.markerDQT() - # DRI - elif (0xffdd == word_b): - logging.info("[ {} : DRI ]".format(hex_str)) - self.markerDRI() - # SOF0 - Start of Frame 0 - elif (0xffc0 == word_b): - logging.info("[ {} : SOF0 ]".format(hex_str)) - self.markerSOF0() - # SOF2 - Start of Frame 2 - elif (0xffc2 == word_b): - logging.info("[ {} : SOF2 ]".format(hex_str)) - self.markerSOF2() - # DHT - elif (0xffC4 == word_b): - logging.info("[ {} : DHT ]".format(hex_str)) - self.markerDHT() - # SOS - elif (0xffda == word_b): - logging.info("[ {} : SOS ]".format(hex_str)) - self.markerSOS() - # EOI - elif (0xffd9 == word_b): - logging.info("[ {} : EOI ]".format(hex_str)) - self.markerEOI() - # APP 0xE(n) - App Exif Data - # struct.pack(">H", intt).hex() - elif ( re.search(r'ffe.', hex_str ) ): - logging.info("[ {} : App Data ]".format(hex_str)) - self.markerAppData() - else: - self.data_idx += 1 - - - # - # Image Metadata, Exif - # - # - def markerAppData(self): - self.calcSeekBytes() - # - def markerComment(self): - self.calcSeekBytes() - - # - # Image Data - # - # - def markerSOS(self): - self.calcSeekBytes() - - def markerRST(self): - self.data_idx += 2 - - def markerDQT(self): - self.calcSeekBytes() - - # - def markerDRI(self): - self.data_idx += 4 - - # - def markerSOF0(self): - self.calcSeekBytes() - - # - def markerSOF2(self): - self.calcSeekBytes() - - def markerDHT(self): - self.calcSeekBytes() - - def markerEOI(self): - self.data_idx += 2 - # self.continue_process = False - - def __repr__(self): - pass - + return repr(self.jpg_fp) + # def findMarkers222(self): last_idx = len(self.data_buf) diff --git a/libs/jpg_fp.py b/libs/jpg_fp.py new file mode 100644 index 0000000..c6c2148 --- /dev/null +++ b/libs/jpg_fp.py @@ -0,0 +1,69 @@ +# +# +# +class JpgFingerprint: + + def __init__(self): + self.markers_a = [] + self.markers_img = [] + self.markers_meta = [] + # self.markers_h = {} + + + def addImgMetadata(self, mhex, mpos, mlen, mstr): + marker = JpgMarker(mhex, mpos, mlen, mstr) + self.markers_a.append(marker) + self.markers_meta.append(marker) + + # + def addImgData(self, mhex, mpos, mlen, mstr): + marker = JpgMarker(mhex, mpos, mlen, mstr) + self.markers_a.append(marker) + self.markers_img.append(marker) + + # + def printImgMarkers(self): + # print ("Got here dummies") + # print ("len=={}".format(len(self.markers_img))) + # print ("*** *** *** ***\n{}".format(retstr)) + return JpgFingerprint.__printMarker(self.markers_img) + + # + def printMDMarkers(self): + return JpgFingerprint.__printMarker(self.markers_meta) + + # + def __repr__(self): + return JpgFingerprint.__printMarker(self.markers_a) + + # + @staticmethod + def __printMarker(markers): + str = "" + total = 0 + for marker in markers: + str += repr(marker) + "\n" + total += marker.marker_size + str += "[TOT ] bytes=={}".format(total) + str += "\n" + return str + + +# +class JpgMarker: + """ + Marker Data Type + """ + def __init__(self, mhex, fpos, mlen, mstr): + self.marker_hex = mhex + self.marker_hexstr = self.marker_hex.to_bytes(2, 'big').hex() + self.marker_filepos = fpos + self.marker_size = mlen + self.marker_cat = mstr + + + def __repr__(self): + return "[{}] {} {}(len) {}(fpos)".format(self.marker_cat, self.marker_hexstr, self.marker_size, self.marker_filepos) + + + diff --git a/libs/jpg_tools.py b/libs/jpg_tools.py index b559f8c..50219cd 100644 --- a/libs/jpg_tools.py +++ b/libs/jpg_tools.py @@ -1,45 +1,33 @@ # # # -from PIL import Image +import logging +# from PIL import Image from libs.jpg_bin import JpgBin class JpgTools: - fh = None - def __init__(self): + self.fh = None pass + # + # process a jpg file, read only + # def getJpgBin(self, fname): - self.processFile(fname) + self.fh = open(fname, "rb") + self.jpg = JpgBin() + retval = self.jpg.processFile(self.fh) + logging.info("processFile()=={}".format(retval)) + return self.jpg + # + # process a jpg file, create new jpg with crypto keys # def jpgHash(self): self.fh = open(fname, "rb") self.jpg = JpgBin() retval = self.jpg.processFile(self.fh) - - print("processFile()=={}".format(retval)) - - # - def processFile(self, fname): - self.fh = open(fname, "rb") - self.jpg = JpgBin() - retval = self.jpg.processFile(self.fh) - print("processFile()=={}".format(retval)) - - # - def process_OLD(self, fname): - Image.open(fname) - # image as a sequence object containing pixel values - bin_data = list( im.getdata() ) - # returns a string containing pixel data, using the standard "raw" encoder - im.tostring() - - # - # def getJpgBin_OLD(self, fname): - # img_h = Image.open(fname) - # img_h.??? + logging.info("processFile()=={}".format(retval)) diff --git a/picseal.py b/picseal.py index 1fd98ae..367c361 100644 --- a/picseal.py +++ b/picseal.py @@ -7,10 +7,13 @@ import logging from shutil import copyfile #from subprocess import Popen, PIPE, check_call from libs.crypto_pub import Signature -from libs.img_exif import ImgExif from libs.toolbox import Toolbox from libs.jpg_tools import JpgTools +fingerprint = False +printmeta = False +printimage = False + def main(): parseArgs() @@ -21,8 +24,10 @@ def main(): # export signature & public key to a new image file def processImage(image_fn): jpg = JpgTools() - img_bin = jpg.getJpgBin(image_fn) - + jpg_bin = jpg.getJpgBin(image_fn) + + printImageInfo(jpg_bin) + # sig = Signature() # sig.genSig(img_bin) @@ -34,17 +39,19 @@ def processImage(image_fn): # add a digital signature to the metadata def writePubImg(pub_fn, sig): - img = ImgExif(pub_fn) - img.addKey(sig.getPubKeyPEM()) - img.addSig(sig.sig_data) - img.saveFile() + pass + # img = ImgExif(pub_fn) + # img.addKey(sig.getPubKeyPEM()) + # img.addSig(sig.sig_data) + # img.saveFile() # def writePrivImg(priv_fn, sig): - img = ImgExif(priv_fn) - img.addKey(sig.getPrivKeyPEM()) + pass + # img = ImgExif(priv_fn) + # img.addKey(sig.getPrivKeyPEM()) #img.addSig(sig.sig_data) - img.saveFile() + # img.saveFile() # def copyImage(image_fn): @@ -55,16 +62,30 @@ def copyImage(image_fn): copyfile(image_fn, newFileName) return (pubFileName, privFileName) +# +def printImageInfo(jpg_bin2): + if (fingerprint): + print( str(jpg_bin2) ) + + if (printimage): + print( jpg_bin2.printMarkerImg()) + + if (printmeta): + print( jpg_bin2.printMarkerMeta()) + def parseArgs(): print("***** ***** ***** *****") print(" ** Pic * Seal ** ") print("***** ***** ***** *****\n") parser = argparse.ArgumentParser() - parser.add_argument('-i', '--image', required=False, help="source image file") + parser.add_argument('-f', '--file', required=False, help="source image file") parser.add_argument('-v', '--verbose', action='store_true', help="will set logging level to INFO") parser.add_argument('-vv', '--vverbose', action='store_true', help="will set logging level to DEBUG") parser.add_argument('-l', '--logging', action='store_true', help="will supercede the -v option and send all logging to a file, logging.DEBUG") + parser.add_argument('-pm', '--printmeta', action='store_true', help="print the metadata markers") + parser.add_argument('-pi', '--printimage', action='store_true', help="print the image markers") + parser.add_argument('-fp', '--fingerprint', action='store_true', help="fingerprint") args = parser.parse_args() if (args.logging): @@ -77,8 +98,20 @@ def parseArgs(): else: logging.basicConfig(level=logging.CRITICAL) - if (args.image): - processImage(args.image) + if (args.fingerprint): + global fingerprint + fingerprint = True + + if (args.printmeta): + global printmeta + printmeta = True + + if (args.printimage): + global printimage + printimage = True + + if (args.file): + processImage(args.file) else: print('Create PicSeal images') print(' picseal.py -i ') diff --git a/pip-req.txt b/pip-req.txt index 2bd77a7..ded0367 100644 --- a/pip-req.txt +++ b/pip-req.txt @@ -1,5 +1,2 @@ -Pillow==3.3.1 pycrypto==2.6.1 -pygobject==3.20.1 -requests==2.11.0 -shellescape==3.4.1 + diff --git a/test/decrypt_rsa.py b/test/decrypt_rsa.py deleted file mode 100644 index 7cab933..0000000 --- a/test/decrypt_rsa.py +++ /dev/null @@ -1,20 +0,0 @@ -#!/usr/bin/python -# -# -from Crypto.PublicKey import RSA - -def decrypt(): - externKey="/home/borrajax/myTestKey.pub" - publickey = open(externKey, "r") - decryptor = RSA.importKey(publickey, passphrase="f00bar") - retval=None - - file = open("/tmp/cryptThingy.txt", "rb") - retval = decryptor.decrypt(file.read()) - file.close() - return retval - - -if __name__ == "__main__": - decryptedThingy=decrypt() - print "Decrypted: %s" % decryptedThingy diff --git a/test/encrypt_rsa.py b/test/encrypt_rsa.py deleted file mode 100644 index 081ac37..0000000 --- a/test/encrypt_rsa.py +++ /dev/null @@ -1,17 +0,0 @@ -#!/usr/bin/python -# -# -from Crypto.PublicKey import RSA - -def encrypt(message): - externKey="/home/borrajax/myTestKey.pem" - privatekey = open(externKey, "r") - encryptor = RSA.importKey(privatekey, passphrase="f00bar") - encriptedData=encryptor.encrypt(message, 0) - file = open("/tmp/cryptThingy.txt", "wb") - file.write(encriptedData[0]) - file.close() - -if __name__ == "__main__": - encryptedThingy=encrypt("Loren ipsum") - \ No newline at end of file diff --git a/test/hieee.sdf.sdf.. b/test/hieee.sdf.sdf.. deleted file mode 100644 index e69de29..0000000 diff --git a/test/signature_test.py b/test/signature_test.py index 3b34ad1..502bd47 100644 --- a/test/signature_test.py +++ b/test/signature_test.py @@ -1,7 +1,8 @@ # # Test Signature Class # -from ..libs.crypto_pub import Signature +# run from root of project +from libs.crypto_pub import Signature msg = b'Hieee, this is a test =)'