Skip to content

Commit

Permalink
Update
Browse files Browse the repository at this point in the history
  • Loading branch information
erjiaqing committed Feb 23, 2023
1 parent f1964ff commit 6f2ff6a
Show file tree
Hide file tree
Showing 7 changed files with 209 additions and 12 deletions.
4 changes: 4 additions & 0 deletions src/controller/python/BUILD.gn
Original file line number Diff line number Diff line change
Expand Up @@ -68,6 +68,7 @@ shared_library("ChipDeviceCtrl") {
"OpCredsBinding.cpp",
"chip/clusters/attribute.cpp",
"chip/clusters/command.cpp",
"chip/credentials/cert.cpp",
"chip/crypto/p256keypair.cpp",
"chip/discovery/NodeResolution.cpp",
"chip/interaction_model/Delegate.cpp",
Expand Down Expand Up @@ -218,6 +219,8 @@ chip_python_wheel_action("chip-core") {
"chip/commissioning/commissioning_flow_blocks.py",
"chip/commissioning/pase.py",
"chip/configuration/__init__.py",
"chip/credentials/cert.py",
"chip/crypto/fabric.py",
"chip/crypto/p256keypair.py",
"chip/discovery/__init__.py",
"chip/discovery/library_handle.py",
Expand Down Expand Up @@ -277,6 +280,7 @@ chip_python_wheel_action("chip-core") {
"chip.configuration",
"chip.commissioning",
"chip.clusters",
"chip.credentials",
"chip.crypto",
"chip.utils",
"chip.discovery",
Expand Down
6 changes: 3 additions & 3 deletions src/controller/python/OpCredsBinding.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -591,11 +591,11 @@ PyChipError pychip_DeviceController_SetIpk(chip::Controller::DeviceCommissioner
uint8_t compressedFabricId[sizeof(uint64_t)] = { 0 };
chip::MutableByteSpan compressedFabricIdSpan(compressedFabricId);

err = devCtrl->GetCompressedFabricIdBytes(compressedFabricIdSpan);
CHIP_ERROR err = devCtrl->GetCompressedFabricIdBytes(compressedFabricIdSpan);
VerifyOrReturnError(err == CHIP_NO_ERROR, ToPyChipError(err));

CHIP_ERROR err = chip::Credentials::SetSingleIpkEpochKey(&sGroupDataProvider, devCtrl->GetFabricIndex(), ByteSpan(ipk, ipkLen),
compressedFabricIdSpan);
err = chip::Credentials::SetSingleIpkEpochKey(&sGroupDataProvider, devCtrl->GetFabricIndex(), ByteSpan(ipk, ipkLen),
compressedFabricIdSpan);

return ToPyChipError(err);
}
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -21,9 +21,15 @@
from chip import ChipDeviceCtrl
from chip import clusters as Clusters
from chip import commissioning, tlv
import chip.credentials.cert
import chip.crypto.fabric

from . import pase

from cryptography import x509
from cryptography.hazmat.primitives import serialization
import base64


class CommissioningFlowBlocks:
def __init__(self, devCtrl: ChipDeviceCtrl.ChipDeviceControllerBase, credential_provider: commissioning.CredentialProvider, logger: logging.Logger):
Expand Down Expand Up @@ -105,20 +111,39 @@ async def operational_credentials_commissioning(self, parameter: commissioning.P
raise commissioning.CommissionFailure(f"Failed to add Root Certificate: {ex}")

try:
certificate_data = tlv.TLVReader(commissionee_credentials.noc).get()['Any']

self._logger.info(f"Parsed NOC TLV: {certificate_data}")
x509_rcac = x509.load_pem_x509_certificate(
b'''-----BEGIN CERTIFICATE-----\n''' +
base64.b64encode(chip.credentials.cert.convert_chip_cert_to_x509_cert(commissionee_credentials.rcac)) +
b'''\n-----END CERTIFICATE-----''')
root_public_key = x509_rcac.public_key().public_bytes(serialization.Encoding.X962,
serialization.PublicFormat.UncompressedPoint)

x509_noc = x509.load_pem_x509_certificate(
b'''-----BEGIN CERTIFICATE-----\n''' +
base64.b64encode(chip.credentials.cert.convert_chip_cert_to_x509_cert(commissionee_credentials.noc)) +
b'''\n-----END CERTIFICATE-----''')

for subject in x509_noc.subject:
if subject.oid.dotted_string == '1.3.6.1.4.1.37244.1.1':
cert_fabric_id = int(subject.value, 16)
elif subject.oid.dotted_string == '1.3.6.1.4.1.37244.1.5':
cert_node_id = int(subject.value, 16)

if cert_fabric_id != commissionee_credentials.fabric_id:
self._logger.warning("Fabric ID in certificate does not match the fabric id in commissionee credentials struct.")
if cert_node_id != commissionee_credentials.node_id:
self._logger.warning("Node ID in certificate does not match the node id in commissionee credentials struct.")

compressed_fabric_id = chip.crypto.fabric.generate_compressed_fabric_id(root_public_key, cert_fabric_id)

# TODO: chip.tlv.Reader will remove tag information in List containers. Which is used in NOC TLV
# Should extract matter-node-id and matter-fabric-id after List TLV is well handled.
except:
self._logger.exception("The certificate should be a valid CHIP Certificate, but failed to parse it")
raise

# TODO: Calculate compressed fabric id based on the NOC infomations.
self._logger.info(
f"Commissioning FabricID: {commissionee_credentials.fabric_id:016X} "
f"Compressed FabricID: {self._devCtrl.GetCompressedFabricId():016X} "
f"Node ID: {commissionee_credentials.node_id:016X}")
f"Commissioning FabricID: {cert_fabric_id:016X} "
f"Compressed FabricID: {compressed_fabric_id:016X} "
f"Node ID: {cert_node_id:016X}")

self._logger.info("Adding Operational Certificate")
response = await self._devCtrl.SendCommand(node_id, commissioning.ROOT_ENDPOINT_ID, Clusters.OperationalCredentials.Commands.AddNOC(
Expand Down
49 changes: 49 additions & 0 deletions src/controller/python/chip/credentials/cert.cpp
Original file line number Diff line number Diff line change
@@ -0,0 +1,49 @@
/*
*
* Copyright (c) 2023 Project CHIP Authors
* All rights reserved.
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/

#include "cert.h"

#include <credentials/CHIPCert.h>
#include <lib/support/CodeUtils.h>

using namespace chip;
using namespace chip::Credentials;

PyChipError pychip_ConvertX509CertToChipCert(const uint8_t * x509Cert, size_t x509CertLen, uint8_t * chipCert, size_t * chipCertLen)
{
MutableByteSpan output(chipCert, *chipCertLen);
CHIP_ERROR err = CHIP_NO_ERROR;

VerifyOrReturnError((err = ConvertX509CertToChipCert(ByteSpan(x509Cert, x509CertLen), output)) == CHIP_NO_ERROR,
ToPyChipError(err));
*chipCertLen = output.size();

return ToPyChipError(err);
}

PyChipError pychip_ConvertChipCertToX509Cert(const uint8_t * chipCert, size_t chipCertLen, uint8_t * x509Cert, size_t * x509CertLen)
{
MutableByteSpan output(x509Cert, *x509CertLen);
CHIP_ERROR err = CHIP_NO_ERROR;

VerifyOrReturnError((err = ConvertChipCertToX509Cert(ByteSpan(chipCert, chipCertLen), output)) == CHIP_NO_ERROR,
ToPyChipError(err));
*x509CertLen = output.size();

return ToPyChipError(err);
}
30 changes: 30 additions & 0 deletions src/controller/python/chip/credentials/cert.h
Original file line number Diff line number Diff line change
@@ -0,0 +1,30 @@
/*
*
* Copyright (c) 2023 Project CHIP Authors
* All rights reserved.
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/

#pragma once

#include <stddef.h>

#include <controller/python/chip/native/PyChipError.h>

extern "C" {
PyChipError pychip_ConvertX509CertToChipCert(const uint8_t * x509Cert, size_t x509CertLen, uint8_t * chipCert,
size_t * chipCertLen);
PyChipError pychip_ConvertChipCertToX509Cert(const uint8_t * chipCert, size_t chipCertLen, uint8_t * x509Cert,
size_t * x509CertLen);
}
51 changes: 51 additions & 0 deletions src/controller/python/chip/credentials/cert.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,51 @@
#
# Copyright (c) 2023 Project CHIP Authors
# All rights reserved.
#
# Licensed under the Apache License, Version 2.0 (the "License");
# you may not use this file except in compliance with the License.
# You may obtain a copy of the License at
#
# http://www.apache.org/licenses/LICENSE-2.0
#
# Unless required by applicable law or agreed to in writing, software
# distributed under the License is distributed on an "AS IS" BASIS,
# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
# See the License for the specific language governing permissions and
# limitations under the License.
#

import chip.native
import builtins
import ctypes


def _handle():
handle = chip.native.GetLibraryHandle()
if handle.pychip_ConvertX509CertToChipCert.argtypes is None:
setter = chip.native.NativeLibraryHandleMethodArguments(handle)
setter.Set("pychip_ConvertX509CertToChipCert", chip.native.PyChipError, [ctypes.POINTER(
ctypes.c_uint8), ctypes.c_size_t, ctypes.POINTER(ctypes.c_uint8), ctypes.POINTER(ctypes.c_size_t)])
setter.Set("pychip_ConvertChipCertToX509Cert", chip.native.PyChipError, [ctypes.POINTER(
ctypes.c_uint8), ctypes.c_size_t, ctypes.POINTER(ctypes.c_uint8), ctypes.POINTER(ctypes.c_size_t)])
return handle


def convert_x509_cert_to_chip_cert(x509Cert: bytes) -> bytes:
"""Converts a x509 certificate to CHIP Certificate."""
output_buffer = (ctypes.c_uint8 * 1024)()
output_size = ctypes.c_size_t(1024)

_handle().pychip_ConvertX509CertToChipCert(x509Cert, len(x509Cert), output_buffer, ctypes.byref(output_size)).raise_on_error()

return bytes(output_buffer)[:output_size.value]


def convert_chip_cert_to_x509_cert(chipCert: bytes) -> bytes:
"""Converts a x509 certificate to CHIP Certificate."""
output_buffer = (ctypes.c_byte * 1024)()
output_size = ctypes.c_size_t(1024)

_handle().pychip_ConvertChipCertToX509Cert(chipCert, len(chipCert), output_buffer, ctypes.byref(output_size)).raise_on_error()

return bytes(output_buffer)[:output_size.value]
38 changes: 38 additions & 0 deletions src/controller/python/chip/crypto/fabric.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,38 @@
#
# Copyright (c) 2023 Project CHIP Authors
# All rights reserved.
#
# Licensed under the Apache License, Version 2.0 (the "License");
# you may not use this file except in compliance with the License.
# You may obtain a copy of the License at
#
# http://www.apache.org/licenses/LICENSE-2.0
#
# Unless required by applicable law or agreed to in writing, software
# distributed under the License is distributed on an "AS IS" BASIS,
# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
# See the License for the specific language governing permissions and
# limitations under the License.
#

from cryptography.hazmat.primitives import hashes
from cryptography.hazmat.primitives.kdf.hkdf import HKDF

from . import p256keypair


def generate_compressed_fabric_id(root_public_key: bytes, fabric_id: int) -> int:
"""Generates compressed fabric id from Root CA's public key and fabric id.
Returns:
Compressed fabric id as a int
"""
if len(root_public_key) != p256keypair.P256_PUBLIC_KEY_LENGTH and root_public_key[0] != b'\x04':
raise ValueError("Root public key must be an uncompressed P256 point.")

return int.from_bytes(HKDF(
algorithm=hashes.SHA256(),
length=8,
salt=fabric_id.to_bytes(length=8, byteorder="big", signed=False),
info=b"CompressedFabric",
).derive(key_material=root_public_key[1:]), byteorder="big")

0 comments on commit 6f2ff6a

Please sign in to comment.