Skip to content
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
14 changes: 11 additions & 3 deletions cwt/algs/ec2.py
Original file line number Diff line number Diff line change
Expand Up @@ -119,10 +119,18 @@ def __init__(self, params: Dict[int, Any]):
self._key_ops = [7, 8]
elif self._alg in COSE_ALGORITHMS_HPKE.values():
if self._key_ops:
if not (set(self._key_ops) & set([7, 8])):
raise ValueError("Invalid key_ops for key derivation.")
if -4 in params:
# private key for key derivation.
if len(self._key_ops) != 1 or self._key_ops[0] != 8:
raise ValueError("Invalid key_ops for HPKE private key.")
else:
# public key for key derivation.
if len(self._key_ops) > 0:
raise ValueError("Invalid key_ops for HPKE public key.")
else:
self._key_ops = [7, 8]
if -4 in params and isinstance(self._key_ops, list) and len(self._key_ops) == 0:
raise ValueError("Invalid key_ops for HPKE private key.")
self._key_ops = [8] if -4 in params else []
else:
raise ValueError(f"Unsupported or unknown alg(3) for EC2: {self._alg}.")
else:
Expand Down
107 changes: 68 additions & 39 deletions cwt/algs/okp.py
Original file line number Diff line number Diff line change
Expand Up @@ -24,7 +24,6 @@
)

