Basic structure in place

This commit is contained in:
Darryl Nixon 2023-06-06 15:51:54 -07:00
parent ffd4fe4012
commit 8edd610e7c
9 changed files with 201 additions and 1 deletions

3
.flake8 Normal file
View file

@ -0,0 +1,3 @@
[flake8]
max-line-length = 160
exclude = docs/*, .git, __pycache__, build

29
.pre-commit-config.yaml Normal file
View file

@ -0,0 +1,29 @@
repos:
- repo: https://github.com/pre-commit/pre-commit-hooks
rev: v3.2.0
hooks:
- id: trailing-whitespace
- id: end-of-file-fixer
- id: check-yaml
- id: check-added-large-files
- repo: https://github.com/asottile/reorder_python_imports
rev: v3.9.0
hooks:
- id: reorder-python-imports
args: [--application-directories, '.:crowdtls', --py39-plus]
- repo: https://github.com/psf/black
rev: 23.3.0
hooks:
- id: black
language_version: python3.11
- repo: https://github.com/PyCQA/bandit
rev: 1.7.5
hooks:
- id: bandit
args: ["-c", "pyproject.toml"]
additional_dependencies: ["bandit[toml]"]
- repo: https://github.com/pycqa/flake8
rev: 6.0.0
hooks:
- id: flake8
exclude: ^migrations/versions/

View file

@ -1 +1,30 @@
# CrowdTLS-server <div align="center">
<img src="icons/crowdtls.png" alt="CrowdTLS Logo">
# CrowdTLS-server
CrowdTLS validates SSL/TLS certificates against the crowd.
This is the backend server repository for it.<br/>
[Installation](#installation) •
[License](#license)
</div>
## Installation
TODO
## FAQ
**What is this? I'm looking for the browser extension!**
You're in the wrong place. The browser extension can be found [here](https://sillyhats.mips.uk/pdf/CrowdTLS).
## License
This project is licensed under the MPL 2.0 License. See the `LICENSE` file for details.
I carefully evaluated various open-source licenses and chose the Mozilla Public License 2.0 (MPL 2.0) for CrowdTLS due to its compatibility with other licenses, strong copyleft provisions, and its alignment with my values and goals. MPL 2.0 ensures that the source code remains open and available, while allowing for flexibility in terms of collaboration and incorporation into other projects.
While I understand that different licenses may have their merits, I believe that MPL 2.0 provides the best balance of openness, collaborative potential, and legal clarity for the development and distribution of CrowdTLS.

BIN
crowdtls.png Normal file

Binary file not shown.

After

Width:  |  Height:  |  Size: 40 KiB

0
crowdtls/__init__.py Normal file
View file

50
crowdtls/cli.py Normal file
View file

@ -0,0 +1,50 @@
from typing import Dict
from typing import List
from fastapi import FastAPI
from sqlalchemy.ext.asyncio import AsyncSession
from sqlalchemy.ext.asyncio import create_async_engine
from sqlalchemy.future import select
from sqlmodel import SQLModel
from crowdtls.helpers import decode_der
from db import CertificateChain
DATABASE_URL = "postgresql+asyncpg://user:password@localhost:5432/database"
engine = create_async_engine(DATABASE_URL, echo=True)
app = FastAPI()
@app.on_event("startup")
async def startup_event():
async with engine.begin() as connection:
await connection.run_sync(SQLModel.metadata.create_all)
@app.post("/check")
async def check_fingerprints(fingerprints: Dict[str, List[int]]):
fps = fingerprints.get("fps")
async with AsyncSession(engine) as session:
for fp in fps:
stmt = select(CertificateChain).where(CertificateChain.fingerprint == fp)
result = await session.execute(stmt)
certificate = result.scalars().first()
if not certificate:
return {"send": True}
return {"send": False}
@app.post("/new")
async def new_fingerprints(fingerprints: Dict[str, List[int]]):
async with AsyncSession(engine) as session:
for fp, _ in fingerprints.items():
stmt = select(CertificateChain).where(CertificateChain.fingerprint == fp)
result = await session.execute(stmt)
certificate = result.scalars().first()
if not certificate:
new_certificate = decode_der(fp)
session.add(new_certificate)
pass
await session.commit()
return {"status": "OK"}

22
crowdtls/db.py Normal file
View file

@ -0,0 +1,22 @@
from sqlalchemy import Integer
from sqlalchemy import LargeBinary
from sqlalchemy.dialects.postgresql import JSONB
from sqlmodel import Field
from sqlmodel import SQLModel
class CertificateChain(SQLModel, table=True):
id: int = Field(default=None, primary_key=True)
fingerprint: str = Field(index=True)
domain_name: str
raw_der_certificate: LargeBinary
version: int
serial_number: str
signature: LargeBinary
issuer: JSONB
validity: JSONB
subject: JSONB
subject_public_key_info: JSONB
issuer_unique_id: Integer
subject_unique_id: Integer
extensions: JSONB

30
crowdtls/helpers.py Normal file
View file

@ -0,0 +1,30 @@
from typing import List
from cryptography import x509
from cryptography.hazmat.backends import default_backend
from crowdtls.db import CertificateChain
def decode_der(raw_der_certificate: List[int]) -> CertificateChain:
# Convert list of integers to bytes
der_cert_bytes = bytes(raw_der_certificate)
# Parse the DER certificate
cert = x509.load_der_x509_certificate(der_cert_bytes, default_backend())
certificate_chain = CertificateChain(
raw_der_certificate=der_cert_bytes,
version=cert.version.value,
serial_number=cert.serial_number,
signature=cert.signature,
issuer=cert.issuer.rfc4514_string(),
validity={"not_valid_before": cert.not_valid_before, "not_valid_after": cert.not_valid_after},
subject=cert.subject.rfc4514_string(),
subject_public_key_info=cert.public_key().public_bytes(),
issuer_unique_id=cert.issuer_unique_id,
subject_unique_id=cert.subject_unique_id,
extensions=[str(ext) for ext in cert.extensions],
)
return certificate_chain

37
pyproject.toml Normal file
View file

@ -0,0 +1,37 @@
[build-system]
requires = ["setuptools>=67.8"]
build-backend = "setuptools.build_meta"
[project]
name = "crowdtls"
version = "0.0.1"
authors = [{ name = "crowdtls", email = "git@nixon.mozmail.com" }]
description = "Backend server for CrowdTLS browser extension"
readme = "README.md"
requires-python = ">=3.9"
license = { text = "MIT" }
dependencies = [
"fastapi==0.95.2",
"uvicorn==0.22.0",
"loguru==0.7.0",
"pydantic==1.10.8",
"asyncpg==0.27.0",
"greenlet==2.0.2",
"sqlmodel==0.0.8",
"sqlalchemy==1.4.41",
]
[project.urls]
homepage = "https://github.com/DarrylNixon/CrowdTLS"
repository = "https://github.com/DarrylNixon/CrowdTLS-server"
[tool.setuptools]
py-modules = ["crowdtls"]
[tool.bandit]
exclude_dirs = ["/doc", "/build"]
# TODO: Stop skipping B104 (binding on 0.0.0.0), is there a nice way to get a good docker bind address?
skips = ["B104"]
[tool.black]
line-length = 120