Skip to content

Commit 43b251c

Browse files
authored
Merge pull request #470 from dajiaji/fix-key-ops
Fix key_ops validation.
2 parents b579231 + ec57c24 commit 43b251c

6 files changed

Lines changed: 459 additions & 134 deletions

File tree

cwt/algs/ec2.py

Lines changed: 11 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -119,10 +119,18 @@ def __init__(self, params: Dict[int, Any]):
119119
self._key_ops = [7, 8]
120120
elif self._alg in COSE_ALGORITHMS_HPKE.values():
121121
if self._key_ops:
122-
if not (set(self._key_ops) & set([7, 8])):
123-
raise ValueError("Invalid key_ops for key derivation.")
122+
if -4 in params:
123+
# private key for key derivation.
124+
if len(self._key_ops) != 1 or self._key_ops[0] != 8:
125+
raise ValueError("Invalid key_ops for HPKE private key.")
126+
else:
127+
# public key for key derivation.
128+
if len(self._key_ops) > 0:
129+
raise ValueError("Invalid key_ops for HPKE public key.")
124130
else:
125-
self._key_ops = [7, 8]
131+
if -4 in params and isinstance(self._key_ops, list) and len(self._key_ops) == 0:
132+
raise ValueError("Invalid key_ops for HPKE private key.")
133+
self._key_ops = [8] if -4 in params else []
126134
else:
127135
raise ValueError(f"Unsupported or unknown alg(3) for EC2: {self._alg}.")
128136
else:

cwt/algs/okp.py

Lines changed: 68 additions & 39 deletions
Original file line numberDiff line numberDiff line change
@@ -24,7 +24,6 @@
2424
)
2525

