purecrypt 0.1.1__tar.gz
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- purecrypt-0.1.1/.github/CODEOWNERS +3 -0
- purecrypt-0.1.1/.github/dependabot.yml +16 -0
- purecrypt-0.1.1/.github/workflows/ci.yml +59 -0
- purecrypt-0.1.1/.github/workflows/codeql.yml +35 -0
- purecrypt-0.1.1/.github/workflows/dependency-review.yml +22 -0
- purecrypt-0.1.1/.github/workflows/release.yml +63 -0
- purecrypt-0.1.1/.github/workflows/scorecard.yml +42 -0
- purecrypt-0.1.1/.github/workflows/zizmor.yml +28 -0
- purecrypt-0.1.1/.gitignore +11 -0
- purecrypt-0.1.1/CHANGELOG.md +34 -0
- purecrypt-0.1.1/CONTRIBUTING.md +19 -0
- purecrypt-0.1.1/LICENSE +12 -0
- purecrypt-0.1.1/Makefile +16 -0
- purecrypt-0.1.1/PKG-INFO +103 -0
- purecrypt-0.1.1/README.md +82 -0
- purecrypt-0.1.1/SECURITY.md +6 -0
- purecrypt-0.1.1/pyproject.toml +111 -0
- purecrypt-0.1.1/src/purecrypt/__init__.py +39 -0
- purecrypt-0.1.1/src/purecrypt/_pbes2.py +215 -0
- purecrypt-0.1.1/src/purecrypt/_utils.py +51 -0
- purecrypt-0.1.1/src/purecrypt/aes.py +449 -0
- purecrypt-0.1.1/src/purecrypt/asn1.py +404 -0
- purecrypt-0.1.1/src/purecrypt/chacha.py +216 -0
- purecrypt-0.1.1/src/purecrypt/ec.py +726 -0
- purecrypt-0.1.1/src/purecrypt/eddsa.py +267 -0
- purecrypt-0.1.1/src/purecrypt/exceptions.py +38 -0
- purecrypt-0.1.1/src/purecrypt/hashes.py +152 -0
- purecrypt-0.1.1/src/purecrypt/kdf.py +447 -0
- purecrypt-0.1.1/src/purecrypt/pem.py +107 -0
- purecrypt-0.1.1/src/purecrypt/py.typed +0 -0
- purecrypt-0.1.1/src/purecrypt/rsa.py +794 -0
- purecrypt-0.1.1/src/purecrypt/x25519.py +184 -0
- purecrypt-0.1.1/src/purecrypt/x509.py +1203 -0
- purecrypt-0.1.1/tests/__init__.py +1 -0
- purecrypt-0.1.1/tests/data/bundle.pem +66 -0
- purecrypt-0.1.1/tests/data/critext_leaf.pem +21 -0
- purecrypt-0.1.1/tests/data/critext_leaf_key.pem +28 -0
- purecrypt-0.1.1/tests/data/critsan_leaf.pem +22 -0
- purecrypt-0.1.1/tests/data/critsan_leaf_key.pem +28 -0
- purecrypt-0.1.1/tests/data/ec_leaf.pem +13 -0
- purecrypt-0.1.1/tests/data/ec_leaf_key.pem +5 -0
- purecrypt-0.1.1/tests/data/ec_root.pem +13 -0
- purecrypt-0.1.1/tests/data/ec_root_key.pem +5 -0
- purecrypt-0.1.1/tests/data/ed25519_leaf.pem +11 -0
- purecrypt-0.1.1/tests/data/ed25519_leaf_key.pem +3 -0
- purecrypt-0.1.1/tests/data/ed25519_root.pem +12 -0
- purecrypt-0.1.1/tests/data/ed25519_root_key.pem +3 -0
- purecrypt-0.1.1/tests/data/expired_leaf.pem +20 -0
- purecrypt-0.1.1/tests/data/expired_leaf_key.pem +28 -0
- purecrypt-0.1.1/tests/data/expired_root.pem +20 -0
- purecrypt-0.1.1/tests/data/expired_root_key.pem +28 -0
- purecrypt-0.1.1/tests/data/generate.sh +260 -0
- purecrypt-0.1.1/tests/data/ku_leaf.pem +20 -0
- purecrypt-0.1.1/tests/data/ku_leaf_key.pem +28 -0
- purecrypt-0.1.1/tests/data/ku_root.pem +21 -0
- purecrypt-0.1.1/tests/data/ku_root_key.pem +28 -0
- purecrypt-0.1.1/tests/data/plain_leaf.pem +20 -0
- purecrypt-0.1.1/tests/data/plain_leaf_key.pem +28 -0
- purecrypt-0.1.1/tests/data/plain_root.pem +20 -0
- purecrypt-0.1.1/tests/data/plain_root_key.pem +28 -0
- purecrypt-0.1.1/tests/data/pss_leaf.pem +22 -0
- purecrypt-0.1.1/tests/data/pss_leaf_key.pem +28 -0
- purecrypt-0.1.1/tests/data/rsa_inter.pem +21 -0
- purecrypt-0.1.1/tests/data/rsa_inter_key.pem +28 -0
- purecrypt-0.1.1/tests/data/rsa_inter_p0.pem +21 -0
- purecrypt-0.1.1/tests/data/rsa_inter_p0_key.pem +28 -0
- purecrypt-0.1.1/tests/data/rsa_leaf.der +0 -0
- purecrypt-0.1.1/tests/data/rsa_leaf.pem +24 -0
- purecrypt-0.1.1/tests/data/rsa_leaf_key.pem +28 -0
- purecrypt-0.1.1/tests/data/rsa_leaf_nods.pem +20 -0
- purecrypt-0.1.1/tests/data/rsa_leaf_nods_key.pem +28 -0
- purecrypt-0.1.1/tests/data/rsa_leaf_nosan.pem +21 -0
- purecrypt-0.1.1/tests/data/rsa_leaf_nosan_key.pem +28 -0
- purecrypt-0.1.1/tests/data/rsa_root.der +0 -0
- purecrypt-0.1.1/tests/data/rsa_root.pem +21 -0
- purecrypt-0.1.1/tests/data/rsa_root_key.pem +28 -0
- purecrypt-0.1.1/tests/data/rsa_subca.pem +20 -0
- purecrypt-0.1.1/tests/data/rsa_subca_key.pem +28 -0
- purecrypt-0.1.1/tests/data/rsa_subleaf.pem +20 -0
- purecrypt-0.1.1/tests/data/rsa_subleaf_key.pem +28 -0
- purecrypt-0.1.1/tests/data/sha1_leaf.pem +20 -0
- purecrypt-0.1.1/tests/data/sha1_leaf_key.pem +28 -0
- purecrypt-0.1.1/tests/data/untrusted_leaf.pem +20 -0
- purecrypt-0.1.1/tests/data/untrusted_leaf_key.pem +28 -0
- purecrypt-0.1.1/tests/data/untrusted_root.pem +21 -0
- purecrypt-0.1.1/tests/data/untrusted_root_key.pem +28 -0
- purecrypt-0.1.1/tests/data/v1_leaf.pem +22 -0
- purecrypt-0.1.1/tests/data/v1_leaf_key.pem +28 -0
- purecrypt-0.1.1/tests/data/v1_root.pem +18 -0
- purecrypt-0.1.1/tests/data/v1_root_key.pem +28 -0
- purecrypt-0.1.1/tests/data/zero_serial.pem +20 -0
- purecrypt-0.1.1/tests/data/zero_serial_key.pem +28 -0
- purecrypt-0.1.1/tests/fixtures/ec_enc.pem +8 -0
- purecrypt-0.1.1/tests/fixtures/ec_enc_aes192_sha224.pem +8 -0
- purecrypt-0.1.1/tests/fixtures/ec_enc_default_prf.pem +8 -0
- purecrypt-0.1.1/tests/fixtures/ec_pkcs8.pem +5 -0
- purecrypt-0.1.1/tests/fixtures/generate.sh +26 -0
- purecrypt-0.1.1/tests/fixtures/rsa_enc.pem +30 -0
- purecrypt-0.1.1/tests/fixtures/rsa_enc_aes128_sha512.pem +30 -0
- purecrypt-0.1.1/tests/fixtures/rsa_pkcs8.pem +28 -0
- purecrypt-0.1.1/tests/test_aes.py +469 -0
- purecrypt-0.1.1/tests/test_asn1.py +260 -0
- purecrypt-0.1.1/tests/test_chacha.py +217 -0
- purecrypt-0.1.1/tests/test_ec.py +478 -0
- purecrypt-0.1.1/tests/test_eddsa.py +154 -0
- purecrypt-0.1.1/tests/test_fuzz_asymmetric.py +180 -0
- purecrypt-0.1.1/tests/test_fuzz_symmetric.py +127 -0
- purecrypt-0.1.1/tests/test_hashes.py +122 -0
- purecrypt-0.1.1/tests/test_kdf.py +320 -0
- purecrypt-0.1.1/tests/test_pbes2.py +447 -0
- purecrypt-0.1.1/tests/test_pem.py +87 -0
- purecrypt-0.1.1/tests/test_rsa.py +502 -0
- purecrypt-0.1.1/tests/test_x25519.py +116 -0
- purecrypt-0.1.1/tests/test_x509.py +893 -0
- purecrypt-0.1.1/tests/vectors.py +236 -0
- purecrypt-0.1.1/uv.lock +890 -0
|
@@ -0,0 +1,16 @@
|
|
|
1
|
+
version: 2
|
|
2
|
+
updates:
|
|
3
|
+
- package-ecosystem: github-actions
|
|
4
|
+
directory: /
|
|
5
|
+
schedule:
|
|
6
|
+
interval: weekly
|
|
7
|
+
cooldown:
|
|
8
|
+
default-days: 7
|
|
9
|
+
open-pull-requests-limit: 5
|
|
10
|
+
- package-ecosystem: uv
|
|
11
|
+
directory: /
|
|
12
|
+
schedule:
|
|
13
|
+
interval: weekly
|
|
14
|
+
cooldown:
|
|
15
|
+
default-days: 7
|
|
16
|
+
open-pull-requests-limit: 5
|
|
@@ -0,0 +1,59 @@
|
|
|
1
|
+
name: CI
|
|
2
|
+
|
|
3
|
+
on:
|
|
4
|
+
push:
|
|
5
|
+
branches: [master]
|
|
6
|
+
pull_request:
|
|
7
|
+
schedule:
|
|
8
|
+
- cron: "9 7 * * 1"
|
|
9
|
+
|
|
10
|
+
permissions: {}
|
|
11
|
+
|
|
12
|
+
jobs:
|
|
13
|
+
lint:
|
|
14
|
+
runs-on: ubuntu-latest
|
|
15
|
+
steps:
|
|
16
|
+
- uses: step-security/harden-runner@e14015d583714f6e62063499dc959a02595150a1 # v2.21.1
|
|
17
|
+
with:
|
|
18
|
+
egress-policy: audit
|
|
19
|
+
|
|
20
|
+
- uses: actions/checkout@3d3c42e5aac5ba805825da76410c181273ba90b1 # v7.0.1
|
|
21
|
+
with:
|
|
22
|
+
persist-credentials: false
|
|
23
|
+
|
|
24
|
+
- uses: astral-sh/setup-uv@c18668ad3cf93ea998bef934396af7bb5c839dc7 # v10.2.0
|
|
25
|
+
with:
|
|
26
|
+
version: "0.11.32"
|
|
27
|
+
enable-cache: true
|
|
28
|
+
|
|
29
|
+
- run: uv sync --locked
|
|
30
|
+
|
|
31
|
+
- run: uv run ruff check .
|
|
32
|
+
- run: uv run ruff format --check .
|
|
33
|
+
- run: uv run bandit -r src/
|
|
34
|
+
- run: uv run mypy
|
|
35
|
+
- run: uvx ty check src tests
|
|
36
|
+
|
|
37
|
+
test:
|
|
38
|
+
runs-on: ubuntu-latest
|
|
39
|
+
strategy:
|
|
40
|
+
fail-fast: false
|
|
41
|
+
matrix:
|
|
42
|
+
python: ["3.10", "3.11", "3.12", "3.13", "3.14"]
|
|
43
|
+
steps:
|
|
44
|
+
- uses: step-security/harden-runner@e14015d583714f6e62063499dc959a02595150a1 # v2.21.1
|
|
45
|
+
with:
|
|
46
|
+
egress-policy: audit
|
|
47
|
+
|
|
48
|
+
- uses: actions/checkout@3d3c42e5aac5ba805825da76410c181273ba90b1 # v7.0.1
|
|
49
|
+
with:
|
|
50
|
+
persist-credentials: false
|
|
51
|
+
|
|
52
|
+
- uses: astral-sh/setup-uv@c18668ad3cf93ea998bef934396af7bb5c839dc7 # v10.2.0
|
|
53
|
+
with:
|
|
54
|
+
version: "0.11.32"
|
|
55
|
+
enable-cache: true
|
|
56
|
+
|
|
57
|
+
- run: uv python install ${{ matrix.python }}
|
|
58
|
+
- run: uv sync --locked --python ${{ matrix.python }}
|
|
59
|
+
- run: uv run pytest --cov
|
|
@@ -0,0 +1,35 @@
|
|
|
1
|
+
name: CodeQL
|
|
2
|
+
|
|
3
|
+
on:
|
|
4
|
+
push:
|
|
5
|
+
branches: [master]
|
|
6
|
+
pull_request:
|
|
7
|
+
schedule:
|
|
8
|
+
- cron: "17 3 * * 4"
|
|
9
|
+
|
|
10
|
+
permissions: {}
|
|
11
|
+
|
|
12
|
+
jobs:
|
|
13
|
+
analyze:
|
|
14
|
+
runs-on: ubuntu-latest
|
|
15
|
+
permissions:
|
|
16
|
+
actions: read
|
|
17
|
+
contents: read
|
|
18
|
+
security-events: write
|
|
19
|
+
steps:
|
|
20
|
+
- uses: step-security/harden-runner@e14015d583714f6e62063499dc959a02595150a1 # v2.21.1
|
|
21
|
+
with:
|
|
22
|
+
egress-policy: audit
|
|
23
|
+
|
|
24
|
+
- uses: actions/checkout@3d3c42e5aac5ba805825da76410c181273ba90b1 # v7.0.1
|
|
25
|
+
with:
|
|
26
|
+
persist-credentials: false
|
|
27
|
+
|
|
28
|
+
- uses: github/codeql-action/init@1c5b675653bb5c22dbe9b12b556ec555138e09fd # v4.38.1
|
|
29
|
+
with:
|
|
30
|
+
languages: python
|
|
31
|
+
queries: security-and-quality
|
|
32
|
+
|
|
33
|
+
- uses: github/codeql-action/analyze@1c5b675653bb5c22dbe9b12b556ec555138e09fd # v4.38.1
|
|
34
|
+
with:
|
|
35
|
+
category: "/language:python"
|
|
@@ -0,0 +1,22 @@
|
|
|
1
|
+
name: Dependency Review
|
|
2
|
+
|
|
3
|
+
on:
|
|
4
|
+
pull_request:
|
|
5
|
+
|
|
6
|
+
permissions: {}
|
|
7
|
+
|
|
8
|
+
jobs:
|
|
9
|
+
dependency-review:
|
|
10
|
+
runs-on: ubuntu-latest
|
|
11
|
+
permissions:
|
|
12
|
+
contents: read
|
|
13
|
+
steps:
|
|
14
|
+
- uses: step-security/harden-runner@e14015d583714f6e62063499dc959a02595150a1 # v2.21.1
|
|
15
|
+
with:
|
|
16
|
+
egress-policy: audit
|
|
17
|
+
|
|
18
|
+
- uses: actions/checkout@3d3c42e5aac5ba805825da76410c181273ba90b1 # v7.0.1
|
|
19
|
+
with:
|
|
20
|
+
persist-credentials: false
|
|
21
|
+
|
|
22
|
+
- uses: actions/dependency-review-action@a1d282b36b6f3519aa1f3fc636f609c47dddb294 # v5.0.0
|
|
@@ -0,0 +1,63 @@
|
|
|
1
|
+
name: Release
|
|
2
|
+
|
|
3
|
+
on:
|
|
4
|
+
push:
|
|
5
|
+
tags: ["v*"]
|
|
6
|
+
|
|
7
|
+
permissions: {}
|
|
8
|
+
|
|
9
|
+
jobs:
|
|
10
|
+
release:
|
|
11
|
+
runs-on: ubuntu-latest
|
|
12
|
+
environment:
|
|
13
|
+
name: pypi
|
|
14
|
+
url: https://pypi.org/p/purecrypt
|
|
15
|
+
permissions:
|
|
16
|
+
contents: write
|
|
17
|
+
id-token: write
|
|
18
|
+
attestations: write
|
|
19
|
+
steps:
|
|
20
|
+
- uses: step-security/harden-runner@e14015d583714f6e62063499dc959a02595150a1 # v2.21.1
|
|
21
|
+
with:
|
|
22
|
+
egress-policy: audit
|
|
23
|
+
|
|
24
|
+
- uses: actions/checkout@3d3c42e5aac5ba805825da76410c181273ba90b1 # v7.0.1
|
|
25
|
+
with:
|
|
26
|
+
persist-credentials: false
|
|
27
|
+
|
|
28
|
+
- uses: astral-sh/setup-uv@c18668ad3cf93ea998bef934396af7bb5c839dc7 # v10.2.0
|
|
29
|
+
with:
|
|
30
|
+
version: "0.11.32"
|
|
31
|
+
|
|
32
|
+
- run: uv sync --locked
|
|
33
|
+
|
|
34
|
+
- name: Verify tag matches package version
|
|
35
|
+
run: |
|
|
36
|
+
tag="${GITHUB_REF_NAME#v}"
|
|
37
|
+
version="$(uv run python -c 'import purecrypt; print(purecrypt.__version__)')"
|
|
38
|
+
if [ "$tag" != "$version" ]; then
|
|
39
|
+
echo "tag $tag does not match package version $version" >&2
|
|
40
|
+
exit 1
|
|
41
|
+
fi
|
|
42
|
+
|
|
43
|
+
- run: uv build
|
|
44
|
+
|
|
45
|
+
- name: Attest build provenance
|
|
46
|
+
uses: actions/attest-build-provenance@4d101475d8b20a2381f78447822ac1eab6504dd8 # v4.2.2
|
|
47
|
+
with:
|
|
48
|
+
subject-path: dist/*
|
|
49
|
+
|
|
50
|
+
- uses: pypa/gh-action-pypi-publish@dc37677b2e1c63e2034f94d8a5b11f265b73ba33 # release/v1
|
|
51
|
+
with:
|
|
52
|
+
attestations: true
|
|
53
|
+
|
|
54
|
+
- name: Create GitHub release
|
|
55
|
+
env:
|
|
56
|
+
GH_TOKEN: ${{ secrets.GITHUB_TOKEN }}
|
|
57
|
+
run: |
|
|
58
|
+
gh release create "${GITHUB_REF_NAME}" \
|
|
59
|
+
--repo "${GITHUB_REPOSITORY}" \
|
|
60
|
+
--verify-tag \
|
|
61
|
+
--title "${GITHUB_REF_NAME}" \
|
|
62
|
+
--generate-notes \
|
|
63
|
+
dist/*
|
|
@@ -0,0 +1,42 @@
|
|
|
1
|
+
name: OpenSSF Scorecard
|
|
2
|
+
|
|
3
|
+
on:
|
|
4
|
+
branch_protection_rule:
|
|
5
|
+
push:
|
|
6
|
+
branches: [master]
|
|
7
|
+
workflow_dispatch:
|
|
8
|
+
schedule:
|
|
9
|
+
- cron: "42 5 * * 6"
|
|
10
|
+
|
|
11
|
+
permissions: read-all
|
|
12
|
+
|
|
13
|
+
jobs:
|
|
14
|
+
analysis:
|
|
15
|
+
runs-on: ubuntu-latest
|
|
16
|
+
permissions:
|
|
17
|
+
security-events: write
|
|
18
|
+
id-token: write
|
|
19
|
+
steps:
|
|
20
|
+
- uses: step-security/harden-runner@e14015d583714f6e62063499dc959a02595150a1 # v2.21.1
|
|
21
|
+
with:
|
|
22
|
+
egress-policy: audit
|
|
23
|
+
|
|
24
|
+
- uses: actions/checkout@3d3c42e5aac5ba805825da76410c181273ba90b1 # v7.0.1
|
|
25
|
+
with:
|
|
26
|
+
persist-credentials: false
|
|
27
|
+
|
|
28
|
+
- uses: ossf/scorecard-action@2d1146689b8cda280b9bc96326124645441f03bc # v2.4.4
|
|
29
|
+
with:
|
|
30
|
+
results_file: results.sarif
|
|
31
|
+
results_format: sarif
|
|
32
|
+
publish_results: true
|
|
33
|
+
|
|
34
|
+
- uses: actions/upload-artifact@043fb46d1a93c77aae656e7c1c64a875d1fc6a0a # v7.0.1
|
|
35
|
+
with:
|
|
36
|
+
name: SARIF file
|
|
37
|
+
path: results.sarif
|
|
38
|
+
retention-days: 5
|
|
39
|
+
|
|
40
|
+
- uses: github/codeql-action/upload-sarif@1c5b675653bb5c22dbe9b12b556ec555138e09fd # v4.38.1
|
|
41
|
+
with:
|
|
42
|
+
sarif_file: results.sarif
|
|
@@ -0,0 +1,28 @@
|
|
|
1
|
+
name: zizmor
|
|
2
|
+
|
|
3
|
+
on:
|
|
4
|
+
push:
|
|
5
|
+
branches: [master]
|
|
6
|
+
paths: [.github/**]
|
|
7
|
+
pull_request:
|
|
8
|
+
paths: [.github/**]
|
|
9
|
+
|
|
10
|
+
permissions: {}
|
|
11
|
+
|
|
12
|
+
jobs:
|
|
13
|
+
zizmor:
|
|
14
|
+
runs-on: ubuntu-latest
|
|
15
|
+
permissions:
|
|
16
|
+
security-events: write
|
|
17
|
+
steps:
|
|
18
|
+
- uses: step-security/harden-runner@e14015d583714f6e62063499dc959a02595150a1 # v2.21.1
|
|
19
|
+
with:
|
|
20
|
+
egress-policy: audit
|
|
21
|
+
|
|
22
|
+
- uses: actions/checkout@3d3c42e5aac5ba805825da76410c181273ba90b1 # v7.0.1
|
|
23
|
+
with:
|
|
24
|
+
persist-credentials: false
|
|
25
|
+
|
|
26
|
+
- uses: zizmorcore/zizmor-action@cc914d7f3750a2d13d75c7f184a1060aa0e9d482 # v0.6.4
|
|
27
|
+
with:
|
|
28
|
+
advanced-security: false
|
|
@@ -0,0 +1,34 @@
|
|
|
1
|
+
# Changelog
|
|
2
|
+
|
|
3
|
+
## [0.1.1] - Unreleased
|
|
4
|
+
|
|
5
|
+
Fix the release workflow's package-name placeholder. No library
|
|
6
|
+
changes.
|
|
7
|
+
|
|
8
|
+
## [0.1.0] - Unreleased
|
|
9
|
+
|
|
10
|
+
Initial release. Pure-Python primitives with no dependencies and no
|
|
11
|
+
Rust. See the README security notice: pure Python cannot provide
|
|
12
|
+
constant-time execution, so secret-key operations must not run where a
|
|
13
|
+
co-located attacker can measure timing.
|
|
14
|
+
|
|
15
|
+
- AES-128/192/256 block cipher with ECB, CBC, CTR and GCM (AEAD)
|
|
16
|
+
- ChaCha20, Poly1305, ChaCha20-Poly1305 and XChaCha20-Poly1305
|
|
17
|
+
- Ed25519 signatures (RFC 8032) and X25519/X448 key exchange (RFC 7748)
|
|
18
|
+
- ECDSA and ECDH over P-256, P-384, P-521 and secp256k1 with RFC 6979
|
|
19
|
+
deterministic k
|
|
20
|
+
- RSA key generation, PSS and PKCS#1 v1.5 signatures, OAEP and
|
|
21
|
+
PKCS#1 v1.5 encryption, PKCS#1/PKCS#8/SPKI/SEC1 serialization
|
|
22
|
+
- Encrypted PKCS#8 (RFC 5958) via PBES2 with PBKDF2-HMAC and AES-CBC
|
|
23
|
+
- X.509 certificate parsing and validation (RFC 5280): chain building,
|
|
24
|
+
signature verification for RSA/ECDSA/Ed25519, critical-extension
|
|
25
|
+
policy, and RFC 6125 hostname matching
|
|
26
|
+
- HKDF, PBKDF2, scrypt and Argon2id
|
|
27
|
+
- Minimal strict DER and PEM codecs for keys and certificates
|
|
28
|
+
- Hash and HMAC facades over hashlib
|
|
29
|
+
|
|
30
|
+
Side-channel hardening: RSA private operations blinded with a random
|
|
31
|
+
factor and verified against the public exponent before release, fixed
|
|
32
|
+
iteration scalar multiplication with mask-selected adds on all curves,
|
|
33
|
+
mask-arithmetic Montgomery cswap, uniform full-buffer padding scans,
|
|
34
|
+
and constant-time-style comparisons on all secret-derived values.
|
|
@@ -0,0 +1,19 @@
|
|
|
1
|
+
# Contributing
|
|
2
|
+
|
|
3
|
+
Report bugs through GitHub issues. Security reports go to
|
|
4
|
+
security@quad4.io (see SECURITY.md).
|
|
5
|
+
|
|
6
|
+
## Setup
|
|
7
|
+
|
|
8
|
+
uv sync
|
|
9
|
+
make check
|
|
10
|
+
|
|
11
|
+
`make check` runs the full local gate: ruff lint and format, bandit,
|
|
12
|
+
mypy strict, ty, and pytest with coverage.
|
|
13
|
+
|
|
14
|
+
## Conventions
|
|
15
|
+
|
|
16
|
+
- No runtime dependencies unless the project genuinely needs one.
|
|
17
|
+
- Source files start with the SPDX license identifier.
|
|
18
|
+
- Public API changes need tests.
|
|
19
|
+
- Commit messages: short, imperative, formal.
|
purecrypt-0.1.1/LICENSE
ADDED
|
@@ -0,0 +1,12 @@
|
|
|
1
|
+
Copyright (c) 2026 Quad4
|
|
2
|
+
|
|
3
|
+
Permission to use, copy, modify, and/or distribute this software for any purpose
|
|
4
|
+
with or without fee is hereby granted.
|
|
5
|
+
|
|
6
|
+
THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES WITH
|
|
7
|
+
REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF MERCHANTABILITY AND
|
|
8
|
+
FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY SPECIAL, DIRECT,
|
|
9
|
+
INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES WHATSOEVER RESULTING FROM
|
|
10
|
+
LOSS OF USE, DATA OR PROFITS, WHETHER IN AN ACTION OF CONTRACT, NEGLIGENCE OR
|
|
11
|
+
OTHER TORTIOUS ACTION, ARISING OUT OF OR IN CONNECTION WITH THE USE OR
|
|
12
|
+
PERFORMANCE OF THIS SOFTWARE.
|
purecrypt-0.1.1/Makefile
ADDED
|
@@ -0,0 +1,16 @@
|
|
|
1
|
+
.PHONY: check lint test build
|
|
2
|
+
|
|
3
|
+
check: lint test
|
|
4
|
+
|
|
5
|
+
lint:
|
|
6
|
+
uv run ruff check .
|
|
7
|
+
uv run ruff format --check .
|
|
8
|
+
uv run bandit -r src/
|
|
9
|
+
uv run mypy
|
|
10
|
+
uvx ty check src tests
|
|
11
|
+
|
|
12
|
+
test:
|
|
13
|
+
uv run pytest --cov --cov-report=term-missing
|
|
14
|
+
|
|
15
|
+
build:
|
|
16
|
+
uv build
|
purecrypt-0.1.1/PKG-INFO
ADDED
|
@@ -0,0 +1,103 @@
|
|
|
1
|
+
Metadata-Version: 2.5
|
|
2
|
+
Name: purecrypt
|
|
3
|
+
Version: 0.1.1
|
|
4
|
+
Summary: Pure-Python cryptographic primitives. No dependencies, no Rust. NOT READY FOR PRODUCTION.
|
|
5
|
+
Project-URL: Homepage, https://quad4.io
|
|
6
|
+
Project-URL: Repository, https://github.com/Quad4-Software/python-library-template
|
|
7
|
+
Project-URL: Issues, https://github.com/Quad4-Software/python-library-template/issues
|
|
8
|
+
Project-URL: Changelog, https://github.com/Quad4-Software/python-library-template/blob/master/CHANGELOG.md
|
|
9
|
+
Author: Quad4
|
|
10
|
+
License-Expression: 0BSD
|
|
11
|
+
License-File: LICENSE
|
|
12
|
+
Keywords: aes,chacha20,cryptography,ed25519,pure-python,rsa
|
|
13
|
+
Classifier: Development Status :: 4 - Beta
|
|
14
|
+
Classifier: Intended Audience :: Developers
|
|
15
|
+
Classifier: License :: OSI Approved :: BSD License
|
|
16
|
+
Classifier: Operating System :: OS Independent
|
|
17
|
+
Classifier: Programming Language :: Python :: 3
|
|
18
|
+
Classifier: Typing :: Typed
|
|
19
|
+
Requires-Python: >=3.10
|
|
20
|
+
Description-Content-Type: text/markdown
|
|
21
|
+
|
|
22
|
+
# purecrypt
|
|
23
|
+
|
|
24
|
+
[](https://github.com/Quad4-Software/purecrypt/actions/workflows/ci.yml)
|
|
25
|
+
[](https://github.com/Quad4-Software/purecrypt/actions/workflows/codeql.yml)
|
|
26
|
+
[](https://securityscorecards.dev/viewer/?uri=github.com/Quad4-Software/purecrypt)
|
|
27
|
+
[](https://pypi.org/project/purecrypt/)
|
|
28
|
+
[](LICENSE)
|
|
29
|
+
|
|
30
|
+
> **Security notice.** Pure Python cannot provide constant-time
|
|
31
|
+
> execution. Secret-key operations (signing, decryption, key exchange)
|
|
32
|
+
> are hardened against the obvious leaks: RSA blinding, fixed-iteration
|
|
33
|
+
> scalar multiplication with mask-selected adds, uniform error paths,
|
|
34
|
+
> and constant-time-style comparisons. Residual timing variance in the
|
|
35
|
+
> interpreter remains, so do not run secret-key operations where a
|
|
36
|
+
> co-located attacker can measure timing. Public-data operations such
|
|
37
|
+
> as certificate validation and signature verification are unaffected.
|
|
38
|
+
> For high-assurance deployments use audited native implementations
|
|
39
|
+
> such as `cryptography` (OpenSSL) or `PyNaCl` (libsodium).
|
|
40
|
+
|
|
41
|
+
Pure-Python cryptographic primitives for Python 3.10+. No
|
|
42
|
+
dependencies, no Rust, no C extensions: only `hashlib`, `hmac` and
|
|
43
|
+
`secrets` from the standard library.
|
|
44
|
+
|
|
45
|
+
## Install
|
|
46
|
+
|
|
47
|
+
pip install purecrypt
|
|
48
|
+
|
|
49
|
+
## Contents
|
|
50
|
+
|
|
51
|
+
- AES-128/192/256 with ECB, CBC, CTR and GCM (AEAD)
|
|
52
|
+
- ChaCha20, Poly1305, ChaCha20-Poly1305 and XChaCha20-Poly1305
|
|
53
|
+
- Ed25519 signatures and X25519 key exchange
|
|
54
|
+
- ECDSA and ECDH over P-256, P-384, P-521 and secp256k1 (RFC 6979
|
|
55
|
+
deterministic signatures)
|
|
56
|
+
- RSA: key generation, PSS and PKCS#1 v1.5 signatures, OAEP and
|
|
57
|
+
PKCS#1 v1.5 encryption, PKCS#1/PKCS#8/SPKI serialization, encrypted
|
|
58
|
+
PKCS#8 (PBES2/AES-CBC)
|
|
59
|
+
- X.509 certificate parsing, signature verification, RFC 5280 chain
|
|
60
|
+
validation and RFC 6125 hostname matching
|
|
61
|
+
- HKDF, PBKDF2, scrypt and Argon2id
|
|
62
|
+
- Minimal DER/PEM handling for key serialization
|
|
63
|
+
- SHA-2, SHA-3, SHAKE, BLAKE2 and HMAC facades over hashlib
|
|
64
|
+
|
|
65
|
+
## Example
|
|
66
|
+
|
|
67
|
+
```python
|
|
68
|
+
from purecrypt.aes import gcm_decrypt, gcm_encrypt
|
|
69
|
+
from purecrypt.eddsa import Ed25519PrivateKey
|
|
70
|
+
from purecrypt.x509 import Certificate, validate_chain, verify_hostname
|
|
71
|
+
|
|
72
|
+
key, nonce = b"k" * 32, b"n" * 12
|
|
73
|
+
ct, tag = gcm_encrypt(key, nonce, b"secret", aad=b"hdr")
|
|
74
|
+
assert gcm_decrypt(key, nonce, ct, tag, aad=b"hdr") == b"secret"
|
|
75
|
+
|
|
76
|
+
priv = Ed25519PrivateKey.generate()
|
|
77
|
+
sig = priv.sign(b"message")
|
|
78
|
+
priv.public_key().verify(sig, b"message")
|
|
79
|
+
|
|
80
|
+
leaf = Certificate.from_pem(pem_text)
|
|
81
|
+
validate_chain(leaf, intermediates, trust_roots)
|
|
82
|
+
verify_hostname(leaf, "example.com")
|
|
83
|
+
```
|
|
84
|
+
|
|
85
|
+
## Known limitations
|
|
86
|
+
|
|
87
|
+
- No constant-time guarantees. Python semantics preclude them.
|
|
88
|
+
- Secret zeroization is best-effort (mutable buffers are wiped where
|
|
89
|
+
practical, but interpreter copies are out of scope).
|
|
90
|
+
- RSA PKCS#1 v1.5 decryption is inherently Bleichenbacher-prone. Use
|
|
91
|
+
OAEP.
|
|
92
|
+
- X.509 validation covers RFC 5280 path rules and RFC 6125 hostnames,
|
|
93
|
+
but there is no revocation checking (no CRL or OCSP) and no name or
|
|
94
|
+
policy constraint enforcement. A critical extension whose semantics
|
|
95
|
+
are not enforced fails validation.
|
|
96
|
+
- No TLS. That is a different and much larger problem.
|
|
97
|
+
|
|
98
|
+
## Development
|
|
99
|
+
|
|
100
|
+
uv sync --group dev
|
|
101
|
+
make check
|
|
102
|
+
|
|
103
|
+
License: 0BSD. Quad4 Software, https://quad4.io
|
|
@@ -0,0 +1,82 @@
|
|
|
1
|
+
# purecrypt
|
|
2
|
+
|
|
3
|
+
[](https://github.com/Quad4-Software/purecrypt/actions/workflows/ci.yml)
|
|
4
|
+
[](https://github.com/Quad4-Software/purecrypt/actions/workflows/codeql.yml)
|
|
5
|
+
[](https://securityscorecards.dev/viewer/?uri=github.com/Quad4-Software/purecrypt)
|
|
6
|
+
[](https://pypi.org/project/purecrypt/)
|
|
7
|
+
[](LICENSE)
|
|
8
|
+
|
|
9
|
+
> **Security notice.** Pure Python cannot provide constant-time
|
|
10
|
+
> execution. Secret-key operations (signing, decryption, key exchange)
|
|
11
|
+
> are hardened against the obvious leaks: RSA blinding, fixed-iteration
|
|
12
|
+
> scalar multiplication with mask-selected adds, uniform error paths,
|
|
13
|
+
> and constant-time-style comparisons. Residual timing variance in the
|
|
14
|
+
> interpreter remains, so do not run secret-key operations where a
|
|
15
|
+
> co-located attacker can measure timing. Public-data operations such
|
|
16
|
+
> as certificate validation and signature verification are unaffected.
|
|
17
|
+
> For high-assurance deployments use audited native implementations
|
|
18
|
+
> such as `cryptography` (OpenSSL) or `PyNaCl` (libsodium).
|
|
19
|
+
|
|
20
|
+
Pure-Python cryptographic primitives for Python 3.10+. No
|
|
21
|
+
dependencies, no Rust, no C extensions: only `hashlib`, `hmac` and
|
|
22
|
+
`secrets` from the standard library.
|
|
23
|
+
|
|
24
|
+
## Install
|
|
25
|
+
|
|
26
|
+
pip install purecrypt
|
|
27
|
+
|
|
28
|
+
## Contents
|
|
29
|
+
|
|
30
|
+
- AES-128/192/256 with ECB, CBC, CTR and GCM (AEAD)
|
|
31
|
+
- ChaCha20, Poly1305, ChaCha20-Poly1305 and XChaCha20-Poly1305
|
|
32
|
+
- Ed25519 signatures and X25519 key exchange
|
|
33
|
+
- ECDSA and ECDH over P-256, P-384, P-521 and secp256k1 (RFC 6979
|
|
34
|
+
deterministic signatures)
|
|
35
|
+
- RSA: key generation, PSS and PKCS#1 v1.5 signatures, OAEP and
|
|
36
|
+
PKCS#1 v1.5 encryption, PKCS#1/PKCS#8/SPKI serialization, encrypted
|
|
37
|
+
PKCS#8 (PBES2/AES-CBC)
|
|
38
|
+
- X.509 certificate parsing, signature verification, RFC 5280 chain
|
|
39
|
+
validation and RFC 6125 hostname matching
|
|
40
|
+
- HKDF, PBKDF2, scrypt and Argon2id
|
|
41
|
+
- Minimal DER/PEM handling for key serialization
|
|
42
|
+
- SHA-2, SHA-3, SHAKE, BLAKE2 and HMAC facades over hashlib
|
|
43
|
+
|
|
44
|
+
## Example
|
|
45
|
+
|
|
46
|
+
```python
|
|
47
|
+
from purecrypt.aes import gcm_decrypt, gcm_encrypt
|
|
48
|
+
from purecrypt.eddsa import Ed25519PrivateKey
|
|
49
|
+
from purecrypt.x509 import Certificate, validate_chain, verify_hostname
|
|
50
|
+
|
|
51
|
+
key, nonce = b"k" * 32, b"n" * 12
|
|
52
|
+
ct, tag = gcm_encrypt(key, nonce, b"secret", aad=b"hdr")
|
|
53
|
+
assert gcm_decrypt(key, nonce, ct, tag, aad=b"hdr") == b"secret"
|
|
54
|
+
|
|
55
|
+
priv = Ed25519PrivateKey.generate()
|
|
56
|
+
sig = priv.sign(b"message")
|
|
57
|
+
priv.public_key().verify(sig, b"message")
|
|
58
|
+
|
|
59
|
+
leaf = Certificate.from_pem(pem_text)
|
|
60
|
+
validate_chain(leaf, intermediates, trust_roots)
|
|
61
|
+
verify_hostname(leaf, "example.com")
|
|
62
|
+
```
|
|
63
|
+
|
|
64
|
+
## Known limitations
|
|
65
|
+
|
|
66
|
+
- No constant-time guarantees. Python semantics preclude them.
|
|
67
|
+
- Secret zeroization is best-effort (mutable buffers are wiped where
|
|
68
|
+
practical, but interpreter copies are out of scope).
|
|
69
|
+
- RSA PKCS#1 v1.5 decryption is inherently Bleichenbacher-prone. Use
|
|
70
|
+
OAEP.
|
|
71
|
+
- X.509 validation covers RFC 5280 path rules and RFC 6125 hostnames,
|
|
72
|
+
but there is no revocation checking (no CRL or OCSP) and no name or
|
|
73
|
+
policy constraint enforcement. A critical extension whose semantics
|
|
74
|
+
are not enforced fails validation.
|
|
75
|
+
- No TLS. That is a different and much larger problem.
|
|
76
|
+
|
|
77
|
+
## Development
|
|
78
|
+
|
|
79
|
+
uv sync --group dev
|
|
80
|
+
make check
|
|
81
|
+
|
|
82
|
+
License: 0BSD. Quad4 Software, https://quad4.io
|
|
@@ -0,0 +1,111 @@
|
|
|
1
|
+
[build-system]
|
|
2
|
+
requires = ["hatchling"]
|
|
3
|
+
build-backend = "hatchling.build"
|
|
4
|
+
|
|
5
|
+
[project]
|
|
6
|
+
name = "purecrypt"
|
|
7
|
+
dynamic = ["version"]
|
|
8
|
+
description = "Pure-Python cryptographic primitives. No dependencies, no Rust. NOT READY FOR PRODUCTION."
|
|
9
|
+
readme = "README.md"
|
|
10
|
+
requires-python = ">=3.10"
|
|
11
|
+
license = "0BSD"
|
|
12
|
+
authors = [{ name = "Quad4" }]
|
|
13
|
+
keywords = ["cryptography", "aes", "chacha20", "ed25519", "rsa", "pure-python"]
|
|
14
|
+
classifiers = [
|
|
15
|
+
"Development Status :: 4 - Beta",
|
|
16
|
+
"Intended Audience :: Developers",
|
|
17
|
+
"License :: OSI Approved :: BSD License",
|
|
18
|
+
"Operating System :: OS Independent",
|
|
19
|
+
"Programming Language :: Python :: 3",
|
|
20
|
+
"Typing :: Typed",
|
|
21
|
+
]
|
|
22
|
+
dependencies = []
|
|
23
|
+
|
|
24
|
+
[project.urls]
|
|
25
|
+
Homepage = "https://quad4.io"
|
|
26
|
+
Repository = "https://github.com/Quad4-Software/python-library-template"
|
|
27
|
+
Issues = "https://github.com/Quad4-Software/python-library-template/issues"
|
|
28
|
+
Changelog = "https://github.com/Quad4-Software/python-library-template/blob/master/CHANGELOG.md"
|
|
29
|
+
|
|
30
|
+
[tool.hatch.version]
|
|
31
|
+
path = "src/purecrypt/__init__.py"
|
|
32
|
+
|
|
33
|
+
[dependency-groups]
|
|
34
|
+
dev = [
|
|
35
|
+
"pytest>=9.1.1",
|
|
36
|
+
"pytest-cov>=7.1.0",
|
|
37
|
+
"ruff>=0.16.8",
|
|
38
|
+
"bandit>=1.9.4",
|
|
39
|
+
"mypy>=2.3.1",
|
|
40
|
+
"hypothesis>=6.168.0",
|
|
41
|
+
"ty>=0.0.83",
|
|
42
|
+
]
|
|
43
|
+
|
|
44
|
+
[tool.ruff]
|
|
45
|
+
target-version = "py310"
|
|
46
|
+
src = ["src", "tests"]
|
|
47
|
+
|
|
48
|
+
[tool.ruff.lint]
|
|
49
|
+
select = [
|
|
50
|
+
"E", "W", # pycodestyle
|
|
51
|
+
"F", # pyflakes
|
|
52
|
+
"I", # isort
|
|
53
|
+
"N", # pep8-naming
|
|
54
|
+
"UP", # pyupgrade
|
|
55
|
+
"YTT", # sys.version misuse
|
|
56
|
+
"FA", # future annotations
|
|
57
|
+
"A", # builtins shadowing
|
|
58
|
+
"B", # flake8-bugbear
|
|
59
|
+
"BLE", # blind except
|
|
60
|
+
"C4", # comprehensions
|
|
61
|
+
"C90", # mccabe complexity
|
|
62
|
+
"DTZ", # timezone-aware datetimes
|
|
63
|
+
"ERA", # commented-out code
|
|
64
|
+
"FURB", # refurb
|
|
65
|
+
"G", # logging format
|
|
66
|
+
"ICN", # import conventions
|
|
67
|
+
"ISC", # implicit string concat
|
|
68
|
+
"LOG", # logging
|
|
69
|
+
"PERF", # perflint
|
|
70
|
+
"PGH", # pygrep hooks
|
|
71
|
+
"PIE", # misc lints
|
|
72
|
+
"PT", # pytest style
|
|
73
|
+
"PTH", # pathlib
|
|
74
|
+
"RET", # return
|
|
75
|
+
"RSE", # raise/return
|
|
76
|
+
"S", # bandit rules
|
|
77
|
+
"SIM", # simplify
|
|
78
|
+
"SLOT", # __slots__
|
|
79
|
+
"T20", # print
|
|
80
|
+
"TRY", # exception handling
|
|
81
|
+
"RUF", # ruff-specific
|
|
82
|
+
]
|
|
83
|
+
ignore = [
|
|
84
|
+
"ISC001", # single-line implicit concat conflicts with the formatter
|
|
85
|
+
"TRY003", # long messages in raise calls are fine
|
|
86
|
+
]
|
|
87
|
+
|
|
88
|
+
[tool.ruff.lint.per-file-ignores]
|
|
89
|
+
"tests/*" = ["S101", "S603"]
|
|
90
|
+
# Names intentionally mirror the cryptography package's exception API.
|
|
91
|
+
"src/purecrypt/exceptions.py" = ["N818"]
|
|
92
|
+
|
|
93
|
+
[tool.mypy]
|
|
94
|
+
strict = true
|
|
95
|
+
files = ["src", "tests"]
|
|
96
|
+
|
|
97
|
+
[tool.bandit]
|
|
98
|
+
exclude_dirs = ["tests"]
|
|
99
|
+
|
|
100
|
+
[tool.coverage.run]
|
|
101
|
+
branch = true
|
|
102
|
+
source = ["src/purecrypt"]
|
|
103
|
+
|
|
104
|
+
[tool.coverage.report]
|
|
105
|
+
show_missing = true
|
|
106
|
+
fail_under = 80
|
|
107
|
+
|
|
108
|
+
[tool.pytest.ini_options]
|
|
109
|
+
testpaths = ["tests"]
|
|
110
|
+
xfail_strict = true
|
|
111
|
+
filterwarnings = ["error"]
|