from ..const import (
COSE_ALGORITHMS_CKDM_KEY_AGREEMENT,
COSE_ALGORITHMS_CKDM_KEY_AGREEMENT_ES,
COSE_ALGORITHMS_HPKE,
COSE_ALGORITHMS_SIG_OKP,
Expand Down Expand Up @@ -65,8 +64,6 @@ def __init__(self, params: Dict[int, Any]):
self._crv = params[-1]
if not isinstance(self._crv, int):
raise ValueError("crv(-1) should be int.")
if self._crv not in [4, 5, 6, 7]:
raise ValueError(f"Unsupported or unknown crv(-1) for OKP: {self._crv}.")
if self._crv in [4, 5]:
# if not self._alg:
# raise ValueError("X25519/X448 needs alg explicitly.")
Expand All @@ -78,6 +75,11 @@ def __init__(self, params: Dict[int, Any]):
self._hash_alg = hashes.SHA256 if self._crv == 4 else hashes.SHA512
elif self._alg is not None:
raise ValueError(f"Unsupported or unknown alg used with X25519/X448: {self._alg}.")
elif self._crv in [6, 7]:
if self._alg is not None and self._alg != -8:
raise ValueError(f"Unsupported or unknown alg used with Ed25519/Ed448: {self._alg}.")
else:
raise ValueError(f"Unsupported or unknown crv(-1) for OKP: {self._crv}.")

# Check the existence of the key.
if -2 not in params and -4 not in params:
Expand All @@ -87,55 +89,82 @@ def __init__(self, params: Dict[int, Any]):
if self._key_ops:
if set(self._key_ops) & set([3, 4, 5, 6, 9, 10]):
raise ValueError("Unknown or not permissible key_ops(4) for OKP.")
else:
if self._crv in [4, 5]:
self._key_ops = [7, 8] if -4 in params else []
else: # self._crv in [6, 7]
self._key_ops = [1, 2] if -4 in params else [2]
if self._alg:
if self._alg in COSE_ALGORITHMS_SIG_OKP.values():
if -4 in params:
# private key for signing.
if not (set(self._key_ops) & set([1, 2])):
raise ValueError("Invalid key_ops for signing key.")
if set(self._key_ops) & set([7, 8]):
raise ValueError("Signing key should not be used for key derivation.")
if self._key_ops:
if -4 in params:
# private key for signing.
if not (set(self._key_ops) & set([1, 2])):
raise ValueError("Invalid key_ops for signing key.")
if set(self._key_ops) & set([7, 8]):
raise ValueError("Signing key should not be used for key derivation.")
else:
# public key for signing.
if 2 not in self._key_ops or len(self._key_ops) != 1:
raise ValueError("Invalid key_ops for public key.")
else:
# public key for signing.
if 2 not in self._key_ops or len(self._key_ops) != 1:
raise ValueError("Invalid key_ops for public key.")
elif self._alg in COSE_ALGORITHMS_CKDM_KEY_AGREEMENT.values():
if -4 in params:
# private key for key derivation.
if not (set(self._key_ops) & set([7, 8])):
raise ValueError("Invalid key_ops for key derivation.")
if set(self._key_ops) & set([1, 2]):
raise ValueError("Private key for ECDHE should not be used for signing.")
else:
# public key for key derivation.
if self._key_ops:
raise ValueError("Public key for ECDHE should not have key_ops.")
self._key_ops = [1, 2] if -4 in params else [2]
elif self._alg in COSE_ALGORITHMS_HPKE.values():
if not (set(self._key_ops) & set([7, 8])):
raise ValueError("Invalid key_ops for HPKE.")
if self._key_ops:
if -4 in params:
# private key for key derivation.
if len(self._key_ops) != 1 or self._key_ops[0] != 8:
raise ValueError("Invalid key_ops for HPKE private key.")
else:
# public key for key derivation.
if len(self._key_ops) > 0:
raise ValueError("Invalid key_ops for HPKE public key.")
else:
if -4 in params and isinstance(self._key_ops, list) and len(self._key_ops) == 0:
raise ValueError("Invalid key_ops for HPKE private key.")
self._key_ops = [8] if -4 in params else []
else:
raise ValueError(f"Unsupported or unknown alg(3) for OKP: {self._alg}.")
# self._alg in COSE_ALGORITHMS_CKDM_KEY_AGREEMENT.values():
if self._key_ops:
if -4 in params:
# private key for key derivation.
if not (set(self._key_ops) & set([7, 8])):
raise ValueError("Invalid key_ops for key derivation.")
if set(self._key_ops) & set([1, 2]):
raise ValueError("Private key for ECDHE should not be used for signing.")
else:
# public key for key derivation.
if self._key_ops:
raise ValueError("Public key for ECDHE should not have key_ops.")
else:
self._key_ops = [7, 8] if -4 in params else []
else:
if -4 in params:
# private key.
if set(self._key_ops) & set([1, 2]):
# private key for signing.
if set(self._key_ops) & set([7, 8]):
raise ValueError("OKP private key should not be used for both signing and key derivation.")
if self._crv in [4, 5]: # X25519/X448
if self._key_ops:
# private key for key derivation.
if not (set(self._key_ops) & set([7, 8])):
raise ValueError("Invalid key_ops for X25519/448 private key.")
if set(self._key_ops) & set([1, 2]):
raise ValueError("Invalid key_ops for X25519/448 private key.")
else:
self._key_ops = [7, 8]
else: # Ed25519/Ed448
if self._key_ops:
if not (set(self._key_ops) & set([1, 2])):
raise ValueError("Invalid key_ops for Ed25519/448 private key.")
if set(self._key_ops) & set([7, 8]):
raise ValueError("Invalid key_ops for Ed25519/448 private key.")
else:
self._key_ops = [1, 2]
self._alg = -8 # EdDSA
else:
# public key.
if self._crv in [4, 5]: # X25519/X448
if len(self._key_ops) != 0 and not (set(self._key_ops) & set([7, 8])):
raise ValueError("Invalid key_ops for public key.")
if self._key_ops is not None and len(self._key_ops) != 0:
raise ValueError("Invalid key_ops for X25519/448 public key.")
else: # Ed25519/Ed448
if len(self._key_ops) != 1 or self._key_ops[0] != 2:
raise ValueError("Invalid key_ops for public key.")
if self._key_ops:
if len(self._key_ops) != 1 or self._key_ops[0] != 2:
raise ValueError("Invalid key_ops for Ed25519/448 public key.")
else:
self._key_ops = [2]
self._alg = -8 # EdDSA

if self._alg in COSE_ALGORITHMS_CKDM_KEY_AGREEMENT_ES.values():
Expand Down
129 changes: 112 additions & 17 deletions tests/test_algs_ec2.py
Original file line number Diff line number Diff line change
Expand Up @@ -604,7 +604,6 @@ def test_cose_key_constructor_without_cose_key(self):
},
"Invalid key_ops for public key.",
),
# ???
],
)
def test_ec2_key_constructor_with_invalid_args(self, invalid, msg):
Expand All @@ -624,7 +623,7 @@ def test_ec2_key_sign_with_es256_public_key(self):
)
with pytest.raises(ValueError) as err:
public_key.sign(b"Hello world!")
pytest.fail("sign should not fail.")
pytest.fail("sign should fail.")
assert "Public key cannot be used for signing." in str(err.value)

def test_ec2_key_verify_with_another_es256_public_key(self):
Expand All @@ -649,7 +648,7 @@ def test_ec2_key_verify_with_another_es256_public_key(self):
sig = private_key.sign(b"Hello world!")
with pytest.raises(VerifyError) as err:
public_key2.verify(b"Hello world!", sig)
pytest.fail("verify should not fail.")
pytest.fail("verify should fail.")
assert "Failed to verify." in str(err.value)

def test_ec2_key_verify_with_invalid_signature(self):
Expand Down Expand Up @@ -816,47 +815,143 @@ def test_ec2_key_to_cose_key_with_invalid_key(self):
assert "Unsupported or unknown key for EC2." in str(err.value)

