mirror of
https://github.com/tcsenpai/hmacrypt.git
synced 2025-06-04 10:00:05 +00:00
Added ECDSA Support
This commit is contained in:
parent
3ecd10a200
commit
018bcadd34
3
.gitignore
vendored
3
.gitignore
vendored
@ -4,10 +4,11 @@ test
|
|||||||
test_*
|
test_*
|
||||||
hmacenv
|
hmacenv
|
||||||
__pycache__
|
__pycache__
|
||||||
public_key.pem
|
|
||||||
*.pem
|
*.pem
|
||||||
decrypted.txt
|
decrypted.txt
|
||||||
encrypted.txt
|
encrypted.txt
|
||||||
|
signed.txt
|
||||||
*.enc
|
*.enc
|
||||||
*.dec
|
*.dec
|
||||||
*.part
|
*.part
|
||||||
|
public_ecdsa_key.txt
|
50
README.md
50
README.md
@ -1,5 +1,5 @@
|
|||||||
# hmacrypt
|
# hmacrypt
|
||||||
encryption / decryption using RSA deterministic Key Derivation from password+hmac_secret by an hardware key.
|
encryption / decryption / signing / verifying using RSA and ECDSA a deterministic Key Derivation from password+hmac_secret by an hardware key.
|
||||||
|
|
||||||
## Requirements (BEFORE everything else) & Credits
|
## Requirements (BEFORE everything else) & Credits
|
||||||
|
|
||||||
@ -30,14 +30,15 @@ On Ubuntu 23.10 (untested on other platforms and flavors):
|
|||||||
## FAQ
|
## FAQ
|
||||||
|
|
||||||
- A .keyfile without the passphrase AND the hardware key is not usable and won't be recoverable
|
- A .keyfile without the passphrase AND the hardware key is not usable and won't be recoverable
|
||||||
- Remember to always "conda activate ./hmaenv" prior to running anything from here
|
- Remember to always "conda activate ./hmacenv" prior to running anything from here
|
||||||
|
|
||||||
## Features
|
## Features
|
||||||
|
|
||||||
- 2fa encryption/decryption using RSA deterministic Key Derivation
|
- 2fa encryption/decryption using RSA deterministic Key Derivation
|
||||||
|
- 2fa signing/verifying using ECDSA deterministic Key Derivation
|
||||||
- Possibility to use the same or different keyfiles to enhance security
|
- Possibility to use the same or different keyfiles to enhance security
|
||||||
- Consequently, supports for a wide range of hardware keys as long as they are compatible with libfido2
|
- Consequently, supports for a wide range of hardware keys as long as they are compatible with libfido2
|
||||||
- Low footprint: requires a single python library and a single system library
|
- Low footprint: requires just two (or three) python library and a single system library
|
||||||
- Readable: you are free and encouraged to tinker with this library
|
- Readable: you are free and encouraged to tinker with this library
|
||||||
|
|
||||||
## What is this
|
## What is this
|
||||||
@ -72,6 +73,8 @@ The examples provided allows everybody to use this library programmatically.
|
|||||||
|
|
||||||
You can also use the standalone tools in the root folder to encrypt / decrypt files and strings.
|
You can also use the standalone tools in the root folder to encrypt / decrypt files and strings.
|
||||||
|
|
||||||
|
The tools allows you to sign and verify strings.
|
||||||
|
|
||||||
Please note that the tools are just an utility and may not be suited for large data inputs.
|
Please note that the tools are just an utility and may not be suited for large data inputs.
|
||||||
|
|
||||||
### Documentation
|
### Documentation
|
||||||
@ -92,11 +95,24 @@ Or the path you used for the library.
|
|||||||
|
|
||||||
*If you REALLY have to change the src directory name, please correct the various paths inside.*
|
*If you REALLY have to change the src directory name, please correct the various paths inside.*
|
||||||
|
|
||||||
#### inferKeys
|
|
||||||
|
#### inferECDSAKeys
|
||||||
|
|
||||||
Definition:
|
Definition:
|
||||||
|
|
||||||
def inferKeys(hidePrivate=False, savePublic=False, keyfilePath="src/bins/.keyfile")
|
def inferECDSAKeys(hidePrivate=False, savePublic=False, keyfilePath="src/bins/.keyfile")
|
||||||
|
|
||||||
|
Parameters:
|
||||||
|
|
||||||
|
- hidePrivate (boolean, default to False); if True, does not return the private key
|
||||||
|
- savePublic (boolean, default to False); if True, saves the public key to a file
|
||||||
|
- keyfilePath (string, default to the bins path); allows the usage of different keyfiles, for example to use a different hmac_secret
|
||||||
|
|
||||||
|
#### inferRSAKeys
|
||||||
|
|
||||||
|
Definition:
|
||||||
|
|
||||||
|
def inferRSAKeys(hidePrivate=False, savePublic=False, keyfilePath="src/bins/.keyfile")
|
||||||
|
|
||||||
Parameters:
|
Parameters:
|
||||||
|
|
||||||
@ -104,6 +120,27 @@ Parameters:
|
|||||||
- savePublic (boolean, default to False); if True, saves the PEM encoded public key to a file
|
- savePublic (boolean, default to False); if True, saves the PEM encoded public key to a file
|
||||||
- keyfilePath (string, default to the bins path); allows the usage of different keyfiles, for example to use a different hmac_secret
|
- keyfilePath (string, default to the bins path); allows the usage of different keyfiles, for example to use a different hmac_secret
|
||||||
|
|
||||||
|
#### self_sign
|
||||||
|
|
||||||
|
Definition:
|
||||||
|
|
||||||
|
def self_sign(message):
|
||||||
|
|
||||||
|
Parameters:
|
||||||
|
|
||||||
|
- message (string); the message to sign
|
||||||
|
|
||||||
|
#### self_verify
|
||||||
|
|
||||||
|
Definition:
|
||||||
|
|
||||||
|
def self_verify(signature, message):
|
||||||
|
|
||||||
|
Parameters:
|
||||||
|
|
||||||
|
- signature (bytes); the signature to verify
|
||||||
|
- message (string); the message that is supposed to be corresponding to the signature
|
||||||
|
|
||||||
#### self_encrypt
|
#### self_encrypt
|
||||||
|
|
||||||
Definition:
|
Definition:
|
||||||
@ -162,6 +199,9 @@ The script encodes and encrypt a string using the same inferred RSA keypair as a
|
|||||||
|
|
||||||
The script creates, encodes and encrypts a simple text file, then decrypts it using the same keypair as above.
|
The script creates, encodes and encrypts a simple text file, then decrypts it using the same keypair as above.
|
||||||
|
|
||||||
|
#### string_sign_and_verify.py
|
||||||
|
|
||||||
|
The script sign and verify a given string using the keypair derived from the hmac_secret + password + key
|
||||||
|
|
||||||
## Known Issues
|
## Known Issues
|
||||||
|
|
||||||
|
25
examples/string_sign_and_verify.py
Normal file
25
examples/string_sign_and_verify.py
Normal file
@ -0,0 +1,25 @@
|
|||||||
|
#!./hmacenv/bin/python
|
||||||
|
|
||||||
|
import src.hmacrypt as hmacrypt
|
||||||
|
import sys
|
||||||
|
import os
|
||||||
|
|
||||||
|
# Getting and requiring exactly 1 argument
|
||||||
|
if len(sys.argv) != 2:
|
||||||
|
print("Usage: python3 string_decryptor.py <binary_file_with_encrypted_string>")
|
||||||
|
sys.exit(1)
|
||||||
|
stringToSign = sys.argv[1].encode()
|
||||||
|
|
||||||
|
|
||||||
|
signed = hmacrypt.self_sign(stringToSign)
|
||||||
|
|
||||||
|
with open("signed.txt", "wb+") as signedFile:
|
||||||
|
signedFile.write(signed)
|
||||||
|
|
||||||
|
print(signed)
|
||||||
|
|
||||||
|
# Verify
|
||||||
|
with open("signed.txt", "rb") as f:
|
||||||
|
signed = f.read()
|
||||||
|
verified = hmacrypt.self_verify(signed, stringToSign)
|
||||||
|
print(verified)
|
3
public_ecdsa_key_saver
Executable file
3
public_ecdsa_key_saver
Executable file
@ -0,0 +1,3 @@
|
|||||||
|
#!./hmacenv/bin/python
|
||||||
|
import src.hmacrypt as hmacrypt
|
||||||
|
hmacrypt.inferECDSAKeys(hidePrivate=True, savePublic=True)
|
@ -1,3 +0,0 @@
|
|||||||
#!./hmacenv/bin/python
|
|
||||||
import src.hmacrypt as hmacrypt
|
|
||||||
hmacrypt.inferKeys(hidePrivate=True, savePublic=True)
|
|
3
public_rsa_key_saver
Executable file
3
public_rsa_key_saver
Executable file
@ -0,0 +1,3 @@
|
|||||||
|
#!./hmacenv/bin/python
|
||||||
|
import src.hmacrypt as hmacrypt
|
||||||
|
hmacrypt.inferRSAKeys(hidePrivate=True, savePublic=True)
|
@ -1,2 +1,3 @@
|
|||||||
ByteSplitter
|
ByteSplitter
|
||||||
pycrypto
|
pycrypto
|
||||||
|
ecdsa
|
||||||
|
@ -1,10 +1,9 @@
|
|||||||
import subprocess
|
import subprocess
|
||||||
|
from src.libs.seedable_rsa import decrypt, encrypt, generate_rsa_key
|
||||||
|
from src.libs.seedable_ecdsa import generate_ecdsa_key, sign, verify
|
||||||
|
|
||||||
from src.libs.seedable_rsa import decrypt, encrypt, generate_key
|
# INFO This method derives the HMAC secret from the hardware key and the stored secret
|
||||||
|
def getHMACSecret(keyfilePath="src/bins/.keyfile"):
|
||||||
|
|
||||||
# INFO This method is the core of the whole process as it derives a RSA keypair from the stored secret and the hardware key
|
|
||||||
def inferKeys(hidePrivate=False, savePublic=False, keyfilePath="src/bins/.keyfile"):
|
|
||||||
"""Infer keys from the secret stored in the hardware key"""
|
"""Infer keys from the secret stored in the hardware key"""
|
||||||
hmac_secret_raw = subprocess.check_output(
|
hmac_secret_raw = subprocess.check_output(
|
||||||
["src/bins/hmac_secret_regenerate", keyfilePath]
|
["src/bins/hmac_secret_regenerate", keyfilePath]
|
||||||
@ -14,7 +13,40 @@ def inferKeys(hidePrivate=False, savePublic=False, keyfilePath="src/bins/.keyfil
|
|||||||
# Divide lines and keep last one
|
# Divide lines and keep last one
|
||||||
hmac_secret = hmac_secret_dirty.splitlines()[-1]
|
hmac_secret = hmac_secret_dirty.splitlines()[-1]
|
||||||
hmac_secret = hmac_secret.strip()
|
hmac_secret = hmac_secret.strip()
|
||||||
secret_key = generate_key(hmac_secret) # RSA Key (2048) derivation
|
return hmac_secret
|
||||||
|
|
||||||
|
# INFO This method derives an ECDSA keypair from the stored secret and the hardware key
|
||||||
|
def inferECDSAKeys(hidePrivate=False, savePublic=False):
|
||||||
|
hmac_secret = getHMACSecret()
|
||||||
|
key_pair = generate_ecdsa_key(hmac_secret)
|
||||||
|
# We use them in memory, we never save them
|
||||||
|
# Privacy should be possible here
|
||||||
|
if hidePrivate:
|
||||||
|
private_key = "REDACTED"
|
||||||
|
else:
|
||||||
|
private_key = key_pair[0]
|
||||||
|
public_key = key_pair[1]
|
||||||
|
# Saving public key is permitted
|
||||||
|
if savePublic:
|
||||||
|
with open("public_ecdsa_key.pem", "wb+") as f:
|
||||||
|
f.write(public_key.to_pem())
|
||||||
|
return private_key, public_key
|
||||||
|
|
||||||
|
def self_sign(message):
|
||||||
|
"""Sign a message with the private key"""
|
||||||
|
private_key, public_key = inferECDSAKeys()
|
||||||
|
signature = sign(private_key, message)
|
||||||
|
return signature
|
||||||
|
|
||||||
|
def self_verify(signature, message):
|
||||||
|
"""Verify a message with the public key"""
|
||||||
|
private_key, public_key = inferECDSAKeys(hidePrivate=True)
|
||||||
|
return verify(public_key, signature, message)
|
||||||
|
|
||||||
|
# INFO This method derives a RSA keypair from the stored secret and the hardware key
|
||||||
|
def inferRSAKeys(hidePrivate=False, savePublic=False):
|
||||||
|
hmac_secret = getHMACSecret()
|
||||||
|
secret_key = generate_rsa_key(hmac_secret) # RSA Key (2048) derivation
|
||||||
# We use them in memory, we never save them
|
# We use them in memory, we never save them
|
||||||
# Privacy should be possible here
|
# Privacy should be possible here
|
||||||
if hidePrivate:
|
if hidePrivate:
|
||||||
@ -24,7 +56,7 @@ def inferKeys(hidePrivate=False, savePublic=False, keyfilePath="src/bins/.keyfil
|
|||||||
public_key = secret_key.publickey().exportKey("PEM")
|
public_key = secret_key.publickey().exportKey("PEM")
|
||||||
# Saving public key is permitted
|
# Saving public key is permitted
|
||||||
if savePublic:
|
if savePublic:
|
||||||
with open("public_key.pem", "wb") as f:
|
with open("public_rsa_key.pem", "wb") as f:
|
||||||
f.write(public_key)
|
f.write(public_key)
|
||||||
return private_key, public_key
|
return private_key, public_key
|
||||||
|
|
||||||
@ -38,14 +70,14 @@ def inferKeys(hidePrivate=False, savePublic=False, keyfilePath="src/bins/.keyfil
|
|||||||
|
|
||||||
def self_encrypt(secret, encoded=False):
|
def self_encrypt(secret, encoded=False):
|
||||||
"""Encrypt secret with public key"""
|
"""Encrypt secret with public key"""
|
||||||
private_key, public_key = inferKeys()
|
private_key, public_key = inferRSAKeys(hidePrivate=True)
|
||||||
secret = encrypt(secret, public_key, encoded)
|
secret = encrypt(secret, public_key, encoded)
|
||||||
return secret
|
return secret
|
||||||
|
|
||||||
|
|
||||||
def self_decrypt(encrypted):
|
def self_decrypt(encrypted):
|
||||||
"""Decrypt secret with private key"""
|
"""Decrypt secret with private key"""
|
||||||
private_key, public_key = inferKeys()
|
private_key, public_key = inferRSAKeys()
|
||||||
secret = decrypt(encrypted, private_key)
|
secret = decrypt(encrypted, private_key)
|
||||||
return secret
|
return secret
|
||||||
|
|
||||||
@ -55,7 +87,7 @@ def self_decrypt(encrypted):
|
|||||||
|
|
||||||
def self_encrypt_file(filepath, outpath):
|
def self_encrypt_file(filepath, outpath):
|
||||||
"""Encrypt file with public key"""
|
"""Encrypt file with public key"""
|
||||||
private_key, public_key = inferKeys(hidePrivate=True)
|
private_key, public_key = inferRSAKeys(hidePrivate=True)
|
||||||
with open(filepath, "rb") as f:
|
with open(filepath, "rb") as f:
|
||||||
filebytes = f.read()
|
filebytes = f.read()
|
||||||
encrypted = encrypt(filebytes, public_key, encoded=True)
|
encrypted = encrypt(filebytes, public_key, encoded=True)
|
||||||
@ -66,7 +98,7 @@ def self_encrypt_file(filepath, outpath):
|
|||||||
|
|
||||||
def self_decrypt_file(filepath, outpath):
|
def self_decrypt_file(filepath, outpath):
|
||||||
"""Decrypt file with private key"""
|
"""Decrypt file with private key"""
|
||||||
private_key, public_key = inferKeys()
|
private_key, public_key = inferRSAKeys()
|
||||||
with open(filepath, "rb") as f:
|
with open(filepath, "rb") as f:
|
||||||
filebytes = f.read()
|
filebytes = f.read()
|
||||||
decrypted = decrypt(filebytes, private_key)
|
decrypted = decrypt(filebytes, private_key)
|
||||||
@ -80,7 +112,7 @@ def self_decrypt_file(filepath, outpath):
|
|||||||
|
|
||||||
# Self testing
|
# Self testing
|
||||||
if __name__ == "__main__":
|
if __name__ == "__main__":
|
||||||
private_key, public_key = inferKeys()
|
private_key, public_key = inferRSAKeys()
|
||||||
secret = encrypt("secret message", public_key)
|
secret = encrypt("secret message", public_key)
|
||||||
print(secret)
|
print(secret)
|
||||||
decrypted = decrypt(secret, private_key)
|
decrypted = decrypt(secret, private_key)
|
||||||
|
26
src/libs/seedable_ecdsa.py
Normal file
26
src/libs/seedable_ecdsa.py
Normal file
@ -0,0 +1,26 @@
|
|||||||
|
import ecdsa
|
||||||
|
from hashlib import sha256
|
||||||
|
|
||||||
|
# Sign
|
||||||
|
def sign(sk, message):
|
||||||
|
"""Sign a message using a private key"""
|
||||||
|
# Sign a message using a private key
|
||||||
|
signature = sk.sign(message, hashfunc=sha256)
|
||||||
|
return signature
|
||||||
|
|
||||||
|
# Verify
|
||||||
|
def verify(vk, signature, message):
|
||||||
|
"""Verify a message using a public key"""
|
||||||
|
# Verify a message using a public key
|
||||||
|
return vk.verify(signature, message, hashfunc=sha256)
|
||||||
|
|
||||||
|
# Generate a key pair using a seed
|
||||||
|
def generate_ecdsa_key(seed):
|
||||||
|
"""Generate a key pair using a seed"""
|
||||||
|
seed = seed.encode()
|
||||||
|
hashed = sha256(seed).digest()
|
||||||
|
# Generate a key pair using a seed
|
||||||
|
sk = ecdsa.SigningKey.from_string(hashed, curve=ecdsa.SECP256k1)
|
||||||
|
vk = sk.get_verifying_key()
|
||||||
|
return sk, vk
|
||||||
|
|
@ -30,7 +30,7 @@ def decrypt(encrypted_message, private_key):
|
|||||||
return None
|
return None
|
||||||
|
|
||||||
|
|
||||||
def generate_key(seed, nbytes=2048):
|
def generate_rsa_key(seed, nbytes=2048):
|
||||||
# based on https://stackoverflow.com/questions/18264314/#answer-18266970
|
# based on https://stackoverflow.com/questions/18264314/#answer-18266970
|
||||||
seed_128 = HMAC.new(
|
seed_128 = HMAC.new(
|
||||||
bytes(
|
bytes(
|
||||||
|
20
string_signer
Executable file
20
string_signer
Executable file
@ -0,0 +1,20 @@
|
|||||||
|
#!./hmacenv/bin/python
|
||||||
|
|
||||||
|
import src.hmacrypt as hmacrypt
|
||||||
|
import sys
|
||||||
|
import os
|
||||||
|
|
||||||
|
# Getting and requiring exactly 1 argument
|
||||||
|
if len(sys.argv) != 2:
|
||||||
|
print("Usage: python3 string_signer <string>")
|
||||||
|
sys.exit(1)
|
||||||
|
|
||||||
|
stringToSign = sys.argv[1].encode()
|
||||||
|
|
||||||
|
|
||||||
|
signed = hmacrypt.self_sign(stringToSign)
|
||||||
|
|
||||||
|
with open("signed.txt", "wb+") as signedFile:
|
||||||
|
signedFile.write(signed)
|
||||||
|
|
||||||
|
print(signed)
|
19
string_verifier
Executable file
19
string_verifier
Executable file
@ -0,0 +1,19 @@
|
|||||||
|
#!./hmacenv/bin/python
|
||||||
|
|
||||||
|
import src.hmacrypt as hmacrypt
|
||||||
|
import sys
|
||||||
|
import os
|
||||||
|
|
||||||
|
# Getting and requiring exactly 1 argument
|
||||||
|
if len(sys.argv) != 3:
|
||||||
|
print("Usage: python3 string_verifier <message> <binary file of the signature>")
|
||||||
|
sys.exit(1)
|
||||||
|
|
||||||
|
message = sys.argv[1].encode()
|
||||||
|
signaturePath = sys.argv[2].encode()
|
||||||
|
|
||||||
|
with open(signaturePath, "rb") as f:
|
||||||
|
signature = f.read()
|
||||||
|
|
||||||
|
verified = hmacrypt.self_verify(signature, message)
|
||||||
|
print(verified)
|
Loading…
x
Reference in New Issue
Block a user