| // Copyright 2011-2016 The OpenSSL Project Authors. All Rights Reserved. |
| // Copyright (c) 2002, Oracle and/or its affiliates. All rights reserved. |
| // |
| // Licensed under the Apache License, Version 2.0 (the "License"); |
| // you may not use this file except in compliance with the License. |
| // You may obtain a copy of the License at |
| // |
| // https://www.apache.org/licenses/LICENSE-2.0 |
| // |
| // Unless required by applicable law or agreed to in writing, software |
| // distributed under the License is distributed on an "AS IS" BASIS, |
| // WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. |
| // See the License for the specific language governing permissions and |
| // limitations under the License. |
| |
| #include <openssl/ec.h> |
| |
| #include <openssl/bn.h> |
| #include <openssl/err.h> |
| |
| #include "internal.h" |
| |
| |
| using namespace bssl; |
| |
| size_t bssl::ec_point_byte_len(const EC_GROUP *group, |
| point_conversion_form_t form) { |
| if (form != POINT_CONVERSION_COMPRESSED && |
| form != POINT_CONVERSION_UNCOMPRESSED) { |
| OPENSSL_PUT_ERROR(EC, EC_R_INVALID_FORM); |
| return 0; |
| } |
| |
| const size_t field_len = BN_num_bytes(&group->field.N); |
| size_t output_len = 1 /* type byte */ + field_len; |
| if (form == POINT_CONVERSION_UNCOMPRESSED) { |
| // Uncompressed points have a second coordinate. |
| output_len += field_len; |
| } |
| return output_len; |
| } |
| |
| size_t bssl::ec_point_to_bytes(const EC_GROUP *group, const EC_AFFINE *point, |
| point_conversion_form_t form, uint8_t *buf, |
| size_t max_out) { |
| size_t output_len = ec_point_byte_len(group, form); |
| if (max_out < output_len) { |
| OPENSSL_PUT_ERROR(EC, EC_R_BUFFER_TOO_SMALL); |
| return 0; |
| } |
| |
| size_t field_len; |
| ec_felem_to_bytes(group, buf + 1, &field_len, &point->X); |
| assert(field_len == BN_num_bytes(&group->field.N)); |
| |
| if (form == POINT_CONVERSION_UNCOMPRESSED) { |
| ec_felem_to_bytes(group, buf + 1 + field_len, &field_len, &point->Y); |
| assert(field_len == BN_num_bytes(&group->field.N)); |
| buf[0] = form; |
| } else { |
| uint8_t y_buf[EC_MAX_BYTES]; |
| ec_felem_to_bytes(group, y_buf, &field_len, &point->Y); |
| buf[0] = form + (y_buf[field_len - 1] & 1); |
| } |
| |
| return output_len; |
| } |
| |
| int bssl::ec_point_from_uncompressed(const EC_GROUP *group, EC_AFFINE *out, |
| const uint8_t *in, size_t len) { |
| const size_t field_len = BN_num_bytes(&group->field.N); |
| if (len != 1 /* form */ + 2 * field_len || |
| in[0] != POINT_CONVERSION_UNCOMPRESSED) { |
| OPENSSL_PUT_ERROR(EC, EC_R_INVALID_ENCODING); |
| return 0; |
| } |
| |
| EC_FELEM x, y; |
| return ec_felem_from_bytes(group, &x, in + 1, field_len) && |
| ec_felem_from_bytes(group, &y, in + 1 + field_len, field_len) && |
| ec_point_set_affine_coordinates(group, out, &x, &y); |
| } |
| |
| int bssl::ec_point_from_compressed(const EC_GROUP *group, EC_AFFINE *out, |
| const uint8_t *in, size_t len) { |
| const size_t field_len = BN_num_bytes(&group->field.N); |
| if (len != 1 /* form */ + field_len || |
| (in[0] & ~1u) != POINT_CONVERSION_COMPRESSED) { |
| OPENSSL_PUT_ERROR(EC, EC_R_INVALID_ENCODING); |
| return 0; |
| } |
| |
| crypto_word_t y_bit = in[0] & 1; |
| EC_FELEM x; |
| return ec_felem_from_bytes(group, &x, in + 1, field_len) && |
| ec_point_set_compressed_coordinates(group, out, &x, y_bit); |
| } |
| |
| static int ec_GFp_simple_oct2point(const EC_GROUP *group, EC_POINT *point, |
| const uint8_t *buf, size_t len, |
| BN_CTX *ctx) { |
| if (len == 0) { |
| OPENSSL_PUT_ERROR(EC, EC_R_BUFFER_TOO_SMALL); |
| return 0; |
| } |
| |
| const uint8_t form = buf[0]; |
| EC_AFFINE affine; |
| bool ok = form == static_cast<uint8_t>(POINT_CONVERSION_UNCOMPRESSED) |
| ? ec_point_from_uncompressed(group, &affine, buf, len) |
| : ec_point_from_compressed(group, &affine, buf, len); |
| if (!ok) { |
| // In the event of an error, defend against the caller not checking the |
| // return value by setting a known safe value. |
| ec_set_to_safe_point(group, &point->raw); |
| return 0; |
| } |
| ec_affine_to_jacobian(group, &point->raw, &affine); |
| return 1; |
| } |
| |
| int EC_POINT_oct2point(const EC_GROUP *group, EC_POINT *point, |
| const uint8_t *buf, size_t len, BN_CTX *ctx) { |
| if (EC_GROUP_cmp(group, point->group, nullptr) != 0) { |
| OPENSSL_PUT_ERROR(EC, EC_R_INCOMPATIBLE_OBJECTS); |
| return 0; |
| } |
| return ec_GFp_simple_oct2point(group, point, buf, len, ctx); |
| } |
| |
| size_t EC_POINT_point2oct(const EC_GROUP *group, const EC_POINT *point, |
| point_conversion_form_t form, uint8_t *buf, |
| size_t max_out, BN_CTX *ctx) { |
| if (EC_GROUP_cmp(group, point->group, nullptr) != 0) { |
| OPENSSL_PUT_ERROR(EC, EC_R_INCOMPATIBLE_OBJECTS); |
| return 0; |
| } |
| if (buf == nullptr) { |
| // When `buf` is NULL, just return the number of bytes that would be |
| // written, without doing an expensive Jacobian-to-affine conversion. |
| if (constant_time_declassify_int( |
| ec_GFp_simple_is_at_infinity(group, &point->raw))) { |
| OPENSSL_PUT_ERROR(EC, EC_R_POINT_AT_INFINITY); |
| return 0; |
| } |
| return ec_point_byte_len(group, form); |
| } |
| EC_AFFINE affine; |
| if (!ec_jacobian_to_affine(group, &affine, &point->raw)) { |
| return 0; |
| } |
| return ec_point_to_bytes(group, &affine, form, buf, max_out); |
| } |
| |
| size_t EC_POINT_point2buf(const EC_GROUP *group, const EC_POINT *point, |
| point_conversion_form_t form, uint8_t **out_buf, |
| BN_CTX *ctx) { |
| *out_buf = nullptr; |
| size_t len = EC_POINT_point2oct(group, point, form, nullptr, 0, ctx); |
| if (len == 0) { |
| return 0; |
| } |
| uint8_t *buf = reinterpret_cast<uint8_t *>(OPENSSL_malloc(len)); |
| if (buf == nullptr) { |
| return 0; |
| } |
| len = EC_POINT_point2oct(group, point, form, buf, len, ctx); |
| if (len == 0) { |
| OPENSSL_free(buf); |
| return 0; |
| } |
| *out_buf = buf; |
| return len; |
| } |