summaryrefslogtreecommitdiff
path: root/src
diff options
context:
space:
mode:
authortb <>2024-11-30 16:18:01 +0000
committertb <>2024-11-30 16:18:01 +0000
commita6c5e92ff8006052ab380ce91bd003b45d6eb794 (patch)
tree8e412cf44f05ab80a79f627987d104e6c89f2753 /src
parent3f6eb9bb1205bfa957d4cfefd8e4f33fd954952a (diff)
downloadopenbsd-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.c212
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
1123ec_points_make_affine(const EC_GROUP *group, size_t num, EC_POINT *points[], 1123ec_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