Skip to content

Commit c7d635c

Browse files
Add hooks for multi-scalar-multiplication (#214)
* Hook for G1 MSM * Hook for G2 MSM * G1 MSM tests * G2 MSM tests * Fix lint errors.
1 parent 5341429 commit c7d635c

3 files changed

Lines changed: 251 additions & 12 deletions

File tree

krypto/src/tests/integration/test_hooks.py

Lines changed: 80 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -984,3 +984,83 @@ def test_verify_bls12g2oncurve(
984984

985985
# Then
986986
assert expected == actual
987+
988+
989+
# https://github.com/ethereum/execution-spec-tests/blob/b48d1dc81233af6e4d6c7c84e60e8eaa4067a288/tests/prague/eip2537_bls_12_381_precompiles/test_bls12_g1msm.py
990+
VERIFYBLS12G1MSM_TEST_DATA: Final = (
991+
('g1_plus_inf', [BLS12_G1, BLS12_G1_INFTY], [1, 1], BLS12_G1),
992+
('all_zero_scalars', [BLS12_G1, BLS12_G1_INFTY], [0, 0], BLS12_G1_INFTY),
993+
('sum_to_identity_opposite', [BLS12_G1, bls12_neg_g1(BLS12_G1)], [1, 1], BLS12_G1_INFTY),
994+
('scalars_sum_to_q', [BLS12_G1, BLS12_G1], [BLS12_Q - 1, 1], BLS12_G1_INFTY),
995+
('combined_basic_cases', [BLS12_G1, BLS12_G1, BLS12_G1_INFTY], [1, 0, 5], BLS12_G1),
996+
('identity_with_large_scalar', [BLS12_G1, BLS12_G1_INFTY], [1, 500], BLS12_G1),
997+
('multiple_points_zero_scalar', [BLS12_G1, BLS12_P1, bls12_neg_g1(BLS12_G1)], [0, 0, 0], BLS12_G1_INFTY),
998+
('max_discount', [BLS12_P1] * 3, [BLS12_Q] * 3, BLS12_G1_INFTY),
999+
)
1000+
1001+
1002+
@pytest.mark.parametrize(
1003+
'id, first,second,result', VERIFYBLS12G1MSM_TEST_DATA, ids=[id for id, *_ in VERIFYBLS12G1MSM_TEST_DATA]
1004+
)
1005+
def test_verify_bls12g1msm(
1006+
definition_dir: Path,
1007+
id: str,
1008+
first: list[tuple[int, int]],
1009+
second: list[int],
1010+
result: tuple[int, int],
1011+
) -> None:
1012+
# Given
1013+
point_list = [f'ListItem(({x[0]} , {x[1]}))' for x in first]
1014+
scalar_list = [f'ListItem({x})' for x in second]
1015+
point_str = ' '.join(point_list)
1016+
scalar_str = ' '.join(scalar_list)
1017+
pgm = f'BLS12G1Msm( {scalar_str} , {point_str} )'
1018+
1019+
result_str = f'( {result[0]} , {result[1]} )'
1020+
expected = f'<k>\n {result_str} ~> .K\n</k>'
1021+
1022+
# When
1023+
actual = run(definition_dir, pgm)
1024+
1025+
# Then
1026+
assert expected == actual
1027+
1028+
1029+
# https://github.com/ethereum/execution-spec-tests/blob/b48d1dc81233af6e4d6c7c84e60e8eaa4067a288/tests/prague/eip2537_bls_12_381_precompiles/test_bls12_g2msm.py
1030+
VERIFYBLS12G2MSM_TEST_DATA: Final = (
1031+
('g2_plus_inf', [BLS12_G2, BLS12_G2_INFTY], [1, 1], BLS12_G2),
1032+
('all_zero_scalars', [BLS12_G2, BLS12_P2, BLS12_G2_INFTY], [0, 0, 0], BLS12_G2_INFTY),
1033+
('sum_to_identity_opposite', [BLS12_G2, bls12_neg_g2(BLS12_G2)], [1, 1], BLS12_G2_INFTY),
1034+
('scalars_sum_to_q', [BLS12_G2, BLS12_G2], [BLS12_Q - 1, 1], BLS12_G2_INFTY),
1035+
('combined_basic_cases', [BLS12_G2, BLS12_G2, BLS12_G2_INFTY], [1, 0, 5], BLS12_G2),
1036+
('identity_with_large_scalar', [BLS12_G2, BLS12_G2_INFTY], [1, 500], BLS12_G2),
1037+
('multiple_points_zero_scalar', [BLS12_G2, BLS12_P2, bls12_neg_g2(BLS12_G2)], [0, 0, 0], BLS12_G2_INFTY),
1038+
('max_discount', [BLS12_P2] * 3, [BLS12_Q] * 3, BLS12_G2_INFTY),
1039+
)
1040+
1041+
1042+
@pytest.mark.parametrize(
1043+
'id, first,second,result', VERIFYBLS12G2MSM_TEST_DATA, ids=[id for id, *_ in VERIFYBLS12G2MSM_TEST_DATA]
1044+
)
1045+
def test_verify_bls12g2msm(
1046+
definition_dir: Path,
1047+
id: str,
1048+
first: list[tuple[tuple[int, int], tuple[int, int]]],
1049+
second: list[int],
1050+
result: tuple[tuple[int, int], tuple[int, int]],
1051+
) -> None:
1052+
# Given
1053+
point_list = [f'ListItem(({x[0][0]} x {x[0][1]} , {x[1][0]} x {x[1][1]}))' for x in first]
1054+
scalar_list = [f'ListItem({x})' for x in second]
1055+
point_str = ' '.join(point_list)
1056+
scalar_str = ' '.join(scalar_list)
1057+
pgm = f'BLS12G2Msm( {scalar_str} , {point_str} )'
1058+
1059+
result_str = f'( {result[0][0]} x {result[0][1]} , {result[1][0]} x {result[1][1]} )'
1060+
expected = f'<k>\n {result_str} ~> .K\n</k>'
1061+
1062+
# When
1063+
actual = run(definition_dir, pgm)
1064+
1065+
# Then
1066+
assert expected == actual

plugin-c/crypto.cpp

Lines changed: 157 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -4,6 +4,7 @@
44
#include <cryptopp/sha3.h>
55
#include <openssl/evp.h>
66
#include <secp256k1_recovery.h>
7+
#include <vector>
78

89
#include <libff/algebra/curves/alt_bn128/alt_bn128_pp.hpp>
910
#include <libff/common/profiling.hpp>
@@ -408,6 +409,18 @@ void blst_p2_affine_set_infinity(blst_p2_affine *blstp) {
408409
memset(blstp, 0, sizeof(*blstp));
409410
}
410411

412+
g1point* g1point_inf() {
413+
struct g1point *result = (struct g1point *)kore_alloc(sizeof(struct g1point));
414+
415+
blockheader g1pointhdr =
416+
get_block_header_for_symbol((uint64_t)get_tag_for_symbol_name("Lblg1Point{}"));
417+
result->h = g1pointhdr;
418+
419+
result->x = zero_mpz_ptr();
420+
result->y = zero_mpz_ptr();
421+
return result;
422+
}
423+
411424
g1point* blst_p1_to_g1point(const blst_p1 *p) {
412425
struct g1point *result = (struct g1point *)kore_alloc(sizeof(struct g1point));
413426

@@ -430,6 +443,20 @@ g1point* blst_p1_to_g1point(const blst_p1 *p) {
430443
return result;
431444
}
432445

446+
g2point* g2point_inf() {
447+
struct g2point *result = (struct g2point *)kore_alloc(sizeof(struct g2point));
448+
449+
blockheader g2pointhdr =
450+
get_block_header_for_symbol((uint64_t)get_tag_for_symbol_name("Lblg2Point{}"));
451+
result->h = g2pointhdr;
452+
453+
result->x0 = zero_mpz_ptr();
454+
result->y0 = zero_mpz_ptr();
455+
result->x1 = zero_mpz_ptr();
456+
result->y1 = zero_mpz_ptr();
457+
return result;
458+
}
459+
433460
g2point* blst_p2_to_g2point(const blst_p2 *p) {
434461
struct g2point *result = (struct g2point *)kore_alloc(sizeof(struct g2point));
435462

@@ -556,6 +583,71 @@ struct g1point *hook_KRYPTO_bls12G1Mul(g1point *point, mpz_t scalar) {
556583
return blst_p1_to_g1point(&result);
557584
}
558585

586+
struct g1point *hook_KRYPTO_bls12G1Msm(list* scalars, list* g1) {
587+
mpz_ptr scalars_size = hook_LIST_size(scalars);
588+
mpz_ptr g1size = hook_LIST_size(g1);
589+
unsigned long scalars_size_long = mpz_get_ui(scalars_size);
590+
unsigned long g1size_long = mpz_get_ui(g1size);
591+
mpz_clear(scalars_size);
592+
mpz_clear(g1size);
593+
594+
if (scalars_size_long != g1size_long) {
595+
throw std::invalid_argument("mismatched list sizes");
596+
}
597+
598+
std::vector<blst_p1_affine> points(g1size_long);
599+
std::vector<blst_scalar> blst_scalars(g1size_long);
600+
601+
int valid_point_count = 0;
602+
int first_nbits = 0;
603+
for (unsigned long i = 0; i < g1size_long; i++) {
604+
inj *injg1 = (inj *)hook_LIST_get_long(g1, i);
605+
g1point* g1pt = (g1point *)injg1->data;
606+
607+
if (!g1point_to_blst_p1_affine(&points[valid_point_count], g1pt)) {
608+
throw std::invalid_argument("Invalid point");
609+
}
610+
if (blst_p1_affine_is_inf(&points[valid_point_count])) {
611+
continue;
612+
}
613+
614+
inj *injs1 = (inj *)hook_LIST_get_long(scalars, i);
615+
mpz_ptr scalar = (mpz_ptr)injs1->data;
616+
if (valid_point_count == 0) {
617+
first_nbits = mpz_cmp_ui(scalar, 0) == 0 ? 0 : mpz_sizeinbase(scalar, 2);
618+
}
619+
if (!mpz_ptr_to_blst_scalar(&blst_scalars[valid_point_count], scalar)) {
620+
throw std::invalid_argument("Invalid scalar");
621+
}
622+
valid_point_count++;
623+
}
624+
625+
if (valid_point_count == 0) {
626+
return g1point_inf();
627+
}
628+
if (valid_point_count == 1) {
629+
blst_p1 blstp;
630+
blst_p1_from_affine(&blstp, &points[0]);
631+
blst_p1 result;
632+
blst_p1_mult(&result, &blstp, blst_scalars[0].b, first_nbits);
633+
return blst_p1_to_g1point(&result);
634+
}
635+
636+
size_t scratch_size = blst_p1s_mult_pippenger_scratch_sizeof(valid_point_count);
637+
std::vector<limb_t> scratch(scratch_size / sizeof(limb_t) + 1);
638+
639+
const byte *scalars_arg[2] = {(byte *)blst_scalars.data(), NULL};
640+
const blst_p1_affine *points_arg[2] = {points.data(), NULL};
641+
blst_p1 result;
642+
blst_p1s_mult_pippenger
643+
( &result
644+
, points_arg, valid_point_count
645+
, scalars_arg, sizeof(blst_scalars[0]) * 8
646+
, scratch.data()
647+
);
648+
return blst_p1_to_g1point(&result);
649+
}
650+
559651
struct g2point *hook_KRYPTO_bls12G2Mul(g2point *point, mpz_t scalar) {
560652
blst_scalar blstscalar;
561653
blst_p2 blstp;
@@ -572,6 +664,71 @@ struct g2point *hook_KRYPTO_bls12G2Mul(g2point *point, mpz_t scalar) {
572664
return blst_p2_to_g2point(&result);
573665
}
574666

667+
struct g2point *hook_KRYPTO_bls12G2Msm(list* scalars, list* g2) {
668+
mpz_ptr scalars_size = hook_LIST_size(scalars);
669+
mpz_ptr g2size = hook_LIST_size(g2);
670+
unsigned long scalars_size_long = mpz_get_ui(scalars_size);
671+
unsigned long g2size_long = mpz_get_ui(g2size);
672+
mpz_clear(scalars_size);
673+
mpz_clear(g2size);
674+
675+
if (scalars_size_long != g2size_long) {
676+
throw std::invalid_argument("mismatched list sizes");
677+
}
678+
679+
std::vector<blst_p2_affine> points(g2size_long);
680+
std::vector<blst_scalar> blst_scalars(g2size_long);
681+
682+
int valid_point_count = 0;
683+
int first_nbits = 0;
684+
for (unsigned long i = 0; i < g2size_long; i++) {
685+
inj *injg2 = (inj *)hook_LIST_get_long(g2, i);
686+
g2point* g2pt = (g2point *)injg2->data;
687+
688+
if (!g2point_to_blst_p2_affine(&points[valid_point_count], g2pt)) {
689+
throw std::invalid_argument("Invalid point");
690+
}
691+
if (blst_p2_affine_is_inf(&points[valid_point_count])) {
692+
continue;
693+
}
694+
695+
inj *injs = (inj *)hook_LIST_get_long(scalars, i);
696+
mpz_ptr scalar = (mpz_ptr)injs->data;
697+
if (valid_point_count == 0) {
698+
first_nbits = mpz_cmp_ui(scalar, 0) == 0 ? 0 : mpz_sizeinbase(scalar, 2);
699+
}
700+
if (!mpz_ptr_to_blst_scalar(&blst_scalars[valid_point_count], scalar)) {
701+
throw std::invalid_argument("Invalid scalar");
702+
}
703+
valid_point_count++;
704+
}
705+
706+
if (valid_point_count == 0) {
707+
return g2point_inf();
708+
}
709+
if (valid_point_count == 1) {
710+
blst_p2 blstp;
711+
blst_p2_from_affine(&blstp, &points[0]);
712+
blst_p2 result;
713+
blst_p2_mult(&result, &blstp, blst_scalars[0].b, first_nbits);
714+
return blst_p2_to_g2point(&result);
715+
}
716+
717+
size_t scratch_size = blst_p2s_mult_pippenger_scratch_sizeof(valid_point_count);
718+
std::vector<limb_t> scratch(scratch_size / sizeof(limb_t) + 1);
719+
720+
const byte *scalars_arg[2] = {(byte *)blst_scalars.data(), NULL};
721+
const blst_p2_affine *points_arg[2] = {points.data(), NULL};
722+
blst_p2 result;
723+
blst_p2s_mult_pippenger
724+
( &result
725+
, points_arg, valid_point_count
726+
, scalars_arg, sizeof(blst_scalars[0]) * 8
727+
, scratch.data()
728+
);
729+
return blst_p2_to_g2point(&result);
730+
}
731+
575732
bool hook_KRYPTO_bls12G1InSubgroup(g1point *point) {
576733
blst_p1 blstp;
577734

plugin/krypto.md

Lines changed: 14 additions & 12 deletions
Original file line numberDiff line numberDiff line change
@@ -142,18 +142,20 @@ fit in 256 bits, or the field values do not fit in 384 bits, all BLS12_381
142142
functions evaluate to `#False`.
143143

144144
```k
145-
syntax G1Point ::= BLS12G1Add ( G1Point, G1Point ) [function, hook(KRYPTO.bls12G1Add)]
146-
syntax G2Point ::= BLS12G2Add ( G2Point, G2Point ) [function, hook(KRYPTO.bls12G2Add)]
147-
syntax G1Point ::= BLS12G1Mul ( G1Point, Int ) [function, hook(KRYPTO.bls12G1Mul)]
148-
syntax G2Point ::= BLS12G2Mul ( G2Point, Int ) [function, hook(KRYPTO.bls12G2Mul)]
149-
syntax Bool ::= BLS12PairingCheck ( List, List) [function, hook(KRYPTO.bls12PairingCheck)]
150-
syntax G1Point ::= BLS12MapFpToG1 ( Int ) [function, hook(KRYPTO.bls12MapFpToG1)]
151-
syntax G2Point ::= BLS12MapFp2ToG2 ( Int, Int ) [function, hook(KRYPTO.bls12MapFp2ToG2)]
152-
153-
syntax Bool ::= BLS12G1InSubgroup ( G1Point ) [function, hook(KRYPTO.bls12G1InSubgroup)]
154-
syntax Bool ::= BLS12G2InSubgroup ( G2Point ) [function, hook(KRYPTO.bls12G2InSubgroup)]
155-
syntax Bool ::= BLS12G1OnCurve ( G1Point ) [function, hook(KRYPTO.bls12G1OnCurve)]
156-
syntax Bool ::= BLS12G2OnCurve ( G2Point ) [function, hook(KRYPTO.bls12G2OnCurve)]
145+
syntax G1Point ::= BLS12G1Add ( G1Point, G1Point ) [function, hook(KRYPTO.bls12G1Add)]
146+
syntax G2Point ::= BLS12G2Add ( G2Point, G2Point ) [function, hook(KRYPTO.bls12G2Add)]
147+
syntax G1Point ::= BLS12G1Mul ( G1Point, Int ) [function, hook(KRYPTO.bls12G1Mul)]
148+
syntax G1Point ::= BLS12G1Msm ( scalars:List, points:List ) [function, hook(KRYPTO.bls12G1Msm)]
149+
syntax G2Point ::= BLS12G2Mul ( G2Point, Int ) [function, hook(KRYPTO.bls12G2Mul)]
150+
syntax G2Point ::= BLS12G2Msm ( scalars:List, points:List ) [function, hook(KRYPTO.bls12G2Msm)]
151+
syntax Bool ::= BLS12PairingCheck ( List, List) [function, hook(KRYPTO.bls12PairingCheck)]
152+
syntax G1Point ::= BLS12MapFpToG1 ( Int ) [function, hook(KRYPTO.bls12MapFpToG1)]
153+
syntax G2Point ::= BLS12MapFp2ToG2 ( Int, Int ) [function, hook(KRYPTO.bls12MapFp2ToG2)]
154+
155+
syntax Bool ::= BLS12G1InSubgroup ( G1Point ) [function, hook(KRYPTO.bls12G1InSubgroup)]
156+
syntax Bool ::= BLS12G2InSubgroup ( G2Point ) [function, hook(KRYPTO.bls12G2InSubgroup)]
157+
syntax Bool ::= BLS12G1OnCurve ( G1Point ) [function, hook(KRYPTO.bls12G1OnCurve)]
158+
syntax Bool ::= BLS12G2OnCurve ( G2Point ) [function, hook(KRYPTO.bls12G2OnCurve)]
157159
158160
syntax Int ::= "BLS12_FIELD_MODULUS" [alias]
159161
rule BLS12_FIELD_MODULUS => 4002409555221667393417789825735904156556882819939007885332058136124031650490837864442687629129015664037894272559787

0 commit comments

Comments
 (0)