diff options
author | tb <> | 2024-11-30 16:18:01 +0000 |
---|---|---|
committer | tb <> | 2024-11-30 16:18:01 +0000 |
commit | a6c5e92ff8006052ab380ce91bd003b45d6eb794 (patch) | |
tree | 8e412cf44f05ab80a79f627987d104e6c89f2753 /src | |
parent | 3f6eb9bb1205bfa957d4cfefd8e4f33fd954952a (diff) | |
download | openbsd-a6c5e92ff8006052ab380ce91bd003b45d6eb794.tar.gz openbsd-a6c5e92ff8006052ab380ce91bd003b45d6eb794.tar.bz2 openbsd-a6c5e92ff8006052ab380ce91bd003b45d6eb794.zip |
Improve ec_points_make_affine()
It is unclear how the original code was supposed to work. It clearly
missed a few corner cases (like handling points at infinity correctly)
and the badly mangled comment that was supposed to display a binary
search tree didn't help at all.
Instead do something much more straightforward: multiply all the non-zero
Z coordinates of the points not at infinity together, keeping track of the
intermediate products. Then do a single expensive modular inversion before
working backwards to compute all the inverses. Then the transformation from
Jacobian coordinates to affine coordiantes (x, y, z) -> (x/z^2, y/z^3, 1)
becomes cheap. A little bit of care has to be taken for Montgomery curves
but that's very simple compared to the mess that was there before.
ok jsing
This is a cleaned up version of:
commit 0fe73d6c3641cb175871463bdddbbea3ee0b62ae
Author: Bodo Moeller <bodo@openssl.org>
Date: Fri Aug 1 17:18:14 2014 +0200
Simplify and fix ec_GFp_simple_points_make_affine
(which didn't always handle value 0 correctly).
Reviewed-by: emilia@openssl.org
Diffstat (limited to 'src')
-rw-r--r-- | src/lib/libcrypto/ec/ecp_methods.c | 212 |
1 files changed, 93 insertions, 119 deletions
diff --git a/src/lib/libcrypto/ec/ecp_methods.c b/src/lib/libcrypto/ec/ecp_methods.c index 65dfd5ef00..a4713bdcdc 100644 --- a/src/lib/libcrypto/ec/ecp_methods.c +++ b/src/lib/libcrypto/ec/ecp_methods.c | |||
@@ -1,4 +1,4 @@ | |||
1 | /* $OpenBSD: ecp_methods.c,v 1.9 2024/11/17 08:19:08 tb Exp $ */ | 1 | /* $OpenBSD: ecp_methods.c,v 1.10 2024/11/30 16:18:01 tb Exp $ */ |
2 | /* Includes code written by Lenka Fibikova <fibikova@exp-math.uni-essen.de> | 2 | /* Includes code written by Lenka Fibikova <fibikova@exp-math.uni-essen.de> |
3 | * for the OpenSSL project. | 3 | * for the OpenSSL project. |
4 | * Includes code written by Bodo Moeller for the OpenSSL project. | 4 | * Includes code written by Bodo Moeller for the OpenSSL project. |
@@ -1123,9 +1123,8 @@ static int | |||
1123 | ec_points_make_affine(const EC_GROUP *group, size_t num, EC_POINT *points[], | 1123 | ec_points_make_affine(const EC_GROUP *group, size_t num, EC_POINT *points[], |
1124 | BN_CTX *ctx) | 1124 | BN_CTX *ctx) |
1125 | { | 1125 | { |
1126 | BIGNUM *tmp0, *tmp1; | 1126 | BIGNUM **prod_Z = NULL; |
1127 | size_t pow2 = 0; | 1127 | BIGNUM *tmp, *tmp_Z; |
1128 | BIGNUM **heap = NULL; | ||
1129 | size_t i; | 1128 | size_t i; |
1130 | int ret = 0; | 1129 | int ret = 0; |
1131 | 1130 | ||
@@ -1134,141 +1133,120 @@ ec_points_make_affine(const EC_GROUP *group, size_t num, EC_POINT *points[], | |||
1134 | 1133 | ||
1135 | BN_CTX_start(ctx); | 1134 | BN_CTX_start(ctx); |
1136 | 1135 | ||
1137 | if ((tmp0 = BN_CTX_get(ctx)) == NULL) | 1136 | if ((tmp = BN_CTX_get(ctx)) == NULL) |
1138 | goto err; | 1137 | goto err; |
1139 | if ((tmp1 = BN_CTX_get(ctx)) == NULL) | 1138 | if ((tmp_Z = BN_CTX_get(ctx)) == NULL) |
1140 | goto err; | 1139 | goto err; |
1141 | 1140 | ||
1142 | /* | 1141 | if ((prod_Z = calloc(num, sizeof *prod_Z)) == NULL) |
1143 | * Before converting the individual points, compute inverses of all Z | ||
1144 | * values. Modular inversion is rather slow, but luckily we can do | ||
1145 | * with a single explicit inversion, plus about 3 multiplications per | ||
1146 | * input value. | ||
1147 | */ | ||
1148 | |||
1149 | pow2 = 1; | ||
1150 | while (num > pow2) | ||
1151 | pow2 <<= 1; | ||
1152 | /* | ||
1153 | * Now pow2 is the smallest power of 2 satifsying pow2 >= num. We | ||
1154 | * need twice that. | ||
1155 | */ | ||
1156 | pow2 <<= 1; | ||
1157 | |||
1158 | heap = reallocarray(NULL, pow2, sizeof heap[0]); | ||
1159 | if (heap == NULL) | ||
1160 | goto err; | 1142 | goto err; |
1143 | for (i = 0; i < num; i++) { | ||
1144 | if ((prod_Z[i] = BN_new()) == NULL) | ||
1145 | goto err; | ||
1146 | } | ||
1161 | 1147 | ||
1162 | /* | 1148 | /* |
1163 | * The array is used as a binary tree, exactly as in heapsort: | 1149 | * Set prod_Z[i] to the product of points[0]->Z, ..., points[i]->Z, |
1164 | * | 1150 | * skipping any zero-valued inputs (pretend that they're 1). |
1165 | * heap[1] heap[2] heap[3] heap[4] heap[5] | ||
1166 | * heap[6] heap[7] heap[8]heap[9] heap[10]heap[11] | ||
1167 | * heap[12]heap[13] heap[14] heap[15] | ||
1168 | * | ||
1169 | * We put the Z's in the last line; then we set each other node to the | ||
1170 | * product of its two child-nodes (where empty or 0 entries are | ||
1171 | * treated as ones); then we invert heap[1]; then we invert each | ||
1172 | * other node by replacing it by the product of its parent (after | ||
1173 | * inversion) and its sibling (before inversion). | ||
1174 | */ | 1151 | */ |
1175 | heap[0] = NULL; | ||
1176 | for (i = pow2 / 2 - 1; i > 0; i--) | ||
1177 | heap[i] = NULL; | ||
1178 | for (i = 0; i < num; i++) | ||
1179 | heap[pow2 / 2 + i] = &points[i]->Z; | ||
1180 | for (i = pow2 / 2 + num; i < pow2; i++) | ||
1181 | heap[i] = NULL; | ||
1182 | |||
1183 | /* set each node to the product of its children */ | ||
1184 | for (i = pow2 / 2 - 1; i > 0; i--) { | ||
1185 | heap[i] = BN_new(); | ||
1186 | if (heap[i] == NULL) | ||
1187 | goto err; | ||
1188 | 1152 | ||
1189 | if (heap[2 * i] != NULL) { | 1153 | if (!BN_is_zero(&points[0]->Z)) { |
1190 | if ((heap[2 * i + 1] == NULL) || BN_is_zero(heap[2 * i + 1])) { | 1154 | if (!bn_copy(prod_Z[0], &points[0]->Z)) |
1191 | if (!bn_copy(heap[i], heap[2 * i])) | 1155 | goto err; |
1192 | goto err; | 1156 | } else { |
1193 | } else { | 1157 | if (group->meth->field_set_to_one != NULL) { |
1194 | if (BN_is_zero(heap[2 * i])) { | 1158 | if (!group->meth->field_set_to_one(group, prod_Z[0], ctx)) |
1195 | if (!bn_copy(heap[i], heap[2 * i + 1])) | 1159 | goto err; |
1196 | goto err; | 1160 | } else { |
1197 | } else { | 1161 | if (!BN_one(prod_Z[0])) |
1198 | if (!group->meth->field_mul(group, heap[i], | 1162 | goto err; |
1199 | heap[2 * i], heap[2 * i + 1], ctx)) | ||
1200 | goto err; | ||
1201 | } | ||
1202 | } | ||
1203 | } | 1163 | } |
1204 | } | 1164 | } |
1205 | 1165 | ||
1206 | /* invert heap[1] */ | 1166 | for (i = 1; i < num; i++) { |
1207 | if (!BN_is_zero(heap[1])) { | 1167 | if (!BN_is_zero(&points[i]->Z)) { |
1208 | if (BN_mod_inverse_ct(heap[1], heap[1], &group->field, ctx) == NULL) { | 1168 | if (!group->meth->field_mul(group, prod_Z[i], |
1209 | ECerror(ERR_R_BN_LIB); | 1169 | prod_Z[i - 1], &points[i]->Z, ctx)) |
1210 | goto err; | 1170 | goto err; |
1171 | } else { | ||
1172 | if (!bn_copy(prod_Z[i], prod_Z[i - 1])) | ||
1173 | goto err; | ||
1211 | } | 1174 | } |
1212 | } | 1175 | } |
1176 | |||
1177 | /* | ||
1178 | * Now use a single explicit inversion to replace every non-zero | ||
1179 | * points[i]->Z by its inverse. | ||
1180 | */ | ||
1181 | if (!BN_mod_inverse_nonct(tmp, prod_Z[num - 1], &group->field, ctx)) { | ||
1182 | ECerror(ERR_R_BN_LIB); | ||
1183 | goto err; | ||
1184 | } | ||
1185 | |||
1213 | if (group->meth->field_encode != NULL) { | 1186 | if (group->meth->field_encode != NULL) { |
1214 | /* | 1187 | /* |
1215 | * in the Montgomery case, we just turned R*H (representing | 1188 | * In the Montgomery case we just turned R*H (representing H) |
1216 | * H) into 1/(R*H), but we need R*(1/H) (representing | 1189 | * into 1/(R*H), but we need R*(1/H) (representing 1/H); i.e., |
1217 | * 1/H); i.e. we have need to multiply by the Montgomery | 1190 | * we need to multiply by the Montgomery factor twice. |
1218 | * factor twice | ||
1219 | */ | 1191 | */ |
1220 | if (!group->meth->field_encode(group, heap[1], heap[1], ctx)) | 1192 | if (!group->meth->field_encode(group, tmp, tmp, ctx)) |
1221 | goto err; | 1193 | goto err; |
1222 | if (!group->meth->field_encode(group, heap[1], heap[1], ctx)) | 1194 | if (!group->meth->field_encode(group, tmp, tmp, ctx)) |
1223 | goto err; | 1195 | goto err; |
1224 | } | 1196 | } |
1225 | /* set other heap[i]'s to their inverses */ | 1197 | |
1226 | for (i = 2; i < pow2 / 2 + num; i += 2) { | 1198 | for (i = num - 1; i > 0; i--) { |
1227 | /* i is even */ | 1199 | /* |
1228 | if ((heap[i + 1] != NULL) && !BN_is_zero(heap[i + 1])) { | 1200 | * Loop invariant: tmp is the product of the inverses of |
1229 | if (!group->meth->field_mul(group, tmp0, heap[i / 2], heap[i + 1], ctx)) | 1201 | * points[0]->Z, ..., points[i]->Z (zero-valued inputs skipped). |
1230 | goto err; | 1202 | */ |
1231 | if (!group->meth->field_mul(group, tmp1, heap[i / 2], heap[i], ctx)) | 1203 | if (BN_is_zero(&points[i]->Z)) |
1232 | goto err; | 1204 | continue; |
1233 | if (!bn_copy(heap[i], tmp0)) | 1205 | |
1234 | goto err; | 1206 | /* Set tmp_Z to the inverse of points[i]->Z. */ |
1235 | if (!bn_copy(heap[i + 1], tmp1)) | 1207 | if (!group->meth->field_mul(group, tmp_Z, prod_Z[i - 1], tmp, ctx)) |
1236 | goto err; | 1208 | goto err; |
1237 | } else { | 1209 | /* Adjust tmp to satisfy loop invariant. */ |
1238 | if (!bn_copy(heap[i], heap[i / 2])) | 1210 | if (!group->meth->field_mul(group, tmp, tmp, &points[i]->Z, ctx)) |
1239 | goto err; | 1211 | goto err; |
1240 | } | 1212 | /* Replace points[i]->Z by its inverse. */ |
1213 | if (!bn_copy(&points[i]->Z, tmp_Z)) | ||
1214 | goto err; | ||
1241 | } | 1215 | } |
1242 | 1216 | ||
1243 | /* | 1217 | if (!BN_is_zero(&points[0]->Z)) { |
1244 | * we have replaced all non-zero Z's by their inverses, now fix up | 1218 | /* Replace points[0]->Z by its inverse. */ |
1245 | * all the points | 1219 | if (!bn_copy(&points[0]->Z, tmp)) |
1246 | */ | 1220 | goto err; |
1221 | } | ||
1222 | |||
1223 | /* Finally, fix up the X and Y coordinates for all points. */ | ||
1247 | for (i = 0; i < num; i++) { | 1224 | for (i = 0; i < num; i++) { |
1248 | EC_POINT *p = points[i]; | 1225 | EC_POINT *p = points[i]; |
1249 | 1226 | ||
1250 | if (!BN_is_zero(&p->Z)) { | 1227 | if (BN_is_zero(&p->Z)) |
1251 | /* turn (X, Y, 1/Z) into (X/Z^2, Y/Z^3, 1) */ | 1228 | continue; |
1252 | 1229 | ||
1253 | if (!group->meth->field_sqr(group, tmp1, &p->Z, ctx)) | 1230 | /* turn (X, Y, 1/Z) into (X/Z^2, Y/Z^3, 1) */ |
1254 | goto err; | 1231 | |
1255 | if (!group->meth->field_mul(group, &p->X, &p->X, tmp1, ctx)) | 1232 | if (!group->meth->field_sqr(group, tmp, &p->Z, ctx)) |
1256 | goto err; | 1233 | goto err; |
1234 | if (!group->meth->field_mul(group, &p->X, &p->X, tmp, ctx)) | ||
1235 | goto err; | ||
1257 | 1236 | ||
1258 | if (!group->meth->field_mul(group, tmp1, tmp1, &p->Z, ctx)) | 1237 | if (!group->meth->field_mul(group, tmp, tmp, &p->Z, ctx)) |
1238 | goto err; | ||
1239 | if (!group->meth->field_mul(group, &p->Y, &p->Y, tmp, ctx)) | ||
1240 | goto err; | ||
1241 | |||
1242 | if (group->meth->field_set_to_one != NULL) { | ||
1243 | if (!group->meth->field_set_to_one(group, &p->Z, ctx)) | ||
1259 | goto err; | 1244 | goto err; |
1260 | if (!group->meth->field_mul(group, &p->Y, &p->Y, tmp1, ctx)) | 1245 | } else { |
1246 | if (!BN_one(&p->Z)) | ||
1261 | goto err; | 1247 | goto err; |
1262 | |||
1263 | if (group->meth->field_set_to_one != NULL) { | ||
1264 | if (!group->meth->field_set_to_one(group, &p->Z, ctx)) | ||
1265 | goto err; | ||
1266 | } else { | ||
1267 | if (!BN_one(&p->Z)) | ||
1268 | goto err; | ||
1269 | } | ||
1270 | p->Z_is_one = 1; | ||
1271 | } | 1248 | } |
1249 | p->Z_is_one = 1; | ||
1272 | } | 1250 | } |
1273 | 1251 | ||
1274 | ret = 1; | 1252 | ret = 1; |
@@ -1276,16 +1254,12 @@ ec_points_make_affine(const EC_GROUP *group, size_t num, EC_POINT *points[], | |||
1276 | err: | 1254 | err: |
1277 | BN_CTX_end(ctx); | 1255 | BN_CTX_end(ctx); |
1278 | 1256 | ||
1279 | if (heap != NULL) { | 1257 | if (prod_Z != NULL) { |
1280 | /* | 1258 | for (i = 0; i < num; i++) |
1281 | * heap[pow2/2] .. heap[pow2-1] have not been allocated | 1259 | BN_free(prod_Z[i]); |
1282 | * locally! | 1260 | free(prod_Z); |
1283 | */ | ||
1284 | for (i = pow2 / 2 - 1; i > 0; i--) { | ||
1285 | BN_free(heap[i]); | ||
1286 | } | ||
1287 | free(heap); | ||
1288 | } | 1261 | } |
1262 | |||
1289 | return ret; | 1263 | return ret; |
1290 | } | 1264 | } |
1291 | 1265 | ||