2626
from ..const import (
27-
COSE_ALGORITHMS_CKDM_KEY_AGREEMENT,
2827
COSE_ALGORITHMS_CKDM_KEY_AGREEMENT_ES,
2928
COSE_ALGORITHMS_HPKE,
3029
COSE_ALGORITHMS_SIG_OKP,
@@ -65,8 +64,6 @@ def __init__(self, params: Dict[int, Any]):
6564
self._crv = params[-1]
6665
if not isinstance(self._crv, int):
6766
raise ValueError("crv(-1) should be int.")
68-
if self._crv not in [4, 5, 6, 7]:
69-
raise ValueError(f"Unsupported or unknown crv(-1) for OKP: {self._crv}.")
7067
if self._crv in [4, 5]:
7168
# if not self._alg:
7269
# raise ValueError("X25519/X448 needs alg explicitly.")
@@ -78,6 +75,11 @@ def __init__(self, params: Dict[int, Any]):
7875
self._hash_alg = hashes.SHA256 if self._crv == 4 else hashes.SHA512
7976
elif self._alg is not None:
8077
raise ValueError(f"Unsupported or unknown alg used with X25519/X448: {self._alg}.")
78+
elif self._crv in [6, 7]:
79+
if self._alg is not None and self._alg != -8:
80+
raise ValueError(f"Unsupported or unknown alg used with Ed25519/Ed448: {self._alg}.")
81+
else:
82+
raise ValueError(f"Unsupported or unknown crv(-1) for OKP: {self._crv}.")
8183

8284
# Check the existence of the key.
8385
if -2 not in params and -4 not in params:
@@ -87,55 +89,82 @@ def __init__(self, params: Dict[int, Any]):
8789
if self._key_ops:
8890
if set(self._key_ops) & set([3, 4, 5, 6, 9, 10]):
8991
raise ValueError("Unknown or not permissible key_ops(4) for OKP.")
90-
else:
91-
if self._crv in [4, 5]:
92-
self._key_ops = [7, 8] if -4 in params else []
93-
else: # self._crv in [6, 7]
94-
self._key_ops = [1, 2] if -4 in params else [2]
9592
if self._alg:
9693
if self._alg in COSE_ALGORITHMS_SIG_OKP.values():
97-
if -4 in params:
98-
# private key for signing.
99-
if not (set(self._key_ops) & set([1, 2])):
100-
raise ValueError("Invalid key_ops for signing key.")
101-
if set(self._key_ops) & set([7, 8]):
102-
raise ValueError("Signing key should not be used for key derivation.")
94+
if self._key_ops:
95+
if -4 in params:
96+
# private key for signing.
97+
if not (set(self._key_ops) & set([1, 2])):
98+
raise ValueError("Invalid key_ops for signing key.")
99+
if set(self._key_ops) & set([7, 8]):
100+
raise ValueError("Signing key should not be used for key derivation.")
101+
else:
102+
# public key for signing.
103+
if 2 not in self._key_ops or len(self._key_ops) != 1:
104+
raise ValueError("Invalid key_ops for public key.")
103105
else:
104-
# public key for signing.
105-
if 2 not in self._key_ops or len(self._key_ops) != 1:
106-
raise ValueError("Invalid key_ops for public key.")
107-
elif self._alg in COSE_ALGORITHMS_CKDM_KEY_AGREEMENT.values():
108-
if -4 in params:
109-
# private key for key derivation.
110-
if not (set(self._key_ops) & set([7, 8])):
111-
raise ValueError("Invalid key_ops for key derivation.")
112-
if set(self._key_ops) & set([1, 2]):
113-
raise ValueError("Private key for ECDHE should not be used for signing.")
114-
else:
115-
# public key for key derivation.
116-
if self._key_ops:
117-
raise ValueError("Public key for ECDHE should not have key_ops.")
106+
self._key_ops = [1, 2] if -4 in params else [2]
118107
elif self._alg in COSE_ALGORITHMS_HPKE.values():
119-
if not (set(self._key_ops) & set([7, 8])):
120-
raise ValueError("Invalid key_ops for HPKE.")
108+
if self._key_ops:
109+
if -4 in params:
110+
# private key for key derivation.
111+
if len(self._key_ops) != 1 or self._key_ops[0] != 8:
112+
raise ValueError("Invalid key_ops for HPKE private key.")
113+
else:
114+
# public key for key derivation.
115+
if len(self._key_ops) > 0:
116+
raise ValueError("Invalid key_ops for HPKE public key.")
117+
else:
118+
if -4 in params and isinstance(self._key_ops, list) and len(self._key_ops) == 0:
119+
raise ValueError("Invalid key_ops for HPKE private key.")
120+
self._key_ops = [8] if -4 in params else []
121121
else:
122-
raise ValueError(f"Unsupported or unknown alg(3) for OKP: {self._alg}.")
122+
# self._alg in COSE_ALGORITHMS_CKDM_KEY_AGREEMENT.values():
123+
if self._key_ops:
124+
if -4 in params:
125+
# private key for key derivation.
126+
if not (set(self._key_ops) & set([7, 8])):
127+
raise ValueError("Invalid key_ops for key derivation.")
128+
if set(self._key_ops) & set([1, 2]):
129+
raise ValueError("Private key for ECDHE should not be used for signing.")
130+
else:
131+
# public key for key derivation.
132+
if self._key_ops:
133+
raise ValueError("Public key for ECDHE should not have key_ops.")
134+
else:
135+
self._key_ops = [7, 8] if -4 in params else []
123136
else:
124137
if -4 in params:
125138
# private key.
126-
if set(self._key_ops) & set([1, 2]):
127-
# private key for signing.
128-
if set(self._key_ops) & set([7, 8]):
129-
raise ValueError("OKP private key should not be used for both signing and key derivation.")
139+
if self._crv in [4, 5]: # X25519/X448
140+
if self._key_ops:
141+
# private key for key derivation.
142+
if not (set(self._key_ops) & set([7, 8])):
143+
raise ValueError("Invalid key_ops for X25519/448 private key.")
144+
if set(self._key_ops) & set([1, 2]):
145+
raise ValueError("Invalid key_ops for X25519/448 private key.")
146+
else:
147+
self._key_ops = [7, 8]
148+
else: # Ed25519/Ed448
149+
if self._key_ops:
150+
if not (set(self._key_ops) & set([1, 2])):
151+
raise ValueError("Invalid key_ops for Ed25519/448 private key.")
152+
if set(self._key_ops) & set([7, 8]):
153+
raise ValueError("Invalid key_ops for Ed25519/448 private key.")
154+
else:
155+
self._key_ops = [1, 2]
130156
self._alg = -8 # EdDSA
131157
else:
132158
# public key.
133159
if self._crv in [4, 5]: # X25519/X448
134-
if len(self._key_ops) != 0 and not (set(self._key_ops) & set([7, 8])):
135-
raise ValueError("Invalid key_ops for public key.")
160+
if self._key_ops is not None and len(self._key_ops) != 0:
161+
raise ValueError("Invalid key_ops for X25519/448 public key.")
136162
else: # Ed25519/Ed448
137-
if len(self._key_ops) != 1 or self._key_ops[0] != 2:
138-
raise ValueError("Invalid key_ops for public key.")
163+
if self._key_ops:
164+
if len(self._key_ops) != 1 or self._key_ops[0] != 2:
165+
raise ValueError("Invalid key_ops for Ed25519/448 public key.")
166+
else:
167+
self._key_ops = [2]
139168
self._alg = -8 # EdDSA
140169

141170
if self._alg in COSE_ALGORITHMS_CKDM_KEY_AGREEMENT_ES.values():

tests/test_algs_ec2.py

Lines changed: 112 additions & 17 deletions
Original file line numberDiff line numberDiff line change
@@ -604,7 +604,6 @@ def test_cose_key_constructor_without_cose_key(self):
604604
},
605605
"Invalid key_ops for public key.",
606606
),
607-
# ???
608607
],
609608
)
610609
def test_ec2_key_constructor_with_invalid_args(self, invalid, msg):
@@ -624,7 +623,7 @@ def test_ec2_key_sign_with_es256_public_key(self):
624623
)
625624
with pytest.raises(ValueError) as err:
626625
public_key.sign(b"Hello world!")
627-
pytest.fail("sign should not fail.")
626+
pytest.fail("sign should fail.")
628627
assert "Public key cannot be used for signing." in str(err.value)
629628

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

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

