tool/cms_bench.dart times, on the VM and compiled to JavaScript, one verification of ECDSA on each curve and of RSA of each size and scheme, the reading of a certificate, and a signature and a token read and checked, on the cases of cms_vectors.g.dart. Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>v0.11
parent
dfc290857a
commit
36c7b6c5fc
@ -0,0 +1,120 @@
|
|||||||
|
// Times the CMS reader of datekeys-dart, stage 5a, on the VM and compiled
|
||||||
|
// to JavaScript: the verification of one ECDSA signature on each curve and
|
||||||
|
// of one RSA signature of each size and scheme, the reading of a
|
||||||
|
// certificate, and a whole signature and a whole token read and checked. It
|
||||||
|
// reads no file: the cases are those of test/vectors/cms_vectors.g.dart.
|
||||||
|
//
|
||||||
|
// dart run tool/cms_bench.dart
|
||||||
|
//
|
||||||
|
// dart compile js -O2 -o cms_bench.js tool/cms_bench.dart
|
||||||
|
// node cms_bench.js
|
||||||
|
//
|
||||||
|
// ignore_for_file: avoid_print
|
||||||
|
import 'dart:convert';
|
||||||
|
import 'dart:typed_data';
|
||||||
|
|
||||||
|
import 'package:datekeys/src/bytes.dart';
|
||||||
|
import 'package:datekeys/src/cms.dart';
|
||||||
|
import 'package:datekeys/src/ecdsa.dart';
|
||||||
|
import 'package:datekeys/src/nist_curves.dart';
|
||||||
|
import 'package:datekeys/src/rsa.dart';
|
||||||
|
|
||||||
|
import '../test/vectors/cms_vectors.g.dart';
|
||||||
|
|
||||||
|
typedef Json = Map<String, Object?>;
|
||||||
|
|
||||||
|
List<Json> _cases(String json, String section) =>
|
||||||
|
((jsonDecode(json) as Json)[section]! as List).cast<Json>();
|
||||||
|
|
||||||
|
Json _named(String json, String section, String name) =>
|
||||||
|
_cases(json, section).firstWhere((c) => c['name'] == name);
|
||||||
|
|
||||||
|
Uint8List _hex(Object? v) => fromHex(v! as String);
|
||||||
|
|
||||||
|
/// The mean time of [f] in milliseconds, over at least [minRuns] runs and
|
||||||
|
/// about [budgetMs] milliseconds, after one run to warm up.
|
||||||
|
double time(void Function() f, {int minRuns = 3, int budgetMs = 2000}) {
|
||||||
|
f();
|
||||||
|
final sw = Stopwatch()..start();
|
||||||
|
var runs = 0;
|
||||||
|
while (runs < minRuns || sw.elapsedMilliseconds < budgetMs) {
|
||||||
|
f();
|
||||||
|
runs++;
|
||||||
|
}
|
||||||
|
return sw.elapsedMicroseconds / 1000 / runs;
|
||||||
|
}
|
||||||
|
|
||||||
|
String ms(double v) => '${v.toStringAsFixed(v < 10 ? 2 : 0)} ms';
|
||||||
|
|
||||||
|
void main() {
|
||||||
|
final curves = {'P-256': p256, 'P-384': p384, 'P-521': p521};
|
||||||
|
final keys = _cases(cmsEcdsaJson, 'keys');
|
||||||
|
for (final (curve, hash) in const [
|
||||||
|
('P-256', 'SHA-256'),
|
||||||
|
('P-384', 'SHA-384'),
|
||||||
|
('P-521', 'SHA-512'),
|
||||||
|
]) {
|
||||||
|
final c = _cases(cmsEcdsaJson, 'verify').firstWhere(
|
||||||
|
(c) => (c['name']! as String).startsWith('$curve, valid over $hash, '),
|
||||||
|
);
|
||||||
|
final k = keys[c['key']! as int];
|
||||||
|
final point = decodeUncompressed(curves[curve]!, _hex(k['point']))!;
|
||||||
|
final h = _hex(c['hash']);
|
||||||
|
final sig = _hex(c['sig']);
|
||||||
|
if (!verifyEcdsaAsn1(point, h, sig)) throw StateError('not valid');
|
||||||
|
print(
|
||||||
|
'ECDSA $curve, $hash, one verification: '
|
||||||
|
'${ms(time(() => verifyEcdsaAsn1(point, h, sig)))}',
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
|
final rsaKeys = _cases(cmsRsaJson, 'keys');
|
||||||
|
for (final c in _cases(cmsRsaJson, 'valid')) {
|
||||||
|
final k = rsaKeys[c['key']! as int];
|
||||||
|
if (k['e'] != 65537) continue;
|
||||||
|
final key = RsaPublicKey(
|
||||||
|
BigInt.parse(k['n']! as String, radix: 16),
|
||||||
|
k['e']! as int,
|
||||||
|
);
|
||||||
|
final digest = _hex(c['digest']);
|
||||||
|
final sig = _hex(c['sig']);
|
||||||
|
bool verify() => c['scheme'] == 'pss'
|
||||||
|
? verifyPss(key, Sha2.sha256, digest, sig)
|
||||||
|
: verifyPkcs1v15(key, Sha2.sha256, digest, sig);
|
||||||
|
if (!verify()) throw StateError('not valid');
|
||||||
|
print('RSA ${c['name']}, one verification: ${ms(time(verify))}');
|
||||||
|
}
|
||||||
|
|
||||||
|
for (final name in const ['field by field', 'rsa']) {
|
||||||
|
final der = _hex(_named(cmsCertsJson, 'bases', name)['der']);
|
||||||
|
print(
|
||||||
|
'parseCert, the certificate "$name" (${der.length} bytes): '
|
||||||
|
'${ms(time(() => parseCert(der).holder))}',
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
|
final message = _hex((jsonDecode(cmsMutationsJson) as Json)['message']);
|
||||||
|
for (final name in const ['sealed', 'co-signature']) {
|
||||||
|
final der = _hex(_named(cmsMutationsJson, 'signature_bases', name)['der']);
|
||||||
|
void check() {
|
||||||
|
for (final s in parseSignature(der).signers) {
|
||||||
|
if (s.check(message) != CmsResult.valid) throw StateError('invalid');
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
print(
|
||||||
|
'parseSignature and check, "$name" (${der.length} bytes): '
|
||||||
|
'${ms(time(check))}',
|
||||||
|
);
|
||||||
|
}
|
||||||
|
final subject = _hex((jsonDecode(cmsMutationsJson) as Json)['subject']);
|
||||||
|
final token = _hex(_named(cmsMutationsJson, 'token_bases', 'ecdsa')['der']);
|
||||||
|
void checkToken() {
|
||||||
|
if (!parseToken(token).check(subject)) throw StateError('invalid');
|
||||||
|
}
|
||||||
|
|
||||||
|
print(
|
||||||
|
'parseToken and check, ECDSA P-256 (${token.length} bytes): '
|
||||||
|
'${ms(time(checkToken))}',
|
||||||
|
);
|
||||||
|
}
|
||||||
Loading…
Reference in new issue