Все точки на эллиптической кривой можно разделить на так называемые циклические подгруппы. В разных вариантах кривой может быть разное количество подгрупп, а называется это число
h ко-фактор (cofactor). Например, для secp256k1 кофактор равен 1, то есть все точки лежат в одной подгруппе. Также, общее количество вообще всех точек во всех подгруппах называется порядок кривой n. Для уже известной нам кривой secp256k1 значение n равно (внимание) FFFFFFFF FFFFFFFF FFFFFFFF FFFFFFFE BAAEDCE6 AF48A03B BFD25E8C D0364141 - это в виде hex. То есть вот столько там есть возможных точек и, соответственно, ключей.Почему важны все эти кофакторы? Потому что генераторная точка G может сгенерировать любую другую точку в рамках своей подгруппы, если сделать достаточно большое количество "прыжков" (то есть умножений). Частным случаем является умножение на 0 - там получается бесконечность и такая точка нам, конечно, не подходит. Таким образом, для кривой secp256k1 мы можем попасть в каждую другую точку из генераторной. Собственно, G выбрана умными людьми специально, чтобы из неё можно было попасть в наибольшее количество других точек. Именно поэтому следует использовать уже готовые и протестированные кривые, а не придумывать собственную.
И последний момент, связанный со сжатием координат
(x, y) для наших открытых ключей. Дело в том, что у кривой Вейерштрасса есть другое любопытное свойство: на каждую координату там приходится максимум две точки, что проиллюстрировано на этом рисунке https://60896510-files.gitbook.io/~/files/v0/b/gitbook-x-prod.appspot.com/o/spaces%2F-LhlOQMrG9bRiqWpegM0%2Fuploads%2Fgit-blob-88e7096ea7710831737bc083a128836c03c6da9b%2Felliptic-curve-over-f17-points-per-y-coordinate.png?alt=mediaБольше того, там выходит, что если есть две точки с одинаковыми координатами по оси X, то по для одной точки по Y будет чётная координата, а для другой - нечётная. Неплохо, да? Это значит, что для каждой точки мы можем оставить только её координату по X, а Y выразить как чёт-нечёт, то есть занять фактически лишь один бит. Таким образом, мы можем сжать 512-битную пару
(x, y) до 257 бит."Расжать" координату по Y можно по формулам
y1 = mod_sqrt(x ** 3 + ax + b, p) и y2 = p - mod_sqrt(x ** 3 + ax + b, p), где mod_sqrt - это квадратный корень с операцией модуля (он считается по алгоритму Тонелли-Шенкса, но это не столь важно https://en.wikipedia.org/wiki/Tonelli%E2%80%93Shanks_algorithm). Получив две координаты по Y мы просто выбираем либо чётную, либо нечётную. Хитро, правда?Ну, и подведём итог. Для того, чтобы посчитать открытый ключ с помощью ECC нам требуется:
- Выбрать кривую, например, secp256k1. Выбранная кривая влияет на параметры
a и b, у неё есть известная генераторная точка, а также известный порядок и кофактор.- Каким-то образом создать закрытый ключ k (выбрать случайное число, использовать мнемоническую фразу или как-то ещё).
- Умножить G на k и получить финальные координаты точки P, открытого ключа.
В Ethereum, кстати, потом происходит интересная штука, так как открытый ключ хэшируется, а последние 20 байт (160 бит) этого хэша становятся адресом аккаунта, которому этот публичный ключ соответствует. Так как хэш представляет собой числа hex, то и адрес тоже является шестнадцатиричным числом. А это значит, что мы можем превратить его в обычное число, если вдруг такая необходимость есть
uint256(uint160(some_addr)). В других системах может быть такое, что сам публичный ключ и выступает адресом.