Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Initial PQC algorithm support #145

Merged
merged 8 commits into from
Jan 10, 2025
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
10 changes: 10 additions & 0 deletions CHANGELOG.md
Original file line number Diff line number Diff line change
Expand Up @@ -2,6 +2,16 @@

All notable changes to this project from version 0.9.3 onwards are documented in this file.

## 0.12.6 - 2025-01-13

### New features/enhancements

- Add support for ML-DSA, SLH-DSA, and ML-KEM algorithms (#132)

### Fixes

- Gracefully handle SAN decoding errors when subject emailAddress attribute is present (#143)

## 0.12.5 - 2024-11-27

### Fixes
Expand Down
1 change: 1 addition & 0 deletions pkilint/bin/lint_pkix_cert.py
Original file line number Diff line number Diff line change
Expand Up @@ -34,6 +34,7 @@ def main(cli_args=None) -> int:
certificate.create_validity_validator_container(),
certificate.create_subject_validator_container([]),
certificate.create_extensions_validator_container([]),
certificate.create_spki_validator_container([]),
],
)

Expand Down
7 changes: 3 additions & 4 deletions pkilint/cabf/serverauth/__init__.py
Original file line number Diff line number Diff line change
Expand Up @@ -162,16 +162,15 @@ def create_spki_validator_container(additional_validators=None):
if additional_validators is None:
additional_validators = []

