|
|
- import json
- from base64 import b64encode, b64decode
- from pathlib import Path
-
- import xdgappdirs
- from cryptography.hazmat.primitives.asymmetric import rsa
- from cryptography.hazmat.primitives import serialization
- from jwcrypto import jwk
- from pathvalidate import sanitize_filename
-
- data_dir = xdgappdirs.user_data_dir('pyjod', as_path=True)
-
-
- class RSAPrivateKey:
- def __init__(self, key):
- self.key = key
-
- @classmethod
- def generate(cls):
- key = rsa.generate_private_key(65537, 2048)
- return cls(key)
-
- @classmethod
- def from_pem(cls, data):
- key = serialization.load_pem_private_key(data, None)
- return cls(key)
-
- @property
- def jwk(self):
- return jwk.JWK.from_pyca(self.key)
-
- @property
- def pubkey_b64(self):
- pubkey = self.key.public_key()
- pubkey_bytes = pubkey.public_bytes(
- serialization.Encoding.DER,
- serialization.PublicFormat.SubjectPublicKeyInfo
- )
- return b64encode(pubkey_bytes).decode('utf-8')
-
- def to_pem(self):
- return self.key.private_bytes(
- serialization.Encoding.PEM,
- serialization.PrivateFormat.TraditionalOpenSSL,
- serialization.NoEncryption()
- )
-
-
- class RSAPublicKey:
- def __init__(self, key):
- self.key = key
-
- @classmethod
- def from_b64(cls, data):
- key_bytes = b64decode(data)
- key = serialization.load_der_public_key(key_bytes)
- return cls(key)
-
- @classmethod
- def from_pem(cls, data):
- key = serialization.load_pem_public_key(data)
- return cls(key)
-
- @property
- def jwk(self):
- return jwk.JWK.from_pyca(self.key)
-
- def to_pem(self):
- return self.key.public_bytes(
- serialization.Encoding.PEM,
- serialization.PublicFormat.SubjectPublicKeyInfo
- )
-
-
- class AppData:
- def __init__(self, profile_name):
- self.profile_dir = data_dir / sanitize_filename(profile_name)
- self.profile_dir.mkdir(exist_ok=True, parents=True)
-
- self.values_file = self.profile_dir / "values.json"
-
- @property
- def app_privkey(self):
- key_file = self.profile_dir / 'app_privkey.pem'
- if key_file.is_file():
- with key_file.open('rb') as f:
- key_bytes = f.read()
- return RSAPrivateKey.from_pem(key_bytes)
- else:
- key = RSAPrivateKey.generate()
- with key_file.open('wb') as f:
- f.write(key.to_pem())
- return key
-
- @property
- def serv_pubkey(self):
- key_file = self.profile_dir / 'serv_pubkey.pem'
- if key_file.is_file():
- with key_file.open('rb') as f:
- key_bytes = f.read()
- return RSAPublicKey.from_pem(key_bytes)
- else:
- return None
-
- @serv_pubkey.setter
- def serv_pubkey(self, key_b64):
- key = RSAPublicKey.from_b64(key_b64)
- key_file = self.profile_dir / 'serv_pubkey.pem'
- with key_file.open('wb') as f:
- f.write(key.to_pem())
-
- def __getitem__(self, key):
- if self.values_file.is_file():
- with self.values_file.open() as f:
- values = json.load(f)
- if key in values:
- return values[key]
- return None
-
- def __setitem__(self, key, value):
- if self.values_file.is_file():
- with self.values_file.open() as f:
- values = json.load(f)
- else:
- values = {}
- values[key] = value
- with self.values_file.open('w') as f:
- json.dump(values, f)
|