use core::{
fmt,
hash::{Hash, Hasher},
};
use super::hmac::{HmacSha256, HmacSha384};
use crate::{
SecretBytes,
hashes::crypto::{Sha256, Sha384},
traits::{Mac, VerificationError, ct},
};
#[cfg(all(target_arch = "aarch64", any(target_os = "macos", target_os = "linux")))]
#[path = "ecdsa_aarch64_asm.rs"]
mod ecdsa_aarch64_asm;
#[path = "ecdsa_generator_tables.rs"]
mod ecdsa_generator_tables;
#[path = "ecdsa_p384_field.rs"]
mod ecdsa_p384_field;
#[cfg(all(target_arch = "x86_64", target_os = "linux"))]
#[path = "ecdsa_x86_64_asm.rs"]
mod ecdsa_x86_64_asm;
use ecdsa_generator_tables::{
P256_SIGNING_COMB_WIDTH, P256_SIGNING_GENERATOR_COMB_X, P256_SIGNING_GENERATOR_COMB_Y, P384_SIGNING_COMB_WIDTH,
P384_SIGNING_GENERATOR_COMB_X, P384_SIGNING_GENERATOR_COMB_Y,
};
#[cfg(all(target_arch = "aarch64", any(target_os = "macos", target_os = "linux")))]
use self::ecdsa_aarch64_asm as ecdsa_platform_asm;
#[cfg(all(target_arch = "x86_64", target_os = "linux"))]
use self::ecdsa_x86_64_asm as ecdsa_platform_asm;
const TAG_SEQUENCE: u8 = 0x30;
const TAG_INTEGER: u8 = 0x02;
const TAG_BIT_STRING: u8 = 0x03;
const TAG_OBJECT_IDENTIFIER: u8 = 0x06;
const COMB_WIDTH: usize = 4;
const COMB_TABLE_SIZE: usize = 1 << COMB_WIDTH;
const P256_SIGNING_COMB_ROWS: usize = 37;
const P384_SIGNING_COMB_ROWS: usize = 48;
const ID_EC_PUBLIC_KEY_OID: &[u8] = &[0x2a, 0x86, 0x48, 0xce, 0x3d, 0x02, 0x01];
const SECP256R1_OID: &[u8] = &[0x2a, 0x86, 0x48, 0xce, 0x3d, 0x03, 0x01, 0x07];
const SECP384R1_OID: &[u8] = &[0x2b, 0x81, 0x04, 0x00, 0x22];
const P256_NONCE_DOMAIN: &[u8] = b"rscrypto-ecdsa-p256-sha256-sign-v1";
const P384_NONCE_DOMAIN: &[u8] = b"rscrypto-ecdsa-p384-sha384-sign-v1";
const P256_PUBKEY_BLIND_DOMAIN: &[u8] = b"rscrypto-ecdsa-p256-pubkey-blind-v1";
const P384_PUBKEY_BLIND_DOMAIN: &[u8] = b"rscrypto-ecdsa-p384-pubkey-blind-v1";
const P256_FIELD: Uint<4> = Uint([
0xffff_ffff_ffff_ffff,
0x0000_0000_ffff_ffff,
0x0000_0000_0000_0000,
0xffff_ffff_0000_0001,
]);
const P256_ORDER: Uint<4> = Uint([
0xf3b9_cac2_fc63_2551,
0xbce6_faad_a717_9e84,
0xffff_ffff_ffff_ffff,
0xffff_ffff_0000_0000,
]);
const P256_ORDER_MINUS_TWO: Uint<4> = Uint([
0xf3b9_cac2_fc63_254f,
0xbce6_faad_a717_9e84,
0xffff_ffff_ffff_ffff,
0xffff_ffff_0000_0000,
]);
const P256_ORDER_HALF: Uint<4> = Uint([
0x79dc_e561_7e31_92a8,
0xde73_7d56_d38b_cf42,
0x7fff_ffff_ffff_ffff,
0x7fff_ffff_8000_0000,
]);
const P256_FIELD_MINUS_TWO: Uint<4> = Uint([
0xffff_ffff_ffff_fffd,
0x0000_0000_ffff_ffff,
0x0000_0000_0000_0000,
0xffff_ffff_0000_0001,
]);
const P256_B: Uint<4> = Uint([
0x3bce_3c3e_27d2_604b,
0x651d_06b0_cc53_b0f6,
0xb3eb_bd55_7698_86bc,
0x5ac6_35d8_aa3a_93e7,
]);
const P256_GX: Uint<4> = Uint([
0xf4a1_3945_d898_c296,
0x7703_7d81_2deb_33a0,
0xf8bc_e6e5_63a4_40f2,
0x6b17_d1f2_e12c_4247,
]);
const P256_GY: Uint<4> = Uint([
0xcbb6_4068_37bf_51f5,
0x2bce_3357_6b31_5ece,
0x8ee7_eb4a_7c0f_9e16,
0x4fe3_42e2_fe1a_7f9b,
]);
const P384_FIELD: Uint<6> = Uint([
0x0000_0000_ffff_ffff,
0xffff_ffff_0000_0000,
0xffff_ffff_ffff_fffe,
0xffff_ffff_ffff_ffff,
0xffff_ffff_ffff_ffff,
0xffff_ffff_ffff_ffff,
]);
const P384_ORDER: Uint<6> = Uint([
0xecec_196a_ccc5_2973,
0x581a_0db2_48b0_a77a,
0xc763_4d81_f437_2ddf,
0xffff_ffff_ffff_ffff,
0xffff_ffff_ffff_ffff,
0xffff_ffff_ffff_ffff,
]);
const P384_ORDER_MINUS_TWO: Uint<6> = Uint([
0xecec_196a_ccc5_2971,
0x581a_0db2_48b0_a77a,
0xc763_4d81_f437_2ddf,
0xffff_ffff_ffff_ffff,
0xffff_ffff_ffff_ffff,
0xffff_ffff_ffff_ffff,
]);
const P384_ORDER_HALF: Uint<6> = Uint([
0x7676_0cb5_6662_94b9,
0xac0d_06d9_2458_53bd,
0xe3b1_a6c0_fa1b_96ef,
0xffff_ffff_ffff_ffff,
0xffff_ffff_ffff_ffff,
0x7fff_ffff_ffff_ffff,
]);
const P384_FIELD_MINUS_TWO: Uint<6> = Uint([
0x0000_0000_ffff_fffd,
0xffff_ffff_0000_0000,
0xffff_ffff_ffff_fffe,
0xffff_ffff_ffff_ffff,
0xffff_ffff_ffff_ffff,
0xffff_ffff_ffff_ffff,
]);
const P384_B: Uint<6> = Uint([
0x2a85_c8ed_d3ec_2aef,
0xc656_398d_8a2e_d19d,
0x0314_088f_5013_875a,
0x181d_9c6e_fe81_4112,
0x988e_056b_e3f8_2d19,
0xb331_2fa7_e23e_e7e4,
]);
const P384_GX: Uint<6> = Uint([
0x3a54_5e38_7276_0ab7,
0x5502_f25d_bf55_296c,
0x59f7_41e0_8254_2a38,
0x6e1d_3b62_8ba7_9b98,
0x8eb1_c71e_f320_ad74,
0xaa87_ca22_be8b_0537,
]);
const P384_GY: Uint<6> = Uint([
0x7a43_1d7c_90ea_0e5f,
0x0a60_b1ce_1d7e_819d,
0xe9da_3113_b5f0_b8c0,
0xf8f4_1dbd_289a_147c,
0x5d9e_98bf_9292_dc29,
0x3617_de4a_9626_2c6f,
]);
const P256_GENERATOR_COMB_X: [Uint<4>; COMB_TABLE_SIZE] = [
Uint([
0x79e7_30d4_18a9_143c,
0x75ba_95fc_5fed_b601,
0x79fb_732b_7762_2510,
0x1890_5f76_a537_55c6,
]),
Uint([
0x79e7_30d4_18a9_143c,
0x75ba_95fc_5fed_b601,
0x79fb_732b_7762_2510,
0x1890_5f76_a537_55c6,
]),
Uint([
0x4f92_2fc5_16a0_d2bb,
0x0d5c_c16c_1a62_3499,
0x9241_cf3a_57c6_2c8b,
0x2f5e_6961_fd1b_667f,
]),
Uint([
0x9e56_6847_e137_bbbc,
0xe434_469e_8a6a_0bec,
0xb1c4_2761_79d7_3463,
0x5abe_0285_133d_0015,
]),
Uint([
0x62a8_c244_bfe2_0925,
0x91c1_9ac3_8fdc_e867,
0x5a96_a5d5_dd38_7063,
0x61d5_87d4_21d3_24f6,
]),
Uint([
0x1c89_1f2b_2cb1_9ffd,
0x01ba_8d5b_b192_3c23,
0xb6d0_3d67_8ac5_ca8e,
0x586e_b04c_1f13_bedc,
]),
Uint([
0x6257_7734_d2b5_33d5,
0x673b_8af6_a1bd_ddc0,
0x577e_7c9a_a79e_c293,
0xbb6d_e651_c3b2_66b1,
]),
Uint([
0xbd6a_38e1_1ae5_aa1c,
0xb8b7_652b_49e7_3658,
0x0b13_0014_ee5f_87ed,
0x9d0f_27b2_aeeb_ffcd,
]),
Uint([
0x56f8_410e_f4f8_b16a,
0x9724_1afe_c47b_266a,
0x0a40_6b8e_6d9c_87c1,
0x803f_3e02_cd42_ab1b,
]),
Uint([
0x846a_56f2_c379_ab34,
0xa8ee_068b_841d_f8d1,
0x2031_4459_176c_68ef,
0xf1af_32d5_915f_1f30,
]),
Uint([
0xed93_e225_d5be_5a2b,
0x6fe7_9983_5934_f3c6,
0x4314_0926_2262_6ffc,
0x50bb_b4d9_7990_216a,
]),
Uint([
0xfc68_b5c5_9b39_1593,
0xc385_f5a2_5982_70fc,
0x7144_f3aa_d19a_dcbb,
0xdd55_8999_83fb_ae0c,
]),
Uint([
0x5fe1_4bfe_80ec_21fe,
0xf6ce_116a_c255_be82,
0x98bc_5a07_2f4a_5d67,
0xfad2_7148_db7e_63af,
]),
Uint([
0x1e9e_cc49_a56c_0dd7,
0xa5cf_fcd8_4608_6c74,
0x8f7a_1408_f505_aece,
0xb37b_85c0_bef0_c47e,
]),
Uint([
0x0a1c_7294_95c8_f8be,
0x2961_c480_3bf3_62bf,
0x9e41_8403_df63_d4ac,
0xc109_f9cb_91ec_e900,
]),
Uint([
0x0d5a_e356_4291_3074,
0x5549_1b27_48a5_42b1,
0x469c_a665_b310_732a,
0x2959_1d52_5f1a_4cc1,
]),
];
const P256_GENERATOR_COMB_Y: [Uint<4>; COMB_TABLE_SIZE] = [
Uint([
0xddf2_5357_ce95_560a,
0x8b4a_b8e4_ba19_e45c,
0xd2e8_8688_dd21_f325,
0x8571_ff18_2588_5d85,
]),
Uint([
0xddf2_5357_ce95_560a,
0x8b4a_b8e4_ba19_e45c,
0xd2e8_8688_dd21_f325,
0x8571_ff18_2588_5d85,
]),
Uint([
0x5c15_c70b_f5a0_1797,
0x3d20_b44d_6095_6192,
0x0491_1b37_071f_db52,
0xf648_f916_8d6f_0f7b,
]),
Uint([
0x92aa_837c_c04c_7dab,
0x573d_9f4c_4326_0c07,
0x0c93_1562_78e6_cc37,
0x94bb_725b_6b6f_7383,
]),
Uint([
0xe876_73a2_a371_73ea,
0x2384_8008_5377_8b65,
0x10f8_441e_05ba_b43e,
0xfa11_fe12_4621_efbe,
]),
Uint([
0x0c35_c6e5_27e8_ed09,
0x1e81_a33c_1819_ede2,
0x278f_d6c0_56c6_52fa,
0x19d5_ac08_7086_4f11,
]),
Uint([
0xe7e9_303a_b652_59b3,
0xd6a0_afd3_d03a_7480,
0xc5ac_83d1_9b3c_fc27,
0x60b4_619a_5d18_b99b,
]),
Uint([
0xca92_4631_7a73_0a55,
0x9c95_5b2f_ddbb_c83a,
0x07c1_dfe0_ac01_9a71,
0x244a_566d_356e_c48d,
]),
Uint([
0x7f03_09a8_04db_ec69,
0xa83b_85f7_3bba_d05f,
0xc609_7273_ad8e_197f,
0xc097_440e_5067_adc1,
]),
Uint([
0x99c3_7531_5d75_bd50,
0x837c_ffba_f72f_67bc,
0x0613_a418_48d7_723f,
0x23d0_f130_e2d4_1c8b,
]),
Uint([
0x3781_91c6_e57e_c63e,
0x6542_2c40_181d_cdb2,
0x41a8_099b_0236_e0f6,
0x2b10_0118_01fe_49c3,
]),
Uint([
0x93b8_8b8e_74b8_2ff4,
0xd2e0_3c40_71e7_34c9,
0x9a7a_9eaf_43c0_322a,
0xe6e4_c551_149d_6041,
]),
Uint([
0x90c0_b6ac_29ab_05b3,
0x37a9_a83c_4e25_1ae6,
0x0a7d_c875_c2aa_de7d,
0x7738_7de3_9f0e_1a84,
]),
Uint([
0x3596_b6e4_cc0e_6a8f,
0xfd6d_4bbf_6b38_8f23,
0xaba4_53fa_c39c_ef4e,
0x9c13_5ac8_f9f6_28d5,
]),
Uint([
0xc2d0_95d0_5894_5705,
0xb908_3d96_ddeb_85c0,
0x8469_2b8d_7a40_449b,
0x9bc3_344f_2eee_1ee1,
]),
Uint([
0xe76f_5b6b_b84f_983f,
0xbe7e_ef41_9f5f_84e1,
0x1200_d496_80ba_a189,
0x6376_551f_18ef_332c,
]),
];
const P384_GENERATOR_COMB_X: [Uint<6>; COMB_TABLE_SIZE] = [
Uint([
0x3dd0_7566_49c0_b528,
0x20e3_78e2_a0d6_ce38,
0x879c_3afc_541b_4d6e,
0x6454_8684_59a3_0eff,
0x812f_f723_614e_de2b,
0x4d3a_adc2_299e_1513,
]),
Uint([
0x3dd0_7566_49c0_b528,
0x20e3_78e2_a0d6_ce38,
0x879c_3afc_541b_4d6e,
0x6454_8684_59a3_0eff,
0x812f_f723_614e_de2b,
0x4d3a_adc2_299e_1513,
]),
Uint([
0x2448_0c57_f26f_eef9,
0xc31a_2694_3a0e_1240,
0x7350_02c3_273e_2bc7,
0x8c42_e9c5_3ef1_ed4c,
0x028b_abf6_7f49_48e8,
0x6a50_2f43_8a97_8632,
]),
Uint([
0x1142_6e2e_e349_ddd0,
0x9f11_7ef9_9b2f_c250,
0xff36_b480_ec01_74a6,
0x4f4b_de76_1845_8466,
0x2f2e_db6d_0580_6049,
0x8adc_75d1_19df_ca92,
]),
Uint([
0x3782_05de_2f9f_be67,
0xc4af_cb83_7f72_8e44,
0xdbce_c06c_682e_00f1,
0xf2a1_45c3_114d_5423,
0xa01d_9874_7a52_463e,
0xfc09_35b1_7d71_7b0a,
]),
Uint([
0x73ad_e4da_2341_c342,
0xdd32_6e54_ea70_4422,
0x336c_7d98_3741_cef3,
0x1eaf_a00d_59e6_1549,
0xcd3e_d892_bd9a_3efd,
0x03fa_f26c_c5c6_c7e4,
]),
Uint([
0xfab0_8607_3f3b_236f,
0x19e9_d41d_81e2_21da,
0xf3f6_571e_3927_b428,
0x4348_a933_7550_f1f6,
0x7167_b996_a85e_62f0,
0x62d4_3759_7f54_52bf,
]),
Uint([
0x90b2_e5b3_3062_e8af,
0xa857_2375_e8a3_d369,
0x3fe1_b00b_201d_b7b1,
0xe926_def0_ee65_1aa2,
0x6542_c9be_b9b1_0ad7,
0x098e_309b_a2fc_be74,
]),
Uint([
0x070d_34e1_1697_3cf4,
0x20ae_e08b_7e4f_34f7,
0x269a_f9b9_5eb8_ad29,
0xdde0_a036_a6a4_5dda,
0xa18b_528e_63df_41e0,
0x03cc_71b2_a260_df2a,
]),
Uint([
0xba14_64b4_01ab_5245,
0x9b8d_0b6d_c48d_93ff,
0x9398_67dc_93ad_272c,
0xbebe_085e_ae9f_dc77,
0x73ae_5103_894e_a8bd,
0x740f_c89a_39ac_22e1,
]),
Uint([
0x03cf_2922_0062_3f3b,
0x095c_7111_5f29_ebff,
0x42d7_2247_80aa_6823,
0x044c_7ba1_7458_c0b0,
0xca62_f7ef_0959_ec20,
0x40ae_2ab7_f8ca_929f,
]),
Uint([
0x744d_1400_8435_af04,
0x5f25_5b1d_fec1_92da,
0x1f17_dc12_336d_c542,
0x5c90_c2a7_636a_68a8,
0x960c_9eb7_7704_ca1e,
0x9de8_cf1e_6fb3_d65a,
]),
Uint([
0x867d_b639_9868_3186,
0xfb5c_f424_ddcc_4ea9,
0xcc9a_7ffe_d4f0_e7bd,
0x7c57_f71c_7a77_9f7e,
0x9077_4079_d6b2_5ef2,
0x90ea_e903_b408_1680,
]),
Uint([
0xfbd8_38f9_e054_0015,
0x2c32_3946_c390_77dc,
0x8b1f_b9e6_ad61_9124,
0x9612_440c_0ca6_2ea8,
0x9ad9_b52c_2dbe_00ff,
0xf52a_baa1_ae19_7643,
]),
Uint([
0xcd96_866d_b8a9_e8c9,
0xa119_63b8_5bb8_091e,
0xc7f9_0d53_045b_3cd2,
0x755a_72b5_80f3_6504,
0x46f8_b399_21d3_751c,
0x4bff_dc91_53c1_93de,
]),
Uint([
0xd71e_4aab_6328_e33f,
0x5486_782b_af81_36d1,
0x07a4_995f_86d5_7231,
0xf1f0_a5bd_1651_a968,
0xa5dc_5b24_7680_3b6d,
0x5c58_7cbc_42dd_a935,
]),
];
const P384_GENERATOR_COMB_Y: [Uint<6>; COMB_TABLE_SIZE] = [
Uint([
0x2304_3dad_4b03_a4fe,
0xa1bf_a8bf_7bb4_a9ac,
0x8bad_e756_2e83_b050,
0xc6c3_5219_68f4_ffd9,
0xdd80_0226_3969_a840,
0x2b78_abc2_5a15_c5e9,
]),
Uint([
0x2304_3dad_4b03_a4fe,
0xa1bf_a8bf_7bb4_a9ac,
0x8bad_e756_2e83_b050,
0xc6c3_5219_68f4_ffd9,
0xdd80_0226_3969_a840,
0x2b78_abc2_5a15_c5e9,
]),
Uint([
0xf5f1_3a46_b745_36fe,
0x1d21_8bab_d8a9_f0eb,
0x30f3_6bcc_3723_2768,
0xc531_7b31_576e_8c18,
0xef1d_57a6_9bbc_b766,
0x917c_4930_b3e3_d4dc,
]),
Uint([
0xa619_d097_b7d5_a7ce,
0x8742_75e5_a344_11e9,
0x5403_e047_0da4_b4ef,
0x2eba_afd9_7790_1d8f,
0x5e63_ebce_a747_170f,
0x12a3_6944_7f9d_8036,
]),
Uint([
0x9653_bc4f_d4d0_1f95,
0x9aa8_3ea8_9560_ad34,
0xf779_43dc_af8e_3f3f,
0x7077_4a10_e86f_e16e,
0x6b62_e6f1_bf9f_fdcf,
0x8a72_f39e_5887_45c9,
]),
Uint([
0x087e_2fcf_3045_f8ac,
0x14a6_5532_174f_1e73,
0x2cf8_4f28_fe0a_f9a7,
0xddfd_7a84_2cdc_935b,
0x4c0f_117b_6929_c895,
0x3565_72d6_4c8b_cfcc,
]),
Uint([
0xd85f_eb9e_f295_5926,
0x440a_561f_6df7_8353,
0x3896_68ec_9ca3_6b59,
0x052b_f1a1_a22d_a016,
0xbdfb_ff72_f609_3254,
0x94e5_0f28_e222_09f3,
]),
Uint([
0x779d_eeb3_fff1_d63f,
0x23d0_e80a_20bf_d374,
0x8452_bb3b_8768_f797,
0xcf75_bb4d_1f95_2856,
0x8fe6_b400_29ea_3faa,
0x12bd_3e40_8137_3a53,
]),
Uint([
0x24a6_770a_a06b_1dd7,
0x5bfa_9c11_9d26_75d3,
0x73c1_e2a1_9684_4432,
0x3660_558d_131a_6cf0,
0xb028_9c83_2ee7_9454,
0xa6ae_fb01_c6d8_ddcd,
]),
Uint([
0x5e28_b0a3_28e2_3b23,
0x2352_722e_e131_04d0,
0xf466_7a18_b0a2_640d,
0xac74_a72e_49bb_37c3,
0x79f7_34f0_e81e_183a,
0xbffe_5b6c_3fd9_c0eb,
]),
Uint([
0xb8c5_377a_a927_b102,
0x398a_86a0_dc03_1771,
0x0490_8f9d_c216_a406,
0xb423_a73a_918d_3300,
0x634b_0ff1_e0b9_4739,
0xe29d_e725_2d69_f697,
]),
Uint([
0xc60f_ee0d_511d_3d06,
0x466e_2313_f9eb_52c7,
0x743c_0f5f_206b_0914,
0x42f5_5bac_2191_aa4d,
0xcefc_7c8f_ffeb_dbc2,
0xd4fa_6081_e6e8_ed1c,
]),
Uint([
0xdf2a_ae5e_0ee1_fceb,
0x3ff1_da24_e86c_1a1f,
0x80f5_87d6_ca19_3edf,
0xa569_5523_dc9b_9d6a,
0x7b84_0900_8592_0303,
0x1efa_4dfc_ba6d_bdef,
]),
Uint([
0xd0e8_9894_2cac_32ad,
0xdfb7_9e42_62a9_8f91,
0x6545_2ecf_276f_55cb,
0xdb1a_c0d2_7ad2_3e12,
0xf68c_5f6a_de49_86f0,
0x389a_c37b_82ce_327d,
]),
Uint([
0xcd15_c049_b895_54e7,
0x353c_6754_f7a2_6be6,
0x7960_2370_bd41_d970,
0xde16_470b_12b1_76c0,
0x56ba_1175_40c8_809d,
0xe2db_35c3_e435_fb1e,
]),
Uint([
0x2b6c_db32_bae8_b4c0,
0x66d1_598b_b133_1138,
0x4a23_b2d2_5d7e_9614,
0x93e4_02a6_74a8_c05d,
0x45ac_94e6_da7c_e82e,
0xeb9f_8281_e463_d465,
]),
];
static P256_FIELD_MODULUS: Modulus<4> = Modulus {
value: P256_FIELD,
n0_inv: 0x0000_0000_0000_0001,
r2: Uint([
0x0000_0000_0000_0003,
0xffff_fffb_ffff_ffff,
0xffff_ffff_ffff_fffe,
0x0000_0004_ffff_fffd,
]),
};
static P256_ORDER_MODULUS: Modulus<4> = Modulus {
value: P256_ORDER,
n0_inv: 0xccd1_c8aa_ee00_bc4f,
r2: Uint([
0x8324_4c95_be79_eea2,
0x4699_799c_49bd_6fa6,
0x2845_b239_2b6b_ec59,
0x66e1_2d94_f3d9_5620,
]),
};
static P384_FIELD_MODULUS: Modulus<6> = Modulus {
value: P384_FIELD,
n0_inv: 0x0000_0001_0000_0001,
r2: Uint([
0xffff_fffe_0000_0001,
0x0000_0002_0000_0000,
0xffff_fffe_0000_0000,
0x0000_0002_0000_0000,
0x0000_0000_0000_0001,
0x0000_0000_0000_0000,
]),
};
static P384_ORDER_MODULUS: Modulus<6> = Modulus {
value: P384_ORDER,
n0_inv: 0x6ed4_6089_e88f_dc45,
r2: Uint([
0x2d31_9b24_19b4_09a9,
0xff3d_81e5_df1a_a419,
0xbc3e_483a_fcb8_2947,
0xd40d_4917_4aab_1cc5,
0x3fb0_5b7a_2826_6895,
0x0c84_ee01_2b39_bf21,
]),
};
static P256: Curve<4> = Curve {
field_modulus: &P256_FIELD_MODULUS,
scalar_modulus: &P256_ORDER_MODULUS,
b: P256_B,
generator_x: P256_GX,
generator_y: P256_GY,
generator_comb_x: P256_GENERATOR_COMB_X,
generator_comb_y: P256_GENERATOR_COMB_Y,
signing_comb_width: P256_SIGNING_COMB_WIDTH,
signing_comb_rows: P256_SIGNING_COMB_ROWS,
signing_generator_comb_x: &P256_SIGNING_GENERATOR_COMB_X,
signing_generator_comb_y: &P256_SIGNING_GENERATOR_COMB_Y,
};
static P384: Curve<6> = Curve {
field_modulus: &P384_FIELD_MODULUS,
scalar_modulus: &P384_ORDER_MODULUS,
b: P384_B,
generator_x: P384_GX,
generator_y: P384_GY,
generator_comb_x: P384_GENERATOR_COMB_X,
generator_comb_y: P384_GENERATOR_COMB_Y,
signing_comb_width: P384_SIGNING_COMB_WIDTH,
signing_comb_rows: P384_SIGNING_COMB_ROWS,
signing_generator_comb_x: &P384_SIGNING_GENERATOR_COMB_X,
signing_generator_comb_y: &P384_SIGNING_GENERATOR_COMB_Y,
};
#[derive(Debug, Clone, Copy, PartialEq, Eq, Hash)]
#[non_exhaustive]
pub enum EcdsaError {
MalformedDer,
UnsupportedAlgorithm,
InvalidPublicKey,
InvalidSecretKey,
InvalidSignature,
SigningFailure,
}
impl fmt::Display for EcdsaError {
fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
let message = match self {
Self::MalformedDer => "malformed ECDSA DER",
Self::UnsupportedAlgorithm => "unsupported ECDSA algorithm",
Self::InvalidPublicKey => "invalid ECDSA public key",
Self::InvalidSecretKey => "invalid ECDSA secret key",
Self::InvalidSignature => "invalid ECDSA signature",
Self::SigningFailure => "ECDSA signing failure",
};
f.write_str(message)
}
}
impl core::error::Error for EcdsaError {}
#[derive(Clone)]
pub struct EcdsaP256SecretKey([u8; Self::LENGTH]);
impl EcdsaP256SecretKey {
pub const LENGTH: usize = 32;
pub fn from_bytes(bytes: [u8; Self::LENGTH]) -> Result<Self, EcdsaError> {
let _ = parse_secret_scalar::<4, { Self::LENGTH }>(&bytes, P256_ORDER)?;
Ok(Self(bytes))
}
pub fn generate(fill: impl FnOnce(&mut [u8; Self::LENGTH])) -> Result<Self, EcdsaError> {
let mut bytes = [0u8; Self::LENGTH];
fill(&mut bytes);
Self::from_bytes(bytes)
}
#[inline]
#[must_use]
pub fn expose_secret(&self) -> SecretBytes<{ Self::LENGTH }> {
SecretBytes::new(self.0)
}
#[inline]
#[must_use]
pub const fn as_bytes(&self) -> &[u8; Self::LENGTH] {
&self.0
}
#[must_use]
pub fn public_key(&self) -> EcdsaP256PublicKey {
EcdsaP256PublicKey::from_affine(public_key_from_secret_p256(self.0))
}
#[must_use]
pub fn public_key_blinded(&self, fill: impl FnOnce(&mut [u8; 64])) -> EcdsaP256PublicKey {
let mut blind = [0u8; 64];
fill(&mut blind);
EcdsaP256PublicKey::from_affine(public_key_from_secret_p256_blinded(self.0, blind))
}
pub fn try_sign(&self, message: &[u8]) -> Result<EcdsaP256Signature, EcdsaError> {
let digest = Sha256::digest(message);
sign_digest_p256(self.0, &digest)
}
pub fn try_sign_blinded(
&self,
message: &[u8],
fill: impl FnOnce(&mut [u8; 64]),
) -> Result<EcdsaP256Signature, EcdsaError> {
let digest = Sha256::digest(message);
let mut blind = [0u8; 64];
fill(&mut blind);
sign_digest_p256_blinded(self.0, &digest, blind)
}
#[must_use]
pub fn display_secret(&self) -> crate::hex::DisplaySecret<'_> {
crate::hex::DisplaySecret(self.as_bytes())
}
}
impl PartialEq for EcdsaP256SecretKey {
fn eq(&self, other: &Self) -> bool {
ct::constant_time_eq(&self.0, &other.0)
}
}
impl Eq for EcdsaP256SecretKey {}
impl fmt::Debug for EcdsaP256SecretKey {
fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
f.write_str("EcdsaP256SecretKey(****)")
}
}
impl Drop for EcdsaP256SecretKey {
fn drop(&mut self) {
ct::zeroize(&mut self.0);
}
}
impl_ct_eq!(EcdsaP256SecretKey);
#[derive(Clone)]
pub struct EcdsaP384SecretKey([u8; Self::LENGTH]);
impl EcdsaP384SecretKey {
pub const LENGTH: usize = 48;
pub fn from_bytes(bytes: [u8; Self::LENGTH]) -> Result<Self, EcdsaError> {
let _ = parse_secret_scalar::<6, { Self::LENGTH }>(&bytes, P384_ORDER)?;
Ok(Self(bytes))
}
pub fn generate(fill: impl FnOnce(&mut [u8; Self::LENGTH])) -> Result<Self, EcdsaError> {
let mut bytes = [0u8; Self::LENGTH];
fill(&mut bytes);
Self::from_bytes(bytes)
}
#[inline]
#[must_use]
pub fn expose_secret(&self) -> SecretBytes<{ Self::LENGTH }> {
SecretBytes::new(self.0)
}
#[inline]
#[must_use]
pub const fn as_bytes(&self) -> &[u8; Self::LENGTH] {
&self.0
}
#[must_use]
pub fn public_key(&self) -> EcdsaP384PublicKey {
EcdsaP384PublicKey::from_affine(public_key_from_secret_p384(self.0))
}
#[must_use]
pub fn public_key_blinded(&self, fill: impl FnOnce(&mut [u8; 96])) -> EcdsaP384PublicKey {
let mut blind = [0u8; 96];
fill(&mut blind);
EcdsaP384PublicKey::from_affine(public_key_from_secret_p384_blinded(self.0, blind))
}
pub fn try_sign(&self, message: &[u8]) -> Result<EcdsaP384Signature, EcdsaError> {
let digest = Sha384::digest(message);
sign_digest_p384(self.0, &digest)
}
pub fn try_sign_blinded(
&self,
message: &[u8],
fill: impl FnOnce(&mut [u8; 96]),
) -> Result<EcdsaP384Signature, EcdsaError> {
let digest = Sha384::digest(message);
let mut blind = [0u8; 96];
fill(&mut blind);
sign_digest_p384_blinded(self.0, &digest, blind)
}
#[must_use]
pub fn display_secret(&self) -> crate::hex::DisplaySecret<'_> {
crate::hex::DisplaySecret(self.as_bytes())
}
}
impl PartialEq for EcdsaP384SecretKey {
fn eq(&self, other: &Self) -> bool {
ct::constant_time_eq(&self.0, &other.0)
}
}
impl Eq for EcdsaP384SecretKey {}
impl fmt::Debug for EcdsaP384SecretKey {
fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
f.write_str("EcdsaP384SecretKey(****)")
}
}
impl Drop for EcdsaP384SecretKey {
fn drop(&mut self) {
ct::zeroize(&mut self.0);
}
}
impl_ct_eq!(EcdsaP384SecretKey);
#[derive(Clone)]
pub struct EcdsaP256Keypair {
secret: EcdsaP256SecretKey,
public: EcdsaP256PublicKey,
}
impl EcdsaP256Keypair {
#[must_use]
pub fn from_secret_key(secret: EcdsaP256SecretKey) -> Self {
let public = secret.public_key();
Self { secret, public }
}
#[must_use]
pub const fn secret_key(&self) -> &EcdsaP256SecretKey {
&self.secret
}
#[must_use]
pub fn public_key(&self) -> EcdsaP256PublicKey {
self.public.clone()
}
pub fn try_sign(&self, message: &[u8]) -> Result<EcdsaP256Signature, EcdsaError> {
self.secret.try_sign(message)
}
pub fn try_sign_blinded(
&self,
message: &[u8],
fill: impl FnOnce(&mut [u8; 64]),
) -> Result<EcdsaP256Signature, EcdsaError> {
self.secret.try_sign_blinded(message, fill)
}
}
impl fmt::Debug for EcdsaP256Keypair {
fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
f.debug_struct("EcdsaP256Keypair")
.field("public", &self.public)
.finish_non_exhaustive()
}
}
#[derive(Clone)]
pub struct EcdsaP384Keypair {
secret: EcdsaP384SecretKey,
public: EcdsaP384PublicKey,
}
impl EcdsaP384Keypair {
#[must_use]
pub fn from_secret_key(secret: EcdsaP384SecretKey) -> Self {
let public = secret.public_key();
Self { secret, public }
}
#[must_use]
pub const fn secret_key(&self) -> &EcdsaP384SecretKey {
&self.secret
}
#[must_use]
pub fn public_key(&self) -> EcdsaP384PublicKey {
self.public.clone()
}
pub fn try_sign(&self, message: &[u8]) -> Result<EcdsaP384Signature, EcdsaError> {
self.secret.try_sign(message)
}
pub fn try_sign_blinded(
&self,
message: &[u8],
fill: impl FnOnce(&mut [u8; 96]),
) -> Result<EcdsaP384Signature, EcdsaError> {
self.secret.try_sign_blinded(message, fill)
}
}
impl fmt::Debug for EcdsaP384Keypair {
fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
f.debug_struct("EcdsaP384Keypair")
.field("public", &self.public)
.finish_non_exhaustive()
}
}
#[derive(Clone)]
pub struct EcdsaP256PublicKey {
point: Affine<4>,
table: [Affine<4>; COMB_TABLE_SIZE],
}
impl EcdsaP256PublicKey {
pub const SEC1_LENGTH: usize = 65;
fn from_affine(point: Affine<4>) -> Self {
Self {
point,
table: precompute_comb_table(point),
}
}
pub fn from_sec1_bytes(bytes: &[u8]) -> Result<Self, EcdsaError> {
parse_public_key(bytes, &P256).map(Self::from_affine)
}
pub fn from_spki_der(der: &[u8]) -> Result<Self, EcdsaError> {
let public_key = parse_spki_der(der, SECP256R1_OID)?;
Self::from_sec1_bytes(public_key)
}
#[must_use]
pub fn to_sec1_bytes(&self) -> [u8; Self::SEC1_LENGTH] {
encode_sec1(&self.point)
}
#[must_use = "signature verification must be checked; a dropped Result silently accepts a forged signature"]
pub fn verify(&self, message: &[u8], signature: &EcdsaP256Signature) -> Result<(), VerificationError> {
let digest = Sha256::digest(message);
verify_digest(&P256, &self.table, signature.r, signature.s, &digest)
}
}
impl PartialEq for EcdsaP256PublicKey {
fn eq(&self, other: &Self) -> bool {
self.point == other.point
}
}
impl Eq for EcdsaP256PublicKey {}
impl Hash for EcdsaP256PublicKey {
fn hash<H: Hasher>(&self, state: &mut H) {
self.to_sec1_bytes().hash(state);
}
}
impl fmt::Debug for EcdsaP256PublicKey {
fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
write!(f, "EcdsaP256PublicKey(")?;
crate::hex::fmt_hex_lower(&self.to_sec1_bytes(), f)?;
write!(f, ")")
}
}
#[derive(Clone)]
pub struct EcdsaP384PublicKey {
point: Affine<6>,
table: [Affine<6>; COMB_TABLE_SIZE],
}
impl EcdsaP384PublicKey {
pub const SEC1_LENGTH: usize = 97;
fn from_affine(point: Affine<6>) -> Self {
Self {
point,
table: precompute_comb_table(point),
}
}
pub fn from_sec1_bytes(bytes: &[u8]) -> Result<Self, EcdsaError> {
parse_public_key(bytes, &P384).map(Self::from_affine)
}
pub fn from_spki_der(der: &[u8]) -> Result<Self, EcdsaError> {
let public_key = parse_spki_der(der, SECP384R1_OID)?;
Self::from_sec1_bytes(public_key)
}
#[must_use]
pub fn to_sec1_bytes(&self) -> [u8; Self::SEC1_LENGTH] {
encode_sec1(&self.point)
}
#[must_use = "signature verification must be checked; a dropped Result silently accepts a forged signature"]
pub fn verify(&self, message: &[u8], signature: &EcdsaP384Signature) -> Result<(), VerificationError> {
let digest = Sha384::digest(message);
verify_digest(&P384, &self.table, signature.r, signature.s, &digest)
}
}
impl PartialEq for EcdsaP384PublicKey {
fn eq(&self, other: &Self) -> bool {
self.point == other.point
}
}
impl Eq for EcdsaP384PublicKey {}
impl Hash for EcdsaP384PublicKey {
fn hash<H: Hasher>(&self, state: &mut H) {
self.to_sec1_bytes().hash(state);
}
}
impl fmt::Debug for EcdsaP384PublicKey {
fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
write!(f, "EcdsaP384PublicKey(")?;
crate::hex::fmt_hex_lower(&self.to_sec1_bytes(), f)?;
write!(f, ")")
}
}
#[derive(Clone, Copy, PartialEq, Eq, Hash)]
pub struct EcdsaP256Signature {
bytes: [u8; Self::LENGTH],
r: Uint<4>,
s: Uint<4>,
}
impl EcdsaP256Signature {
pub const LENGTH: usize = 64;
pub fn from_bytes(bytes: [u8; Self::LENGTH]) -> Result<Self, EcdsaError> {
let (r, s) = parse_signature_scalars(&bytes, P256_ORDER)?;
Ok(Self { bytes, r, s })
}
pub fn from_der(der: &[u8]) -> Result<Self, EcdsaError> {
Self::from_bytes(parse_signature_der_bytes::<4, { Self::LENGTH }>(der)?)
}
fn from_scalars(r: Uint<4>, s: Uint<4>) -> Self {
let mut bytes = [0u8; Self::LENGTH];
r.write_be(&mut bytes[..32]);
s.write_be(&mut bytes[32..]);
Self { bytes, r, s }
}
#[inline]
#[must_use]
pub const fn to_bytes(self) -> [u8; Self::LENGTH] {
self.bytes
}
#[inline]
#[must_use]
pub const fn as_bytes(&self) -> &[u8; Self::LENGTH] {
&self.bytes
}
}
impl fmt::Debug for EcdsaP256Signature {
fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
write!(f, "EcdsaP256Signature(")?;
crate::hex::fmt_hex_lower(&self.bytes, f)?;
write!(f, ")")
}
}
impl_ct_eq!(EcdsaP256Signature, bytes);
#[derive(Clone, Copy, PartialEq, Eq, Hash)]
pub struct EcdsaP384Signature {
bytes: [u8; Self::LENGTH],
r: Uint<6>,
s: Uint<6>,
}
impl EcdsaP384Signature {
pub const LENGTH: usize = 96;
pub fn from_bytes(bytes: [u8; Self::LENGTH]) -> Result<Self, EcdsaError> {
let (r, s) = parse_signature_scalars(&bytes, P384_ORDER)?;
Ok(Self { bytes, r, s })
}
pub fn from_der(der: &[u8]) -> Result<Self, EcdsaError> {
Self::from_bytes(parse_signature_der_bytes::<6, { Self::LENGTH }>(der)?)
}
fn from_scalars(r: Uint<6>, s: Uint<6>) -> Self {
let mut bytes = [0u8; Self::LENGTH];
r.write_be(&mut bytes[..48]);
s.write_be(&mut bytes[48..]);
Self { bytes, r, s }
}
#[inline]
#[must_use]
pub const fn to_bytes(self) -> [u8; Self::LENGTH] {
self.bytes
}
#[inline]
#[must_use]
pub const fn as_bytes(&self) -> &[u8; Self::LENGTH] {
&self.bytes
}
}
impl fmt::Debug for EcdsaP384Signature {
fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
write!(f, "EcdsaP384Signature(")?;
crate::hex::fmt_hex_lower(&self.bytes, f)?;
write!(f, ")")
}
}
impl_ct_eq!(EcdsaP384Signature, bytes);
#[inline(always)]
fn mask_nonzero_u64(value: u64) -> u64 {
0u64.wrapping_sub((value | value.wrapping_neg()) >> 63)
}
#[inline(always)]
fn mask_zero_u64(value: u64) -> u64 {
!mask_nonzero_u64(value)
}
#[inline(always)]
fn mask_eq_usize(lhs: usize, rhs: usize) -> u64 {
mask_zero_u64((lhs ^ rhs) as u64)
}
#[inline(always)]
fn mask_not(mask: u64) -> u64 {
!mask
}
#[derive(Clone, Copy, PartialEq, Eq, Hash)]
struct Uint<const L: usize>([u64; L]);
impl<const L: usize> Uint<L> {
const ZERO: Self = Self([0u64; L]);
const ONE: Self = Self::from_u64(1);
const fn from_u64(value: u64) -> Self {
let mut limbs = [0u64; L];
if L > 0 {
limbs[0] = value;
}
Self(limbs)
}
fn from_be_slice(bytes: &[u8]) -> Result<Self, EcdsaError> {
if bytes.len() > L * 8 {
return Err(EcdsaError::InvalidSignature);
}
let mut out = Self::ZERO;
for (index, byte) in bytes.iter().rev().copied().enumerate() {
let limb = index / 8;
let shift = (index % 8) * 8;
let Some(out_limb) = out.0.get_mut(limb) else {
return Err(EcdsaError::InvalidSignature);
};
*out_limb |= u64::from(byte) << shift;
}
Ok(out)
}
fn from_be_array<const N: usize>(bytes: [u8; N]) -> Self {
Self::from_be_slice(&bytes).unwrap_or(Self::ZERO)
}
fn from_be_slice_mod(bytes: &[u8], modulus: Self) -> Self {
let mut out = Self::from_be_slice(bytes).unwrap_or(Self::ZERO);
while out.cmp(&modulus).is_ge() {
out = out.sub_raw(&modulus).0;
}
out
}
fn write_be(self, out: &mut [u8]) {
out.fill(0);
for (index, byte) in out.iter_mut().rev().enumerate() {
let limb = index / 8;
let shift = (index % 8) * 8;
if let Some(limb_value) = self.0.get(limb) {
*byte = (*limb_value >> shift) as u8;
}
}
}
fn is_zero(&self) -> bool {
self.0.iter().copied().fold(0u64, |acc, limb| acc | limb) == 0
}
fn is_in_range(&self, modulus: &Self) -> bool {
!self.is_zero() && self.cmp(modulus).is_lt()
}
fn is_in_range_ct(&self, modulus: &Self) -> bool {
let zero = self.ct_is_zero_mask();
let ge = self.ct_ge_mask(modulus);
(zero | ge) == 0
}
fn cmp(&self, other: &Self) -> core::cmp::Ordering {
for (&left, &right) in self.0.iter().zip(other.0.iter()).rev() {
if left < right {
return core::cmp::Ordering::Less;
}
if left > right {
return core::cmp::Ordering::Greater;
}
}
core::cmp::Ordering::Equal
}
fn is_one(&self) -> bool {
self.0.first().copied() == Some(1) && self.0.iter().skip(1).all(|&limb| limb == 0)
}
fn is_even(&self) -> bool {
self.0.first().copied().unwrap_or(0) & 1 == 0
}
fn bit(&self, index: usize) -> bool {
let limb = index / 64;
let shift = index % 64;
self.0.get(limb).copied().unwrap_or(0) & (1u64 << shift) != 0
}
fn shr1_with_carry(&mut self, mut carry: u64) {
for limb in self.0.iter_mut().rev() {
let next = *limb & 1;
*limb = (*limb >> 1) | (carry << 63);
carry = next;
}
}
fn add_raw(&self, other: &Self) -> (Self, u64) {
let mut out = [0u64; L];
let mut carry = 0u64;
for ((out_limb, &left), &right) in out.iter_mut().zip(self.0.iter()).zip(other.0.iter()) {
let (sum, overflow0) = left.overflowing_add(right);
let (sum, overflow1) = sum.overflowing_add(carry);
*out_limb = sum;
carry = u64::from(overflow0 | overflow1);
}
(Self(out), carry)
}
fn sub_raw(&self, other: &Self) -> (Self, u64) {
let mut out = [0u64; L];
let mut borrow = 0u64;
for ((out_limb, &left), &right) in out.iter_mut().zip(self.0.iter()).zip(other.0.iter()) {
let (diff, overflow0) = left.overflowing_sub(right);
let (diff, overflow1) = diff.overflowing_sub(borrow);
*out_limb = diff;
borrow = u64::from(overflow0 | overflow1);
}
(Self(out), borrow)
}
fn add_mod_ct(&self, other: &Self, modulus: Self) -> Self {
let (sum, carry) = self.add_raw(other);
let (reduced, borrow) = sum.sub_raw(&modulus);
Self::select(sum, reduced, mask_nonzero_u64(carry) | mask_zero_u64(borrow))
}
fn sub_mod(&self, other: &Self, modulus: Self) -> Self {
let (diff, borrow) = self.sub_raw(other);
if borrow == 0 { diff } else { diff.add_raw(&modulus).0 }
}
fn sub_mod_ct(&self, other: &Self, modulus: Self) -> Self {
let (diff, borrow) = self.sub_raw(other);
let added = diff.add_raw(&modulus).0;
Self::select(diff, added, mask_nonzero_u64(borrow))
}
fn reduce_once_ct(&self, modulus: Self) -> Self {
let (reduced, borrow) = self.sub_raw(&modulus);
Self::select(reduced, *self, mask_nonzero_u64(borrow))
}
fn ct_is_zero_mask(&self) -> u64 {
let diff = self.0.iter().copied().fold(0u64, |acc, limb| acc | limb);
mask_zero_u64(diff)
}
fn ct_lt_mask(&self, other: &Self) -> u64 {
let (_, borrow) = self.sub_raw(other);
mask_nonzero_u64(borrow)
}
fn ct_ge_mask(&self, other: &Self) -> u64 {
!self.ct_lt_mask(other)
}
fn ct_gt_mask(&self, other: &Self) -> u64 {
other.ct_lt_mask(self)
}
fn ct_bit_mask(&self, index: usize) -> u64 {
let limb = index / 64;
let shift = index % 64;
mask_nonzero_u64((self.0.get(limb).copied().unwrap_or(0) >> shift) & 1)
}
fn public_window(&self, start: usize, width: usize) -> usize {
let mut out = 0usize;
for offset in 0..width {
out |= usize::from(self.bit(start.strict_add(offset))) << offset;
}
out
}
fn select(lhs: Self, rhs: Self, mask: u64) -> Self {
let mut out = [0u64; L];
for ((dst, &left), &right) in out.iter_mut().zip(lhs.0.iter()).zip(rhs.0.iter()) {
*dst = left ^ (mask & (left ^ right));
}
Self(out)
}
fn inv_mod_vartime(&self, modulus: Self) -> Self {
if self.is_zero() {
return Self::ZERO;
}
let mut u = *self;
let mut v = modulus;
let mut x1 = Self::ONE;
let mut x2 = Self::ZERO;
while !u.is_one() && !v.is_one() {
while u.is_even() {
u.shr1_with_carry(0);
if x1.is_even() {
x1.shr1_with_carry(0);
} else {
let (sum, carry) = x1.add_raw(&modulus);
x1 = sum;
x1.shr1_with_carry(carry);
}
}
while v.is_even() {
v.shr1_with_carry(0);
if x2.is_even() {
x2.shr1_with_carry(0);
} else {
let (sum, carry) = x2.add_raw(&modulus);
x2 = sum;
x2.shr1_with_carry(carry);
}
}
if u.cmp(&v).is_ge() {
u = u.sub_raw(&v).0;
x1 = x1.sub_mod(&x2, modulus);
} else {
v = v.sub_raw(&u).0;
x2 = x2.sub_mod(&x1, modulus);
}
}
if u.is_one() { x1 } else { x2 }
}
fn inv_mod_ct(&self, modulus: &'static Modulus<L>, exponent: Self) -> Self {
montgomery_mul(self.inv_mod_ct_montgomery(modulus, exponent), Self::ONE, modulus)
}
fn inv_mod_ct_montgomery(&self, modulus: &'static Modulus<L>, exponent: Self) -> Self {
#[cfg(any(
all(target_arch = "aarch64", any(target_os = "macos", target_os = "linux")),
all(target_arch = "x86_64", target_os = "linux")
))]
{
if is_p256_order_modulus(modulus) || is_p384_order_modulus(modulus) {
let inverse = ecdsa_platform_asm::scalar_inverse(&self.0, &modulus.value.0);
return montgomery_mul(Uint(inverse), modulus.r2, modulus);
}
}
const WINDOW_BITS: usize = 4;
const WINDOW_SIZE: usize = 1 << WINDOW_BITS;
let base = montgomery_mul(*self, modulus.r2, modulus);
let mut acc = montgomery_mul(Self::ONE, modulus.r2, modulus);
let mut powers = [acc; WINDOW_SIZE];
let mut next_power = base;
let mut remaining = WINDOW_SIZE - 1;
for power in powers.iter_mut().skip(1) {
*power = next_power;
remaining -= 1;
if remaining != 0 {
next_power = montgomery_mul(next_power, base, modulus);
}
}
let mut bit = L * 64;
while bit > 0 {
bit -= WINDOW_BITS;
for _ in 0..WINDOW_BITS {
acc = montgomery_square(acc, modulus);
}
let digit = exponent.public_window(bit, WINDOW_BITS);
if digit != 0
&& let Some(power) = powers.get(digit).copied()
{
acc = montgomery_mul(acc, power, modulus);
}
}
acc
}
}
#[derive(Clone, Copy, PartialEq, Eq, Hash)]
struct Modulus<const L: usize> {
value: Uint<L>,
n0_inv: u64,
r2: Uint<L>,
}
#[derive(Clone, Copy, PartialEq, Eq, Hash)]
struct FieldElement<const L: usize> {
value: Uint<L>,
modulus: &'static Modulus<L>,
}
impl<const L: usize> FieldElement<L> {
fn from_uint(value: Uint<L>, modulus: &'static Modulus<L>) -> Self {
Self {
value: montgomery_mul(value, modulus.r2, modulus),
modulus,
}
}
const fn from_montgomery(value: Uint<L>, modulus: &'static Modulus<L>) -> Self {
Self { value, modulus }
}
fn to_uint(self) -> Uint<L> {
montgomery_mul(self.value, Uint::ONE, self.modulus)
}
fn zero(modulus: &'static Modulus<L>) -> Self {
Self::from_montgomery(Uint::ZERO, modulus)
}
fn one(modulus: &'static Modulus<L>) -> Self {
Self::from_uint(Uint::ONE, modulus)
}
#[allow(clippy::indexing_slicing)]
fn add(self, rhs: Self) -> Self {
if is_p384_field_modulus(self.modulus) {
let reduced = add_p384_field(
[
self.value.0[0],
self.value.0[1],
self.value.0[2],
self.value.0[3],
self.value.0[4],
self.value.0[5],
],
[
rhs.value.0[0],
rhs.value.0[1],
rhs.value.0[2],
rhs.value.0[3],
rhs.value.0[4],
rhs.value.0[5],
],
);
let mut out = [0u64; L];
out.copy_from_slice(&reduced.0);
return Self::from_montgomery(Uint(out), self.modulus);
}
Self::from_montgomery(self.value.add_mod_ct(&rhs.value, self.modulus.value), self.modulus)
}
#[allow(clippy::indexing_slicing)]
fn sub(self, rhs: Self) -> Self {
if is_p384_field_modulus(self.modulus) {
let reduced = sub_p384_field(
[
self.value.0[0],
self.value.0[1],
self.value.0[2],
self.value.0[3],
self.value.0[4],
self.value.0[5],
],
[
rhs.value.0[0],
rhs.value.0[1],
rhs.value.0[2],
rhs.value.0[3],
rhs.value.0[4],
rhs.value.0[5],
],
);
let mut out = [0u64; L];
out.copy_from_slice(&reduced.0);
return Self::from_montgomery(Uint(out), self.modulus);
}
Self::from_montgomery(self.value.sub_mod_ct(&rhs.value, self.modulus.value), self.modulus)
}
fn mul(self, rhs: Self) -> Self {
Self::from_montgomery(montgomery_mul(self.value, rhs.value, self.modulus), self.modulus)
}
fn square(self) -> Self {
Self::from_montgomery(montgomery_square(self.value, self.modulus), self.modulus)
}
fn double(self) -> Self {
self.add(self)
}
fn triple(self) -> Self {
self.double().add(self)
}
fn inv(self) -> Self {
Self::from_uint(self.to_uint().inv_mod_vartime(self.modulus.value), self.modulus)
}
fn inv_ct(self, exponent: Uint<L>) -> Self {
#[cfg(any(
all(target_arch = "aarch64", any(target_os = "macos", target_os = "linux")),
all(target_arch = "x86_64", target_os = "linux")
))]
{
if is_p384_field_modulus(self.modulus) {
let value = [
self.value.0[0],
self.value.0[1],
self.value.0[2],
self.value.0[3],
self.value.0[4],
self.value.0[5],
];
let inverse = ecdsa_platform_asm::p384_field_inverse(&value);
let mut out = [0u64; L];
out.copy_from_slice(&inverse);
return Self::from_montgomery(Uint(out), self.modulus);
}
}
Self::from_uint(self.to_uint().inv_mod_ct(self.modulus, exponent), self.modulus)
}
fn select(lhs: Self, rhs: Self, mask: u64) -> Self {
Self::from_montgomery(Uint::select(lhs.value, rhs.value, mask), lhs.modulus)
}
}
#[derive(Clone, Copy, PartialEq, Eq, Hash)]
struct Affine<const L: usize> {
x: FieldElement<L>,
y: FieldElement<L>,
}
#[derive(Clone, Copy)]
struct Jacobian<const L: usize> {
x: FieldElement<L>,
y: FieldElement<L>,
z: FieldElement<L>,
infinity: bool,
}
impl<const L: usize> Jacobian<L> {
fn infinity(modulus: &'static Modulus<L>) -> Self {
Self {
x: FieldElement::zero(modulus),
y: FieldElement::one(modulus),
z: FieldElement::zero(modulus),
infinity: true,
}
}
fn from_affine(point: Affine<L>) -> Self {
Self {
x: point.x,
y: point.y,
z: FieldElement::one(point.x.modulus),
infinity: false,
}
}
fn select(lhs: Self, rhs: Self, mask: u64) -> Self {
let lhs_infinity = 0u64.wrapping_sub(u64::from(lhs.infinity));
let rhs_infinity = 0u64.wrapping_sub(u64::from(rhs.infinity));
let selected_infinity = lhs_infinity ^ (mask & (lhs_infinity ^ rhs_infinity));
Self {
x: FieldElement::select(lhs.x, rhs.x, mask),
y: FieldElement::select(lhs.y, rhs.y, mask),
z: FieldElement::select(lhs.z, rhs.z, mask),
infinity: selected_infinity != 0,
}
}
fn infinity_mask(&self) -> u64 {
0u64.wrapping_sub(u64::from(self.infinity))
}
fn double(self) -> Self {
if self.infinity || self.y.value.is_zero() {
return Self::infinity(self.x.modulus);
}
let delta = self.z.square();
let gamma = self.y.square();
let beta = self.x.mul(gamma);
let alpha = self.x.sub(delta).mul(self.x.add(delta)).triple();
let x3 = alpha.square().sub(beta.double().double().double());
let z3 = self.y.add(self.z).square().sub(gamma).sub(delta);
let y3 = alpha
.mul(beta.double().double().sub(x3))
.sub(gamma.square().double().double().double());
Self {
x: x3,
y: y3,
z: z3,
infinity: false,
}
}
fn double_ct(self) -> Self {
let delta = self.z.square();
let gamma = self.y.square();
let beta = self.x.mul(gamma);
let alpha = self.x.sub(delta).mul(self.x.add(delta)).triple();
let x3 = alpha.square().sub(beta.double().double().double());
let z3 = self.y.add(self.z).square().sub(gamma).sub(delta);
let y3 = alpha
.mul(beta.double().double().sub(x3))
.sub(gamma.square().double().double().double());
let raw = Self {
x: x3,
y: y3,
z: z3,
infinity: false,
};
Self::select(
raw,
Self::infinity(self.x.modulus),
self.infinity_mask() | self.y.value.ct_is_zero_mask(),
)
}
fn add_mixed(self, rhs: Affine<L>) -> Self {
if self.infinity {
return Self::from_affine(rhs);
}
let z1z1 = self.z.square();
let u2 = rhs.x.mul(z1z1);
let s2 = rhs.y.mul(self.z).mul(z1z1);
let h = u2.sub(self.x);
let r = s2.sub(self.y).double();
if h.value.is_zero() {
return if r.value.is_zero() {
self.double()
} else {
Self::infinity(self.x.modulus)
};
}
let hh = h.square();
let i = hh.double().double();
let j = h.mul(i);
let v = self.x.mul(i);
let x3 = r.square().sub(j).sub(v.double());
let y3 = r.mul(v.sub(x3)).sub(self.y.mul(j).double());
let z3 = self.z.add(h).square().sub(z1z1).sub(hh);
Self {
x: x3,
y: y3,
z: z3,
infinity: false,
}
}
fn add_mixed_ct(self, rhs: Affine<L>, rhs_infinity_mask: u64) -> Self {
let z1z1 = self.z.square();
let u2 = rhs.x.mul(z1z1);
let s2 = rhs.y.mul(self.z).mul(z1z1);
let h = u2.sub(self.x);
let r = s2.sub(self.y).double();
let hh = h.square();
let i = hh.double().double();
let j = h.mul(i);
let v = self.x.mul(i);
let x3 = r.square().sub(j).sub(v.double());
let y3 = r.mul(v.sub(x3)).sub(self.y.mul(j).double());
let z3 = self.z.add(h).square().sub(z1z1).sub(hh);
let raw = Self {
x: x3,
y: y3,
z: z3,
infinity: false,
};
let h_zero = h.value.ct_is_zero_mask();
let r_zero = r.value.ct_is_zero_mask();
let double = self.double_ct();
let infinity = Self::infinity(self.x.modulus);
let rhs_jacobian = Self::from_affine(rhs);
let exceptional = h_zero & r_zero;
let opposite = h_zero & mask_not(r_zero);
let with_exception = Self::select(Self::select(raw, double, exceptional), infinity, opposite);
let with_self_infinity = Self::select(with_exception, rhs_jacobian, self.infinity_mask());
Self::select(with_self_infinity, self, rhs_infinity_mask)
}
fn randomize_z(self, z: FieldElement<L>) -> Self {
let z2 = z.square();
let z3 = z2.mul(z);
Self {
x: self.x.mul(z2),
y: self.y.mul(z3),
z: self.z.mul(z),
infinity: self.infinity,
}
}
fn to_affine_ct(self, exponent: Uint<L>) -> Affine<L> {
let inv_z = self.z.inv_ct(exponent);
let z2 = inv_z.square();
let z3 = z2.mul(inv_z);
Affine {
x: self.x.mul(z2),
y: self.y.mul(z3),
}
}
}
#[derive(Clone, Copy)]
struct Curve<const L: usize> {
field_modulus: &'static Modulus<L>,
scalar_modulus: &'static Modulus<L>,
b: Uint<L>,
generator_x: Uint<L>,
generator_y: Uint<L>,
generator_comb_x: [Uint<L>; COMB_TABLE_SIZE],
generator_comb_y: [Uint<L>; COMB_TABLE_SIZE],
signing_comb_width: usize,
signing_comb_rows: usize,
signing_generator_comb_x: &'static [Uint<L>],
signing_generator_comb_y: &'static [Uint<L>],
}
impl<const L: usize> Curve<L> {
fn generator(&self) -> Affine<L> {
Affine {
x: FieldElement::from_uint(self.generator_x, self.field_modulus),
y: FieldElement::from_uint(self.generator_y, self.field_modulus),
}
}
fn generator_comb_table(&self) -> [Affine<L>; COMB_TABLE_SIZE] {
let mut table = [self.generator(); COMB_TABLE_SIZE];
for ((point, &x), &y) in table
.iter_mut()
.zip(self.generator_comb_x.iter())
.zip(self.generator_comb_y.iter())
{
*point = Affine {
x: FieldElement::from_montgomery(x, self.field_modulus),
y: FieldElement::from_montgomery(y, self.field_modulus),
};
}
table
}
}
fn verify_digest<const L: usize>(
curve: &Curve<L>,
public_table: &[Affine<L>; COMB_TABLE_SIZE],
r: Uint<L>,
s: Uint<L>,
digest: &[u8],
) -> Result<(), VerificationError> {
if !r.is_in_range(&curve.scalar_modulus.value) || !s.is_in_range(&curve.scalar_modulus.value) {
return Err(VerificationError::new());
}
let z = Uint::from_be_slice_mod(digest, curve.scalar_modulus.value);
let w = s.inv_mod_vartime(curve.scalar_modulus.value);
let u1 = mul_mod_montgomery(z, w, curve.scalar_modulus);
let u2 = mul_mod_montgomery(r, w, curve.scalar_modulus);
let point = scalar_mul_two(curve, u1, u2, public_table);
if projective_x_matches_scalar(point, r, curve) {
Ok(())
} else {
Err(VerificationError::new())
}
}
fn sign_digest_p256(secret: [u8; 32], digest: &[u8; 32]) -> Result<EcdsaP256Signature, EcdsaError> {
let secret_scalar = Uint::from_be_array(secret);
let mut wide = [0u8; 64];
hmac_expand_p256(&secret, digest, &mut wide);
let nonce = reduce_wide_order_nonzero(&wide, &P256_ORDER_MODULUS);
let (r, s) = sign_digest_with_nonce(
&P256,
secret_scalar,
nonce,
digest,
P256_ORDER_MINUS_TWO,
P256_FIELD_MINUS_TWO,
P256_ORDER_HALF,
)?;
assemble_p256_signature(r, s)
}
fn sign_digest_p256_blinded(
secret: [u8; 32],
digest: &[u8; 32],
blind: [u8; 64],
) -> Result<EcdsaP256Signature, EcdsaError> {
let secret_scalar = Uint::from_be_array(secret);
let mut wide = [0u8; 64];
hmac_expand_p256(&secret, digest, &mut wide);
let nonce = reduce_wide_order_nonzero(&wide, &P256_ORDER_MODULUS);
let nonce_blind = reduce_wide_order_nonzero(&blind, &P256_ORDER_MODULUS);
let (r, s) = sign_digest_with_nonce_blinded(
&P256,
secret_scalar,
nonce,
nonce_blind,
digest,
P256_ORDER_MINUS_TWO,
P256_FIELD_MINUS_TWO,
P256_ORDER_HALF,
)?;
assemble_p256_signature(r, s)
}
fn sign_digest_p384(secret: [u8; 48], digest: &[u8; 48]) -> Result<EcdsaP384Signature, EcdsaError> {
let secret_scalar = Uint::from_be_array(secret);
let mut wide = [0u8; 96];
hmac_expand_p384(&secret, digest, &mut wide);
let nonce = reduce_wide_order_nonzero(&wide, &P384_ORDER_MODULUS);
let (r, s) = sign_digest_with_nonce(
&P384,
secret_scalar,
nonce,
digest,
P384_ORDER_MINUS_TWO,
P384_FIELD_MINUS_TWO,
P384_ORDER_HALF,
)?;
assemble_p384_signature(r, s)
}
fn sign_digest_p384_blinded(
secret: [u8; 48],
digest: &[u8; 48],
blind: [u8; 96],
) -> Result<EcdsaP384Signature, EcdsaError> {
let secret_scalar = Uint::from_be_array(secret);
let mut wide = [0u8; 96];
hmac_expand_p384(&secret, digest, &mut wide);
let nonce = reduce_wide_order_nonzero(&wide, &P384_ORDER_MODULUS);
let nonce_blind = reduce_wide_order_nonzero(&blind, &P384_ORDER_MODULUS);
let (r, s) = sign_digest_with_nonce_blinded(
&P384,
secret_scalar,
nonce,
nonce_blind,
digest,
P384_ORDER_MINUS_TWO,
P384_FIELD_MINUS_TWO,
P384_ORDER_HALF,
)?;
assemble_p384_signature(r, s)
}
fn hmac_expand_p256(secret: &[u8; 32], digest: &[u8; 32], out: &mut [u8; 64]) {
for (block_index, block) in out.chunks_exact_mut(HmacSha256::TAG_SIZE).enumerate() {
let mut mac = HmacSha256::new(secret);
mac.update(P256_NONCE_DOMAIN);
mac.update(&[block_index as u8]);
mac.update(digest);
block.copy_from_slice(&mac.finalize());
}
}
fn hmac_expand_p256_public_blind(secret: &[u8; 32], out: &mut [u8; 64]) {
for (block_index, block) in out.chunks_exact_mut(HmacSha256::TAG_SIZE).enumerate() {
let mut mac = HmacSha256::new(secret);
mac.update(P256_PUBKEY_BLIND_DOMAIN);
mac.update(&[block_index as u8]);
block.copy_from_slice(&mac.finalize());
}
}
fn hmac_expand_p384(secret: &[u8; 48], digest: &[u8; 48], out: &mut [u8; 96]) {
for (block_index, block) in out.chunks_exact_mut(HmacSha384::TAG_SIZE).enumerate() {
let mut mac = HmacSha384::new(secret);
mac.update(P384_NONCE_DOMAIN);
mac.update(&[block_index as u8]);
mac.update(digest);
block.copy_from_slice(&mac.finalize());
}
}
fn hmac_expand_p384_public_blind(secret: &[u8; 48], out: &mut [u8; 96]) {
for (block_index, block) in out.chunks_exact_mut(HmacSha384::TAG_SIZE).enumerate() {
let mut mac = HmacSha384::new(secret);
mac.update(P384_PUBKEY_BLIND_DOMAIN);
mac.update(&[block_index as u8]);
block.copy_from_slice(&mac.finalize());
}
}
fn sign_digest_with_nonce<const L: usize>(
curve: &Curve<L>,
secret_scalar: Uint<L>,
nonce: Uint<L>,
digest: &[u8],
scalar_inverse_exponent: Uint<L>,
field_inverse_exponent: Uint<L>,
half_order: Uint<L>,
) -> Result<(Uint<L>, Uint<L>), EcdsaError> {
if let Some(result) =
sign_digest_with_nonce_backend(curve, secret_scalar, nonce, digest, scalar_inverse_exponent, half_order)
{
return result;
}
let r_point = scalar_mul_basepoint_affine(curve, nonce, field_inverse_exponent);
sign_digest_with_r_point(
curve,
secret_scalar,
nonce,
digest,
scalar_inverse_exponent,
half_order,
r_point,
)
}
#[cfg(any(
all(target_arch = "aarch64", any(target_os = "macos", target_os = "linux")),
all(target_arch = "x86_64", target_os = "linux")
))]
fn sign_digest_with_nonce_backend<const L: usize>(
curve: &Curve<L>,
secret_scalar: Uint<L>,
nonce: Uint<L>,
digest: &[u8],
scalar_inverse_exponent: Uint<L>,
half_order: Uint<L>,
) -> Option<Result<(Uint<L>, Uint<L>), EcdsaError>> {
if is_p256_curve(curve) {
let mut scalar_words = [0u64; 4];
scalar_words.copy_from_slice(&nonce.0[..4]);
let out = ecdsa_platform_asm::p256_scalarmulbase_generator(&scalar_words);
let mut r_words = [0u64; L];
r_words.copy_from_slice(&out[..4]);
let r = Uint(r_words).reduce_once_ct(curve.scalar_modulus.value);
return Some(sign_digest_with_r(
curve,
secret_scalar,
nonce,
digest,
scalar_inverse_exponent,
half_order,
r,
));
}
None
}
#[cfg(not(any(
all(target_arch = "aarch64", any(target_os = "macos", target_os = "linux")),
all(target_arch = "x86_64", target_os = "linux")
)))]
fn sign_digest_with_nonce_backend<const L: usize>(
_curve: &Curve<L>,
_secret_scalar: Uint<L>,
_nonce: Uint<L>,
_digest: &[u8],
_scalar_inverse_exponent: Uint<L>,
_half_order: Uint<L>,
) -> Option<Result<(Uint<L>, Uint<L>), EcdsaError>> {
None
}
fn scalar_mul_basepoint_affine<const L: usize>(
curve: &Curve<L>,
scalar: Uint<L>,
field_inverse_exponent: Uint<L>,
) -> Affine<L> {
scalar_mul_basepoint_affine_backend(curve, scalar)
.or_else(|| scalar_mul_basepoint_backend(curve, scalar).map(|point| point.to_affine_ct(field_inverse_exponent)))
.unwrap_or_else(|| scalar_mul_basepoint_comb_ct(curve, scalar).to_affine_ct(field_inverse_exponent))
}
#[allow(clippy::too_many_arguments)]
fn sign_digest_with_nonce_blinded<const L: usize>(
curve: &Curve<L>,
secret_scalar: Uint<L>,
nonce: Uint<L>,
nonce_blind: Uint<L>,
digest: &[u8],
scalar_inverse_exponent: Uint<L>,
field_inverse_exponent: Uint<L>,
half_order: Uint<L>,
) -> Result<(Uint<L>, Uint<L>), EcdsaError> {
let r_point = scalar_mul_basepoint_blinded(curve, nonce, nonce_blind, field_inverse_exponent);
sign_digest_with_r_point(
curve,
secret_scalar,
nonce,
digest,
scalar_inverse_exponent,
half_order,
r_point,
)
}
fn sign_digest_with_r_point<const L: usize>(
curve: &Curve<L>,
secret_scalar: Uint<L>,
nonce: Uint<L>,
digest: &[u8],
scalar_inverse_exponent: Uint<L>,
half_order: Uint<L>,
r_point: Affine<L>,
) -> Result<(Uint<L>, Uint<L>), EcdsaError> {
let r = r_point.x.to_uint().reduce_once_ct(curve.scalar_modulus.value);
sign_digest_with_r(
curve,
secret_scalar,
nonce,
digest,
scalar_inverse_exponent,
half_order,
r,
)
}
fn sign_digest_with_r<const L: usize>(
curve: &Curve<L>,
secret_scalar: Uint<L>,
nonce: Uint<L>,
digest: &[u8],
scalar_inverse_exponent: Uint<L>,
half_order: Uint<L>,
r: Uint<L>,
) -> Result<(Uint<L>, Uint<L>), EcdsaError> {
let z = reduce_digest_for_scalar(digest, curve.scalar_modulus.value);
let rd = mul_mod_montgomery_ct(r, secret_scalar, curve.scalar_modulus);
let sum = z.add_mod_ct(&rd, curve.scalar_modulus.value);
let nonce_inverse = nonce.inv_mod_ct_montgomery(curve.scalar_modulus, scalar_inverse_exponent);
let sum = montgomery_mul(sum, curve.scalar_modulus.r2, curve.scalar_modulus);
let s = montgomery_mul(nonce_inverse, sum, curve.scalar_modulus);
let s = montgomery_mul(s, Uint::ONE, curve.scalar_modulus);
let failure_mask = r.ct_is_zero_mask() | s.ct_is_zero_mask();
if failure_mask != 0 {
return Err(EcdsaError::SigningFailure);
}
Ok((r, normalize_low_s(s, curve.scalar_modulus.value, half_order)))
}
fn public_key_from_secret_p256(secret: [u8; 32]) -> Affine<4> {
let mut wide = [0u8; 64];
hmac_expand_p256_public_blind(&secret, &mut wide);
let mask = reduce_wide_order_nonzero(&wide, &P256_ORDER_MODULUS);
public_key_from_secret_blinded(&P256, Uint::from_be_array(secret), mask, P256_FIELD_MINUS_TWO)
}
fn public_key_from_secret_p256_blinded(secret: [u8; 32], blind: [u8; 64]) -> Affine<4> {
let mask = reduce_wide_order_nonzero(&blind, &P256_ORDER_MODULUS);
public_key_from_secret_blinded(&P256, Uint::from_be_array(secret), mask, P256_FIELD_MINUS_TWO)
}
fn public_key_from_secret_p384(secret: [u8; 48]) -> Affine<6> {
let mut wide = [0u8; 96];
hmac_expand_p384_public_blind(&secret, &mut wide);
let mask = reduce_wide_order_nonzero(&wide, &P384_ORDER_MODULUS);
public_key_from_secret_blinded(&P384, Uint::from_be_array(secret), mask, P384_FIELD_MINUS_TWO)
}
fn public_key_from_secret_p384_blinded(secret: [u8; 48], blind: [u8; 96]) -> Affine<6> {
let mask = reduce_wide_order_nonzero(&blind, &P384_ORDER_MODULUS);
public_key_from_secret_blinded(&P384, Uint::from_be_array(secret), mask, P384_FIELD_MINUS_TWO)
}
fn public_key_from_secret_blinded<const L: usize>(
curve: &Curve<L>,
secret_scalar: Uint<L>,
mask: Uint<L>,
field_inverse_exponent: Uint<L>,
) -> Affine<L> {
scalar_mul_basepoint_blinded(curve, secret_scalar, mask, field_inverse_exponent)
}
fn reduce_digest_for_scalar<const L: usize>(digest: &[u8], modulus: Uint<L>) -> Uint<L> {
Uint::from_be_slice_mod(digest, modulus)
}
fn reduce_wide_order_nonzero<const L: usize, const N: usize>(bytes: &[u8; N], modulus: &'static Modulus<L>) -> Uint<L> {
#[cfg(any(
all(target_arch = "aarch64", any(target_os = "macos", target_os = "linux")),
all(target_arch = "x86_64", target_os = "linux")
))]
{
if N == 64 && is_p256_order_modulus(modulus) {
let mut fixed = [0u8; 64];
fixed.copy_from_slice(bytes);
let reduced_words = ecdsa_platform_asm::p256_reduce_order_64(&fixed);
let mut out = [0u64; L];
out.copy_from_slice(&reduced_words);
let reduced = Uint(out);
return Uint::select(reduced, Uint::ONE, reduced.ct_is_zero_mask());
}
if N == 96 && is_p384_order_modulus(modulus) {
let mut fixed = [0u8; 96];
fixed.copy_from_slice(bytes);
let reduced_words = ecdsa_platform_asm::p384_reduce_order_96(&fixed);
let mut out = [0u64; L];
out.copy_from_slice(&reduced_words);
let reduced = Uint(out);
return Uint::select(reduced, Uint::ONE, reduced.ct_is_zero_mask());
}
}
let mut radix = Uint::ZERO;
if let Some(limb) = radix.0.get_mut(1) {
*limb = 1;
}
let mut acc = Uint::ZERO;
for chunk in bytes.chunks_exact(8) {
acc = mul_mod_montgomery_ct(acc, radix, modulus);
let word = chunk
.iter()
.copied()
.fold(0u64, |value, byte| (value << 8) | u64::from(byte));
acc = acc.add_mod_ct(&Uint::from_u64(word), modulus.value);
}
Uint::select(acc, Uint::ONE, acc.ct_is_zero_mask())
}
fn is_p256_order_modulus<const L: usize>(modulus: &'static Modulus<L>) -> bool {
L == 4 && core::ptr::from_ref(modulus).cast::<()>() == core::ptr::from_ref(&P256_ORDER_MODULUS).cast::<()>()
}
fn is_p384_order_modulus<const L: usize>(modulus: &'static Modulus<L>) -> bool {
L == 6 && core::ptr::from_ref(modulus).cast::<()>() == core::ptr::from_ref(&P384_ORDER_MODULUS).cast::<()>()
}
fn normalize_low_s<const L: usize>(s: Uint<L>, order: Uint<L>, half_order: Uint<L>) -> Uint<L> {
let low = order.sub_mod_ct(&s, order);
Uint::select(s, low, s.ct_gt_mask(&half_order))
}
fn assemble_p256_signature(r: Uint<4>, s: Uint<4>) -> Result<EcdsaP256Signature, EcdsaError> {
Ok(EcdsaP256Signature::from_scalars(r, s))
}
fn assemble_p384_signature(r: Uint<6>, s: Uint<6>) -> Result<EcdsaP384Signature, EcdsaError> {
Ok(EcdsaP384Signature::from_scalars(r, s))
}
fn parse_secret_scalar<const LIMBS: usize, const BYTES: usize>(
bytes: &[u8; BYTES],
scalar_modulus: Uint<LIMBS>,
) -> Result<Uint<LIMBS>, EcdsaError> {
let scalar = Uint::from_be_slice(bytes).map_err(|_| EcdsaError::InvalidSecretKey)?;
if scalar.is_in_range_ct(&scalar_modulus) {
Ok(scalar)
} else {
Err(EcdsaError::InvalidSecretKey)
}
}
fn scalar_mul_basepoint_blinded<const L: usize>(
curve: &Curve<L>,
scalar: Uint<L>,
blind: Uint<L>,
field_inverse_exponent: Uint<L>,
) -> Affine<L> {
let z = FieldElement::from_uint(blind, curve.field_modulus);
scalar_mul_basepoint_backend(curve, scalar)
.unwrap_or_else(|| scalar_mul_basepoint_comb_ct(curve, scalar))
.randomize_z(z)
.to_affine_ct(field_inverse_exponent)
}
#[cfg(any(
all(target_arch = "aarch64", any(target_os = "macos", target_os = "linux")),
all(target_arch = "x86_64", target_os = "linux")
))]
fn scalar_mul_basepoint_backend<const L: usize>(curve: &Curve<L>, scalar: Uint<L>) -> Option<Jacobian<L>> {
if is_p384_curve(curve) {
return Some(p384_scalar_mul_basepoint_platform(curve, scalar));
}
scalar_mul_basepoint_affine_backend(curve, scalar).map(Jacobian::from_affine)
}
#[cfg(all(target_arch = "x86_64", target_os = "linux"))]
fn p384_scalar_mul_basepoint_platform<const L: usize>(curve: &Curve<L>, scalar: Uint<L>) -> Jacobian<L> {
let mut scalar_words = [0u64; 6];
scalar_words.copy_from_slice(&scalar.0[..6]);
let generator_words = p384_jacobian_to_words(Jacobian::from_affine(P384.generator()));
let words = ecdsa_platform_asm::p384_point_scalarmul(&scalar_words, &generator_words);
jacobian_from_p384_words(curve.field_modulus, &words)
}
#[cfg(all(target_arch = "aarch64", any(target_os = "macos", target_os = "linux")))]
fn p384_scalar_mul_basepoint_platform<const L: usize>(curve: &Curve<L>, scalar: Uint<L>) -> Jacobian<L> {
let mut scalar_words = [0u64; 6];
scalar_words.copy_from_slice(&scalar.0[..6]);
let point = p384_scalar_mul_basepoint_comb_backend(Uint(scalar_words));
let words = p384_jacobian_to_words(point);
jacobian_from_p384_words(curve.field_modulus, &words)
}
#[cfg(not(any(
all(target_arch = "aarch64", any(target_os = "macos", target_os = "linux")),
all(target_arch = "x86_64", target_os = "linux")
)))]
fn scalar_mul_basepoint_backend<const L: usize>(_curve: &Curve<L>, _scalar: Uint<L>) -> Option<Jacobian<L>> {
None
}
#[cfg(any(
all(target_arch = "aarch64", any(target_os = "macos", target_os = "linux")),
all(target_arch = "x86_64", target_os = "linux")
))]
fn scalar_mul_basepoint_affine_backend<const L: usize>(curve: &Curve<L>, scalar: Uint<L>) -> Option<Affine<L>> {
if is_p256_curve(curve) {
let mut scalar_words = [0u64; 4];
scalar_words.copy_from_slice(&scalar.0[..4]);
let out = ecdsa_platform_asm::p256_scalarmulbase_generator(&scalar_words);
return Some(affine_from_words(curve.field_modulus, &out));
}
None
}
#[cfg(not(any(
all(target_arch = "aarch64", any(target_os = "macos", target_os = "linux")),
all(target_arch = "x86_64", target_os = "linux")
)))]
fn scalar_mul_basepoint_affine_backend<const L: usize>(_curve: &Curve<L>, _scalar: Uint<L>) -> Option<Affine<L>> {
None
}
#[cfg(any(
all(target_arch = "aarch64", any(target_os = "macos", target_os = "linux")),
all(target_arch = "x86_64", target_os = "linux")
))]
fn is_p256_curve<const L: usize>(curve: &Curve<L>) -> bool {
L == 4
&& core::ptr::from_ref(curve.field_modulus).cast::<()>() == core::ptr::from_ref(&P256_FIELD_MODULUS).cast::<()>()
}
#[cfg(any(
all(target_arch = "aarch64", any(target_os = "macos", target_os = "linux")),
all(target_arch = "x86_64", target_os = "linux")
))]
fn is_p384_curve<const L: usize>(curve: &Curve<L>) -> bool {
L == 6
&& core::ptr::from_ref(curve.field_modulus).cast::<()>() == core::ptr::from_ref(&P384_FIELD_MODULUS).cast::<()>()
}
#[cfg(any(
all(target_arch = "aarch64", any(target_os = "macos", target_os = "linux")),
all(target_arch = "x86_64", target_os = "linux")
))]
#[allow(clippy::indexing_slicing)]
fn affine_from_words<const L: usize>(modulus: &'static Modulus<L>, words: &[u64]) -> Affine<L> {
let mut x = [0u64; L];
let mut y = [0u64; L];
x.copy_from_slice(&words[..L]);
y.copy_from_slice(&words[L..L * 2]);
let x = Uint(x);
let y = Uint(y);
Affine {
x: FieldElement::from_uint(x, modulus),
y: FieldElement::from_uint(y, modulus),
}
}
#[cfg(all(target_arch = "aarch64", any(target_os = "macos", target_os = "linux")))]
fn p384_scalar_mul_basepoint_comb_backend(scalar: Uint<6>) -> Jacobian<6> {
let curve = &P384;
let rows = curve.signing_comb_rows;
let mut acc = Jacobian::infinity(curve.field_modulus);
for row in (0..rows).rev() {
let doubled_words = ecdsa_platform_asm::p384_point_double(&p384_jacobian_to_words(acc));
let doubled = p384_jacobian_from_words(&doubled_words);
acc = Jacobian::select(
doubled,
Jacobian::infinity(curve.field_modulus),
acc.infinity_mask() | acc.y.value.ct_is_zero_mask(),
);
let digit = signing_comb_digit_ct(scalar, row, rows, curve.signing_comb_width);
let selected = select_signing_generator_affine_ct(curve, digit);
let added_words =
ecdsa_platform_asm::p384_point_mixadd(&p384_jacobian_to_words(acc), &p384_affine_to_words(selected));
let added = p384_jacobian_from_words(&added_words);
let with_self_infinity = Jacobian::select(added, Jacobian::from_affine(selected), acc.infinity_mask());
acc = Jacobian::select(with_self_infinity, acc, mask_eq_usize(digit, 0));
}
acc
}
#[cfg(any(
all(target_arch = "aarch64", any(target_os = "macos", target_os = "linux")),
all(target_arch = "x86_64", target_os = "linux")
))]
fn p384_jacobian_to_words(point: Jacobian<6>) -> [u64; 18] {
let mut out = [0u64; 18];
out[..6].copy_from_slice(&point.x.value.0);
out[6..12].copy_from_slice(&point.y.value.0);
out[12..18].copy_from_slice(&point.z.value.0);
out
}
#[cfg(all(target_arch = "aarch64", any(target_os = "macos", target_os = "linux")))]
fn p384_affine_to_words(point: Affine<6>) -> [u64; 12] {
let mut out = [0u64; 12];
out[..6].copy_from_slice(&point.x.value.0);
out[6..12].copy_from_slice(&point.y.value.0);
out
}
#[cfg(all(target_arch = "aarch64", any(target_os = "macos", target_os = "linux")))]
fn p384_jacobian_from_words(words: &[u64; 18]) -> Jacobian<6> {
Jacobian {
x: FieldElement::from_montgomery(
Uint([words[0], words[1], words[2], words[3], words[4], words[5]]),
&P384_FIELD_MODULUS,
),
y: FieldElement::from_montgomery(
Uint([words[6], words[7], words[8], words[9], words[10], words[11]]),
&P384_FIELD_MODULUS,
),
z: FieldElement::from_montgomery(
Uint([words[12], words[13], words[14], words[15], words[16], words[17]]),
&P384_FIELD_MODULUS,
),
infinity: false,
}
}
#[cfg(any(
all(target_arch = "aarch64", any(target_os = "macos", target_os = "linux")),
all(target_arch = "x86_64", target_os = "linux")
))]
#[allow(clippy::indexing_slicing)]
fn jacobian_from_p384_words<const L: usize>(modulus: &'static Modulus<L>, words: &[u64; 18]) -> Jacobian<L> {
let mut x = [0u64; L];
let mut y = [0u64; L];
let mut z = [0u64; L];
x.copy_from_slice(&words[..L]);
y.copy_from_slice(&words[6..6 + L]);
z.copy_from_slice(&words[12..12 + L]);
Jacobian {
x: FieldElement::from_montgomery(Uint(x), modulus),
y: FieldElement::from_montgomery(Uint(y), modulus),
z: FieldElement::from_montgomery(Uint(z), modulus),
infinity: false,
}
}
#[allow(clippy::indexing_slicing)]
fn scalar_mul_basepoint_comb_ct<const L: usize>(curve: &Curve<L>, scalar: Uint<L>) -> Jacobian<L> {
let rows = curve.signing_comb_rows;
let mut acc = Jacobian::infinity(curve.field_modulus);
for row in (0..rows).rev() {
acc = acc.double_ct();
let digit = signing_comb_digit_ct(scalar, row, rows, curve.signing_comb_width);
let selected = select_signing_generator_affine_ct(curve, digit);
acc = acc.add_mixed_ct(selected, mask_eq_usize(digit, 0));
}
acc
}
fn signing_comb_digit_ct<const L: usize>(scalar: Uint<L>, row: usize, rows: usize, width: usize) -> usize {
let mut digit = 0usize;
for column in 0usize..width {
let bit = row.strict_add(column.strict_mul(rows));
digit |= ((scalar.ct_bit_mask(bit) & 1) as usize) << column;
}
digit
}
#[allow(clippy::indexing_slicing)]
fn select_signing_generator_affine_ct<const L: usize>(curve: &Curve<L>, digit: usize) -> Affine<L> {
let mut x = curve.signing_generator_comb_x[0];
let mut y = curve.signing_generator_comb_y[0];
for (index, (&candidate_x, &candidate_y)) in curve
.signing_generator_comb_x
.iter()
.zip(curve.signing_generator_comb_y.iter())
.enumerate()
{
let mask = mask_eq_usize(digit, index);
x = Uint::select(x, candidate_x, mask);
y = Uint::select(y, candidate_y, mask);
}
Affine {
x: FieldElement::from_montgomery(x, curve.field_modulus),
y: FieldElement::from_montgomery(y, curve.field_modulus),
}
}
#[cfg(all(feature = "diag", feature = "ecdsa-p256"))]
pub fn diag_ecdsa_p256_select_signing_generator_affine_limb_digest(digit: u8) -> [u64; 8] {
let selected = select_signing_generator_affine_ct(&P256, usize::from(digit));
let mut out = [0u64; 8];
out[..4].copy_from_slice(&selected.x.value.0);
out[4..].copy_from_slice(&selected.y.value.0);
out
}
#[cfg(all(feature = "diag", feature = "ecdsa-p256"))]
pub fn diag_ecdsa_p256_nonce_reduce_limb_digest(secret: [u8; 32], message: &[u8]) -> [u64; 4] {
let digest = Sha256::digest(message);
let mut wide = [0u8; 64];
hmac_expand_p256(&secret, &digest, &mut wide);
reduce_wide_order_nonzero(&wide, &P256_ORDER_MODULUS).0
}
#[cfg(all(feature = "diag", feature = "ecdsa-p256"))]
pub fn diag_ecdsa_p256_reduce_wide_order_limb_digest(wide: [u8; 64]) -> [u64; 4] {
reduce_wide_order_nonzero(&wide, &P256_ORDER_MODULUS).0
}
#[cfg(all(feature = "diag", feature = "ecdsa-p256"))]
pub fn diag_ecdsa_p256_basepoint_blinded_limb_digest(secret: [u8; 32], blind: [u8; 64], message: &[u8]) -> [u64; 8] {
let digest = Sha256::digest(message);
let mut wide = [0u8; 64];
hmac_expand_p256(&secret, &digest, &mut wide);
let nonce = reduce_wide_order_nonzero(&wide, &P256_ORDER_MODULUS);
let nonce_blind = reduce_wide_order_nonzero(&blind, &P256_ORDER_MODULUS);
let point = scalar_mul_basepoint_blinded(&P256, nonce, nonce_blind, P256_FIELD_MINUS_TWO);
let mut out = [0u64; 8];
out[..4].copy_from_slice(&point.x.value.0);
out[4..].copy_from_slice(&point.y.value.0);
out
}
#[cfg(all(feature = "diag", feature = "ecdsa-p256"))]
pub fn diag_ecdsa_p256_scalar_finish_limb_digest(secret: [u8; 32], nonce_wide: [u8; 64], message: &[u8]) -> [u64; 8] {
let digest = Sha256::digest(message);
let secret_scalar = Uint::from_be_array(secret);
let nonce = reduce_wide_order_nonzero(&nonce_wide, &P256_ORDER_MODULUS);
let r = P256_GX.reduce_once_ct(P256_ORDER_MODULUS.value);
let (r, s) = sign_digest_with_r(
&P256,
secret_scalar,
nonce,
&digest,
P256_ORDER_MINUS_TWO,
P256_ORDER_HALF,
r,
)
.unwrap_or((Uint::ZERO, Uint::ZERO));
let mut out = [0u64; 8];
out[..4].copy_from_slice(&r.0);
out[4..].copy_from_slice(&s.0);
out
}
#[cfg(all(feature = "diag", feature = "ecdsa-p256"))]
pub fn diag_ecdsa_p256_order_mul_fixed_r_limb_digest(secret: [u8; 32]) -> [u64; 4] {
let secret_scalar = Uint::from_be_array(secret);
let r = P256_GX.reduce_once_ct(P256_ORDER_MODULUS.value);
mul_mod_montgomery_ct(r, secret_scalar, &P256_ORDER_MODULUS).0
}
#[cfg(all(feature = "diag", feature = "ecdsa-p256"))]
pub fn diag_ecdsa_p256_nonce_inverse_limb_digest(secret: [u8; 32], message: &[u8]) -> [u64; 4] {
let digest = Sha256::digest(message);
let mut wide = [0u8; 64];
hmac_expand_p256(&secret, &digest, &mut wide);
let nonce = reduce_wide_order_nonzero(&wide, &P256_ORDER_MODULUS);
nonce.inv_mod_ct_montgomery(&P256_ORDER_MODULUS, P256_ORDER_MINUS_TWO).0
}
#[cfg(all(feature = "diag", feature = "ecdsa-p256"))]
pub fn diag_ecdsa_p256_final_multiply_limb_digest(secret: [u8; 32], nonce_wide: [u8; 64], message: &[u8]) -> [u64; 4] {
let digest = Sha256::digest(message);
let secret_scalar = Uint::from_be_array(secret);
let nonce = reduce_wide_order_nonzero(&nonce_wide, &P256_ORDER_MODULUS);
let r = P256_GX.reduce_once_ct(P256_ORDER_MODULUS.value);
let z = reduce_digest_for_scalar(&digest, P256_ORDER_MODULUS.value);
let rd = mul_mod_montgomery_ct(r, secret_scalar, &P256_ORDER_MODULUS);
let sum = montgomery_mul(
z.add_mod_ct(&rd, P256_ORDER_MODULUS.value),
P256_ORDER_MODULUS.r2,
&P256_ORDER_MODULUS,
);
let nonce_inverse = nonce.inv_mod_ct_montgomery(&P256_ORDER_MODULUS, P256_ORDER_MINUS_TWO);
montgomery_mul(nonce_inverse, sum, &P256_ORDER_MODULUS).0
}
#[cfg(all(feature = "diag", feature = "ecdsa-p384"))]
pub fn diag_ecdsa_p384_select_signing_generator_affine_limb_digest(digit: u8) -> [u64; 12] {
let selected = select_signing_generator_affine_ct(&P384, usize::from(digit));
let mut out = [0u64; 12];
out[..6].copy_from_slice(&selected.x.value.0);
out[6..].copy_from_slice(&selected.y.value.0);
out
}
#[cfg(all(feature = "diag", feature = "ecdsa-p384"))]
pub fn diag_ecdsa_p384_nonce_reduce_limb_digest(secret: [u8; 48], message: &[u8]) -> [u64; 6] {
let digest = Sha384::digest(message);
let mut wide = [0u8; 96];
hmac_expand_p384(&secret, &digest, &mut wide);
reduce_wide_order_nonzero(&wide, &P384_ORDER_MODULUS).0
}
#[cfg(all(feature = "diag", feature = "ecdsa-p384"))]
pub fn diag_ecdsa_p384_reduce_wide_order_limb_digest(wide: [u8; 96]) -> [u64; 6] {
reduce_wide_order_nonzero(&wide, &P384_ORDER_MODULUS).0
}
#[cfg(all(feature = "diag", feature = "ecdsa-p384"))]
pub fn diag_ecdsa_p384_basepoint_blinded_limb_digest(secret: [u8; 48], blind: [u8; 96], message: &[u8]) -> [u64; 12] {
let digest = Sha384::digest(message);
let mut wide = [0u8; 96];
hmac_expand_p384(&secret, &digest, &mut wide);
let nonce = reduce_wide_order_nonzero(&wide, &P384_ORDER_MODULUS);
let nonce_blind = reduce_wide_order_nonzero(&blind, &P384_ORDER_MODULUS);
let point = scalar_mul_basepoint_blinded(&P384, nonce, nonce_blind, P384_FIELD_MINUS_TWO);
let mut out = [0u64; 12];
out[..6].copy_from_slice(&point.x.value.0);
out[6..].copy_from_slice(&point.y.value.0);
out
}
#[cfg(all(feature = "diag", feature = "ecdsa-p384"))]
pub fn diag_ecdsa_p384_scalar_finish_limb_digest(secret: [u8; 48], nonce_wide: [u8; 96], message: &[u8]) -> [u64; 12] {
let digest = Sha384::digest(message);
let secret_scalar = Uint::from_be_array(secret);
let nonce = reduce_wide_order_nonzero(&nonce_wide, &P384_ORDER_MODULUS);
let r = P384_GX.reduce_once_ct(P384_ORDER_MODULUS.value);
let (r, s) = sign_digest_with_r(
&P384,
secret_scalar,
nonce,
&digest,
P384_ORDER_MINUS_TWO,
P384_ORDER_HALF,
r,
)
.unwrap_or((Uint::ZERO, Uint::ZERO));
let mut out = [0u64; 12];
out[..6].copy_from_slice(&r.0);
out[6..].copy_from_slice(&s.0);
out
}
#[cfg(all(feature = "diag", feature = "ecdsa-p384"))]
pub fn diag_ecdsa_p384_order_mul_fixed_r_limb_digest(secret: [u8; 48]) -> [u64; 6] {
let secret_scalar = Uint::from_be_array(secret);
let r = P384_GX.reduce_once_ct(P384_ORDER_MODULUS.value);
mul_mod_montgomery_ct(r, secret_scalar, &P384_ORDER_MODULUS).0
}
#[cfg(all(feature = "diag", feature = "ecdsa-p384"))]
pub fn diag_ecdsa_p384_nonce_inverse_limb_digest(secret: [u8; 48], message: &[u8]) -> [u64; 6] {
let digest = Sha384::digest(message);
let mut wide = [0u8; 96];
hmac_expand_p384(&secret, &digest, &mut wide);
let nonce = reduce_wide_order_nonzero(&wide, &P384_ORDER_MODULUS);
nonce.inv_mod_ct_montgomery(&P384_ORDER_MODULUS, P384_ORDER_MINUS_TWO).0
}
#[cfg(all(feature = "diag", feature = "ecdsa-p384"))]
pub fn diag_ecdsa_p384_final_multiply_limb_digest(secret: [u8; 48], nonce_wide: [u8; 96], message: &[u8]) -> [u64; 6] {
let digest = Sha384::digest(message);
let secret_scalar = Uint::from_be_array(secret);
let nonce = reduce_wide_order_nonzero(&nonce_wide, &P384_ORDER_MODULUS);
let r = P384_GX.reduce_once_ct(P384_ORDER_MODULUS.value);
let z = reduce_digest_for_scalar(&digest, P384_ORDER_MODULUS.value);
let rd = mul_mod_montgomery_ct(r, secret_scalar, &P384_ORDER_MODULUS);
let sum = montgomery_mul(
z.add_mod_ct(&rd, P384_ORDER_MODULUS.value),
P384_ORDER_MODULUS.r2,
&P384_ORDER_MODULUS,
);
let nonce_inverse = nonce.inv_mod_ct_montgomery(&P384_ORDER_MODULUS, P384_ORDER_MINUS_TWO);
montgomery_mul(nonce_inverse, sum, &P384_ORDER_MODULUS).0
}
fn projective_x_matches_scalar<const L: usize>(point: Jacobian<L>, scalar: Uint<L>, curve: &Curve<L>) -> bool {
if point.infinity || point.z.value.is_zero() {
return false;
}
let z2 = point.z.square();
let x = FieldElement::from_uint(scalar, curve.field_modulus).mul(z2);
if point.x == x {
return true;
}
let (scalar_plus_order, carry) = scalar.add_raw(&curve.scalar_modulus.value);
if carry == 0 && scalar_plus_order.cmp(&curve.field_modulus.value).is_lt() {
let x = FieldElement::from_uint(scalar_plus_order, curve.field_modulus).mul(z2);
point.x == x
} else {
false
}
}
#[allow(clippy::indexing_slicing)]
fn scalar_mul_two<const L: usize>(
curve: &Curve<L>,
lhs_scalar: Uint<L>,
rhs_scalar: Uint<L>,
rhs_table: &[Affine<L>; COMB_TABLE_SIZE],
) -> Jacobian<L> {
let lhs_table = curve.generator_comb_table();
let rows = comb_rows::<L>();
let mut acc = Jacobian::infinity(curve.field_modulus);
for i in (0..rows).rev() {
acc = acc.double();
let lhs_digit = comb_digit(lhs_scalar, i, rows);
if lhs_digit != 0 {
acc = acc.add_mixed(lhs_table[lhs_digit]);
}
let rhs_digit = comb_digit(rhs_scalar, i, rows);
if rhs_digit != 0 {
acc = acc.add_mixed(rhs_table[rhs_digit]);
}
}
acc
}
const fn comb_rows<const L: usize>() -> usize {
(L * 64).div_ceil(COMB_WIDTH)
}
fn comb_digit<const L: usize>(scalar: Uint<L>, row: usize, rows: usize) -> usize {
let mut digit = 0usize;
for column in 0..COMB_WIDTH {
let bit = row + column * rows;
if scalar.bit(bit) {
digit |= 1usize << column;
}
}
digit
}
#[allow(clippy::indexing_slicing)]
fn precompute_comb_table<const L: usize>(point: Affine<L>) -> [Affine<L>; COMB_TABLE_SIZE] {
let rows = comb_rows::<L>();
let mut column_points = [Jacobian::from_affine(point); COMB_WIDTH];
let mut current = Jacobian::from_affine(point);
for column in column_points.iter_mut().skip(1) {
for _ in 0..rows {
current = current.double();
}
*column = current;
}
let columns = normalize_jacobian_table(column_points);
let mut table = [Jacobian::from_affine(point); COMB_TABLE_SIZE];
for (mask, entry) in table.iter_mut().enumerate().skip(1) {
let mut acc = Jacobian::infinity(point.x.modulus);
for (column, &column_point) in columns.iter().enumerate() {
if mask & (1usize << column) != 0 {
acc = acc.add_mixed(column_point);
}
}
*entry = acc;
}
normalize_jacobian_table(table)
}
#[allow(clippy::indexing_slicing)]
fn normalize_jacobian_table<const L: usize, const N: usize>(table: [Jacobian<L>; N]) -> [Affine<L>; N] {
let modulus = table[0].x.modulus;
let mut prefixes = [FieldElement::one(modulus); N];
let mut acc = FieldElement::one(modulus);
for i in 0..N {
prefixes[i] = acc;
acc = acc.mul(table[i].z);
}
let mut acc_inv = acc.inv();
let mut out = [Affine {
x: FieldElement::zero(modulus),
y: FieldElement::zero(modulus),
}; N];
for i in (0..N).rev() {
let z_inv = acc_inv.mul(prefixes[i]);
acc_inv = acc_inv.mul(table[i].z);
let z2 = z_inv.square();
let z3 = z2.mul(z_inv);
out[i] = Affine {
x: table[i].x.mul(z2),
y: table[i].y.mul(z3),
};
}
out
}
#[allow(clippy::indexing_slicing)]
fn parse_public_key<const L: usize>(bytes: &[u8], curve: &Curve<L>) -> Result<Affine<L>, EcdsaError> {
let field_len = L * 8;
if bytes.len() != field_len.strict_mul(2).strict_add(1) || bytes.first().copied() != Some(0x04) {
return Err(EcdsaError::InvalidPublicKey);
}
let x = Uint::from_be_slice(bytes.get(1..1 + field_len).ok_or(EcdsaError::InvalidPublicKey)?)
.map_err(|_| EcdsaError::InvalidPublicKey)?;
let y = Uint::from_be_slice(bytes.get(1 + field_len..).ok_or(EcdsaError::InvalidPublicKey)?)
.map_err(|_| EcdsaError::InvalidPublicKey)?;
if x.cmp(&curve.field_modulus.value).is_ge() || y.cmp(&curve.field_modulus.value).is_ge() {
return Err(EcdsaError::InvalidPublicKey);
}
let point = Affine {
x: FieldElement::from_uint(x, curve.field_modulus),
y: FieldElement::from_uint(y, curve.field_modulus),
};
if !is_on_curve(point, curve) {
return Err(EcdsaError::InvalidPublicKey);
}
Ok(point)
}
fn is_on_curve<const L: usize>(point: Affine<L>, curve: &Curve<L>) -> bool {
let lhs = point.y.square();
let x2 = point.x.square();
let x3 = x2.mul(point.x);
let three_x = point.x.triple();
let b = FieldElement::from_uint(curve.b, curve.field_modulus);
lhs == x3.sub(three_x).add(b)
}
fn encode_sec1<const L: usize, const N: usize>(point: &Affine<L>) -> [u8; N] {
let mut out = [0u8; N];
out[0] = 0x04;
let field_len = L * 8;
point.x.to_uint().write_be(&mut out[1..1 + field_len]);
point.y.to_uint().write_be(&mut out[1 + field_len..]);
out
}
fn parse_signature_scalars<const LIMBS: usize, const BYTES: usize>(
bytes: &[u8; BYTES],
scalar_modulus: Uint<LIMBS>,
) -> Result<(Uint<LIMBS>, Uint<LIMBS>), EcdsaError> {
let field_len = LIMBS * 8;
let r = Uint::from_be_slice(&bytes[..field_len])?;
let s = Uint::from_be_slice(&bytes[field_len..])?;
if !r.is_in_range(&scalar_modulus) || !s.is_in_range(&scalar_modulus) {
return Err(EcdsaError::InvalidSignature);
}
Ok((r, s))
}
fn parse_signature_der_bytes<const LIMBS: usize, const BYTES: usize>(der: &[u8]) -> Result<[u8; BYTES], EcdsaError> {
let field_len = LIMBS * 8;
let mut root = DerReader::new(der);
let sig = root.read_constructed(TAG_SEQUENCE)?;
root.finish()?;
let mut sig = DerReader::new(sig);
let r_value = sig.read_primitive(TAG_INTEGER)?;
let s_value = sig.read_primitive(TAG_INTEGER)?;
sig.finish()?;
let mut bytes = [0u8; BYTES];
parse_der_integer_into(r_value, &mut bytes[..field_len])?;
parse_der_integer_into(s_value, &mut bytes[field_len..])?;
Ok(bytes)
}
fn parse_der_integer_into(input: &[u8], out: &mut [u8]) -> Result<(), EcdsaError> {
let value = canonical_unsigned_integer(input)?;
if value.len() > out.len() {
return Err(EcdsaError::InvalidSignature);
}
let start = out.len().strict_sub(value.len());
let Some(suffix) = out.get_mut(start..) else {
return Err(EcdsaError::InvalidSignature);
};
suffix.copy_from_slice(value);
Ok(())
}
fn canonical_unsigned_integer(input: &[u8]) -> Result<&[u8], EcdsaError> {
let Some((&first, rest)) = input.split_first() else {
return Err(EcdsaError::MalformedDer);
};
if first & 0x80 != 0 {
return Err(EcdsaError::MalformedDer);
}
if first == 0 {
let Some((&next, _)) = rest.split_first() else {
return Err(EcdsaError::MalformedDer);
};
if next & 0x80 == 0 {
return Err(EcdsaError::MalformedDer);
}
Ok(rest)
} else {
Ok(input)
}
}
fn parse_spki_der<'a>(der: &'a [u8], curve_oid: &[u8]) -> Result<&'a [u8], EcdsaError> {
let mut root = DerReader::new(der);
let spki = root.read_constructed(TAG_SEQUENCE)?;
root.finish()?;
let mut spki = DerReader::new(spki);
let algorithm = spki.read_constructed(TAG_SEQUENCE)?;
let subject_public_key = spki.read_primitive(TAG_BIT_STRING)?;
spki.finish()?;
let mut algorithm = DerReader::new(algorithm);
let algorithm_oid = algorithm.read_primitive(TAG_OBJECT_IDENTIFIER)?;
let named_curve = algorithm.read_primitive(TAG_OBJECT_IDENTIFIER)?;
algorithm.finish()?;
if algorithm_oid != ID_EC_PUBLIC_KEY_OID {
return Err(EcdsaError::UnsupportedAlgorithm);
}
if named_curve != curve_oid {
return Err(EcdsaError::UnsupportedAlgorithm);
}
let (&unused_bits, public_key) = subject_public_key.split_first().ok_or(EcdsaError::MalformedDer)?;
if unused_bits != 0 || public_key.is_empty() {
return Err(EcdsaError::MalformedDer);
}
Ok(public_key)
}
fn mul_mod_montgomery<const L: usize>(lhs: Uint<L>, rhs: Uint<L>, modulus: &'static Modulus<L>) -> Uint<L> {
let lhs = montgomery_mul(lhs, modulus.r2, modulus);
let rhs = montgomery_mul(rhs, modulus.r2, modulus);
let product = montgomery_mul(lhs, rhs, modulus);
montgomery_mul(product, Uint::ONE, modulus)
}
fn mul_mod_montgomery_ct<const L: usize>(lhs: Uint<L>, rhs: Uint<L>, modulus: &'static Modulus<L>) -> Uint<L> {
mul_mod_montgomery(lhs, rhs, modulus)
}
#[allow(clippy::indexing_slicing)]
fn montgomery_mul<const L: usize>(lhs: Uint<L>, rhs: Uint<L>, modulus: &'static Modulus<L>) -> Uint<L> {
debug_assert!(L <= 6);
if is_p256_field_modulus(modulus) {
let reduced = montgomery_mul_p256_field(
[lhs.0[0], lhs.0[1], lhs.0[2], lhs.0[3]],
[rhs.0[0], rhs.0[1], rhs.0[2], rhs.0[3]],
);
let mut out = [0u64; L];
out.copy_from_slice(&reduced.0);
return Uint(out);
}
if is_p384_field_modulus(modulus) {
let lhs = [lhs.0[0], lhs.0[1], lhs.0[2], lhs.0[3], lhs.0[4], lhs.0[5]];
let rhs = [rhs.0[0], rhs.0[1], rhs.0[2], rhs.0[3], rhs.0[4], rhs.0[5]];
#[cfg(any(
all(target_arch = "aarch64", any(target_os = "macos", target_os = "linux")),
all(target_arch = "x86_64", target_os = "linux")
))]
let reduced = ecdsa_platform_asm::p384_field_mul(&lhs, &rhs);
#[cfg(not(any(
all(target_arch = "aarch64", any(target_os = "macos", target_os = "linux")),
all(target_arch = "x86_64", target_os = "linux")
)))]
let reduced = ecdsa_p384_field::mul(lhs, rhs);
let mut out = [0u64; L];
out.copy_from_slice(&reduced);
return Uint(out);
}
if is_p256_order_modulus(modulus) {
let reduced = montgomery_mul_p256_order(
[lhs.0[0], lhs.0[1], lhs.0[2], lhs.0[3]],
[rhs.0[0], rhs.0[1], rhs.0[2], rhs.0[3]],
);
let mut out = [0u64; L];
out.copy_from_slice(&reduced.0);
return Uint(out);
}
if is_p384_order_modulus(modulus) {
let reduced = montgomery_mul_p384_order(
[lhs.0[0], lhs.0[1], lhs.0[2], lhs.0[3], lhs.0[4], lhs.0[5]],
[rhs.0[0], rhs.0[1], rhs.0[2], rhs.0[3], rhs.0[4], rhs.0[5]],
);
let mut out = [0u64; L];
out.copy_from_slice(&reduced.0);
return Uint(out);
}
let mut limbs = [0u64; 13];
for i in 0..L {
let mut carry = 0u128;
for j in 0..L {
let k = i + j;
let acc = u128::from(limbs[k]) + (u128::from(lhs.0[i]) * u128::from(rhs.0[j])) + carry;
limbs[k] = acc as u64;
carry = acc >> 64;
}
add_limb(&mut limbs, i + L, carry as u64);
}
for i in 0..L {
let factor = limbs[i].wrapping_mul(modulus.n0_inv);
let mut carry = 0u128;
for j in 0..L {
let k = i + j;
let acc = u128::from(limbs[k]) + (u128::from(factor) * u128::from(modulus.value.0[j])) + carry;
limbs[k] = acc as u64;
carry = acc >> 64;
}
add_limb(&mut limbs, i + L, carry as u64);
}
let mut out = [0u64; L];
out.copy_from_slice(&limbs[L..L + L]);
let out = Uint(out);
let (reduced, borrow) = out.sub_raw(&modulus.value);
Uint::select(out, reduced, mask_nonzero_u64(limbs[L + L]) | mask_zero_u64(borrow))
}
fn montgomery_square<const L: usize>(value: Uint<L>, modulus: &'static Modulus<L>) -> Uint<L> {
if is_p384_field_modulus(modulus) {
let value = [value.0[0], value.0[1], value.0[2], value.0[3], value.0[4], value.0[5]];
#[cfg(any(
all(target_arch = "aarch64", any(target_os = "macos", target_os = "linux")),
all(target_arch = "x86_64", target_os = "linux")
))]
let reduced = ecdsa_platform_asm::p384_field_square(&value);
#[cfg(not(any(
all(target_arch = "aarch64", any(target_os = "macos", target_os = "linux")),
all(target_arch = "x86_64", target_os = "linux")
)))]
let reduced = ecdsa_p384_field::square(value);
let mut out = [0u64; L];
out.copy_from_slice(&reduced);
Uint(out)
} else {
montgomery_mul(value, value, modulus)
}
}
fn is_p256_field_modulus<const L: usize>(modulus: &'static Modulus<L>) -> bool {
L == 4 && core::ptr::from_ref(modulus).cast::<()>() == core::ptr::from_ref(&P256_FIELD_MODULUS).cast::<()>()
}
fn is_p384_field_modulus<const L: usize>(modulus: &'static Modulus<L>) -> bool {
L == 6 && core::ptr::from_ref(modulus).cast::<()>() == core::ptr::from_ref(&P384_FIELD_MODULUS).cast::<()>()
}
fn montgomery_mul_p256_order(lhs: [u64; 4], rhs: [u64; 4]) -> Uint<4> {
let n = P256_ORDER.0;
let n0_inv = P256_ORDER_MODULUS.n0_inv;
let mut t0 = 0u64;
let mut t1 = 0u64;
let mut t2 = 0u64;
let mut t3 = 0u64;
let mut t4 = 0u64;
macro_rules! cios_step {
($rhs_limb:expr) => {{
let (u0, carry) = mac_limb(t0, lhs[0], $rhs_limb, 0);
let (u1, carry) = mac_limb(t1, lhs[1], $rhs_limb, carry);
let (u2, carry) = mac_limb(t2, lhs[2], $rhs_limb, carry);
let (u3, carry) = mac_limb(t3, lhs[3], $rhs_limb, carry);
let (u4, carry_extra) = adc_limb(t4, carry, 0);
let factor = u0.wrapping_mul(n0_inv);
let (_, carry) = mac_limb(u0, factor, n[0], 0);
let (v0, carry) = mac_limb(u1, factor, n[1], carry);
let (v1, carry) = mac_limb(u2, factor, n[2], carry);
let (v2, carry) = mac_limb(u3, factor, n[3], carry);
let (v3, carry) = adc_limb(u4, carry, 0);
t0 = v0;
t1 = v1;
t2 = v2;
t3 = v3;
t4 = carry_extra + carry;
}};
}
cios_step!(rhs[0]);
cios_step!(rhs[1]);
cios_step!(rhs[2]);
cios_step!(rhs[3]);
sub_p256_order_once([t0, t1, t2, t3, t4])
}
fn sub_p256_order_once(limbs: [u64; 5]) -> Uint<4> {
let n = P256_ORDER.0;
let (w0, borrow) = sbb_limb(limbs[0], n[0], 0);
let (w1, borrow) = sbb_limb(limbs[1], n[1], borrow);
let (w2, borrow) = sbb_limb(limbs[2], n[2], borrow);
let (w3, borrow) = sbb_limb(limbs[3], n[3], borrow);
let (_, borrow) = sbb_limb(limbs[4], 0, borrow);
let reduced = Uint([w0, w1, w2, w3]);
Uint::select(
reduced,
Uint([limbs[0], limbs[1], limbs[2], limbs[3]]),
mask_nonzero_u64(borrow),
)
}
fn montgomery_mul_p384_order(lhs: [u64; 6], rhs: [u64; 6]) -> Uint<6> {
let n = P384_ORDER.0;
let n0_inv = P384_ORDER_MODULUS.n0_inv;
let mut t0 = 0u64;
let mut t1 = 0u64;
let mut t2 = 0u64;
let mut t3 = 0u64;
let mut t4 = 0u64;
let mut t5 = 0u64;
let mut t6 = 0u64;
macro_rules! cios_step {
($rhs_limb:expr) => {{
let (u0, carry) = mac_limb(t0, lhs[0], $rhs_limb, 0);
let (u1, carry) = mac_limb(t1, lhs[1], $rhs_limb, carry);
let (u2, carry) = mac_limb(t2, lhs[2], $rhs_limb, carry);
let (u3, carry) = mac_limb(t3, lhs[3], $rhs_limb, carry);
let (u4, carry) = mac_limb(t4, lhs[4], $rhs_limb, carry);
let (u5, carry) = mac_limb(t5, lhs[5], $rhs_limb, carry);
let (u6, carry_extra) = adc_limb(t6, carry, 0);
let factor = u0.wrapping_mul(n0_inv);
let (_, carry) = mac_limb(u0, factor, n[0], 0);
let (v0, carry) = mac_limb(u1, factor, n[1], carry);
let (v1, carry) = mac_limb(u2, factor, n[2], carry);
let (v2, carry) = mac_limb(u3, factor, n[3], carry);
let (v3, carry) = mac_limb(u4, factor, n[4], carry);
let (v4, carry) = mac_limb(u5, factor, n[5], carry);
let (v5, carry) = adc_limb(u6, carry, 0);
t0 = v0;
t1 = v1;
t2 = v2;
t3 = v3;
t4 = v4;
t5 = v5;
t6 = carry_extra + carry;
}};
}
cios_step!(rhs[0]);
cios_step!(rhs[1]);
cios_step!(rhs[2]);
cios_step!(rhs[3]);
cios_step!(rhs[4]);
cios_step!(rhs[5]);
sub_p384_order_once([t0, t1, t2, t3, t4, t5, t6])
}
fn sub_p384_order_once(limbs: [u64; 7]) -> Uint<6> {
let n = P384_ORDER.0;
let (w0, borrow) = sbb_limb(limbs[0], n[0], 0);
let (w1, borrow) = sbb_limb(limbs[1], n[1], borrow);
let (w2, borrow) = sbb_limb(limbs[2], n[2], borrow);
let (w3, borrow) = sbb_limb(limbs[3], n[3], borrow);
let (w4, borrow) = sbb_limb(limbs[4], n[4], borrow);
let (w5, borrow) = sbb_limb(limbs[5], n[5], borrow);
let (_, borrow) = sbb_limb(limbs[6], 0, borrow);
let reduced = Uint([w0, w1, w2, w3, w4, w5]);
Uint::select(
reduced,
Uint([limbs[0], limbs[1], limbs[2], limbs[3], limbs[4], limbs[5]]),
mask_nonzero_u64(borrow),
)
}
fn add_p384_field(lhs: [u64; 6], rhs: [u64; 6]) -> Uint<6> {
let p = P384_FIELD.0;
let (s0, carry) = adc_limb(lhs[0], rhs[0], 0);
let (s1, carry) = adc_limb(lhs[1], rhs[1], carry);
let (s2, carry) = adc_limb(lhs[2], rhs[2], carry);
let (s3, carry) = adc_limb(lhs[3], rhs[3], carry);
let (s4, carry) = adc_limb(lhs[4], rhs[4], carry);
let (s5, carry) = adc_limb(lhs[5], rhs[5], carry);
let (r0, borrow) = sbb_limb(s0, p[0], 0);
let (r1, borrow) = sbb_limb(s1, p[1], borrow);
let (r2, borrow) = sbb_limb(s2, p[2], borrow);
let (r3, borrow) = sbb_limb(s3, p[3], borrow);
let (r4, borrow) = sbb_limb(s4, p[4], borrow);
let (r5, borrow) = sbb_limb(s5, p[5], borrow);
Uint::select(
Uint([s0, s1, s2, s3, s4, s5]),
Uint([r0, r1, r2, r3, r4, r5]),
mask_nonzero_u64(carry) | mask_zero_u64(borrow),
)
}
fn sub_p384_field(lhs: [u64; 6], rhs: [u64; 6]) -> Uint<6> {
let p = P384_FIELD.0;
let (d0, borrow) = sbb_limb(lhs[0], rhs[0], 0);
let (d1, borrow) = sbb_limb(lhs[1], rhs[1], borrow);
let (d2, borrow) = sbb_limb(lhs[2], rhs[2], borrow);
let (d3, borrow) = sbb_limb(lhs[3], rhs[3], borrow);
let (d4, borrow) = sbb_limb(lhs[4], rhs[4], borrow);
let (d5, borrow) = sbb_limb(lhs[5], rhs[5], borrow);
let (r0, carry) = adc_limb(d0, p[0], 0);
let (r1, carry) = adc_limb(d1, p[1], carry);
let (r2, carry) = adc_limb(d2, p[2], carry);
let (r3, carry) = adc_limb(d3, p[3], carry);
let (r4, carry) = adc_limb(d4, p[4], carry);
let (r5, _) = adc_limb(d5, p[5], carry);
Uint::select(
Uint([d0, d1, d2, d3, d4, d5]),
Uint([r0, r1, r2, r3, r4, r5]),
mask_nonzero_u64(borrow),
)
}
fn montgomery_mul_p256_field(lhs: [u64; 4], rhs: [u64; 4]) -> Uint<4> {
let (w0, carry) = mac_limb(0, lhs[0], rhs[0], 0);
let (w1, carry) = mac_limb(0, lhs[0], rhs[1], carry);
let (w2, carry) = mac_limb(0, lhs[0], rhs[2], carry);
let (w3, w4) = mac_limb(0, lhs[0], rhs[3], carry);
let (w1, carry) = mac_limb(w1, lhs[1], rhs[0], 0);
let (w2, carry) = mac_limb(w2, lhs[1], rhs[1], carry);
let (w3, carry) = mac_limb(w3, lhs[1], rhs[2], carry);
let (w4, w5) = mac_limb(w4, lhs[1], rhs[3], carry);
let (w2, carry) = mac_limb(w2, lhs[2], rhs[0], 0);
let (w3, carry) = mac_limb(w3, lhs[2], rhs[1], carry);
let (w4, carry) = mac_limb(w4, lhs[2], rhs[2], carry);
let (w5, w6) = mac_limb(w5, lhs[2], rhs[3], carry);
let (w3, carry) = mac_limb(w3, lhs[3], rhs[0], 0);
let (w4, carry) = mac_limb(w4, lhs[3], rhs[1], carry);
let (w5, carry) = mac_limb(w5, lhs[3], rhs[2], carry);
let (w6, w7) = mac_limb(w6, lhs[3], rhs[3], carry);
montgomery_reduce_p256_field([w0, w1, w2, w3, w4, w5, w6, w7])
}
#[allow(clippy::indexing_slicing)]
fn montgomery_reduce_p256_field(limbs: [u64; 8]) -> Uint<4> {
let [r0, r1, r2, r3, r4, r5, r6, r7] = limbs;
let p = P256_FIELD.0;
let (r1, carry) = mac_limb(r1, r0, p[1], r0);
let (r2, carry) = adc_limb(r2, 0, carry);
let (r3, carry) = mac_limb(r3, r0, p[3], carry);
let (r4, carry2) = adc_limb(r4, 0, carry);
let (r2, carry) = mac_limb(r2, r1, p[1], r1);
let (r3, carry) = adc_limb(r3, 0, carry);
let (r4, carry) = mac_limb(r4, r1, p[3], carry);
let (r5, carry2) = adc_limb(r5, carry2, carry);
let (r3, carry) = mac_limb(r3, r2, p[1], r2);
let (r4, carry) = adc_limb(r4, 0, carry);
let (r5, carry) = mac_limb(r5, r2, p[3], carry);
let (r6, carry2) = adc_limb(r6, carry2, carry);
let (r4, carry) = mac_limb(r4, r3, p[1], r3);
let (r5, carry) = adc_limb(r5, 0, carry);
let (r6, carry) = mac_limb(r6, r3, p[3], carry);
let (r7, r8) = adc_limb(r7, carry2, carry);
sub_p256_field_once([r4, r5, r6, r7, r8])
}
fn sub_p256_field_once(limbs: [u64; 5]) -> Uint<4> {
let p = P256_FIELD.0;
let (w0, borrow) = sbb_limb(limbs[0], p[0], 0);
let (w1, borrow) = sbb_limb(limbs[1], p[1], borrow);
let (w2, borrow) = sbb_limb(limbs[2], p[2], borrow);
let (w3, borrow) = sbb_limb(limbs[3], p[3], borrow);
let (_, borrow) = sbb_limb(limbs[4], 0, borrow);
let reduced = Uint([w0, w1, w2, w3]);
let (s0, carry) = adc_limb(w0, p[0], 0);
let (s1, carry) = adc_limb(w1, p[1], carry);
let (s2, carry) = adc_limb(w2, p[2], carry);
let (s3, _) = adc_limb(w3, p[3], carry);
Uint::select(reduced, Uint([s0, s1, s2, s3]), mask_nonzero_u64(borrow))
}
#[inline(always)]
fn adc_limb(lhs: u64, rhs: u64, carry: u64) -> (u64, u64) {
let result = u128::from(lhs) + u128::from(rhs) + u128::from(carry);
(result as u64, (result >> 64) as u64)
}
#[inline(always)]
fn sbb_limb(lhs: u64, rhs: u64, borrow: u64) -> (u64, u64) {
let (diff, overflow0) = lhs.overflowing_sub(rhs);
let (diff, overflow1) = diff.overflowing_sub(borrow);
(diff, u64::from(overflow0 | overflow1))
}
#[inline(always)]
fn mac_limb(acc: u64, lhs: u64, rhs: u64, carry: u64) -> (u64, u64) {
let result = u128::from(acc) + (u128::from(lhs) * u128::from(rhs)) + u128::from(carry);
(result as u64, (result >> 64) as u64)
}
#[allow(clippy::indexing_slicing)]
fn add_limb(limbs: &mut [u64; 13], mut index: usize, mut value: u64) {
while index < limbs.len() {
let (sum, carry) = limbs[index].overflowing_add(value);
limbs[index] = sum;
value = u64::from(carry);
index = index.strict_add(1);
}
}
struct DerReader<'a> {
input: &'a [u8],
offset: usize,
}
impl<'a> DerReader<'a> {
const fn new(input: &'a [u8]) -> Self {
Self { input, offset: 0 }
}
fn read_constructed(&mut self, tag: u8) -> Result<&'a [u8], EcdsaError> {
self.read_primitive(tag)
}
fn read_primitive(&mut self, tag: u8) -> Result<&'a [u8], EcdsaError> {
let actual = self.read_byte()?;
if actual != tag {
return Err(EcdsaError::MalformedDer);
}
let len = self.read_len()?;
let end = self.offset.checked_add(len).ok_or(EcdsaError::MalformedDer)?;
if end > self.input.len() {
return Err(EcdsaError::MalformedDer);
}
let value = self.input.get(self.offset..end).ok_or(EcdsaError::MalformedDer)?;
self.offset = end;
Ok(value)
}
fn finish(&self) -> Result<(), EcdsaError> {
if self.offset == self.input.len() {
Ok(())
} else {
Err(EcdsaError::MalformedDer)
}
}
fn read_byte(&mut self) -> Result<u8, EcdsaError> {
let byte = *self.input.get(self.offset).ok_or(EcdsaError::MalformedDer)?;
self.offset = self.offset.strict_add(1);
Ok(byte)
}
fn read_len(&mut self) -> Result<usize, EcdsaError> {
let first = self.read_byte()?;
if first & 0x80 == 0 {
return Ok(usize::from(first));
}
let len_len = usize::from(first & 0x7f);
if len_len == 0 || len_len > core::mem::size_of::<usize>() {
return Err(EcdsaError::MalformedDer);
}
let first_len_byte = self.read_byte()?;
if first_len_byte == 0 {
return Err(EcdsaError::MalformedDer);
}
let mut len = usize::from(first_len_byte);
for _ in 1..len_len {
len = len.checked_shl(8).ok_or(EcdsaError::MalformedDer)?;
len |= usize::from(self.read_byte()?);
}
if len < 128 {
return Err(EcdsaError::MalformedDer);
}
Ok(len)
}
}
#[cfg(test)]
mod tests {
use alloc::format;
use super::*;
fn p256_public_key() -> EcdsaP256PublicKey {
let mut sec1 = [0u8; EcdsaP256PublicKey::SEC1_LENGTH];
sec1[0] = 0x04;
P256_GX.write_be(&mut sec1[1..33]);
P256_GY.write_be(&mut sec1[33..]);
EcdsaP256PublicKey::from_sec1_bytes(&sec1).unwrap()
}
fn p384_public_key() -> EcdsaP384PublicKey {
let mut sec1 = [0u8; EcdsaP384PublicKey::SEC1_LENGTH];
sec1[0] = 0x04;
P384_GX.write_be(&mut sec1[1..49]);
P384_GY.write_be(&mut sec1[49..]);
EcdsaP384PublicKey::from_sec1_bytes(&sec1).unwrap()
}
#[test]
fn p256_sec1_rejects_wrong_shape_and_off_curve_points() {
assert_eq!(
EcdsaP256PublicKey::from_sec1_bytes(&[]).err(),
Some(EcdsaError::InvalidPublicKey)
);
let mut sec1 = p256_public_key().to_sec1_bytes();
sec1[0] = 0x02;
assert_eq!(
EcdsaP256PublicKey::from_sec1_bytes(&sec1).err(),
Some(EcdsaError::InvalidPublicKey)
);
let mut sec1 = p256_public_key().to_sec1_bytes();
sec1[64] ^= 1;
assert_eq!(
EcdsaP256PublicKey::from_sec1_bytes(&sec1).err(),
Some(EcdsaError::InvalidPublicKey)
);
}
#[test]
fn p384_sec1_rejects_wrong_shape_and_off_curve_points() {
assert_eq!(
EcdsaP384PublicKey::from_sec1_bytes(&[]).err(),
Some(EcdsaError::InvalidPublicKey)
);
let mut sec1 = p384_public_key().to_sec1_bytes();
sec1[0] = 0x03;
assert_eq!(
EcdsaP384PublicKey::from_sec1_bytes(&sec1).err(),
Some(EcdsaError::InvalidPublicKey)
);
let mut sec1 = p384_public_key().to_sec1_bytes();
sec1[96] ^= 1;
assert_eq!(
EcdsaP384PublicKey::from_sec1_bytes(&sec1).err(),
Some(EcdsaError::InvalidPublicKey)
);
}
#[test]
fn p256_signature_rejects_zero_or_out_of_range_scalars() {
let mut bytes = [0u8; EcdsaP256Signature::LENGTH];
bytes[31] = 1;
assert_eq!(
EcdsaP256Signature::from_bytes(bytes).err(),
Some(EcdsaError::InvalidSignature)
);
bytes[63] = 1;
assert!(EcdsaP256Signature::from_bytes(bytes).is_ok());
let mut out_of_range = bytes;
P256_ORDER.write_be(&mut out_of_range[..32]);
assert_eq!(
EcdsaP256Signature::from_bytes(out_of_range).err(),
Some(EcdsaError::InvalidSignature)
);
}
#[test]
fn p384_signature_rejects_zero_or_out_of_range_scalars() {
let mut bytes = [0u8; EcdsaP384Signature::LENGTH];
bytes[47] = 1;
assert_eq!(
EcdsaP384Signature::from_bytes(bytes).err(),
Some(EcdsaError::InvalidSignature)
);
bytes[95] = 1;
assert!(EcdsaP384Signature::from_bytes(bytes).is_ok());
let mut out_of_range = bytes;
P384_ORDER.write_be(&mut out_of_range[..48]);
assert_eq!(
EcdsaP384Signature::from_bytes(out_of_range).err(),
Some(EcdsaError::InvalidSignature)
);
}
#[test]
fn der_signature_parser_requires_canonical_unsigned_integers() {
let good = [0x30, 0x06, 0x02, 0x01, 0x01, 0x02, 0x01, 0x01];
assert!(EcdsaP256Signature::from_der(&good).is_ok());
let negative = [0x30, 0x07, 0x02, 0x02, 0x80, 0x01, 0x02, 0x01, 0x01];
assert_eq!(
EcdsaP256Signature::from_der(&negative).err(),
Some(EcdsaError::MalformedDer)
);
let overlong = [0x30, 0x08, 0x02, 0x03, 0x00, 0x01, 0x01, 0x02, 0x01, 0x01];
assert_eq!(
EcdsaP256Signature::from_der(&overlong).err(),
Some(EcdsaError::MalformedDer)
);
}
#[test]
fn p256_secret_key_rejects_zero_and_group_order() {
assert_eq!(
EcdsaP256SecretKey::from_bytes([0u8; EcdsaP256SecretKey::LENGTH]).err(),
Some(EcdsaError::InvalidSecretKey)
);
let mut order = [0u8; EcdsaP256SecretKey::LENGTH];
P256_ORDER.write_be(&mut order);
assert_eq!(
EcdsaP256SecretKey::from_bytes(order).err(),
Some(EcdsaError::InvalidSecretKey)
);
}
#[test]
fn p384_secret_key_rejects_zero_and_group_order() {
assert_eq!(
EcdsaP384SecretKey::from_bytes([0u8; EcdsaP384SecretKey::LENGTH]).err(),
Some(EcdsaError::InvalidSecretKey)
);
let mut order = [0u8; EcdsaP384SecretKey::LENGTH];
P384_ORDER.write_be(&mut order);
assert_eq!(
EcdsaP384SecretKey::from_bytes(order).err(),
Some(EcdsaError::InvalidSecretKey)
);
}
#[test]
fn p256_secret_key_one_derives_generator_public_key() {
let mut one = [0u8; EcdsaP256SecretKey::LENGTH];
one[EcdsaP256SecretKey::LENGTH - 1] = 1;
let secret = EcdsaP256SecretKey::from_bytes(one).unwrap();
assert_eq!(secret.public_key(), p256_public_key());
assert_eq!(secret.public_key_blinded(|blind| blind.fill(0xa5)), p256_public_key());
}
#[test]
fn p384_secret_key_one_derives_generator_public_key() {
let mut one = [0u8; EcdsaP384SecretKey::LENGTH];
one[EcdsaP384SecretKey::LENGTH - 1] = 1;
let secret = EcdsaP384SecretKey::from_bytes(one).unwrap();
assert_eq!(secret.public_key(), p384_public_key());
assert_eq!(secret.public_key_blinded(|blind| blind.fill(0x5a)), p384_public_key());
}
#[test]
fn p256_try_sign_is_deterministic_low_s_and_verifies() {
let secret = EcdsaP256SecretKey::from_bytes([0x42; EcdsaP256SecretKey::LENGTH]).unwrap();
let public = secret.public_key();
let message = b"rscrypto ecdsa p256 signing";
let first = secret.try_sign(message).unwrap();
let second = secret.try_sign(message).unwrap();
let blinded = secret.try_sign_blinded(message, |blind| blind.fill(0x7b)).unwrap();
assert_eq!(first, second);
assert!(public.verify(message, &first).is_ok());
assert!(public.verify(message, &blinded).is_ok());
assert_eq!(first, blinded);
assert!(
Uint::from_be_slice(&first.as_bytes()[32..])
.unwrap()
.cmp(&P256_ORDER_HALF)
.is_le()
);
}
#[test]
fn p384_try_sign_is_deterministic_low_s_and_verifies() {
let secret = EcdsaP384SecretKey::from_bytes([0x24; EcdsaP384SecretKey::LENGTH]).unwrap();
let public = secret.public_key();
let message = b"rscrypto ecdsa p384 signing";
let first = secret.try_sign(message).unwrap();
let second = secret.try_sign(message).unwrap();
let blinded = secret.try_sign_blinded(message, |blind| blind.fill(0xb7)).unwrap();
assert_eq!(first, second);
assert!(public.verify(message, &first).is_ok());
assert!(public.verify(message, &blinded).is_ok());
assert_eq!(first, blinded);
assert!(
Uint::from_be_slice(&first.as_bytes()[48..])
.unwrap()
.cmp(&P384_ORDER_HALF)
.is_le()
);
}
#[cfg(any(
all(target_arch = "aarch64", any(target_os = "macos", target_os = "linux")),
all(target_arch = "x86_64", target_os = "linux")
))]
#[test]
fn p256_platform_basepoint_matches_portable_comb_for_sample_scalars() {
let scalars = [
Uint::ONE,
Uint::from_u64(2),
Uint::from_u64(0x1234_5678_9abc_def0),
P256_ORDER.sub_raw(&Uint::ONE).0,
];
for scalar in scalars {
let portable = scalar_mul_basepoint_comb_ct(&P256, scalar).to_affine_ct(P256_FIELD_MINUS_TWO);
let backend = scalar_mul_basepoint_affine_backend(&P256, scalar).expect("P-256 platform backend must be present");
assert!(
backend == portable,
"P-256 platform basepoint backend must match portable comb"
);
}
}
#[cfg(any(
all(target_arch = "aarch64", any(target_os = "macos", target_os = "linux")),
all(target_arch = "x86_64", target_os = "linux")
))]
#[test]
fn p384_platform_basepoint_matches_portable_comb_for_sample_scalars() {
let scalars = [
Uint::ONE,
Uint::from_u64(2),
Uint::from_u64(0x1234_5678_9abc_def0),
P384_ORDER.sub_raw(&Uint::ONE).0,
];
for scalar in scalars {
let portable = scalar_mul_basepoint_comb_ct(&P384, scalar).to_affine_ct(P384_FIELD_MINUS_TWO);
let backend = scalar_mul_basepoint_backend(&P384, scalar)
.expect("P-384 platform backend must be present")
.to_affine_ct(P384_FIELD_MINUS_TWO);
assert!(
backend == portable,
"P-384 platform basepoint backend must match portable comb"
);
}
}
#[test]
fn ecdsa_secret_key_debug_is_redacted() {
let p256 = EcdsaP256SecretKey::from_bytes([0x11; EcdsaP256SecretKey::LENGTH]).unwrap();
let p384 = EcdsaP384SecretKey::from_bytes([0x22; EcdsaP384SecretKey::LENGTH]).unwrap();
assert_eq!(format!("{p256:?}"), "EcdsaP256SecretKey(****)");
assert_eq!(format!("{p384:?}"), "EcdsaP384SecretKey(****)");
}
#[test]
fn p384_field_add_sub_specializations_match_generic_modular_arithmetic() {
let p_minus_one = P384_FIELD.sub_raw(&Uint::ONE).0;
let values = [
Uint::ZERO,
Uint::ONE,
Uint::from_u64(2),
P384_GX,
P384_GY,
P384_B,
p_minus_one,
];
for lhs in values {
for rhs in values {
assert_eq!(add_p384_field(lhs.0, rhs.0).0, lhs.add_mod_ct(&rhs, P384_FIELD).0);
assert_eq!(sub_p384_field(lhs.0, rhs.0).0, lhs.sub_mod(&rhs, P384_FIELD).0);
}
}
}
}