return validation.ValidatorContainer(
validators=[
return certificate.create_spki_validator_container(
[
serverauth_key.ServerauthAllowedPublicKeyAlgorithmEncodingValidator(
path="certificate.tbsCertificate.subjectPublicKeyInfo.algorithm"
),
cabf_key.RsaKeyValidator(),
cabf_key.EcdsaKeyValidator(),
]
+ additional_validators,
path="certificate.tbsCertificate.subjectPublicKeyInfo",
+ additional_validators
)


Expand Down
7 changes: 3 additions & 4 deletions pkilint/cabf/smime/__init__.py
Original file line number Diff line number Diff line change
Expand Up @@ -146,16 +146,15 @@ def create_decoding_validators():


def create_spki_validation_container():
return validation.ValidatorContainer(
validators=[
return certificate.create_spki_validator_container(
[
smime_key.SmimeAllowedPublicKeyAlgorithmEncodingValidator(
path="certificate.tbsCertificate.subjectPublicKeyInfo.algorithm"
),
cabf_key.RsaKeyValidator(),
cabf_key.EcdsaKeyValidator(),
smime_key.GmailAllowedModulusLengthValidator(),
],
path="certificate.tbsCertificate.subjectPublicKeyInfo",
]
)


Expand Down
5 changes: 2 additions & 3 deletions pkilint/etsi/__init__.py
Original file line number Diff line number Diff line change
Expand Up @@ -384,9 +384,8 @@ def create_validators(
additional_spki_validators=spki_validators,
)
else:
spki_validator_container = validation.ValidatorContainer(
validators=spki_validators,
path="certificate.tbsCertificate.subjectPublicKeyInfo",
spki_validator_container = certificate.create_spki_validator_container(
spki_validators
)

top_level_container = validation.ValidatorContainer(
Expand Down
Empty file added pkilint/nist/__init__.py
Empty file.
Empty file added pkilint/nist/asn1/__init__.py
Empty file.
134 changes: 134 additions & 0 deletions pkilint/nist/asn1/csor.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,134 @@
from pyasn1.type.univ import ObjectIdentifier


# top-level OID arcs

nistAlgorithms = ObjectIdentifier("2.16.840.1.101.3.4")

sigAlgs = nistAlgorithms + (3,)

kems = nistAlgorithms + (4,)

# ML-DSA

id_ml_dsa_44 = sigAlgs + (17,)

id_ml_dsa_65 = sigAlgs + (18,)

id_ml_dsa_87 = sigAlgs + (19,)

MLDSA_OIDS = {
id_ml_dsa_44,
id_ml_dsa_65,
id_ml_dsa_87,
}

# HashML-DSA

id_hash_ml_dsa_44_with_sha512 = sigAlgs + (32,)

id_hash_ml_dsa_65_with_sha512 = sigAlgs + (33,)

id_hash_ml_dsa_87_with_sha512 = sigAlgs + (34,)

HASH_MLDSA_OIDS = {
id_hash_ml_dsa_44_with_sha512,
id_hash_ml_dsa_65_with_sha512,
id_hash_ml_dsa_87_with_sha512,
}

# SLH-DSA

id_slh_dsa_sha2_128s = sigAlgs + (20,)

id_slh_dsa_sha2_128f = sigAlgs + (21,)

id_slh_dsa_sha2_192s = sigAlgs + (22,)

id_slh_dsa_sha2_192f = sigAlgs + (23,)

id_slh_dsa_sha2_256s = sigAlgs + (24,)

id_slh_dsa_sha2_256f = sigAlgs + (25,)

id_slh_dsa_shake_128s = sigAlgs + (26,)

id_slh_dsa_shake_128f = sigAlgs + (27,)

id_slh_dsa_shake_192s = sigAlgs + (28,)

id_slh_dsa_shake_192f = sigAlgs + (29,)

id_slh_dsa_shake_256s = sigAlgs + (30,)

id_slh_dsa_shake_256f = sigAlgs + (31,)

SLHDSA_OIDS = {
id_slh_dsa_sha2_128s,
id_slh_dsa_sha2_128f,
id_slh_dsa_sha2_192s,
id_slh_dsa_sha2_192f,
id_slh_dsa_sha2_256s,
id_slh_dsa_sha2_256f,
id_slh_dsa_shake_128s,
id_slh_dsa_shake_128f,
id_slh_dsa_shake_192s,
id_slh_dsa_shake_192f,
id_slh_dsa_shake_256s,
id_slh_dsa_shake_256f,
}

# HashSLH-DSA

id_hash_slh_dsa_sha2_128s_with_sha256 = sigAlgs + (35,)

id_hash_slh_dsa_sha2_128f_with_sha256 = sigAlgs + (36,)

id_hash_slh_dsa_sha2_192s_with_sha512 = sigAlgs + (37,)

id_hash_slh_dsa_sha2_192f_with_sha512 = sigAlgs + (38,)

id_hash_slh_dsa_sha2_256s_with_sha512 = sigAlgs + (39,)

id_hash_slh_dsa_sha2_256f_with_sha512 = sigAlgs + (40,)

id_hash_slh_dsa_shake_128s_with_shake128 = sigAlgs + (41,)

id_hash_slh_dsa_shake_128f_with_shake128 = sigAlgs + (42,)

id_hash_slh_dsa_shake_192s_with_shake256 = sigAlgs + (43,)

id_hash_slh_dsa_shake_192f_with_shake256 = sigAlgs + (44,)

id_hash_slh_dsa_shake_256s_with_shake256 = sigAlgs + (45,)

id_hash_slh_dsa_shake_256f_with_shake256 = sigAlgs + (46,)

HASH_SLHDSA_OIDS = {
id_hash_slh_dsa_sha2_128s_with_sha256,
id_hash_slh_dsa_sha2_128f_with_sha256,
id_hash_slh_dsa_sha2_192s_with_sha512,
id_hash_slh_dsa_sha2_192f_with_sha512,
id_hash_slh_dsa_sha2_256s_with_sha512,
id_hash_slh_dsa_sha2_256f_with_sha512,
id_hash_slh_dsa_shake_128s_with_shake128,
id_hash_slh_dsa_shake_128f_with_shake128,
id_hash_slh_dsa_shake_192s_with_shake256,
id_hash_slh_dsa_shake_192f_with_shake256,
id_hash_slh_dsa_shake_256s_with_shake256,
id_hash_slh_dsa_shake_256f_with_shake256,
}

# ML-KEM

id_alg_ml_kem_512 = kems + (1,)

id_alg_ml_kem_768 = kems + (2,)

id_alg_ml_kem_1024 = kems + (3,)

MLKEM_OIDS = {
id_alg_ml_kem_512,
id_alg_ml_kem_768,
id_alg_ml_kem_1024,
}
40 changes: 40 additions & 0 deletions pkilint/nist/asn1/fips_203.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,40 @@
from pyasn1.type import univ
from pyasn1.type.constraint import ValueSizeConstraint

from pkilint import document
from pkilint.nist.asn1 import csor


ML_KEM_512_PublicKeySize = 800
ML_KEM_768_PublicKeySize = 1184
ML_KEM_1024_PublicKeySize = 1568


class MlKem512PublicKey(univ.OctetString):
subtypeSpec = ValueSizeConstraint(
ML_KEM_512_PublicKeySize, ML_KEM_512_PublicKeySize
)


class MlKem768PublicKey(univ.OctetString):
subtypeSpec = ValueSizeConstraint(
ML_KEM_768_PublicKeySize, ML_KEM_768_PublicKeySize
)


class MlKem1024PublicKey(univ.OctetString):
subtypeSpec = ValueSizeConstraint(
ML_KEM_1024_PublicKeySize, ML_KEM_1024_PublicKeySize
)


ALGORITHM_OID_TO_KEY_MAPPINGS = {
csor.id_alg_ml_kem_512: MlKem512PublicKey(),
csor.id_alg_ml_kem_768: MlKem768PublicKey(),
csor.id_alg_ml_kem_1024: MlKem1024PublicKey(),
}

ALGORITHM_OID_TO_PARAMETER_MAPPINGS = {
k: document.ValueDecoder.VALUE_NODE_ABSENT
for k in ALGORITHM_OID_TO_KEY_MAPPINGS.keys()
}
39 changes: 39 additions & 0 deletions pkilint/nist/asn1/fips_204.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,39 @@
from pyasn1.type import univ
from pyasn1.type.constraint import ValueSizeConstraint

from pkilint import document
from pkilint.nist.asn1 import csor


ML_DSA_44_PublicKeySize = 1312
ML_DSA_65_PublicKeySize = 1952
ML_DSA_87_PublicKeySize = 2592


class MlDsa44PublicKey(univ.OctetString):
subtypeSpec = ValueSizeConstraint(ML_DSA_44_PublicKeySize, ML_DSA_44_PublicKeySize)


class MlDsa65PublicKey(univ.OctetString):
subtypeSpec = ValueSizeConstraint(ML_DSA_65_PublicKeySize, ML_DSA_65_PublicKeySize)


class MlDsa87PublicKey(univ.OctetString):
subtypeSpec = ValueSizeConstraint(ML_DSA_87_PublicKeySize, ML_DSA_87_PublicKeySize)


ALGORITHM_OID_TO_KEY_MAPPINGS = {
# pure
csor.id_ml_dsa_44: MlDsa44PublicKey(),
csor.id_ml_dsa_65: MlDsa65PublicKey(),
csor.id_ml_dsa_87: MlDsa87PublicKey(),
# pre-hashed
csor.id_hash_ml_dsa_44_with_sha512: MlDsa44PublicKey(),
csor.id_hash_ml_dsa_65_with_sha512: MlDsa65PublicKey(),
csor.id_hash_ml_dsa_87_with_sha512: MlDsa87PublicKey(),
}

ALGORITHM_OID_TO_PARAMETER_MAPPINGS = {
k: document.ValueDecoder.VALUE_NODE_ABSENT
for k in ALGORITHM_OID_TO_KEY_MAPPINGS.keys()
}
Loading
Loading