ic-ec 0.2.13

X25519, Ed25519, and elliptic-curve arithmetic for IronCrypto
Documentation
//! `verify_prehash` against signatures made with every SHA-2 hash on every
//! curve: the pairings X.509 and other protocols use besides each curve's own.
//!
//! GENERATED by `scripts/gen_ecdsa_prehash.py` with pyca/cryptography
//! 50.0.0 (OpenSSL 3.5.7), whose deterministic ECDSA implements RFC 6979.
//! The P-256 and P-384 keys are RFC 6979's (appendices A.2.5 and A.2.6), and
//! the generator refuses to run unless it reproduces the RFC values for those
//! curves' native hashes that `p256.rs` and `p384.rs` already check, so these
//! are the RFC's signatures for each hash. The P-521 key is the generator's
//! own. Regenerate rather than edit.
//!
//! The digests cover both of FIPS 186-5's cases: shorter than the group order,
//! used whole (SHA-224 on every curve, SHA-512 on P-521), and longer, truncated
//! to the order's width (SHA-384 and SHA-512 on P-256, SHA-512 on P-384).

use ic_core::codec::hex_decode;
use ic_ec::p256::EcdsaP256Sha256;
use ic_ec::p384::EcdsaP384Sha384;
use ic_ec::p521::EcdsaP521Sha512;

struct Case {
    curve: &'static str,
    hash: &'static str,
    message: &'static str,
    public_key: &'static str,
    digest: &'static str,
    signature: &'static str,
}