818817
@pytest.mark.parametrize(
819-
"key_ops",
818+
"alg, key_ops",
820819
[
821-
["sign"],
822-
["verify"],
823-
["sign", "verify"],
820+
(
821+
"HPKE-Base-P256-SHA256-AES128GCM",
822+
["deriveBits"],
823+
),
824+
(
825+
"HPKE-Base-P256-SHA256-ChaCha20Poly1305",
826+
["deriveBits"],
827+
),
828+
],
829+
)
830+
def test_ec2_key_private_with_alg_hpke(self, alg, key_ops):
831+
try:
832+
_ = COSEKey.from_jwk(
833+
{
834+
"kty": "EC",
835+
"kid": "01",
836+
"crv": "P-256",
837+
"alg": alg,
838+
"x": "usWxHK2PmfnHKwXPS54m0kTcGJ90UiglWiGahtagnv8",
839+
"y": "IBOL-C3BttVivg-lSreASjpkttcsz-1rb7btKLv8EX4",
840+
"d": "V8kgd2ZBRuh2dgyVINBUqpPDr7BOMGcF22CQMIUHtNM",
841+
"key_ops": key_ops,
842+
}
843+
)
844+
except Exception:
845+
pytest.fail("from_jwk should not fail.")
846+
847+
@pytest.mark.parametrize(
848+
"alg, key_ops",
849+
[
850+
(
851+
"HPKE-Base-P256-SHA256-AES128GCM",
852+
[],
853+
),
854+
(
855+
"HPKE-Base-P256-SHA256-ChaCha20Poly1305",
856+
[],
857+
),
858+
],
859+
)
860+
def test_ec2_key_public_with_alg_hpke(self, alg, key_ops):
861+
try:
862+
_ = COSEKey.from_jwk(
863+
{
864+
"kty": "EC",
865+
"kid": "01",
866+
"crv": "P-256",
867+
"alg": alg,
868+
"x": "usWxHK2PmfnHKwXPS54m0kTcGJ90UiglWiGahtagnv8",
869+
"y": "IBOL-C3BttVivg-lSreASjpkttcsz-1rb7btKLv8EX4",
870+
# "d": "V8kgd2ZBRuh2dgyVINBUqpPDr7BOMGcF22CQMIUHtNM",
871+
"key_ops": key_ops,
872+
}
873+
)
874+
except Exception:
875+
pytest.fail("from_jwk should not fail.")
876+
877+
@pytest.mark.parametrize(
878+
"invalid, msg",
879+
[
880+
(
881+
0,
882+
"key_ops should be list.",
883+
),
884+
(
885+
[],
886+
"Invalid key_ops for HPKE private key.",
887+
),
888+
(
889+
["sign"],
890+
"Invalid key_ops for HPKE private key.",
891+
),
892+
(
893+
["deriveKey"],
894+
"Invalid key_ops for HPKE private key.",
895+
),
896+
(
897+
["deriveKey", "deriveBits"],
898+
"Invalid key_ops for HPKE private key.",
899+
),
824900
],
825901
)
826-
def test_ec2_key_hpke_with_alg_hpke_and_invalid_key_ops(self, key_ops):
902+
def test_ec2_key_private_with_alg_hpke_and_invalid_key_ops(self, invalid, msg):
827903
with pytest.raises(ValueError) as err:
828904
COSEKey.from_jwk(
829905
{
830906
"kty": "EC",
831907
"kid": "01",
832908
"crv": "P-256",
833909
"alg": "HPKE-Base-P256-SHA256-AES128GCM",
834-
"key_ops": key_ops,
835910
"x": "usWxHK2PmfnHKwXPS54m0kTcGJ90UiglWiGahtagnv8",
836911
"y": "IBOL-C3BttVivg-lSreASjpkttcsz-1rb7btKLv8EX4",
837912
"d": "V8kgd2ZBRuh2dgyVINBUqpPDr7BOMGcF22CQMIUHtNM",
913+
"key_ops": invalid,
838914
}
839915
)
840-
assert "Invalid key_ops for key derivation." in str(err.value)
916+
assert msg in str(err.value)
841917

