Merge branch 'development' into eddsa

Signed-off-by: Pol Henarejos <pol.henarejos@cttc.es>
This commit is contained in:
Pol Henarejos
2023-08-22 13:27:35 +02:00
5 changed files with 95 additions and 52 deletions

View File

@@ -156,51 +156,6 @@ int cbor_make_credential(const uint8_t *data, size_t len) {
uint8_t rp_id_hash[32]; uint8_t rp_id_hash[32];
mbedtls_sha256((uint8_t *) rp.id.data, rp.id.len, rp_id_hash, 0); mbedtls_sha256((uint8_t *) rp.id.data, rp.id.len, rp_id_hash, 0);
int curve = -1, alg = 0;
if (pubKeyCredParams_len == 0) {
CBOR_ERROR(CTAP2_ERR_MISSING_PARAMETER);
}
for (int i = 0; i < pubKeyCredParams_len; i++) {
if (pubKeyCredParams[i].type.present == false) {
CBOR_ERROR(CTAP2_ERR_MISSING_PARAMETER);
}
if (strcmp(pubKeyCredParams[i].type.data, "public-key") != 0) {
continue;
}
if (pubKeyCredParams[i].alg == FIDO2_ALG_ES256) {
curve = FIDO2_CURVE_P256;
}
else if (pubKeyCredParams[i].alg == FIDO2_ALG_ES384) {
curve = FIDO2_CURVE_P384;
}
else if (pubKeyCredParams[i].alg == FIDO2_ALG_ES512) {
curve = FIDO2_CURVE_P521;
}
else if (pubKeyCredParams[i].alg == FIDO2_ALG_ES256K) {
curve = FIDO2_CURVE_P256K1;
}
else if (pubKeyCredParams[i].alg == FIDO2_ALG_EDDSA) {
curve = FIDO2_CURVE_ED25519;
}
else if (pubKeyCredParams[i].alg == 0) { // no present
curve = -1;
}
else {
curve = 0;
}
if (curve > 0) {
alg = pubKeyCredParams[i].alg;
break;
}
}
if (curve == 0) {
CBOR_ERROR(CTAP2_ERR_UNSUPPORTED_ALGORITHM);
}
else if (curve == -1) {
CBOR_ERROR(CTAP2_ERR_MISSING_PARAMETER);
}
if (pinUvAuthParam.present == true) { if (pinUvAuthParam.present == true) {
if (pinUvAuthParam.len == 0 || pinUvAuthParam.data == NULL) { if (pinUvAuthParam.len == 0 || pinUvAuthParam.data == NULL) {
if (check_user_presence() == false) { if (check_user_presence() == false) {
@@ -222,6 +177,58 @@ int cbor_make_credential(const uint8_t *data, size_t len) {
} }
} }
} }
int curve = -1, alg = 0;
if (pubKeyCredParams_len == 0) {
CBOR_ERROR(CTAP2_ERR_MISSING_PARAMETER);
}
for (int i = 0; i < pubKeyCredParams_len; i++) {
if (pubKeyCredParams[i].type.present == false) {
CBOR_ERROR(CTAP2_ERR_INVALID_CBOR);
}
if (pubKeyCredParams[i].alg == 0) {
CBOR_ERROR(CTAP2_ERR_INVALID_CBOR);
}
if (strcmp(pubKeyCredParams[i].type.data, "public-key") != 0) {
CBOR_ERROR(CTAP2_ERR_CBOR_UNEXPECTED_TYPE);
}
if (pubKeyCredParams[i].alg == FIDO2_ALG_ES256) {
if (curve <= 0) {
curve = FIDO2_CURVE_P256;
}
}
else if (pubKeyCredParams[i].alg == FIDO2_ALG_ES384) {
if (curve <= 0) {
curve = FIDO2_CURVE_P384;
}
}
else if (pubKeyCredParams[i].alg == FIDO2_ALG_ES512) {
if (curve <= 0) {
curve = FIDO2_CURVE_P521;
}
}
else if (pubKeyCredParams[i].alg == FIDO2_ALG_ES256K) {
if (curve <= 0) {
curve = FIDO2_CURVE_P256K1;
}
}
else if (pubKeyCredParams[i].alg == FIDO2_ALG_EDDSA) {
if (curve <= 0) {
curve = FIDO2_CURVE_ED25519;
}
}
else {
CBOR_ERROR(CTAP2_ERR_CBOR_UNEXPECTED_TYPE);
}
if (curve > 0 && alg == 0) {
alg = pubKeyCredParams[i].alg;
}
}
if (curve <= 0) {
CBOR_ERROR(CTAP2_ERR_UNSUPPORTED_ALGORITHM);
}
if (options.present) { if (options.present) {
if (options.uv == ptrue) { //5.3 if (options.uv == ptrue) { //5.3
CBOR_ERROR(CTAP2_ERR_INVALID_OPTION); CBOR_ERROR(CTAP2_ERR_INVALID_OPTION);

View File

@@ -25,7 +25,7 @@ from fido2.attestation import FidoU2FAttestation
from fido2.ctap2.pin import ClientPin from fido2.ctap2.pin import ClientPin
from fido2.server import Fido2Server from fido2.server import Fido2Server
from fido2.ctap import CtapError from fido2.ctap import CtapError
from fido2.webauthn import CollectedClientData, AttestedCredentialData from fido2.webauthn import CollectedClientData, PublicKeyCredentialParameters, PublicKeyCredentialType
from utils import * from utils import *
from fido2.cose import ES256 from fido2.cose import ES256
import sys import sys
@@ -116,6 +116,10 @@ class Device():
self.__rp = rp self.__rp = rp
self.__attestation = attestation self.__attestation = attestation
self.__server = Fido2Server(self.__rp, attestation=self.__attestation) self.__server = Fido2Server(self.__rp, attestation=self.__attestation)
self.__server.allowed_algorithms = [
PublicKeyCredentialParameters(PublicKeyCredentialType.PUBLIC_KEY, p['alg'])
for p in self.__client._backend.info.algorithms
]
def client(self): def client(self):
return self.__client return self.__client

View File

@@ -20,6 +20,7 @@
from fido2.client import CtapError from fido2.client import CtapError
from fido2.cose import ES256, ES384, ES512, EdDSA from fido2.cose import ES256, ES384, ES512, EdDSA
from utils import ES256K
import pytest import pytest
@@ -121,7 +122,7 @@ def test_bad_type_pubKeyCredParams(device):
device.doMC(key_params=["wrong"]) device.doMC(key_params=["wrong"])
@pytest.mark.parametrize( @pytest.mark.parametrize(
"alg", [ES256.ALGORITHM, ES384.ALGORITHM, ES512.ALGORITHM, EdDSA.ALGORITHM] "alg", [ES256.ALGORITHM, ES384.ALGORITHM, ES512.ALGORITHM, ES256K.ALGORITHM, EdDSA.ALGORITHM]
) )
def test_algorithms(device, info, alg): def test_algorithms(device, info, alg):
if ({'alg': alg, 'type': 'public-key'} in info.algorithms): if ({'alg': alg, 'type': 'public-key'} in info.algorithms):
@@ -131,14 +132,14 @@ def test_missing_pubKeyCredParams_type(device):
with pytest.raises(CtapError) as e: with pytest.raises(CtapError) as e:
device.doMC(key_params=[{"alg": ES256.ALGORITHM}]) device.doMC(key_params=[{"alg": ES256.ALGORITHM}])
assert e.value.code == CtapError.ERR.MISSING_PARAMETER assert e.value.code == CtapError.ERR.INVALID_CBOR
def test_missing_pubKeyCredParams_alg(device): def test_missing_pubKeyCredParams_alg(device):
with pytest.raises(CtapError) as e: with pytest.raises(CtapError) as e:
device.doMC(key_params=[{"type": "public-key"}]) device.doMC(key_params=[{"type": "public-key"}])
assert e.value.code in [ assert e.value.code in [
CtapError.ERR.MISSING_PARAMETER, CtapError.ERR.INVALID_CBOR,
CtapError.ERR.UNSUPPORTED_ALGORITHM, CtapError.ERR.UNSUPPORTED_ALGORITHM,
] ]
@@ -150,7 +151,7 @@ def test_unsupported_algorithm(device):
with pytest.raises(CtapError) as e: with pytest.raises(CtapError) as e:
device.doMC(key_params=[{"alg": 1337, "type": "public-key"}]) device.doMC(key_params=[{"alg": 1337, "type": "public-key"}])
assert e.value.code == CtapError.ERR.UNSUPPORTED_ALGORITHM assert e.value.code == CtapError.ERR.CBOR_UNEXPECTED_TYPE
def test_exclude_list(resetdevice): def test_exclude_list(resetdevice):
resetdevice.doMC(exclude_list=[{"id": b"1234", "type": "rot13"}]) resetdevice.doMC(exclude_list=[{"id": b"1234", "type": "rot13"}])

View File

@@ -20,7 +20,7 @@
from fido2.client import CtapError from fido2.client import CtapError
from fido2.cose import ES256, ES384, ES512, EdDSA from fido2.cose import ES256, ES384, ES512, EdDSA
from utils import verify from utils import verify, ES256K
import pytest import pytest
def test_authenticate(device): def test_authenticate(device):
@@ -49,7 +49,7 @@ def test_empty_allowList(device):
assert e.value.code == CtapError.ERR.NO_CREDENTIALS assert e.value.code == CtapError.ERR.NO_CREDENTIALS
@pytest.mark.parametrize( @pytest.mark.parametrize(
"alg", [ES256.ALGORITHM, ES384.ALGORITHM, ES512.ALGORITHM, EdDSA.ALGORITHM] "alg", [ES256.ALGORITHM, ES384.ALGORITHM, ES512.ALGORITHM, ES256K.ALGORITHM, EdDSA.ALGORITHM]
) )
def test_algorithms(device, info, alg): def test_algorithms(device, info, alg):
if ({'alg': alg, 'type': 'public-key'} in info.algorithms): if ({'alg': alg, 'type': 'public-key'} in info.algorithms):

View File

@@ -19,6 +19,11 @@
from fido2.webauthn import AttestedCredentialData from fido2.webauthn import AttestedCredentialData
from fido2.cose import CoseKey
from cryptography.hazmat.primitives import hashes
from cryptography.hazmat.primitives.asymmetric import ec
from fido2.utils import bytes2int, int2bytes
from cryptography.hazmat.backends import default_backend
import random import random
import string import string
import secrets import secrets
@@ -175,3 +180,29 @@ class Timeout(object):
if self.timer: if self.timer:
self.timer.cancel() self.timer.cancel()
self.timer.join() self.timer.join()
class ES256K(CoseKey):
ALGORITHM = -47
_HASH_ALG = hashes.SHA256()
def verify(self, message, signature):
if self[-1] != 8:
raise ValueError("Unsupported elliptic curve")
ec.EllipticCurvePublicNumbers(
bytes2int(self[-2]), bytes2int(self[-3]), ec.SECP256K1()
).public_key(default_backend()).verify(
signature, message, ec.ECDSA(self._HASH_ALG)
)
@classmethod
def from_cryptography_key(cls, public_key):
pn = public_key.public_numbers()
return cls(
{
1: 2,
3: cls.ALGORITHM,
-1: 8,
-2: int2bytes(pn.x, 32),
-3: int2bytes(pn.y, 32),
}
)