Compare commits
No commits in common. "master" and "hash" have entirely different histories.
|
|
@ -1,11 +1,3 @@
|
|||
# Project specific files
|
||||
#
|
||||
# binaries
|
||||
*.jpg
|
||||
|
||||
|
||||
# Python Files
|
||||
#
|
||||
# Byte-compiled / optimized / DLL files
|
||||
__pycache__/
|
||||
*.py[cod]
|
||||
|
|
@ -45,8 +37,11 @@ coverage.xml
|
|||
*.mo
|
||||
*.pot
|
||||
|
||||
# Django stuff:
|
||||
*.log
|
||||
|
||||
# Sphinx documentation
|
||||
docs/_build/
|
||||
|
||||
#binaries
|
||||
*.jpg
|
||||
|
|
|
|||
39
README
39
README
|
|
@ -1,39 +0,0 @@
|
|||
***** ***** ***** *****
|
||||
*** Picseal ***
|
||||
***** ***** ***** *****
|
||||
|
||||
Your Photo is your Crypto Key
|
||||
-protect from photo modification
|
||||
-verify photo ownership
|
||||
-proof of photo originality, proof of photo ownership. (hash time stamp, Certified Secret tech)
|
||||
-use a photo as authentication (what you have), requires chain-of-custody, requires time stamp verify
|
||||
-generate a new key pair to share with every photo
|
||||
|
||||
|
||||
|
||||
[[ Requirements ]]
|
||||
Python 3
|
||||
pycrypto==2.6.1
|
||||
|
||||
|
||||
|
||||
[[ Usage ]]
|
||||
Create PicSeal images
|
||||
picseal.py -i <image_file>
|
||||
|
||||
***** ***** ***** *****
|
||||
|
||||
usage: picseal.py [-h] [-f FILE] [-v] [-vv] [-l] [-pm] [-pi] [-pa] [-w]
|
||||
|
||||
optional arguments:
|
||||
-h, --help show this help message and exit
|
||||
-f FILE, --file FILE source image file
|
||||
-v, --verbose will set logging level to INFO
|
||||
-vv, --vverbose will set logging level to DEBUG
|
||||
-l, --logging will supercede the -v option and send all logging to a
|
||||
file, logging.DEBUG
|
||||
-pm, --printmeta print the metadata markers
|
||||
-pi, --printimage print the image markers
|
||||
-pa, --printall print all markers
|
||||
-w, --write write picseal files
|
||||
|
||||
|
|
@ -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 ]]
|
||||
|
||||
|
||||
|
|
@ -1,26 +0,0 @@
|
|||
[[[ PicSeal Agile ]]]
|
||||
|
||||
|
||||
[[ BACKLOG ]]
|
||||
|
||||
[ current (ver 1.0) ]
|
||||
|
||||
|
||||
|
||||
[ future ]
|
||||
* add option "-d" to decrypt file
|
||||
* add option "-e" to encrypt file
|
||||
* add option "-ex" to export the key to .PEM file
|
||||
* add visual stamp to the file so that a picseal photo can be recognized
|
||||
* use block-chain for "certified secret" technology, insert hash signature into block-chain
|
||||
|
||||
|
||||
|
||||
[[ COMPLETED ]]
|
||||
|
||||
[ current (ver 1.0) ]
|
||||
* reads a picseal .jpg file, understands pub/pvt keys from file
|
||||
* writes new .jpg files with crypto keys
|
||||
* verbose output of image data chunks
|
||||
* reads .jpg file, parses data tags
|
||||
|
||||
|
|
@ -1,60 +0,0 @@
|
|||
[[[ PicSeal Design Document ]]]
|
||||
|
||||
|
||||
[[ 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 key pair
|
||||
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 ]]
|
||||
|
||||
[ binary data blob format ]
|
||||
# Pub: [app15:2|size:2|'picseal':7|type:1|sig:512|pubkey:550]
|
||||
# Pvt: [app15:2|size:2|'picseal':7|type:1|sig:512|pvtkey:2347]
|
||||
|
||||
# 1. app15 jpg marker
|
||||
app15_marker = b'\xff\xef'
|
||||
|
||||
# 2. ascii code for "picseal"
|
||||
picseal_marker = b'\x70\x69\x63\x73\x65\x61\x6C'
|
||||
|
||||
# 3. type
|
||||
pub_marker = b'\x01'
|
||||
pvt_marker = b'\x02'
|
||||
|
||||
# 4. signature is the hash of the image, signed by the private key
|
||||
|
||||
# 5. JSON crypto blob data
|
||||
|
||||
|
||||
|
||||
[ crypto blob format ]
|
||||
JSON:
|
||||
{
|
||||
"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": ""
|
||||
}
|
||||
|
||||
215
docs/ps_proj_dev
215
docs/ps_proj_dev
|
|
@ -1,215 +0,0 @@
|
|||
[[[ Picseal ]]]
|
||||
|
||||
"crypto key sharing using your photos"
|
||||
"exif data providing photo integrity, ownership, and crypto key"
|
||||
|
||||
|
||||
[[ Formats]]
|
||||
@ http://stackoverflow.com/questions/12749858/rsa-public-key-format
|
||||
@ http://security.stackexchange.com/questions/57043/most-popular-rsa-key-format
|
||||
|
||||
DER: binary encoded format, sometimes Asn.1 BER-encoded
|
||||
PEM: base64 format of the same DER-encoded file with header&footer lines
|
||||
XML: xml format
|
||||
|
||||
-----BEGIN RSA PUBLIC KEY-----
|
||||
-----END RSA PUBLIC KEY-----
|
||||
|
||||
---- BEGIN SSH2 PUBLIC KEY ----
|
||||
---- END SSH2 PUBLIC KEY ----
|
||||
|
||||
|
||||
|
||||
[[ WolfSSL ]]
|
||||
|
||||
[ Compile Mac ]
|
||||
@ https://www.wolfssl.com/wolfSSL/Docs-wolfssl-manual-toc.html
|
||||
@ https://www.wolfssl.com/wolfSSL/Docs-wolfssl-manual-2-building-wolfssl.html
|
||||
@ https://wolfssl.github.io/wolfcrypt-py/
|
||||
|
||||
$ brew install autoconf automake libtool
|
||||
$ glibtoolize
|
||||
$ git clone https://github.com/wolfssl/wolfssl.git
|
||||
$ cd wolfssl/
|
||||
$ ./autogen.sh
|
||||
$ ./configure --help
|
||||
$ ./configure --enable-sha512
|
||||
$ make
|
||||
|
||||
|
||||
"In order to prevent conflicts with Apple's own libtool we have prepended a "g"
|
||||
so, you have instead: glibtool and glibtoolize."
|
||||
|
||||
|
||||
|
||||
[[ gexiv2 ]]
|
||||
# gobject wrapper of the libexiv2 library (which is the engine for exiv2)
|
||||
@ https://wiki.gnome.org/Projects/gexiv2
|
||||
@ http://wiki.gnome.org/GObjectIntrospection
|
||||
@ http://wiki.gnome.org/PyGObject/IntrospectionPorting
|
||||
|
||||
# API docs
|
||||
@ http://lazka.github.io/pgi-docs/#GExiv2-0.10
|
||||
|
||||
[ XMP ]
|
||||
# adding new XMP namespaces
|
||||
@ http://dev.exiv2.org/boards/3/topics/1039
|
||||
|
||||
|
||||
[ install ubuntu ]
|
||||
$ sudo apt-get install libexiv2-dev libgexiv2-dev gir1.2-gexiv2-0.10
|
||||
|
||||
[ install mac ]
|
||||
# install globally (python2)
|
||||
$ brew install exiv2 gexiv2 pygobject3
|
||||
|
||||
# install for python3
|
||||
$ brew reinstall pygobject3 --with-python3
|
||||
|
||||
|
||||
[ build ]
|
||||
$ git clone git://git.gnome.org/gexiv2
|
||||
$ sudo apt-get install gobject-introspection
|
||||
$ sudo apt-get install glib libgirepository1.0-dev
|
||||
$ sudo apt-get install libglib2.0-dev libexiv2-dev python-gobject-dev
|
||||
$
|
||||
$ ./autogen.sh
|
||||
$ ./configure --enable-introspection
|
||||
|
||||
|
||||
|
||||
[[ EXIF ]]
|
||||
|
||||
[ exiv2 ]
|
||||
# BUILD
|
||||
http://dev.exiv2.org/projects/exiv2/wiki/How_do_I_build_Exiv2_on_the_XYZ_platform
|
||||
|
||||
[ pyexiv2 ]
|
||||
@ http://tilloy.net/dev/pyexiv2/
|
||||
|
||||
|
||||
[ exiftool]
|
||||
# perl program
|
||||
@ http://www.sno.phy.queensu.ca/~phil/exiftool/
|
||||
|
||||
$ brew install exiftool
|
||||
|
||||
|
||||
[[ exiftool]]
|
||||
# perl program
|
||||
# http://www.sno.phy.queensu.ca/~phil/exiftool/
|
||||
|
||||
$ brew install exiftool
|
||||
|
||||
|
||||
|
||||
|
||||
[[ Competitors ]]
|
||||
|
||||
[ authentication ]
|
||||
https://getclef.com/
|
||||
|
||||
|
||||
|
||||
[[ Debug ]]
|
||||
|
||||
$ exiv2 -ps image.jpg
|
||||
|
||||
|
||||
[[ Crypto ]]
|
||||
|
||||
[ Public Key ]
|
||||
|
||||
# encrypt
|
||||
@ https://pythonhosted.org/pycrypto/Crypto.Cipher.PKCS1_v1_5.PKCS115_Cipher-class.html#encrypt
|
||||
|
||||
|
||||
[ OpenSSL ]
|
||||
|
||||
openssl genrsa -out ~/myTestKey.pem -passout pass:"f00bar" -des3 2048
|
||||
openssl rsa -pubout -in ~/myTestKey.pem -passin pass:"f00bar" -out ~/myTestKey.pub
|
||||
|
||||
|
||||
[ Cryptography.io ]
|
||||
Fernet (Public Key)
|
||||
@ https://cryptography.io/en/latest/fernet/
|
||||
|
||||
|
||||
|
||||
[[ MVP Plan ]]
|
||||
|
||||
[ C ]
|
||||
libexiv --> exif read/write
|
||||
libwolfss --> crypto generation
|
||||
|
||||
[ Functions ]
|
||||
# load image file
|
||||
picseal.init(image_file)
|
||||
|
||||
# create pub/priv key pair
|
||||
# add new public key to exif
|
||||
# store priv key in memory
|
||||
picseal.add_new_key(CIPHER_TYPE)
|
||||
|
||||
# hash the image, encrypt hash with priv-key
|
||||
# hash the image & specific exif tags, encrypt hash with priv-key
|
||||
# add signatures to exif
|
||||
picseal.seal_image(HASH_TYPE)
|
||||
|
||||
# write the image with public key, and signature
|
||||
picseal.write_pub(FILE_HANDLE)
|
||||
|
||||
|
||||
# write the image with private key, and signature
|
||||
# ADD PIN??
|
||||
picseal.write_priv(FILE_HANDLE)
|
||||
|
||||
[ seal ]
|
||||
sha-512,public-key
|
||||
|
||||
|
||||
[[ Libraries ]]
|
||||
libexiv2-14 (= 0.25-2.1), libc6 (>= 2.14), libgcc1 (>= 1:4.1.1), libstdc++6 (>= 5.2)
|
||||
|
||||
|
||||
[[ Crypto ]]
|
||||
@ http://security.stackexchange.com/
|
||||
@ http://security.stackexchange.com/questions/135946/when-should-i-generate-new-public-private-keys-using-rsa
|
||||
|
||||
|
||||
|
||||
|
||||
[[ Images ]]
|
||||
|
||||
[ PIL Pillow ]
|
||||
# use pillow, a PIL fork
|
||||
@ https://github.com/python-pillow/Pillow
|
||||
@ http://pillow.readthedocs.io/en/3.0.x/handbook/tutorial.html#reading-and-writing-images
|
||||
|
||||
# no python 3 version :(
|
||||
ver 1.1.7, for Python 2.7, Nov 15 2009
|
||||
|
||||
|
||||
[[ JPG ]]
|
||||
@ http://stackoverflow.com/questions/4550296/how-to-identify-contents-of-a-byte-is-a-jpeg?rq=1
|
||||
@ https://en.wikipedia.org/wiki/Magic_number_%28programming%29
|
||||
@ http://www.effbot.org/imagingbook/image.htm#tag-Image.Image.tobitmap
|
||||
|
||||
|
||||
|
||||
[[ Testing ]]
|
||||
|
||||
|
||||
[ crypto test ]
|
||||
b'-----BEGIN PUBLIC KEY-----\nMIICIjANBgkqhkiG9w0BAQEFAAOCAg8AMIICCgKCAgEAnvdAyn5JuiRcoL8s+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==\n-----END PUBLIC KEY-----'
|
||||
privkey==
|
||||
b'-----BEGIN RSA PRIVATE KEY-----\n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\n-----END RSA PRIVATE KEY-----'
|
||||
text: attack at dawn
|
||||
crypt: b'A!\x81\t\xac\x87n4qw\x84\xcb\xf1\xdc\x1b\xc0\xdcjQ\xecB:A\xae\x14\xcc\xa9IpV\xa6\xc3\x10\xa2G\xc6\xae\xbbb} \xcf\x12\xfd\x92h\xb9\xd1\xc7\x80\xd9Q\xc1\xd8\x9b\x0c\x04\x14@C\xaa6\x0e\xdc\x95\xbf\xd0\x99\x0fV;i\x14\xbb\x86\xe1\xa2\xbfV]\xa4\x97t\xc7L\xc9sh\xd5\xbc6\x03\xea\xe7\x07a\x01\x08\x0e\\\x90|\xacT\xc2\xfb\xb8\xf7\xa9\xc7\x0c\x8a\xd9\xdbD\x8c\x98aWS\xdf.h\xb6\xb2H\x13\xdac\xd9\x7fM\x85\x8e\xa3\x00_\xeb\x15;\xd9J6\xc0\x99\xef\xf7\xf1D\xdb\xe8\x05\xdf\x16\xf9\x07\x13Gtv\xd9^\xcfr>\xd8w\xc6\xf0\xb8\xff\x1b\xf9rt%#\xc0\x83!\xb85\xb3X\xfd\x1a~\xd51D\x0c~3\xeb\x9fRc*\xb4\xa0\x84v?vI\xe00\xdd\x16\xc1u\x9e\xad`-P\xf8/N<83o\x8d\xb0Q\x82\xd5Z\x13\x8e\xee\xfa\xc0\x89.%\xeb\xdd\xc8.P\x03\x87\xe4C7e\xe9\xd1\x13\n\x83\xe9\x89\x0f^\xfb\xc8$\x95\x7f\xe3\x80G\xf9\x96_l\x03\x92\x01\x9e\xd1\x13\x9e\x8b\xdc-C\x02/^\xb5Nl\xcc\x7f\xb6\xb8v\x95\x998\x0c\x18?@\xaf\xaa\x1e\xea;gt\x83d(G\xefO\x1a\rq9>\xc4\xa5\x01-\xcd?q\x94\xf9\xa0\xa0\xe6}\x9aJjp\xa0\x05\xaf\xda\xf2\xc9\xd8\xf0\x1b\xbe\x0b\x80\xdc\x9c\xed\xabu\xc6\x0eT\x11\xbd8\x12\x10@\x9c\xf8\x97E\xea;\xcb\xf3\xaf\xf9\xc7\xad@\xf3#G\xc9&yE=\xa3nZ\xc7(9\xf15e\xae\xe4\xbdXmw\x98\xf5B\x885$w\x9b\x82\x89\xfa\xf6\xe9\xc9\xea\x95[22\xf4\xc9\xb9\xf9\xfb\xe2\xb4\xe8\xe7\xb2J\xc59\xac\x9e\xa9e\x13\xb4\x1c\xea\x18\xacy+\xb3\x01@$\xef\x15Ao\xb7Y\xf5]0I(\xd1\xd8E\x01^\xba\xa4W\x96D\xe5i\xf4e\x08c\xd37\x15\x1fb\xe5\xca\x9eAN^\xc0\xc8\x82\xa9\xb5N\xd2~\xc6d\xf7\xcbq\x02\x90\x1a#I\xf4\xf8\xe7i\xba\xcdQ\xeb\x87Agn\x8f'
|
||||
text: b'attack at dawn'
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
|
@ -15,11 +15,13 @@ class Signature:
|
|||
self.sig_data = None
|
||||
self.hash_data = None
|
||||
self.hh = SHA512.new()
|
||||
self.genKeys()
|
||||
|
||||
#
|
||||
def genSig(self):
|
||||
def genSig(self, hshh):
|
||||
signer = PKCS1_v1_5.new(self.key_data)
|
||||
self.sig_data = signer.sign(self.hh)
|
||||
self.sig_data = signer.sign(hshh)
|
||||
return self.sig_data
|
||||
|
||||
#
|
||||
def verifySig(self, hshh, bin_sig):
|
||||
|
|
@ -85,20 +87,14 @@ class Signature:
|
|||
|
||||
#
|
||||
def _test():
|
||||
#logging.basicConfig(level=logging.DEBUG)
|
||||
|
||||
msg = b'Hieee, this is a test =)'
|
||||
|
||||
sig = Signature()
|
||||
sig.hh.update(msg)
|
||||
sig.hash_data = sig.hh.digest()
|
||||
sig.genSig()
|
||||
print("Hash created:\n{}".format(sig.hash_data))
|
||||
print("Size=={}".format(str(len(sig.hash_data))))
|
||||
print("Signature created:\n{}".format(sig.sig_data))
|
||||
print("Size=={}".format(str(len(sig.sig_data))))
|
||||
|
||||
isVerified = sig.verifySig(sig.hh, sig.sig_data)
|
||||
sig = Signature()
|
||||
sig_data = sig.genSig(msg)
|
||||
print("Signature created")
|
||||
print("sig_data=={}".format(sig_data))
|
||||
|
||||
isVerified = sig.verifySig(msg, sig_data)
|
||||
print("isVerified=={}".format(str(isVerified)))
|
||||
|
||||
|
||||
|
|
|
|||
|
|
@ -21,16 +21,11 @@ import re
|
|||
import struct
|
||||
import logging
|
||||
from libs.jpg_fp import JpgFingerprint
|
||||
from libs.jpg_picseal import JpgPicSeal
|
||||
|
||||
#
|
||||
class JpgBin:
|
||||
|
||||
BUF_CHUNK_SIZE = 8192
|
||||
|
||||
soi_marker = b'\xff\xd8'
|
||||
eof_marker = b'\xff\xd9'
|
||||
|
||||
BUF_CHUNK_SIZE = 2048
|
||||
|
||||
def __init__(self):
|
||||
self.data_buf = None
|
||||
|
|
@ -49,8 +44,7 @@ class JpgBin:
|
|||
self.prev_mstr = "DUH!"
|
||||
self.prev_imgData = False
|
||||
|
||||
self.jpg_fp = JpgFingerprint()
|
||||
self.importSig = None
|
||||
self.jpg_fp = JpgFingerprint()
|
||||
|
||||
#
|
||||
# check for JPG file type marker
|
||||
|
|
@ -147,20 +141,14 @@ class JpgBin:
|
|||
#
|
||||
#
|
||||
def markerAppData(self, marker_hex):
|
||||
seek = True
|
||||
self.__addPrevMarkerData(marker_hex, "APP ", False)
|
||||
if (0xffef == marker_hex):
|
||||
if (self.__processPicSeal()):
|
||||
seek = False
|
||||
|
||||
if (seek):
|
||||
(rec_len, prev_buf) = self.__calcSeekBytes()
|
||||
logging.info("length=={}".format(str(rec_len)))
|
||||
rec_len = self.__calcSeekBytes()
|
||||
logging.info("length=={}".format(str(rec_len)))
|
||||
|
||||
#
|
||||
def markerComment(self, marker_hex):
|
||||
self.__addPrevMarkerData(marker_hex, "COM ", False)
|
||||
self.__calcSeekBytes()
|
||||
rec_len = self.__calcSeekBytes()
|
||||
|
||||
#
|
||||
# Image Data
|
||||
|
|
@ -179,7 +167,7 @@ class JpgBin:
|
|||
#
|
||||
def markerDQT(self, marker_hex):
|
||||
self.__addPrevMarkerData(marker_hex, "DQT ")
|
||||
self.__calcSeekBytes()
|
||||
rec_len = self.__calcSeekBytes()
|
||||
|
||||
#
|
||||
def markerDRI(self, marker_hex):
|
||||
|
|
@ -189,16 +177,16 @@ class JpgBin:
|
|||
#
|
||||
def markerSOF0(self, marker_hex):
|
||||
self.__addPrevMarkerData(marker_hex, "SOF0")
|
||||
self.__calcSeekBytes()
|
||||
rec_len = self.__calcSeekBytes()
|
||||
|
||||
#
|
||||
def markerSOF2(self, marker_hex):
|
||||
self.__addPrevMarkerData(marker_hex, "SOF2")
|
||||
self.__calcSeekBytes()
|
||||
rec_len = self.__calcSeekBytes()
|
||||
|
||||
def markerDHT(self, marker_hex):
|
||||
self.__addPrevMarkerData(marker_hex, "DHT ")
|
||||
self.__calcSeekBytes()
|
||||
rec_len = self.__calcSeekBytes()
|
||||
|
||||
#
|
||||
# end of file marker is never added to the marker array list (same as start of image marker)
|
||||
|
|
@ -239,17 +227,15 @@ class JpgBin:
|
|||
# move the index 2 bytes, then read the 2 bytes to get the length
|
||||
#
|
||||
def __calcSeekBytes(self):
|
||||
prev_buf = None
|
||||
self.data_idx += 2
|
||||
(rec_len,) = struct.unpack('>H', self.data_buf[self.data_idx:self.data_idx+2])
|
||||
remain_bytes = self.data_len - self.data_idx
|
||||
if (rec_len >= remain_bytes):
|
||||
prev_buf = self.data_buf[self.data_idx:]
|
||||
rec_diff = rec_len - remain_bytes
|
||||
self.__seekBytes(rec_diff)
|
||||
else:
|
||||
self.data_idx += rec_len
|
||||
return rec_len, prev_buf
|
||||
return rec_len
|
||||
|
||||
#
|
||||
def __seekBytes(self, num_bytes):
|
||||
|
|
@ -258,36 +244,6 @@ class JpgBin:
|
|||
logging.debug("SEEK: seek=={}, cur_loc=={}".format(num_bytes, pos))
|
||||
return pos
|
||||
|
||||
#
|
||||
def __getBuf(self):
|
||||
new_buf = None
|
||||
(rec_len, prev_buf) = self.__calcSeekBytes()
|
||||
if (prev_buf):
|
||||
remain_bytes = rec_len - len(prev_buf)
|
||||
new_buf = prev_buf.join(self.data_buf[:remain_bytes])
|
||||
pass
|
||||
else:
|
||||
new_buf = self.data_buf[(self.data_idx-rec_len):self.data_idx]
|
||||
return new_buf
|
||||
|
||||
#
|
||||
# [app_rec_header:2|len:2|picseal_header:7]
|
||||
#
|
||||
def __processPicSeal(self):
|
||||
rec_hdr = 4
|
||||
ps_hdr_size = rec_hdr+len(JpgPicSeal.picseal_marker)
|
||||
remain_buf = self.data_len-(self.data_idx+ps_hdr_size)
|
||||
if (remain_buf > ps_hdr_size):
|
||||
if (JpgPicSeal.isPicSeal(self.data_buf[self.data_idx+rec_hdr:self.data_idx+ps_hdr_size])):
|
||||
#
|
||||
# calculate size, check buffer, maybe read more bytes from file
|
||||
#
|
||||
buf = self.__getBuf()
|
||||
self.importSig = JpgPicSeal.deserialize(buf)
|
||||
return True
|
||||
return False
|
||||
|
||||
|
||||
#
|
||||
def printMarkerImg(self):
|
||||
return self.jpg_fp.printImgMarkers()
|
||||
|
|
|
|||
|
|
@ -1,79 +0,0 @@
|
|||
#
|
||||
# I tihnk this is support for the website =)
|
||||
# instead of reading from a file, read from memory
|
||||
# goal is to avoid reading/writing to the file system
|
||||
#
|
||||
from libs.jpg_bin import JpgBin
|
||||
from libs.jpg_picseal import JpgPicSeal
|
||||
|
||||
|
||||
class JpgBinWriteMem:
|
||||
|
||||
|
||||
#
|
||||
def __init__(self, jpg_in):
|
||||
self.fhr = jpg_in
|
||||
self.mem = none
|
||||
|
||||
#
|
||||
# input is the Crypto Sig class
|
||||
#
|
||||
def writeJpgPicSealPub(self, crypto_sig, fp):
|
||||
self.__writeJpgHeader()
|
||||
|
||||
ps = JpgPicSeal(crypto_sig)
|
||||
ps.writePub(self.fhw)
|
||||
|
||||
self.__writeJpgImg(fp)
|
||||
|
||||
#
|
||||
# input is the Crypto Sig class
|
||||
#
|
||||
def writeJpgPicSealPvt(self, crypto_sig, fp):
|
||||
self.__writeJpgHeader()
|
||||
|
||||
ps = JpgPicSeal(crypto_sig)
|
||||
ps.writePvt(self.fhw)
|
||||
|
||||
self.__writeJpgImg(fp)
|
||||
|
||||
|
||||
#
|
||||
def __writeJpgImg(self, fp):
|
||||
self.__writeJpgMetadata(fp.markers_meta)
|
||||
self.__writeJpgImgData(fp.markers_img)
|
||||
self.__writeJpgFooter()
|
||||
self.fhw.flush()
|
||||
self.fhw.close()
|
||||
|
||||
#
|
||||
# array of the marker information
|
||||
#
|
||||
def __writeJpgMetadata(self, markers):
|
||||
for marker in markers:
|
||||
self.fhr.seek(marker.fpos)
|
||||
data = self.fhr.read(marker.len)
|
||||
self.fhw.write(data)
|
||||
self.fhw.flush()
|
||||
|
||||
#
|
||||
# array of the marker information
|
||||
#
|
||||
def __writeJpgImgData(self, markers):
|
||||
for marker in markers:
|
||||
# most of the image data is sequential
|
||||
cpos = self.fhr.tell()
|
||||
if (marker.fpos != cpos):
|
||||
self.fhr.seek(marker.fpos)
|
||||
|
||||
data = self.fhr.read(marker.len)
|
||||
self.fhw.write(data)
|
||||
self.fhw.flush()
|
||||
|
||||
#
|
||||
def __writeJpgHeader(self):
|
||||
self.fhw.write(bytes(JpgBin.soi_marker))
|
||||
|
||||
#
|
||||
def __writeJpgFooter(self):
|
||||
self.fhw.write(bytes(JpgBin.eof_marker))
|
||||
|
|
@ -1,13 +1,13 @@
|
|||
#
|
||||
#
|
||||
#
|
||||
from libs.jpg_bin import JpgBin
|
||||
from libs.jpg_picseal import JpgPicSeal
|
||||
|
||||
|
||||
class JpgBinWrite:
|
||||
|
||||
|
||||
soi_marker = b'\xff\xd8'
|
||||
eof_marker = b'\xff\xd9'
|
||||
picseal_marker = b'\xff\xef'
|
||||
# picseal_marker = b'\xff\xe0'
|
||||
|
||||
#
|
||||
def __init__(self, jpg_in, jpg_out):
|
||||
self.fhr = jpg_in
|
||||
|
|
@ -18,7 +18,9 @@ class JpgBinWrite:
|
|||
#
|
||||
def writeJpgPicSealPub(self, crypto_sig, fp):
|
||||
self.__writeJpgHeader()
|
||||
JpgPicSeal.writePub(self.fhw, crypto_sig)
|
||||
|
||||
self.__writeJpgPicSeal(crypto_sig, crypto_sig.getPubKeyDER())
|
||||
|
||||
self.__writeJpgImg(fp)
|
||||
|
||||
#
|
||||
|
|
@ -26,10 +28,28 @@ class JpgBinWrite:
|
|||
#
|
||||
def writeJpgPicSealPvt(self, crypto_sig, fp):
|
||||
self.__writeJpgHeader()
|
||||
JpgPicSeal.writePvt(self.fhw, crypto_sig)
|
||||
|
||||
self.__writeJpgPicSeal(crypto_sig, crypto_sig.getPvtKeyDER())
|
||||
|
||||
self.__writeJpgImg(fp)
|
||||
|
||||
|
||||
#
|
||||
def __writeJpgPicSeal(self, crypto_sig, keyder):
|
||||
# must include 2 bytes of length too
|
||||
size = 2
|
||||
size += len(crypto_sig.hash_data)
|
||||
size += len(keyder)
|
||||
|
||||
# write picseal marker
|
||||
self.fhw.write(JpgBinWrite.picseal_marker)
|
||||
# write the size of picseal record
|
||||
self.fhw.write(size.to_bytes(2, byteorder='big'))
|
||||
# write hash of image
|
||||
self.fhw.write(crypto_sig.hash_data)
|
||||
# write the public key
|
||||
self.fhw.write(keyder)
|
||||
|
||||
#
|
||||
def __writeJpgImg(self, fp):
|
||||
self.__writeJpgMetadata(fp.markers_meta)
|
||||
|
|
@ -64,8 +84,8 @@ class JpgBinWrite:
|
|||
|
||||
#
|
||||
def __writeJpgHeader(self):
|
||||
self.fhw.write(bytes(JpgBin.soi_marker))
|
||||
self.fhw.write(bytes(JpgBinWrite.soi_marker))
|
||||
|
||||
#
|
||||
def __writeJpgFooter(self):
|
||||
self.fhw.write(bytes(JpgBin.eof_marker))
|
||||
self.fhw.write(bytes(JpgBinWrite.eof_marker))
|
||||
|
|
|
|||
|
|
@ -1,5 +1,5 @@
|
|||
#
|
||||
# This Class contains information for the jpg markers, length, and position in the file
|
||||
# This Class contains information for the jpg markers, length, an position in the file
|
||||
#
|
||||
class JpgFingerprint:
|
||||
|
||||
|
|
|
|||
|
|
@ -4,115 +4,19 @@
|
|||
# Data: public key, private key, image hash, image signature
|
||||
# Has: JPG fingerprint
|
||||
#
|
||||
# Big-endian
|
||||
# Pub: [app15:2|size:2|'picseal':7|type:1|sig:512|pubkey:550]
|
||||
# Pvt: [app15:2|size:2|'picseal':7|type:1|sig:512|pvtkey:2347]
|
||||
#
|
||||
import struct
|
||||
import logging
|
||||
from Crypto.PublicKey import RSA
|
||||
from libs.crypto_sig import Signature
|
||||
|
||||
|
||||
class JpgPicSeal:
|
||||
|
||||
app15_marker = b'\xff\xef'
|
||||
# ascii code for "picseal"
|
||||
picseal_marker = b'\x70\x69\x63\x73\x65\x61\x6C'
|
||||
pub_marker = b'\x01'
|
||||
pvt_marker = b'\x02'
|
||||
|
||||
|
||||
def __init__(self):
|
||||
pass
|
||||
def __init_(self, sig):
|
||||
self.sig = None
|
||||
pass
|
||||
|
||||
#
|
||||
# READ
|
||||
#
|
||||
|
||||
def isPicSeal(buf):
|
||||
if (buf == JpgPicSeal.picseal_marker):
|
||||
return True
|
||||
return False
|
||||
|
||||
#
|
||||
# [size:2|picseal:7|type:1|sig:512|key:550] == 1072
|
||||
#
|
||||
def deserialize(buf):
|
||||
sig = Signature()
|
||||
retval = False
|
||||
|
||||
# read type 0x01 is public key, 0x02 private key
|
||||
try:
|
||||
if (buf[9] == ord(JpgPicSeal.pub_marker)):
|
||||
sig.importPubKey(buf[522:])
|
||||
print("*** *** ***")
|
||||
print("*** Public Key Import Sucessful")
|
||||
print("*** *** ***")
|
||||
else:
|
||||
sig.importPvtKey(buf[522:])
|
||||
print("*** *** ***")
|
||||
print("*** Private Key Import Sucessful")
|
||||
print("*** *** ***")
|
||||
return sig
|
||||
except Exception as ex:
|
||||
logging.debug(ex)
|
||||
|
||||
return None
|
||||
def serilize(self, fname):
|
||||
pass
|
||||
|
||||
|
||||
#
|
||||
def readPub(self):
|
||||
pass
|
||||
|
||||
def readPvt(self):
|
||||
pass
|
||||
|
||||
|
||||
|
||||
#
|
||||
# WRITE
|
||||
#
|
||||
|
||||
#
|
||||
def serilize(fname):
|
||||
pass
|
||||
#
|
||||
# input is the Crypto Sig class
|
||||
#
|
||||
def writePub(fhw, sig):
|
||||
JpgPicSeal.__writeData(fhw, sig.sig_data, sig.getPubKeyDER(), JpgPicSeal.pub_marker)
|
||||
|
||||
#
|
||||
# input is the Crypto Sig class
|
||||
#
|
||||
def writePvt(fhw, sig):
|
||||
JpgPicSeal.__writeData(fhw, sig.sig_data, sig.getPvtKeyDER(), JpgPicSeal.pvt_marker)
|
||||
|
||||
|
||||
#
|
||||
def __writeData(fhw, sig_data, keyder, pubpvt):
|
||||
# must include 2 bytes for length too, plus 1 for the key type
|
||||
size = 3
|
||||
size += len(JpgPicSeal.picseal_marker)
|
||||
size += len(sig_data)
|
||||
size += len(keyder)
|
||||
|
||||
logging.debug("3+{}+{}+{}=={} | picseal, sig, key".format(str(len(JpgPicSeal.picseal_marker)), str(len(sig_data)), str(len(keyder)), str(size) ))
|
||||
#
|
||||
# write header
|
||||
# write app marker
|
||||
fhw.write(JpgPicSeal.app15_marker)
|
||||
# write the size of picseal record
|
||||
fhw.write(size.to_bytes(2, byteorder='big'))
|
||||
|
||||
#
|
||||
# write picseal data
|
||||
# write picseal marker
|
||||
fhw.write(JpgPicSeal.picseal_marker)
|
||||
fhw.write(pubpvt)
|
||||
# write signature of image
|
||||
fhw.write(sig_data)
|
||||
# write the key data
|
||||
fhw.write(keyder)
|
||||
def deserialize(self, fname):
|
||||
pass
|
||||
|
||||
|
|
|
|||
|
|
@ -44,11 +44,8 @@ class JpgProc:
|
|||
self.sig = Signature()
|
||||
img_hash = self.jpg.genHash(self.sig.hh)
|
||||
self.sig.hash_data = img_hash
|
||||
self.sig.genKeys()
|
||||
self.sig.genSig()
|
||||
logging.debug("*** Public Key:\n{}\n\n".format(self.sig.getPubKeyDER().hex()))
|
||||
logging.debug("*** Private Key:\n{}".format(self.sig.getPvtKeyDER().hex()))
|
||||
|
||||
logging.info("img_hash-size=={}, img_hash=={}".format(len(img_hash), img_hash))
|
||||
return img_hash
|
||||
|
||||
#
|
||||
def writePicSealJpg(self, fname=None):
|
||||
|
|
|
|||
27
picseal.py
27
picseal.py
|
|
@ -3,21 +3,27 @@
|
|||
#
|
||||
import argparse
|
||||
import logging
|
||||
from shutil import copyfile
|
||||
from libs.toolbox import Toolbox
|
||||
from libs.jpg_proc import JpgProc
|
||||
|
||||
printall = False
|
||||
printmeta = False
|
||||
printimage = False
|
||||
printall = False
|
||||
printmeta = False
|
||||
printimage = False
|
||||
write_picseal = False
|
||||
|
||||
def main():
|
||||
parseArgs()
|
||||
|
||||
|
||||
# hash the image binary data only (not metadata)
|
||||
# create new pub keys, sign hash
|
||||
# export signature & public key to a new image file
|
||||
def processImage(image_fn):
|
||||
jpg_proc = JpgProc()
|
||||
jpg_proc.process(image_fn)
|
||||
jpg_proc.hash()
|
||||
if (write_picseal):
|
||||
jpg_proc.hash()
|
||||
print("Writing PicSeal JPG files...")
|
||||
jpg_proc.writePicSealJpg()
|
||||
|
||||
|
|
@ -35,12 +41,7 @@ def printImageInfo(jpg_bin):
|
|||
print( jpg_bin.printMarkerMeta())
|
||||
|
||||
|
||||
|
||||
def main():
|
||||
parseArgs()
|
||||
|
||||
def parseArgs():
|
||||
print
|
||||
print("***** ***** ***** *****")
|
||||
print(" ** Pic * Seal ** ")
|
||||
print("***** ***** ***** *****\n")
|
||||
|
|
@ -84,10 +85,10 @@ def parseArgs():
|
|||
if (args.file):
|
||||
processImage(args.file)
|
||||
else:
|
||||
print('***** ***** ***** *****')
|
||||
print('ERROR: missing image file')
|
||||
print(' picseal.py -f <image_file>')
|
||||
print('***** ***** ***** *****')
|
||||
print('Create PicSeal images')
|
||||
print(' picseal.py -i <image_file>')
|
||||
print('\n***** ***** ***** *****\n')
|
||||
parser.print_help()
|
||||
|
||||
|
||||
|
||||
|
|
|
|||
|
|
@ -2,7 +2,7 @@
|
|||
# Test Signature Class
|
||||
#
|
||||
# run from root of project
|
||||
from libs.crypto_sig import Signature
|
||||
from libs.crypto_pub import Signature
|
||||
|
||||
msg = b'Hieee, this is a test =)'
|
||||
|
||||
|
|
|
|||
Loading…
Reference in New Issue