const CASES: &[Case] = &[
    Case {
        curve: "P-256",
        hash: "SHA-224",
        message: "sample",
        public_key: "0460fed4ba255a9d31c961eb74c6356d68c049b8923b61fa6ce669622e60f29fb67903fe1008b8bc99a41ae9e95628bc64f2f1b20c2d7e9f5177a3c294d4462299",
        digest: "9003e374bc726550c2c289447fd0533160f875709386dfa377bfd41c",
        signature: "53b2fff5d1752b2c689df257c04c40a587fababb3f6fc2702f1343af7ca9aa3fb9afb64fdc03dc1a131c7d2386d11e349f070aa432a4acc918bea988bf75c74c",
    },
    Case {
        curve: "P-256",
        hash: "SHA-224",
        message: "test",
        public_key: "0460fed4ba255a9d31c961eb74c6356d68c049b8923b61fa6ce669622e60f29fb67903fe1008b8bc99a41ae9e95628bc64f2f1b20c2d7e9f5177a3c294d4462299",
        digest: "90a3ed9e32b2aaf4c61c410eb925426119e1a9dc53d4286ade99a809",
        signature: "c37edb6f0ae79d47c3c27e962fa269bb4f441770357e114ee511f662ec34a692c820053a05791e521fcaad6042d40aea1d6b1a540138558f47d0719800e18f2d",
    },
    Case {
        curve: "P-256",
        hash: "SHA-256",
        message: "sample",
        public_key: "0460fed4ba255a9d31c961eb74c6356d68c049b8923b61fa6ce669622e60f29fb67903fe1008b8bc99a41ae9e95628bc64f2f1b20c2d7e9f5177a3c294d4462299",
        digest: "af2bdbe1aa9b6ec1e2ade1d694f41fc71a831d0268e9891562113d8a62add1bf",
        signature: "efd48b2aacb6a8fd1140dd9cd45e81d69d2c877b56aaf991c34d0ea84eaf3716f7cb1c942d657c41d436c7a1b6e29f65f3e900dbb9aff4064dc4ab2f843acda8",
    },
    Case {
        curve: "P-256",
        hash: "SHA-256",
        message: "test",
        public_key: "0460fed4ba255a9d31c961eb74c6356d68c049b8923b61fa6ce669622e60f29fb67903fe1008b8bc99a41ae9e95628bc64f2f1b20c2d7e9f5177a3c294d4462299",
        digest: "9f86d081884c7d659a2feaa0c55ad015a3bf4f1b2b0b822cd15d6c15b0f00a08",
        signature: "f1abb023518351cd71d881567b1ea663ed3efcf6c5132b354f28d3b0b7d38367019f4113742a2b14bd25926b49c649155f267e60d3814b4c0cc84250e46f0083",
    },
    Case {
        curve: "P-256",
        hash: "SHA-384",
        message: "sample",
        public_key: "0460fed4ba255a9d31c961eb74c6356d68c049b8923b61fa6ce669622e60f29fb67903fe1008b8bc99a41ae9e95628bc64f2f1b20c2d7e9f5177a3c294d4462299",
        digest: "9a9083505bc92276aec4be312696ef7bf3bf603f4bbd381196a029f340585312313bca4a9b5b890efee42c77b1ee25fe",
        signature: "0eafea039b20e9b42309fb1d89e213057cbf973dc0cfc8f129edddc800ef77194861f0491e6998b9455193e34e7b0d284ddd7149a74b95b9261f13abde940954",
    },
    Case {
        curve: "P-256",
        hash: "SHA-384",
        message: "test",
        public_key: "0460fed4ba255a9d31c961eb74c6356d68c049b8923b61fa6ce669622e60f29fb67903fe1008b8bc99a41ae9e95628bc64f2f1b20c2d7e9f5177a3c294d4462299",
        digest: "768412320f7b0aa5812fce428dc4706b3cae50e02a64caa16a782249bfe8efc4b7ef1ccb126255d196047dfedf17a0a9",
        signature: "83910e8b48bb0c74244ebdf7f07a1c5413d61472bd941ef3920e623fbccebeb68ddbec54cf8cd5874883841d712142a56a8d0f218f5003cb0296b6b509619f2c",
    },
    Case {
        curve: "P-256",
        hash: "SHA-512",
        message: "sample",
        public_key: "0460fed4ba255a9d31c961eb74c6356d68c049b8923b61fa6ce669622e60f29fb67903fe1008b8bc99a41ae9e95628bc64f2f1b20c2d7e9f5177a3c294d4462299",
        digest: "39a5e04aaff7455d9850c605364f514c11324ce64016960d23d5dc57d3ffd8f49a739468ab8049bf18eef820cdb1ad6c9015f838556bc7fad4138b23fdf986c7",
        signature: "8496a60b5e9b47c825488827e0495b0e3fa109ec4568fd3f8d1097678eb97f002362ab1adbe2b8adf9cb9edab740ea6049c028114f2460f96554f61fae3302fe",
    },
    Case {
        curve: "P-256",
        hash: "SHA-512",
        message: "test",
        public_key: "0460fed4ba255a9d31c961eb74c6356d68c049b8923b61fa6ce669622e60f29fb67903fe1008b8bc99a41ae9e95628bc64f2f1b20c2d7e9f5177a3c294d4462299",
        digest: "ee26b0dd4af7e749aa1a8ee3c10ae9923f618980772e473f8819a5d4940e0db27ac185f8a0e1d5f84f88bc887fd67b143732c304cc5fa9ad8e6f57f50028a8ff",
        signature: "461d93f31b6540894788fd206c07cfa0cc35f46fa3c91816fff1040ad1581a0439af9f15de0db8d97e72719c74820d304ce5226e32dedae67519e840d1194e55",
    },
    Case {
        curve: "P-384",
        hash: "SHA-224",
        message: "sample",
        public_key: "04ec3a4e415b4e19a4568618029f427fa5da9a8bc4ae92e02e06aae5286b300c64def8f0ea9055866064a254515480bc138015d9b72d7d57244ea8ef9ac0c621896708a59367f9dfb9f54ca84b3f1c9db1288b231c3ae0d4fe7344fd2533264720",
        digest: "9003e374bc726550c2c289447fd0533160f875709386dfa377bfd41c",
        signature: "42356e76b55a6d9b4631c865445dbe54e056d3b3431766d0509244793c3f9366450f76ee3de43f5a125333a6be0601229da0c81787064021e78df658f2fbb0b042bf304665db721f077a4298b095e4834c082c03d83028efbf93a3c23940ca8d",
    },
    Case {
        curve: "P-384",
        hash: "SHA-224",
        message: "test",
        public_key: "04ec3a4e415b4e19a4568618029f427fa5da9a8bc4ae92e02e06aae5286b300c64def8f0ea9055866064a254515480bc138015d9b72d7d57244ea8ef9ac0c621896708a59367f9dfb9f54ca84b3f1c9db1288b231c3ae0d4fe7344fd2533264720",
        digest: "90a3ed9e32b2aaf4c61c410eb925426119e1a9dc53d4286ade99a809",
        signature: "e8c9d0b6ea72a0e7837fea1d14a1a9557f29faa45d3e7ee888fc5bf954b5e62464a9a817c47ff78b8c11066b24080e7207041d4a7a0379ac7232ff72e6f77b6ddb8f09b16cce0ec3286b2bd43fa8c6141c53ea5abef0d8231077a04540a96b66",
    },
    Case {
        curve: "P-384",
        hash: "SHA-256",
        message: "sample",
        public_key: "04ec3a4e415b4e19a4568618029f427fa5da9a8bc4ae92e02e06aae5286b300c64def8f0ea9055866064a254515480bc138015d9b72d7d57244ea8ef9ac0c621896708a59367f9dfb9f54ca84b3f1c9db1288b231c3ae0d4fe7344fd2533264720",
        digest: "af2bdbe1aa9b6ec1e2ade1d694f41fc71a831d0268e9891562113d8a62add1bf",
        signature: "21b13d1e013c7fa1392d03c5f99af8b30c570c6f98d4ea8e354b63a21d3daa33bde1e888e63355d92fa2b3c36d8fb2cdf3aa443fb107745bf4bd77cb3891674632068a10ca67e3d45db2266fa7d1feebefdc63eccd1ac42ec0cb8668a4fa0ab0",
    },
    Case {
        curve: "P-384",
        hash: "SHA-256",
        message: "test",
        public_key: "04ec3a4e415b4e19a4568618029f427fa5da9a8bc4ae92e02e06aae5286b300c64def8f0ea9055866064a254515480bc138015d9b72d7d57244ea8ef9ac0c621896708a59367f9dfb9f54ca84b3f1c9db1288b231c3ae0d4fe7344fd2533264720",
        digest: "9f86d081884c7d659a2feaa0c55ad015a3bf4f1b2b0b822cd15d6c15b0f00a08",
        signature: "6d6defac9ab64dabafe36c6bf510352a4cc27001263638e5b16d9bb51d451559f918eedaf2293be5b475cc8f0188636b2d46f3becbcc523d5f1a1256bf0c9b024d879ba9e838144c8ba6baeb4b53b47d51ab373f9845c0514eefb14024787265",
    },
    Case {
        curve: "P-384",
        hash: "SHA-384",
        message: "sample",
        public_key: "04ec3a4e415b4e19a4568618029f427fa5da9a8bc4ae92e02e06aae5286b300c64def8f0ea9055866064a254515480bc138015d9b72d7d57244ea8ef9ac0c621896708a59367f9dfb9f54ca84b3f1c9db1288b231c3ae0d4fe7344fd2533264720",
        digest: "9a9083505bc92276aec4be312696ef7bf3bf603f4bbd381196a029f340585312313bca4a9b5b890efee42c77b1ee25fe",
        signature: "94edbb92a5ecb8aad4736e56c691916b3f88140666ce9fa73d64c4ea95ad133c81a648152e44acf96e36dd1e80fabe4699ef4aeb15f178cea1fe40db2603138f130e740a19624526203b6351d0a3a94fa329c145786e679e7b82c71a38628ac8",
    },
    Case {
        curve: "P-384",
        hash: "SHA-384",
        message: "test",
        public_key: "04ec3a4e415b4e19a4568618029f427fa5da9a8bc4ae92e02e06aae5286b300c64def8f0ea9055866064a254515480bc138015d9b72d7d57244ea8ef9ac0c621896708a59367f9dfb9f54ca84b3f1c9db1288b231c3ae0d4fe7344fd2533264720",
        digest: "768412320f7b0aa5812fce428dc4706b3cae50e02a64caa16a782249bfe8efc4b7ef1ccb126255d196047dfedf17a0a9",
        signature: "8203b63d3c853e8d77227fb377bcf7b7b772e97892a80f36ab775d509d7a5feb0542a7f0812998da8f1dd3ca3cf023dbddd0760448d42d8a43af45af836fce4de8be06b485e9b61b827c2f13173923e06a739f040649a667bf3b828246baa5a5",
    },
    Case {
        curve: "P-384",
        hash: "SHA-512",
        message: "sample",
        public_key: "04ec3a4e415b4e19a4568618029f427fa5da9a8bc4ae92e02e06aae5286b300c64def8f0ea9055866064a254515480bc138015d9b72d7d57244ea8ef9ac0c621896708a59367f9dfb9f54ca84b3f1c9db1288b231c3ae0d4fe7344fd2533264720",
        digest: "39a5e04aaff7455d9850c605364f514c11324ce64016960d23d5dc57d3ffd8f49a739468ab8049bf18eef820cdb1ad6c9015f838556bc7fad4138b23fdf986c7",
        signature: "ed0959d5880ab2d869ae7f6c2915c6d60f96507f9cb3e047c0046861da4a799cfe30f35cc900056d7c99cd7882433709512c8cceee3890a84058ce1e22dbc2198f42323ce8aca9135329f03c068e5112dc7cc3ef3446defceb01a45c2667fdd5",
    },
    Case {
        curve: "P-384",
        hash: "SHA-512",
        message: "test",
        public_key: "04ec3a4e415b4e19a4568618029f427fa5da9a8bc4ae92e02e06aae5286b300c64def8f0ea9055866064a254515480bc138015d9b72d7d57244ea8ef9ac0c621896708a59367f9dfb9f54ca84b3f1c9db1288b231c3ae0d4fe7344fd2533264720",
        digest: "ee26b0dd4af7e749aa1a8ee3c10ae9923f618980772e473f8819a5d4940e0db27ac185f8a0e1d5f84f88bc887fd67b143732c304cc5fa9ad8e6f57f50028a8ff",
        signature: "a0d5d090c9980faf3c2ce57b7ae951d31977dd11c775d314af55f76c676447d06fb6495cd21b4b6e340fc236584fb277976984e59b4c77b0e8e4460dca3d9f20e07b9bb1f63beefaf576f6b2e8b224634a2092cd3792e0159ad9cee37659c736",
    },
    Case {
        curve: "P-521",
        hash: "SHA-224",
        message: "sample",
        public_key: "0400563485cba84979b3e090411134a30d516a20f67e2d5f3c1f7382c2a65296d89bc3e854c68fce18550a2febcc9aa25cd8f215cb14758596f456d2a28208ce1ac55a0065f1104bd427d90d34e2af75347c43a67225d7e1ef8045bde9bf4d568691c1a3d7695e861d7449d0616b113486bc681d681ccf1d4e9ca79ecdcd8d3eb9c73070b8",
        digest: "9003e374bc726550c2c289447fd0533160f875709386dfa377bfd41c",
        signature: "0105e627152e5c3d7bf835a2db5d144f46b706d350abc1cc28652ce12ef457fdcab0da77ea9673d4626466f6cfcdecdb901e3b1d809c6edf7f4f2ac85c412888f4a601fb9e7abacab4a333ac22420fff709bdf0e4e9e45e93c32b885a80f3cc9964f385fd8e3bb16f60268d118265f21b786cf602295b22d39be93c293c2f1cb28c2e566",
    },
    Case {
        curve: "P-521",
        hash: "SHA-224",
        message: "test",
        public_key: "0400563485cba84979b3e090411134a30d516a20f67e2d5f3c1f7382c2a65296d89bc3e854c68fce18550a2febcc9aa25cd8f215cb14758596f456d2a28208ce1ac55a0065f1104bd427d90d34e2af75347c43a67225d7e1ef8045bde9bf4d568691c1a3d7695e861d7449d0616b113486bc681d681ccf1d4e9ca79ecdcd8d3eb9c73070b8",
        digest: "90a3ed9e32b2aaf4c61c410eb925426119e1a9dc53d4286ade99a809",
        signature: "0005a580b43b28cea97b589ca6553bfa85ead8bf649dcec59d69d435da1f4f159a929846b16246c6c643db2de0c4bd4f415b46a682eecc92173e064493558490c00b011a439858ce569c5c36ff4a9cf71de8551b675d32a63815c2ede28f0a837d40b231ecf5a21bfea13317782fd22a0518a2bf7daad0cc4633faeddb902e7c23ffc1f2",
    },
    Case {
        curve: "P-521",
        hash: "SHA-256",
        message: "sample",
        public_key: "0400563485cba84979b3e090411134a30d516a20f67e2d5f3c1f7382c2a65296d89bc3e854c68fce18550a2febcc9aa25cd8f215cb14758596f456d2a28208ce1ac55a0065f1104bd427d90d34e2af75347c43a67225d7e1ef8045bde9bf4d568691c1a3d7695e861d7449d0616b113486bc681d681ccf1d4e9ca79ecdcd8d3eb9c73070b8",
        digest: "af2bdbe1aa9b6ec1e2ade1d694f41fc71a831d0268e9891562113d8a62add1bf",
        signature: "00301b2d777e7993354bf15b54b29ce77fdd0d7130e2bcb6215b1d928babb7c38db5c72e2d1efad853241017d210ba07b6621f11a4260f81a36e3e6cdf2c19ccdce9008b61280adaacacd86fe5ad38d567de7990859f995bb4fafaf673fa3d6be63101368ced0273f02d19a161663e47ca4724a0b209577166f0a62bc9cbc423b31e975d",
    },
    Case {
        curve: "P-521",
        hash: "SHA-256",
        message: "test",
        public_key: "0400563485cba84979b3e090411134a30d516a20f67e2d5f3c1f7382c2a65296d89bc3e854c68fce18550a2febcc9aa25cd8f215cb14758596f456d2a28208ce1ac55a0065f1104bd427d90d34e2af75347c43a67225d7e1ef8045bde9bf4d568691c1a3d7695e861d7449d0616b113486bc681d681ccf1d4e9ca79ecdcd8d3eb9c73070b8",
        digest: "9f86d081884c7d659a2feaa0c55ad015a3bf4f1b2b0b822cd15d6c15b0f00a08",
        signature: "001f367d98d956526edc2b59ac8f4400d94045b57d479d16f1a8328d1aef0c1c1b80c03bc67859c0dc698a8cff13e5c383a90d528b5357094fd1074500e1cd28895c01e5870e2f3b169056dc88151130384f93d52ecdfeff397437d0457cb8a6b7ee236e41aae206d2cbe2d69b9b7f5e7508c452b1edd0d8cbc212fe8e7a9952f5cf3840",
    },
    Case {
        curve: "P-521",
        hash: "SHA-384",
        message: "sample",
        public_key: "0400563485cba84979b3e090411134a30d516a20f67e2d5f3c1f7382c2a65296d89bc3e854c68fce18550a2febcc9aa25cd8f215cb14758596f456d2a28208ce1ac55a0065f1104bd427d90d34e2af75347c43a67225d7e1ef8045bde9bf4d568691c1a3d7695e861d7449d0616b113486bc681d681ccf1d4e9ca79ecdcd8d3eb9c73070b8",
        digest: "9a9083505bc92276aec4be312696ef7bf3bf603f4bbd381196a029f340585312313bca4a9b5b890efee42c77b1ee25fe",
        signature: "010cc122a1ea382dc391725d5a10fe93a1779a3e8961e26fd526d4022f08371a19f13ebe0d14d70e106a7d6371f4b8e62b6c3a0fee3bbf99f56f0b801a6a36d4f67501f7c1fedad9f696fac07e988126cd45ddfb2a32f6e88ba3631deb4d99a71ae88ed5df5e1680997cf4e80ef01ef87f3c0c72946f31694ece197c0c204118beac4c76",
    },
    Case {
        curve: "P-521",
        hash: "SHA-384",
        message: "test",
        public_key: "0400563485cba84979b3e090411134a30d516a20f67e2d5f3c1f7382c2a65296d89bc3e854c68fce18550a2febcc9aa25cd8f215cb14758596f456d2a28208ce1ac55a0065f1104bd427d90d34e2af75347c43a67225d7e1ef8045bde9bf4d568691c1a3d7695e861d7449d0616b113486bc681d681ccf1d4e9ca79ecdcd8d3eb9c73070b8",
        digest: "768412320f7b0aa5812fce428dc4706b3cae50e02a64caa16a782249bfe8efc4b7ef1ccb126255d196047dfedf17a0a9",
        signature: "005c8ae583754913f12f0cd3fc1019b47f21dbc76971b442640047e76bb34864b7a6d941c0ee5766e3f232172337f160559a18d44938e04ef708ebca3495826d87fb014ec158726cb83df9cb94ea819f58ba78f569a79b9a3fdb62fc58442572d0c858965ad7c42cdac70d00d909cdbf053a22cb20a435547b4f6fa4ec1b99ca199be1dd",
    },
    Case {
        curve: "P-521",
        hash: "SHA-512",
        message: "sample",
        public_key: "0400563485cba84979b3e090411134a30d516a20f67e2d5f3c1f7382c2a65296d89bc3e854c68fce18550a2febcc9aa25cd8f215cb14758596f456d2a28208ce1ac55a0065f1104bd427d90d34e2af75347c43a67225d7e1ef8045bde9bf4d568691c1a3d7695e861d7449d0616b113486bc681d681ccf1d4e9ca79ecdcd8d3eb9c73070b8",
        digest: "39a5e04aaff7455d9850c605364f514c11324ce64016960d23d5dc57d3ffd8f49a739468ab8049bf18eef820cdb1ad6c9015f838556bc7fad4138b23fdf986c7",
        signature: "0139281db2c1a54f545dad34339687aa63c79b60028f2c3f452e199e9aec0e76ee9780a638d8af8ba159c42a0e86f2fe88f142fffb74ef44017d6cbda729eff20eee000ecb3235ff333a2433b08e3cac4c72120de211c53dd1e367dc7c6f9af822275c3c2b89c56b6f0806207c3020cd389aa4d23df7234071f091bd0a9e8bace128ecd2",
    },
    Case {
        curve: "P-521",
        hash: "SHA-512",
        message: "test",
        public_key: "0400563485cba84979b3e090411134a30d516a20f67e2d5f3c1f7382c2a65296d89bc3e854c68fce18550a2febcc9aa25cd8f215cb14758596f456d2a28208ce1ac55a0065f1104bd427d90d34e2af75347c43a67225d7e1ef8045bde9bf4d568691c1a3d7695e861d7449d0616b113486bc681d681ccf1d4e9ca79ecdcd8d3eb9c73070b8",
        digest: "ee26b0dd4af7e749aa1a8ee3c10ae9923f618980772e473f8819a5d4940e0db27ac185f8a0e1d5f84f88bc887fd67b143732c304cc5fa9ad8e6f57f50028a8ff",
        signature: "00a1a0023800696db78db04382b6f15d450a8beeb39760325be217224d9d68731600cf87b115b408ed837be8d782eea47812e8720c8ae7a71c63c4e9254e535f42ef00a06d93357b0c716974c47c305091d7c8f6ff4c3dffdd655f5c70f38e61fb4827eb2f2af8f0f08dd2c961939b14c7b77ff81d240982605e1afb886d24ffe2e604cb",
    },
];

