Created
January 1, 2026 18:17
-
-
Save kelbyludwig/4af1aa477c8decc7878c58216068928d to your computer and use it in GitHub Desktop.
Notebook version of https://kel.bz/post/sage-p256/
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
| { | |
| "cells": [ | |
| { | |
| "cell_type": "markdown", | |
| "metadata": {}, | |
| "source": [ | |
| "This notebook demonstrates how to create a NIST P-256 curve ([aka secp256r1](https://tools.ietf.org/search/rfc4492#appendix-A)) and it's standard base point in [Sagemath](https://www.sagemath.org/).\n", | |
| "\n", | |
| "First, we define the parameters that make up the P-256 curve. The parameters are from \"[SEC 2: Recommended Elliptic Curve Domain Parameters](https://www.secg.org/SEC2-Ver-1.0.pdf)\"." | |
| ] | |
| }, | |
| { | |
| "cell_type": "code", | |
| "execution_count": 1, | |
| "metadata": {}, | |
| "outputs": [], | |
| "source": [ | |
| "# Finite field prime\n", | |
| "p256 = 0xFFFFFFFF00000001000000000000000000000000FFFFFFFFFFFFFFFFFFFFFFFF\n", | |
| "\n", | |
| "# Curve parameters for the curve equation: y^2 = x^3 + a256*x +b256\n", | |
| "a256 = p256 - 3 \n", | |
| "b256 = 0x5AC635D8AA3A93E7B3EBBD55769886BC651D06B0CC53B0F63BCE3C3E27D2604B\n", | |
| "\n", | |
| "# Base point (x, y) \n", | |
| "gx = 0x6B17D1F2E12C4247F8BCE6E563A440F277037D812DEB33A0F4A13945D898C296\n", | |
| "gy = 0x4FE342E2FE1A7F9B8EE7EB4A7C0F9E162BCE33576B315ECECBB6406837BF51F5\n", | |
| "\n", | |
| "# Curve order\n", | |
| "qq = 0xFFFFFFFF00000000FFFFFFFFFFFFFFFFBCE6FAADA7179E84F3B9CAC2FC632551" | |
| ] | |
| }, | |
| { | |
| "cell_type": "markdown", | |
| "metadata": {}, | |
| "source": [ | |
| "Then we can create a EllipticCurve sage object over a finite field." | |
| ] | |
| }, | |
| { | |
| "cell_type": "code", | |
| "execution_count": 2, | |
| "metadata": {}, | |
| "outputs": [], | |
| "source": [ | |
| "# Create a finite field of order p256\n", | |
| "FF = GF(p256) \n", | |
| "\n", | |
| "# Define a curve over that field with specified Weierstrass a and b parameters\n", | |
| "EC = EllipticCurve([FF(a256), FF(b256)]) \n", | |
| "\n", | |
| "# Since we know P-256's order we can skip computing it and set it explicitly\n", | |
| "EC.set_order(qq) \n", | |
| "\n", | |
| "# Create a variable for the base point\n", | |
| "G = EC(FF(gx), FF(gy))" | |
| ] | |
| }, | |
| { | |
| "cell_type": "markdown", | |
| "metadata": {}, | |
| "source": [ | |
| "We can compare results to a few [public test vectors](http://point-at-infinity.org/ecc/nisttv) to make sure everything is working as intended.\n", | |
| "\n", | |
| "These test vectors are defined as three-tuples: (scalar `k`, x-coordinate of `k*G`, y-coordinate of `k*G`)" | |
| ] | |
| }, | |
| { | |
| "cell_type": "code", | |
| "execution_count": 3, | |
| "metadata": {}, | |
| "outputs": [], | |
| "source": [ | |
| "test_vectors = [\n", | |
| " (1, \n", | |
| " 0x6B17D1F2E12C4247F8BCE6E563A440F277037D812DEB33A0F4A13945D898C296, \n", | |
| " 0x4FE342E2FE1A7F9B8EE7EB4A7C0F9E162BCE33576B315ECECBB6406837BF51F5),\n", | |
| " (2,\n", | |
| " 0x7CF27B188D034F7E8A52380304B51AC3C08969E277F21B35A60B48FC47669978,\n", | |
| " 0x07775510DB8ED040293D9AC69F7430DBBA7DADE63CE982299E04B79D227873D1),\n", | |
| " (3,\n", | |
| " 0x5ECBE4D1A6330A44C8F7EF951D4BF165E6C6B721EFADA985FB41661BC6E7FD6C,\n", | |
| " 0x8734640C4998FF7E374B06CE1A64A2ECD82AB036384FB83D9A79B127A27D5032),\n", | |
| " (4,\n", | |
| " 0xE2534A3532D08FBBA02DDE659EE62BD0031FE2DB785596EF509302446B030852,\n", | |
| " 0xE0F1575A4C633CC719DFEE5FDA862D764EFC96C3F30EE0055C42C23F184ED8C6),\n", | |
| " (5,\n", | |
| " 0x51590B7A515140D2D784C85608668FDFEF8C82FD1F5BE52421554A0DC3D033ED,\n", | |
| " 0xE0C17DA8904A727D8AE1BF36BF8A79260D012F00D4D80888D1D0BB44FDA16DA4)\n", | |
| "]\n", | |
| "\n", | |
| "for k, x, y in test_vectors:\n", | |
| " P = k*G\n", | |
| " Px, Py = P.xy()\n", | |
| " assert Px == x\n", | |
| " assert Py == y" | |
| ] | |
| }, | |
| { | |
| "cell_type": "markdown", | |
| "metadata": {}, | |
| "source": [ | |
| "That seems alright. We can do a simple ECDH to double check as well." | |
| ] | |
| }, | |
| { | |
| "cell_type": "code", | |
| "execution_count": 4, | |
| "metadata": {}, | |
| "outputs": [], | |
| "source": [ | |
| "for _ in range(100):\n", | |
| " alice_private = randint(0, qq-1)\n", | |
| " alice_public = alice_private*G\n", | |
| "\n", | |
| " bob_private = randint(0, qq-1)\n", | |
| " bob_public = bob_private*G\n", | |
| "\n", | |
| " assert alice_private*bob_public == bob_private*alice_public" | |
| ] | |
| } | |
| ], | |
| "metadata": { | |
| "kernelspec": { | |
| "display_name": "SageMath 8.6", | |
| "language": "", | |
| "name": "sagemath" | |
| }, | |
| "language_info": { | |
| "codemirror_mode": { | |
| "name": "ipython", | |
| "version": 2 | |
| }, | |
| "file_extension": ".py", | |
| "mimetype": "text/x-python", | |
| "name": "python", | |
| "nbconvert_exporter": "python", | |
| "pygments_lexer": "ipython2", | |
| "version": "2.7.16" | |
| } | |
| }, | |
| "nbformat": 4, | |
| "nbformat_minor": 2 | |
| } |
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment