Critical: build_leaf_cert self-signed leaves with the leaf key instead of the YubiKey-held Intermediate CA key. Now extracts TBS, signs via pkcs11-tool (ECDSA-SHA384), reassembles, and verifies against the intermediate CA public key before returning. - setup-certauth.sh: ~1100 lines of stale inline api/ copies replaced with copy-from-repo (single source of truth); writes private /etc/certauth/certauth.env (0600); DB init loads env, no more swallowed errors; systemd unit gets EnvironmentFile= - config.py: YubiKey serials no longer hard-coded (env, fail closed); aarch64-only PKCS#11 path replaced with arch-neutral default; all paths env-overridable (CERTAUTH_*) - main.py: removed dead fastapi.security.CSRFProtection import (crashed startup); module-relative static/templates dirs; created_by resolved from the authenticated user instead of hard-coded 1; unclosed file handles fixed; domain_id 0 stored as NULL (FK bug) - models.py: certificates.domain_id FK pointed at users(id), now domains(id) - login: CSRF token now actually sent and validated - tests: 23 tests (auth, API flows, DER helpers, signing pipeline) - README, LICENSE, requirements.txt, pyproject.toml
56 lines
1.7 KiB
Python
56 lines
1.7 KiB
Python
"""DER helpers and signing pipeline (no YubiKey required)."""
|
|
|
|
from datetime import datetime, timedelta, timezone
|
|
|
|
from cryptography import x509
|
|
from cryptography.hazmat.primitives import hashes
|
|
from cryptography.hazmat.primitives.asymmetric import ec
|
|
from cryptography.x509.oid import NameOID
|
|
|
|
from signing import _der_read_len, _der_seq, _split_cert_der
|
|
|
|
|
|
from cryptography.hazmat.primitives.serialization import Encoding
|
|
|
|
|
|
def _dummy_cert_der():
|
|
key = ec.generate_private_key(ec.SECP384R1())
|
|
name = x509.Name([x509.NameAttribute(NameOID.COMMON_NAME, "dummy.test")])
|
|
cert = (
|
|
x509.CertificateBuilder()
|
|
.subject_name(name)
|
|
.issuer_name(name)
|
|
.public_key(key.public_key())
|
|
.serial_number(x509.random_serial_number())
|
|
.not_valid_before(datetime.now(timezone.utc))
|
|
.not_valid_after(datetime.now(timezone.utc) + timedelta(days=1))
|
|
.sign(key, hashes.SHA384())
|
|
)
|
|
return cert.public_bytes(Encoding.DER)
|
|
|
|
|
|
def test_split_roundtrip():
|
|
der = _dummy_cert_der()
|
|
tbs, alg, sig = _split_cert_der(der)
|
|
assert alg[0] == 0x30
|
|
assert sig[0] == 0x03
|
|
# Reassembling the same parts reproduces the original certificate.
|
|
reassembled = _der_seq(tbs + alg + sig)
|
|
assert reassembled == der
|
|
|
|
|
|
def test_der_read_len_short_and_long():
|
|
assert _der_read_len(b"\x05ABC", 0) == (5, 1)
|
|
long_len = b"\x81\x10"
|
|
assert _der_read_len(long_len + b"A" * 16, 0) == (16, 2)
|
|
|
|
|
|
def test_build_leaf_cert_fails_cleanly_without_ca():
|
|
"""Without the CA files the pipeline fails cleanly (no YubiKey available)."""
|
|
import pytest
|
|
|
|
from signing import build_leaf_cert
|
|
|
|
with pytest.raises(FileNotFoundError):
|
|
build_leaf_cert("nope.test", "", 365)
|