fn bytes(hex: &str) -> Vec<u8> {
    let mut out = vec![0u8; hex.len() / 2];
    hex_decode(hex.as_bytes(), &mut out).expect("hex");
    out
}

fn verify(curve: &str, pk: &[u8], digest: &[u8], sig: &[u8]) -> ic_core::Result<()> {
    match curve {
        "P-256" => EcdsaP256Sha256::verify_prehash(pk, digest, sig),
        "P-384" => EcdsaP384Sha384::verify_prehash(pk, digest, sig),
        "P-521" => EcdsaP521Sha512::verify_prehash(pk, digest, sig),
        _ => unreachable!("{curve}"),
    }
}

/// The narrowest digest each curve accepts: as strong as the curve.
fn min_len(curve: &str) -> usize {
    match curve {
        "P-256" => 32,
        "P-384" => 48,
        _ => 64,
    }
}

/// Every pairing at least as strong as its curve verifies, and the curve's
/// own pairing agrees with `verify`. Every weaker pairing -- SHA-224 on any
/// curve, SHA-256 on P-384 and P-521, SHA-384 on P-521 -- is refused for its
/// width, although the signature is valid.
#[test]
fn every_strong_enough_pairing_verifies_and_weaker_ones_are_refused() {
    use ic_core::traits::SignatureScheme;
    let (mut verified, mut refused) = (0, 0);
    for case in CASES {
        let (pk, digest, sig) = (
            bytes(case.public_key),
            bytes(case.digest),
            bytes(case.signature),
        );
        let result = verify(case.curve, &pk, &digest, &sig);
        if digest.len() < min_len(case.curve) {
            let err = result.expect_err("a digest weaker than the curve");
            assert_eq!(
                err.kind(),
                ic_core::ErrorKind::InvalidLength,
                "{} {}",
                case.curve,
                case.hash
            );
            refused += 1;
            continue;
        }
        result.unwrap_or_else(|e| panic!("{} {} {}: {e:?}", case.curve, case.hash, case.message));
        let native = matches!(
            (case.curve, case.hash),
            ("P-256", "SHA-256") | ("P-384", "SHA-384") | ("P-521", "SHA-512")
        );
        if native {
            let m = case.message.as_bytes();
            match case.curve {
                "P-256" => EcdsaP256Sha256::verify(&pk, m, &sig),
                "P-384" => EcdsaP384Sha384::verify(&pk, m, &sig),
                _ => EcdsaP521Sha512::verify(&pk, m, &sig),
            }
            .expect("the message form agrees");
        }
        verified += 1;
    }
    // Per message: P-256 accepts 3 hashes, P-384 2, P-521 1.
    assert_eq!((verified, refused), (12, 12), "the cases did not run");
}