842918
@pytest.mark.parametrize(
843-
"key_ops",
919+
"invalid, msg",
844920
[
845-
["sign"],
921+
(
922+
0,
923+
"key_ops should be list.",
924+
),
925+
(
926+
["sign"],
927+
"Invalid key_ops for HPKE public key.",
928+
),
929+
(
930+
["deriveKey"],
931+
"Invalid key_ops for HPKE public key.",
932+
),
933+
(
934+
["deriveBits"],
935+
"Invalid key_ops for HPKE public key.",
936+
),
937+
(
938+
["deriveKey", "deriveBits"],
939+
"Invalid key_ops for HPKE public key.",
940+
),
846941
],
847942
)
848-
def test_ec2_key_hpke_with_invalid_key_ops(self, key_ops):
943+
def test_ec2_key_public_with_alg_hpke_and_invalid_key_ops(self, invalid, msg):
849944
with pytest.raises(ValueError) as err:
850945
COSEKey.from_jwk(
851946
{
852947
"kty": "EC",
853948
"kid": "01",
854949
"crv": "P-256",
855-
# "alg": "HPKE-Base-P256-SHA256-AES128GCM",
856-
"key_ops": key_ops,
950+
"alg": "HPKE-Base-P256-SHA256-AES128GCM",
857951
"x": "usWxHK2PmfnHKwXPS54m0kTcGJ90UiglWiGahtagnv8",
858952
"y": "IBOL-C3BttVivg-lSreASjpkttcsz-1rb7btKLv8EX4",
859-
# "d": "V8kgd2ZBRuh2dgyVINBUqpPDr7BOMGcF22CQMIUHtNM",
953+
"key_ops": invalid,
860954
}
861955
)
862-
assert "Invalid key_ops for public key." in str(err.value)
956+
pytest.fail("COSEKey.from_jwk should fail.")
957+
assert msg in str(err.value)

0 commit comments

Comments
 (0)