Added ECDSA Support

This commit is contained in:
tcsenpai 2024-02-09 19:21:55 +01:00
parent 3ecd10a200
commit 018bcadd34
12 changed files with 189 additions and 22 deletions

3
.gitignore vendored
View File

@ -4,10 +4,11 @@ test
test_*
hmacenv
__pycache__
public_key.pem
*.pem
decrypted.txt
encrypted.txt
signed.txt
*.enc
*.dec
*.part
public_ecdsa_key.txt

View File

@ -1,5 +1,5 @@
# 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
@ -30,14 +30,15 @@ On Ubuntu 23.10 (untested on other platforms and flavors):
## FAQ
- 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
- 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
- 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
## 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.
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.
### 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.*
#### inferKeys
#### inferECDSAKeys
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:
@ -104,6 +120,27 @@ Parameters:
- 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
#### 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
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.
#### 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

View 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
View File

@ -0,0 +1,3 @@
#!./hmacenv/bin/python
import src.hmacrypt as hmacrypt
hmacrypt.inferECDSAKeys(hidePrivate=True, savePublic=True)

View File

@ -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
View File

@ -0,0 +1,3 @@
#!./hmacenv/bin/python
import src.hmacrypt as hmacrypt
hmacrypt.inferRSAKeys(hidePrivate=True, savePublic=True)

View File

@ -1,2 +1,3 @@
ByteSplitter
pycrypto
ecdsa

View File

@ -1,10 +1,9 @@
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 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"):
# INFO This method derives the HMAC secret from the hardware key and the stored secret
def getHMACSecret(keyfilePath="src/bins/.keyfile"):
"""Infer keys from the secret stored in the hardware key"""
hmac_secret_raw = subprocess.check_output(
["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
hmac_secret = hmac_secret_dirty.splitlines()[-1]
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
# Privacy should be possible here
if hidePrivate:
@ -24,7 +56,7 @@ def inferKeys(hidePrivate=False, savePublic=False, keyfilePath="src/bins/.keyfil
public_key = secret_key.publickey().exportKey("PEM")
# Saving public key is permitted
if savePublic:
with open("public_key.pem", "wb") as f:
with open("public_rsa_key.pem", "wb") as f:
f.write(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):
"""Encrypt secret with public key"""
private_key, public_key = inferKeys()
private_key, public_key = inferRSAKeys(hidePrivate=True)
secret = encrypt(secret, public_key, encoded)
return secret
def self_decrypt(encrypted):
"""Decrypt secret with private key"""
private_key, public_key = inferKeys()
private_key, public_key = inferRSAKeys()
secret = decrypt(encrypted, private_key)
return secret
@ -55,7 +87,7 @@ def self_decrypt(encrypted):
def self_encrypt_file(filepath, outpath):
"""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:
filebytes = f.read()
encrypted = encrypt(filebytes, public_key, encoded=True)
@ -66,7 +98,7 @@ def self_encrypt_file(filepath, outpath):
def self_decrypt_file(filepath, outpath):
"""Decrypt file with private key"""
private_key, public_key = inferKeys()
private_key, public_key = inferRSAKeys()
with open(filepath, "rb") as f:
filebytes = f.read()
decrypted = decrypt(filebytes, private_key)
@ -80,7 +112,7 @@ def self_decrypt_file(filepath, outpath):
# Self testing
if __name__ == "__main__":
private_key, public_key = inferKeys()
private_key, public_key = inferRSAKeys()
secret = encrypt("secret message", public_key)
print(secret)
decrypted = decrypt(secret, private_key)

View 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

View File

@ -30,7 +30,7 @@ def decrypt(encrypted_message, private_key):
return None
def generate_key(seed, nbytes=2048):
def generate_rsa_key(seed, nbytes=2048):
# based on https://stackoverflow.com/questions/18264314/#answer-18266970
seed_128 = HMAC.new(
bytes(

20
string_signer Executable file
View 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
View 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)