/// A signature is bound to the part of its digest FIPS 186-5 uses, and to its
/// own bytes and message; the part of a digest beyond the order's width is not
/// used, so changing it must change nothing.
///
/// The second half is the rule the truncation implements, tested against the
/// rule rather than a value: a digest longer than the order contributes only
/// its leftmost order-width bits, so bytes past them are ignored.
#[test]
fn a_wrong_digest_key_or_signature_is_refused() {
    let mut beyond_checked = 0;
    for case in CASES
        .iter()
        .filter(|c| c.digest.len() / 2 >= min_len(c.curve))
    {
        let (pk, digest, sig) = (
            bytes(case.public_key),
            bytes(case.digest),
            bytes(case.signature),
        );
        // Bytes of the order: 32, 48, 66.
        let order_bytes = sig.len() / 2;
        let used = digest.len().min(order_bytes);
        for at in [0, used / 2, used - 1] {
            let mut d = digest.clone();
            d[at] ^= 1;
            assert!(
                verify(case.curve, &pk, &d, &sig).is_err(),
                "digest byte {at}"
            );
        }
        for at in used..digest.len() {
            let mut d = digest.clone();
            d[at] ^= 0xff;
            verify(case.curve, &pk, &d, &sig).unwrap_or_else(|_| {
                panic!("{} {}: byte {at} is past the order", case.curve, case.hash)
            });
            beyond_checked += 1;
        }
        let mut s = sig.clone();
        s[sig.len() - 1] ^= 1;
        assert!(verify(case.curve, &pk, &digest, &s).is_err(), "signature");
        // The other message's signature under the same curve and hash.
        let other = CASES
            .iter()
            .find(|c| c.curve == case.curve && c.hash == case.hash && c.message != case.message)
            .expect("both messages present");
        assert!(
            verify(case.curve, &pk, &digest, &bytes(other.signature)).is_err(),
            "message"
        );
    }
    // SHA-384 and SHA-512 on P-256 (16 and 32 bytes past), SHA-512 on P-384
    // (16), for both messages.
    assert_eq!(
        beyond_checked,
        2 * (16 + 32 + 16),
        "the truncated bytes were not all tried"
    );
}

/// Widths outside SHA-224..SHA-512 are refused before any arithmetic, SHA-1's
/// 20 bytes among them, with the error saying so.
#[test]
fn digest_widths_outside_the_sha2_family_are_refused() {
    let case = &CASES[0];
    let (pk, sig) = (bytes(case.public_key), bytes(case.signature));
    for len in [0usize, 1, 20, 27, 29, 31, 33, 47, 49, 63, 65, 128] {
        let err = verify(case.curve, &pk, &vec![0x5a; len], &sig).expect_err("refused");
        assert_eq!(
            err.kind(),
            ic_core::ErrorKind::InvalidLength,
            "length {len}"
        );
    }
}