Files
github-oracle/tools/encrypted_queries_tools.py

144 lines
5.8 KiB
Python

from __future__ import absolute_import
import os
import argparse
import sys
from cryptography.hazmat.primitives import hashes
from cryptography.hazmat.primitives.kdf import x963kdf
from cryptography.hazmat.primitives.asymmetric import ec
from cryptography.hazmat.primitives.ciphers import Cipher,algorithms,modes
from cryptography.hazmat.backends import default_backend
import base64
import base58
backend = default_backend()
def hex_to_key(pub_key_hex):
pub_key_hex = pub_key_hex.strip()
pub_key_point = pub_key_hex.decode('hex')
public_numbers = ec.EllipticCurvePublicNumbers.from_encoded_point(ec.SECP256K1(), pub_key_point)
public_key = public_numbers.public_key(backend)
return public_key
def hex_to_priv_key(priv_key_hex, public_key_hex):
priv_key_value = long(priv_key_hex, 16)
public_key = hex_to_key(public_key_hex)
public_numbers = public_key.public_numbers()
private_numbers = ec.EllipticCurvePrivateNumbers(priv_key_value, public_numbers)
priv_key = private_numbers.private_key(backend)
return priv_key
# Hybrid Encryption Scheme:
# - We perform a Elliptic Curves Diffie-Hellman Key Exchange using:
# - SECP256K1 as curve for key generation
# - ANSI X9.63 KDF as Key Derivation Function to derive the shared secret
# - The symmetric cipher is an AES256.MODE_GCM with authentication tag 16-byte
# of length. Since the key is used only once, we can pick the known nonce/iv
# '000000000000' (96 bits of length). We return concatenation of the encoded
# point, the tag and the ciphertext
def encrypt(message, receiver_public_key):
sender_private_key = ec.generate_private_key(ec.SECP256K1(), backend)
shared_key = sender_private_key.exchange(ec.ECDH(), receiver_public_key)
sender_public_key = sender_private_key.public_key()
point = sender_public_key.public_numbers().encode_point()
iv = '000000000000'
xkdf = x963kdf.X963KDF(
algorithm = hashes.SHA256(),
length = 32,
sharedinfo = '',
backend = backend
)
key = xkdf.derive(shared_key)
encryptor = Cipher(
algorithms.AES(key),
modes.GCM(iv),
backend = backend
).encryptor()
ciphertext = encryptor.update(message) + encryptor.finalize()
return point + encryptor.tag + ciphertext
def decrypt(message, receiver_private_key):
point = message[0:65]
tag = message[65:81]
ciphertext = message[81:]
sender_public_numbers = ec.EllipticCurvePublicNumbers.from_encoded_point(ec.SECP256K1(), point)
sender_public_key = sender_public_numbers.public_key(backend)
shared_key = receiver_private_key.exchange(ec.ECDH(), sender_public_key)
iv = '000000000000'
xkdf = x963kdf.X963KDF(
algorithm = hashes.SHA256(),
length = 32,
sharedinfo = '',
backend = backend
)
key = xkdf.derive(shared_key)
decryptor = Cipher(
algorithms.AES(key),
modes.GCM(iv,tag),
backend = backend
).decryptor()
message = decryptor.update(ciphertext) + decryptor.finalize()
return message
def main():
parser = argparse.ArgumentParser(description='Encrypt messages to Oraclize using Elliptic Curve Integrated Encryption Scheme.')
parser.add_argument('-e', '--encrypt', dest='mode', action='store_const', const='encrypt', help='Encrypt a string. Requires -p')
parser.add_argument('-p', '--with-public-key', dest='public_key', action='store', help='Use the provided hex-encoded public key to encrypt')
parser.add_argument('-d', '--decrypt', dest='mode', action='store_const', const='decrypt', help='Decrypt a string. Provide private key in Wallet Import Format in standard input, or first line of standard input if encrypted text is also provided on standard input. DO NOT PUT YOUR PRIVATE KEY ON THE COMMAND LINE.')
parser.add_argument('-g', '--generate', dest='mode', action='store_const', const='generate', help='Generates a public and a private key')
parser.add_argument('text', nargs='?', action='store', help='String to encrypt, decrypt. If not specified, standard input will be used.')
args = parser.parse_args()
if args.mode != 'encrypt' and args.mode != 'decrypt' and args.mode != 'generate':
parser.print_help()
return
if args.mode == 'encrypt' and not args.public_key:
print "Please, provide a valid public key"
return
if args.mode == 'encrypt':
if args.public_key:
pub_key = hex_to_key(args.public_key)
if args.text:
print base64.b64encode(encrypt(args.text, pub_key))
return
else:
print base64.b64encode(encrypt(sys.stdin.read(), pub_key))
return
elif args.mode == 'decrypt':
if args.text:
print "Insert your public key"
public_key = sys.stdin.read()
print "Insert your private key:"
private_key = sys.stdin.read()
private_key = hex_to_priv_key(private_key, public_key)
text = base64.b64decode(args.text)
else:
print "Insert your public key"
public_key = sys.stdin.read()
print "\nInsert your private key:"
private_key = sys.stdin.read()
private_key = hex_to_priv_key(private_key, public_key)
print "\nInsert encrypted text"
text = base64.b64decode(sys.stdin.read())
print decrypt(text, private_key)
if args.mode == 'generate':
receiver_private_key = ec.generate_private_key(ec.SECP256K1(), backend)
receiver_public_key = receiver_private_key.public_key()
number = receiver_private_key.private_numbers()
print "Public Key:", receiver_public_key.public_numbers().encode_point().encode('hex')
print "Private Key:", hex(number.private_value)
if __name__ == "__main__":
main()