dilithium, hybrid, ctap implementation, experiments
This commit is contained in:
167
benchmarks.py
Normal file
167
benchmarks.py
Normal file
@@ -0,0 +1,167 @@
|
||||
#!/usr/bin/env python3
|
||||
# Copyright 2022 Google LLC
|
||||
#
|
||||
# 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.
|
||||
# Lint as: python3
|
||||
"""Script to benchmark CTAP commands using Dilithium Hybrid signatures."""
|
||||
|
||||
from __future__ import absolute_import
|
||||
from __future__ import division
|
||||
from __future__ import print_function
|
||||
|
||||
import argparse
|
||||
import datetime
|
||||
from subprocess import DEVNULL, STDOUT, check_call
|
||||
import sys
|
||||
from time import sleep
|
||||
from typing import Any
|
||||
import uuid
|
||||
|
||||
import colorama
|
||||
from tqdm.auto import tqdm
|
||||
|
||||
from fido2 import ctap
|
||||
from fido2.webauthn import PublicKeyCredentialRpEntity, PublicKeyCredentialUserEntity, PublicKeyCredentialParameters
|
||||
from fido2 import hid
|
||||
from tools.configure import fatal, info, get_opensk_devices
|
||||
|
||||
ES256_ALGORITHM = PublicKeyCredentialParameters("public-key", -7)
|
||||
HYBRID_ALGORITHM = PublicKeyCredentialParameters("public-key", -65537)
|
||||
|
||||
|
||||
def error(message: str):
|
||||
tqdm.write(message)
|
||||
|
||||
|
||||
def check_info(authenticator: Any):
|
||||
"""Checks if the assumed upgrade info matches the authenticator's."""
|
||||
try:
|
||||
info("Reading info...")
|
||||
if HYBRID_ALGORITHM not in authenticator.info.algorithms:
|
||||
fatal("The device does not support hybrid signatures.")
|
||||
except ctap.CtapError as ex:
|
||||
error(f"Failed to read OpenSK info (error: {ex}")
|
||||
|
||||
|
||||
def f_args(*params):
|
||||
"""Constructs a dict from a list of arguments for sending a CBOR command.
|
||||
None elements will be omitted.
|
||||
:param params: Arguments, in order, to add to the command.
|
||||
:return: The input parameters as a dict.
|
||||
"""
|
||||
return dict((i, v) for i, v in enumerate(params, 1) if v is not None)
|
||||
|
||||
|
||||
def compute_stats(elapsed):
|
||||
n = len(elapsed)
|
||||
mean = sum(elapsed) / n
|
||||
variance = sum((x - mean)**2 for x in elapsed) / n
|
||||
std_dev = variance**0.5
|
||||
return (mean, std_dev)
|
||||
|
||||
|
||||
def get_authenticator():
|
||||
devices = None
|
||||
while not devices:
|
||||
try:
|
||||
devices = get_opensk_devices(False)
|
||||
except Exception as e: # pylint: disable=broad-except
|
||||
error(str(e))
|
||||
check_call(["nrfjprog", "--reset", "--family", "NRF52"],
|
||||
stdout=DEVNULL,
|
||||
stderr=STDOUT)
|
||||
sleep(0.1)
|
||||
return devices[0]
|
||||
|
||||
|
||||
def main(args):
|
||||
colorama.init()
|
||||
|
||||
authenticator = get_authenticator()
|
||||
# If the device supports it, wink to show which device we use.
|
||||
if authenticator.device.capabilities & hid.CAPABILITY.WINK:
|
||||
authenticator.device.wink()
|
||||
aaguid = uuid.UUID(bytes=authenticator.get_info().aaguid)
|
||||
check_info(authenticator)
|
||||
info(f"Testing OpenSK device AAGUID {aaguid} ({authenticator.device}).")
|
||||
|
||||
make_durations = []
|
||||
get_durations = []
|
||||
|
||||
for _ in tqdm(range(args.runs), file=sys.stdout):
|
||||
authenticator = get_authenticator()
|
||||
try:
|
||||
start = datetime.datetime.now()
|
||||
result = authenticator.make_credential(
|
||||
client_data_hash=bytes(32),
|
||||
rp=PublicKeyCredentialRpEntity(id="example.com", name="Example"),
|
||||
user=PublicKeyCredentialUserEntity(id=b"diana", name="Diana"),
|
||||
key_params=[HYBRID_ALGORITHM],
|
||||
)
|
||||
end = datetime.datetime.now()
|
||||
make_delta = (end - start).total_seconds() * 1000.0
|
||||
make_durations.append(make_delta)
|
||||
|
||||
credential_data = result.auth_data.credential_data
|
||||
credential_id_length = 256 * credential_data[16] + credential_data[17]
|
||||
credential_id = credential_data[18:18 + credential_id_length]
|
||||
allow_list = [{"type": "public-key", "id": credential_id}]
|
||||
|
||||
start = datetime.datetime.now()
|
||||
_ = authenticator.get_assertion(
|
||||
rp_id="example.com",
|
||||
client_data_hash=bytes(32),
|
||||
allow_list=allow_list,
|
||||
)
|
||||
end = datetime.datetime.now()
|
||||
get_delta = (end - start).total_seconds() * 1000.0
|
||||
get_durations.append(get_delta)
|
||||
|
||||
with open("make_durations.txt", "a", encoding="utf-8") as file_make:
|
||||
file_make.write(str(make_delta) + ",\n")
|
||||
with open("get_durations.txt", "a", encoding="utf-8") as file_get:
|
||||
file_get.write(str(get_delta) + ",\n")
|
||||
|
||||
except ctap.CtapError as ex:
|
||||
message = "Failed to make a hybrid signature with OpenSK"
|
||||
if ex.code.value == ctap.CtapError.ERR.INVALID_COMMAND:
|
||||
error(f"{message} (unsupported command).")
|
||||
elif ex.code.value == ctap.CtapError.ERR.INVALID_PARAMETER:
|
||||
error(f"{message} (invalid parameter, maybe a wrong byte array size?).")
|
||||
elif ex.code.value == 0xF2: # VENDOR_INTERNAL_ERROR
|
||||
error(f"{message} (internal conditions not met).")
|
||||
elif ex.code.value == 0xF3: # VENDOR_HARDWARE_FAILURE
|
||||
error(f"{message} (internal hardware error).")
|
||||
else:
|
||||
error(f"{message} (unexpected error: {ex})")
|
||||
except Exception as e: # pylint: disable=broad-except
|
||||
error(str(e))
|
||||
|
||||
info(f"Successful operations: {len(make_durations)} and {len(get_durations)}")
|
||||
info("\nMake Credential benchmark:")
|
||||
(mean, std_dev) = compute_stats(make_durations)
|
||||
info(f"Average: {mean} ms/iter (standard deviation: {std_dev} ms/iter)")
|
||||
info("\nGet Assertion benchmark:")
|
||||
(mean, std_dev) = compute_stats(get_durations)
|
||||
info(f"Average: {mean} ms/iter (standard deviation: {std_dev} ms/iter)")
|
||||
|
||||
|
||||
if __name__ == "__main__":
|
||||
parser = argparse.ArgumentParser()
|
||||
parser.add_argument(
|
||||
"--runs",
|
||||
type=int,
|
||||
default=1000,
|
||||
help=("How many iterations to use."),
|
||||
)
|
||||
main(parser.parse_args())
|
||||
Reference in New Issue
Block a user