mirror of
https://github.com/tcsenpai/hmacrypt.git
synced 2025-06-02 17:10:06 +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_*
|
||||
hmacenv
|
||||
__pycache__
|
||||
public_key.pem
|
||||
*.pem
|
||||
decrypted.txt
|
||||
encrypted.txt
|
||||
signed.txt
|
||||
*.enc
|
||||
*.dec
|
||||
*.part
|
||||
public_ecdsa_key.txt
|
50
README.md
50
README.md
@ -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
|
||||
|
||||
|
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
|
||||
pycrypto
|
||||
ecdsa
|
||||
|
@ -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)
|
||||
|
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
|
||||
|
||||
|
||||
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
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