1

I am working on a python script to verify New Zealand Vaccination passports. I struggle at the point where I have the government's certificate and and the decoded COSE message and need to try to verify its signature:

key_as_dict = {
    KpKty: cose.keys.keytype.KtyEC2,
    # OKPKpCurve: cose.keys.curves.EllipticCurve, # this one also does not work
    OKPKpCurve: cose.keys.keytype.KtyEC2,
    OKPKey: kid,
    KpKeyOps: [VerifyOp],
    OKPKpD: jwk_key['x'],
    OKPKpX:jwk_key['y'],
}
govt_key = CoseKey.from_dict(key_as_dict)
cose_decoded.key = govt_key
print("key ready")
print("header", cose_decoded.verify_signature())

Error message:

cose.exceptions.CoseInvalidKey: COSE curve cannot be None

This is the JWK key provided by the government for testing purposes:

{
  "kty": "EC",
  "crv": "P-256",
  "x": "zRR-XGsCp12Vvbgui4DD6O6cqmhfPuXMhi1OxPl8760",
  "y": "Iv5SU6FuW-TRYh5_GOrJlcV_gpF_GpFQhCOD8LSk3T0"
}

Does anyone know how to map the government cert to the key dictionary correctly?

Further information:

hey
  • 2,643
  • 7
  • 29
  • 50

1 Answers1

2

The reason why it fails is that you use the wrong key type for the given key.

For the key provided as a JWK ("kty": "EC") you need to use EC2, but in your code, you're trying to map it to OKP parameters.

Therefore you first need to change two lines in the import section of your code:

#from cose.keys.keyparam import KpKty, OKPKpCurve, OKPKpD, OKPKpX
from cose.keys.keyparam import KpKty, EC2KpCurve, EC2KpX, EC2KpY
#from cose.keys.okp import OKPKey
from cose.keys.ec2 import EC2Key

Then extract the key parameters x and y from the JWK. The parameters are stored in Base64Url encoding and need to be decoded. The Python Base64 decoder insists on padding, even though Base64Url should not need it. Hence the cryptic looking code to add padding "=" on the Base64Url encoded string before decoding:

x = jwk_key['x']
if len(x) % 4:
    x = x + "=" * (4 - len(x)%4)
ec_x = base64.urlsafe_b64decode(x)

y = jwk_key['y']
if len(y) % 4:
    y = y + "=" * (4 - len(y)%4)
ec_y = base64.urlsafe_b64decode(y)

And finally, change the mapping code as follows:

key_as_dict = {
    KpKty: cose.keys.keytype.KtyEC2,
    EC2KpCurve: P256,
    EC2Key: kid,
    KpKeyOps: [VerifyOp],
    EC2KpX: ec_x,
    EC2KpY: ec_y,
}

As you can see, I changed the parameters from OKP... to the EC2... counterparts

The output of the program (I skipped Part 1..5 as you already know the result) is:

*** Part 6: Verifying certificate
key ready
header True


Below is a minimal runnable example, that just shows the key mapping based on a hardcoded JWK key in the code:

import base64
import cose
from cose.keys.curves import P256, CoseCurve
from cose.keys.keyparam import KpKty, EC2KpCurve, EC2KpX, EC2KpY
from cose.keys.keyparam import KpKeyOps
from cose.keys.keyops import SignOp, VerifyOp
from cose.keys import CoseKey
from cose.keys.ec2 import EC2Key
import json

# this is in reality loaded from the web
jwk_string = '''{"kty": "EC", "crv": "P-256",
               "x": "zRR-XGsCp12Vvbgui4DD6O6cqmhfPuXMhi1OxPl8760",
               "y": "Iv5SU6FuW-TRYh5_GOrJlcV_gpF_GpFQhCOD8LSk3T0"}'''
jwk_key = json.loads(jwk_string)
kid = "123"  # just an example

# mapping the incoming JWK to a COSE key
x = jwk_key['x']
if len(x) % 4:
    x = x + "=" * (4 - len(x)%4)
ec_x = base64.urlsafe_b64decode(x)

y = jwk_key['y']
if len(y) % 4:
    y = y + "=" * (4 - len(y)%4)
ec_y = base64.urlsafe_b64decode(y)

key_as_dict = {
    KpKty: cose.keys.keytype.KtyEC2,
    EC2KpCurve: P256,
    EC2Key: kid,
    KpKeyOps: [VerifyOp],
    EC2KpX: ec_x,
    EC2KpY: ec_y,
}

govt_key = CoseKey.from_dict(key_as_dict)
print("key ready")

As a side note: make sure the ECDSA package is up to date. I had an older version already on my PC and got a strange Error during verification:

TypeError: from_public_point() got an unexpected keyword argument 'validate_point'

which disappeared after upgrading:

pip install ecdsa --upgrade
marc_s
  • 732,580
  • 175
  • 1,330
  • 1,459
jps
  • 20,041
  • 15
  • 75
  • 79