@pytest.mark.parametrize(
"key_ops",
"alg, key_ops",
[
["sign"],
["verify"],
["sign", "verify"],
(
"HPKE-Base-P256-SHA256-AES128GCM",
["deriveBits"],
),
(
"HPKE-Base-P256-SHA256-ChaCha20Poly1305",
["deriveBits"],
),
],
)
def test_ec2_key_private_with_alg_hpke(self, alg, key_ops):
try:
_ = COSEKey.from_jwk(
{
"kty": "EC",
"kid": "01",
"crv": "P-256",
"alg": alg,
"x": "usWxHK2PmfnHKwXPS54m0kTcGJ90UiglWiGahtagnv8",
"y": "IBOL-C3BttVivg-lSreASjpkttcsz-1rb7btKLv8EX4",
"d": "V8kgd2ZBRuh2dgyVINBUqpPDr7BOMGcF22CQMIUHtNM",
"key_ops": key_ops,
}
)
except Exception:
pytest.fail("from_jwk should not fail.")

@pytest.mark.parametrize(
"alg, key_ops",
[
(
"HPKE-Base-P256-SHA256-AES128GCM",
[],
),
(
"HPKE-Base-P256-SHA256-ChaCha20Poly1305",
[],
),
],
)
def test_ec2_key_public_with_alg_hpke(self, alg, key_ops):
try:
_ = COSEKey.from_jwk(
{
"kty": "EC",
"kid": "01",
"crv": "P-256",
"alg": alg,
"x": "usWxHK2PmfnHKwXPS54m0kTcGJ90UiglWiGahtagnv8",
"y": "IBOL-C3BttVivg-lSreASjpkttcsz-1rb7btKLv8EX4",
# "d": "V8kgd2ZBRuh2dgyVINBUqpPDr7BOMGcF22CQMIUHtNM",
"key_ops": key_ops,
}
)
except Exception:
pytest.fail("from_jwk should not fail.")

@pytest.mark.parametrize(
"invalid, msg",
[
(
0,
"key_ops should be list.",
),
(
[],
"Invalid key_ops for HPKE private key.",
),
(
["sign"],
"Invalid key_ops for HPKE private key.",
),
(
["deriveKey"],
"Invalid key_ops for HPKE private key.",
),
(
["deriveKey", "deriveBits"],
"Invalid key_ops for HPKE private key.",
),
],
)
def test_ec2_key_hpke_with_alg_hpke_and_invalid_key_ops(self, key_ops):
def test_ec2_key_private_with_alg_hpke_and_invalid_key_ops(self, invalid, msg):
with pytest.raises(ValueError) as err:
COSEKey.from_jwk(
{
"kty": "EC",
"kid": "01",
"crv": "P-256",
"alg": "HPKE-Base-P256-SHA256-AES128GCM",
"key_ops": key_ops,
"x": "usWxHK2PmfnHKwXPS54m0kTcGJ90UiglWiGahtagnv8",
"y": "IBOL-C3BttVivg-lSreASjpkttcsz-1rb7btKLv8EX4",
"d": "V8kgd2ZBRuh2dgyVINBUqpPDr7BOMGcF22CQMIUHtNM",
"key_ops": invalid,
}
)
assert "Invalid key_ops for key derivation." in str(err.value)
assert msg in str(err.value)

@pytest.mark.parametrize(
"key_ops",
"invalid, msg",
[
["sign"],
(
0,
"key_ops should be list.",
),
(
["sign"],
"Invalid key_ops for HPKE public key.",
),
(
["deriveKey"],
"Invalid key_ops for HPKE public key.",
),
(
["deriveBits"],
"Invalid key_ops for HPKE public key.",
),
(
["deriveKey", "deriveBits"],
"Invalid key_ops for HPKE public key.",
),
],
)
def test_ec2_key_hpke_with_invalid_key_ops(self, key_ops):
def test_ec2_key_public_with_alg_hpke_and_invalid_key_ops(self, invalid, msg):
with pytest.raises(ValueError) as err:
COSEKey.from_jwk(
{
"kty": "EC",
"kid": "01",
"crv": "P-256",
# "alg": "HPKE-Base-P256-SHA256-AES128GCM",
"key_ops": key_ops,
"alg": "HPKE-Base-P256-SHA256-AES128GCM",
"x": "usWxHK2PmfnHKwXPS54m0kTcGJ90UiglWiGahtagnv8",
"y": "IBOL-C3BttVivg-lSreASjpkttcsz-1rb7btKLv8EX4",
# "d": "V8kgd2ZBRuh2dgyVINBUqpPDr7BOMGcF22CQMIUHtNM",
"key_ops": invalid,
}
)
assert "Invalid key_ops for public key." in str(err.value)
pytest.fail("COSEKey.from_jwk should fail.")
assert msg in str(err.value)
Loading