-
Notifications
You must be signed in to change notification settings - Fork 0
Commit
This commit does not belong to any branch on this repository, and may belong to a fork outside of the repository.
Merge pull request #20 from p4kl0nc4t/faizj-add-password-enc
feat: Add password encryption
- Loading branch information
Showing
10 changed files
with
318 additions
and
111 deletions.
There are no files selected for viewing
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,2 @@ | ||
SICS_ENABLE_PWD_ENC=0 | ||
SICS_PRIVATE_KEY=YOUR_VALID_PRIVATE_KEY_SEE_README_MD |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -1,49 +1,62 @@ | ||
# `simaster.ics` | ||
|
||
Simple Python-based web app to generate an iCalendar file from SIMASTER courses | ||
schedule. | ||
Simple Python-based web app to generate an iCalendar file from SIMASTER | ||
courses schedule. | ||
|
||
## Quick Usage | ||
|
||
1. Open the [demo](https://simaster-ics.onrender.com) or your server URL if | ||
you self-hosted it. | ||
1. Open the [demo](https://simaster-ics.onrender.com) or your server | ||
URL if you self-hosted it. | ||
2. Fill in all the fields | ||
3. Copy the iCalendar URL | ||
4. Subscribe to it using your preferred calendar app (e.g. Google Calendar) | ||
4. Subscribe to it using your preferred calendar app (e.g. Google | ||
Calendar) | ||
|
||
## API | ||
|
||
Once you have deployed simaster.ics (or, just use the | ||
[demo](https://simaster-ics.onrender.com), you can directly utilize its API to | ||
make an iCalendar file. | ||
|
||
- Method: `GET` | ||
- URI: `/ics` | ||
- Parameters: | ||
- `username`: your SIMASTER account username | ||
- `password`: your SIMASTER account password | ||
- `period`: calendar period (e.g. `20212`, `20211`) | ||
- `type`: optional, calendar event type (possible values: `exam`, `class`) | ||
- `reuse_session`: optional, cache and reuse session (possible values: | ||
`0`, `1`) | ||
[demo](https://simaster-ics.onrender.com), you can directly utilize its | ||
API to make an iCalendar file. | ||
|
||
- Method: `GET` | ||
- URI: `/ics` | ||
- Parameters: | ||
- `username`: your SIMASTER account username | ||
- `password`: your SIMASTER account password | ||
- `period`: calendar period (e.g. `20212`, `20211`) | ||
- `type`: optional, calendar event type (possible values: `exam`, | ||
`class`) | ||
- `reuse_session`: optional, cache and reuse session (possible | ||
values: `0`, `1`) | ||
|
||
## Deployment | ||
|
||
Before deploying, make sure that you have already installed the requirements | ||
(e.g. by using `pip install -r requirements.txt`). | ||
Before deploying, make sure that you have already installed the | ||
requirements (e.g. by using `pip install -r requirements.txt`). | ||
|
||
- Gunicorn: `gunicorn wsgi:app` | ||
- Flask Development Server: `python wsgi.py` | ||
- Heroku: use the provided (or your own) `Procfile` | ||
- Docker: use the `Dockerfile` | ||
- `docker build . -t simasterics` | ||
- `docker run -p 8000:8000 simasterics` | ||
- Gunicorn: `gunicorn wsgi:app` | ||
- Flask Development Server: `python wsgi.py` | ||
- Heroku: use the provided (or your own) `Procfile` | ||
- Docker: use the `Dockerfile` | ||
- `docker build . -t simasterics` | ||
- `docker run -p 8000:8000 simasterics` | ||
|
||
### Enabling Password Encryption | ||
|
||
Password encryption is feature that allows user password to be encrypted | ||
(to hide plaintext password from the public calendar URL). To enable | ||
this feature, you have to provide the following environment variables | ||
(dotenv or `.env` file is also supported). | ||
|
||
- `SICS_ENABLE_PWD_ENC` set to `1` | ||
- `SICS_PRIVATE_KEY` set to the Base64-encoded private key in PEM | ||
encoding and PKCS\#8 format (you may want to use `generate_key.py`) | ||
|
||
## License | ||
|
||
Distributed under the MIT License. | ||
|
||
## Contribution | ||
|
||
Any form of contribution is highly appreciated. Feel free to contribute (or | ||
maybe even [buying me a cofffee](https://github.com/p4kl0nc4t)). | ||
Any form of contribution is highly appreciated. Feel free to contribute | ||
(or maybe even [buying me a cofffee](https://github.com/p4kl0nc4t)). |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,20 @@ | ||
""" | ||
Use this script to generate an RSA private key and prints it | ||
to the standard output. The generated private key is in the | ||
PKCS#8 format with no encryption. | ||
""" | ||
|
||
from base64 import urlsafe_b64encode | ||
|
||
from cryptography.hazmat.primitives.asymmetric import rsa | ||
from cryptography.hazmat.primitives import serialization | ||
|
||
private_key = rsa.generate_private_key(public_exponent=65537, key_size=2048) | ||
serialized_private_key = private_key.private_bytes( | ||
serialization.Encoding.PEM, | ||
serialization.PrivateFormat.PKCS8, | ||
serialization.NoEncryption(), | ||
) | ||
|
||
# set the printed value in .env | ||
print(urlsafe_b64encode(serialized_private_key).decode()) |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -1,11 +1,28 @@ | ||
import os | ||
|
||
import dotenv | ||
from flask import Flask | ||
|
||
from .cryptutils import read_private_bytes_from_b64, derive_serialized_public_key | ||
|
||
|
||
def create_app(): | ||
dotenv.load_dotenv() | ||
app = Flask(__name__) | ||
|
||
private_key_b64 = os.getenv("SICS_PRIVATE_KEY") | ||
if os.getenv("SICS_ENABLE_PWD_ENC") == "1" and private_key_b64 is not None: | ||
private_key = read_private_bytes_from_b64(private_key_b64) | ||
app.config["ENABLE_PWD_ENC"] = True | ||
app.config["PRIVATE_KEY"] = private_key | ||
app.config["PUBLIC_KEY_PEM"] = derive_serialized_public_key(private_key) | ||
else: | ||
app.config["ENABLE_PWD_ENC"] = False | ||
app.config["PRIVATE_KEY"] = None | ||
app.config["PUBLIC_KEY_PEM"] = None | ||
|
||
from . import views | ||
|
||
app.register_blueprint(views.bp) | ||
|
||
return app | ||
|
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,48 @@ | ||
""" | ||
This file contains the high-level cryptographic utilities needed by | ||
simaster.ics. The functions defined here are used internally by the views to | ||
decrypt user-supplied encrypted password or to generate a cache key for reusing | ||
session. | ||
""" | ||
|
||
import base64 | ||
import hashlib | ||
|
||
from cryptography.hazmat.primitives import serialization | ||
from cryptography.hazmat.primitives.asymmetric import types, padding | ||
|
||
|
||
def read_private_bytes_from_b64(private_bytes: str) -> types.PRIVATE_KEY_TYPES: | ||
"""Load private key in PEM format from a Base64-encoded (urlsafe) string""" | ||
decoded_pem = base64.urlsafe_b64decode(private_bytes) | ||
private_key = serialization.load_pem_private_key(decoded_pem, password=None) | ||
return private_key | ||
|
||
|
||
def derive_serialized_public_key(private_key: types.PRIVATE_KEY_TYPES) -> str: | ||
"""Generates a serialized public key""" | ||
public_key = private_key.public_key() | ||
serialized = public_key.public_bytes( | ||
serialization.Encoding.PEM, serialization.PublicFormat.SubjectPublicKeyInfo | ||
) | ||
return serialized.decode() | ||
|
||
|
||
def decrypt_b64_password( | ||
private_key: types.PRIVATE_KEY_TYPES, b64_password: str | ||
) -> str: | ||
"""Decrypts a Base64-encoded (urlsafe) encrypted password (from JSEncrypt, | ||
with PKCS1v15 padding)""" | ||
encrypted_password = base64.urlsafe_b64decode(b64_password) | ||
plaintext_password = private_key.decrypt( | ||
encrypted_password, | ||
padding.PKCS1v15(), | ||
) | ||
return plaintext_password | ||
|
||
|
||
def get_cache_key(username: str, password: str) -> str: | ||
"""Generate a cache key for safer session caching""" | ||
data = f"{username}{password}" | ||
password_hash = hashlib.sha256(data.encode()).hexdigest() | ||
return password_hash |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Oops, something went wrong.