commit
82f8dd2c23
36 changed files with 5079 additions and 0 deletions
-
139.github/workflows/mikrotik_patch.yml
-
2.gitignore
-
15README.md
-
BINinstall.png
-
BINkeygen.exe
-
BINkeygen.png
-
213mikro.py
-
BINmikrotik.ico
-
297npk.py
-
111patch.py
-
BINrouteros.png
-
317sha256.py
-
188toyecc/ASN1.py
-
132toyecc/AffineCurvePoint.py
-
59toyecc/CRT.py
-
842toyecc/CurveDB.py
-
140toyecc/CurveOps.py
-
77toyecc/CurveQuirks.py
-
67toyecc/DivisionPolynomial.py
-
41toyecc/DocInherit.py
-
79toyecc/ECPrivateKey.py
-
44toyecc/ECPublicKey.py
-
202toyecc/EllipticCurve.py
-
34toyecc/Exceptions.py
-
249toyecc/FieldElement.py
-
164toyecc/MontgomeryCurve.py
-
258toyecc/PointOps.py
-
331toyecc/Polynomial.py
-
228toyecc/PrivKeyOps.py
-
166toyecc/PubKeyOps.py
-
48toyecc/Random.py
-
203toyecc/ShortWeierstrassCurve.py
-
69toyecc/Singleton.py
-
98toyecc/Tools.py
-
171toyecc/TwistedEdwardsCurve.py
-
95toyecc/__init__.py
@ -0,0 +1,139 @@ |
|||
name: Patch Mikrotik RouterOS |
|||
on: |
|||
# push: |
|||
# branches: [ "main" ] |
|||
schedule: |
|||
- cron: "0 0 * * *" |
|||
workflow_dispatch: |
|||
|
|||
permissions: |
|||
contents: write |
|||
|
|||
jobs: |
|||
Patch_Mikrotik_RouterOS: |
|||
runs-on: ubuntu-latest |
|||
env: |
|||
TZ: 'Asia/Shanghai' |
|||
LATEST_STABLE_VERSION_URL: 'https://upgrade.mikrotik.com/routeros/NEWESTa7.stable' |
|||
LATEST_VERSION: "7.15" |
|||
CUSTOM_LICENSE_PRIVATE_KEY: ${{ secrets.CUSTOM_LICENSE_PRIVATE_KEY }} |
|||
CUSTOM_LICENSE_PUBLIC_KEY: ${{ secrets.CUSTOM_LICENSE_PUBLIC_KEY }} |
|||
CUSTOM_NPK_SIGN_PRIVATE_KEY: ${{ secrets.CUSTOM_NPK_SIGN_PRIVATE_KEY }} |
|||
CUSTOM_NPK_SIGN_PUBLIC_KEY: ${{ secrets.CUSTOM_NPK_SIGN_PUBLIC_KEY }} |
|||
MIKRO_LICENSE_PUBLIC_KEY: ${{ secrets.MIKRO_LICENSE_PUBLIC_KEY }} |
|||
MIKRO_NPK_SIGN_PUBLIC_LKEY: ${{ secrets.MIKRO_NPK_SIGN_PUBLIC_LKEY }} |
|||
steps: |
|||
- name: Checkout repository |
|||
uses: actions/checkout@v4 |
|||
|
|||
- name: Set up Python |
|||
uses: actions/setup-python@v5 |
|||
with: |
|||
python-version: '3.11' |
|||
- name: Get latest routeros stable version |
|||
run: | |
|||
echo $(uname -a) |
|||
LATEST_VERSION=$(wget -nv -O - $LATEST_STABLE_VERSION_URL | cut -d ' ' -f1) |
|||
echo Latest Stabel Version:$LATEST_VERSION |
|||
echo "LATEST_VERSION=${LATEST_VERSION}" >> $GITHUB_ENV |
|||
|
|||
- name: Create keygen |
|||
run: | |
|||
zip keygen.zip ./keygen.exe |
|||
|
|||
- name: Create squashfs for option npk |
|||
run: | |
|||
cd $GITHUB_WORKSPACE |
|||
sudo wget -O bash -nv https://busybox.net/downloads/binaries/1.31.0-i686-uclibc/busybox_ASH |
|||
sudo wget -O busybox -nv https://busybox.net/downloads/binaries/1.31.0-i686-uclibc/busybox |
|||
sudo chmod +x busybox |
|||
sudo chmod +x bash |
|||
sudo mkdir -p ./option-root/bin/ |
|||
sudo mv busybox ./option-root/bin/ |
|||
sudo mv bash ./option-root/bin/ |
|||
COMMANDS=$(./option-root/bin/busybox --list) |
|||
for cmd in $COMMANDS; do |
|||
sudo ln -sf /pckg/option/bin/busybox ./option-root/bin/$cmd |
|||
done |
|||
sudo rm -f option.sfs |
|||
sudo mksquashfs option-root option.sfs -quiet -comp xz -no-xattrs -b 256k |
|||
sudo rm -rf option-root |
|||
|
|||
- name: Get routeros-${{ env.LATEST_VERSION }}.npk |
|||
run: sudo wget -nv -O routeros-$LATEST_VERSION.npk https://download.mikrotik.com/routeros/$LATEST_VERSION/routeros-$LATEST_VERSION.npk |
|||
|
|||
- name: Patch routeros-${{ env.LATEST_VERSION }}.npk |
|||
run: sudo -E python3 patch.py routeros-$LATEST_VERSION.npk |
|||
|
|||
- name: Get mikrotik-${{ env.LATEST_VERSION }}.iso |
|||
run: sudo wget -nv -O mikrotik-$LATEST_VERSION.iso https://download.mikrotik.com/routeros/$LATEST_VERSION/mikrotik-$LATEST_VERSION.iso |
|||
|
|||
- name: Patch mikrotik-${{ env.LATEST_VERSION }}.iso |
|||
run: | |
|||
sudo apt-get install -y mkisofs > /dev/null |
|||
sudo mkdir ./iso |
|||
sudo mount -o loop,ro mikrotik-$LATEST_VERSION.iso ./iso |
|||
sudo mkdir ./new_iso |
|||
sudo cp -r ./iso/* ./new_iso/ |
|||
sudo rsync -a ./iso/ ./new_iso/ |
|||
sudo umount ./iso |
|||
sudo rm -rf ./iso |
|||
sudo rm -f mikrotik-$LATEST_VERSION.iso |
|||
sudo rm -rf ./new_iso/routeros-$LATEST_VERSION.npk |
|||
NPK_FILES=$(find ./new_iso/*.npk) |
|||
for file in $NPK_FILES; do |
|||
sudo -E python3 npk.py sign $file $file |
|||
done |
|||
sudo cp routeros-$LATEST_VERSION.npk ./new_iso/ |
|||
sudo -E python3 npk.py create ./new_iso/gps-$LATEST_VERSION.npk ./option.sfs ./option-$LATEST_VERSION.npk |
|||
sudo cp option-$LATEST_VERSION.npk ./new_iso/ |
|||
sudo cp linux ./new_iso/isolinux/ |
|||
sudo mkdir ./efiboot |
|||
sudo mount -o loop ./new_iso/efiboot.img ./efiboot |
|||
sudo cp linux ./efiboot/linux.x86_64 |
|||
sudo umount ./efiboot |
|||
sudo rm -rf ./efiboot |
|||
sudo mkisofs -o mikrotik-$LATEST_VERSION.iso \ |
|||
-V "MikroTik $LATEST_VERSION Patched" \ |
|||
-sysid "" -preparer "MiKroTiK" \ |
|||
-publisher "" -A "MiKroTiK RouterOS" \ |
|||
-b isolinux/isolinux.bin \ |
|||
-c isolinux/boot.cat \ |
|||
-no-emul-boot \ |
|||
-boot-load-size 4 \ |
|||
-boot-info-table \ |
|||
-eltorito-alt-boot \ |
|||
-e efiboot.img \ |
|||
-no-emul-boot \ |
|||
-R \ |
|||
./new_iso |
|||
sudo rm -rf ./new_iso |
|||
|
|||
- name: Delete Release tag ${{ env.LATEST_VERSION }} |
|||
run: | |
|||
HEADER="Authorization: token ${{ secrets.GITHUB_TOKEN }}" |
|||
RELEASE_INFO=$(curl -s -H $HEADER https://api.github.com/repos/${{ github.repository }}/releases/tags/${{ env.LATEST_VERSION }}) |
|||
RELEASE_ID=$(echo $RELEASE_INFO | jq -r '.id') |
|||
echo "Release ID: $RELEASE_ID" |
|||
if [ "$RELEASE_ID" != "null" ]; then |
|||
curl -X DELETE -H "$HEADER" https://api.github.com/repos/${{ github.repository }}/git/refs/tags/${{ env.LATEST_VERSION }} |
|||
echo "Tag ${{ env.LATEST_VERSION }} deleted successfully." |
|||
curl -X DELETE -H "$HEADER" https://api.github.com/repos/${{ github.repository }}/releases/$RELEASE_ID |
|||
echo "Release with tag ${{ env.LATEST_VERSION }} deleted successfully." |
|||
else |
|||
echo "Release not found for tag: ${{ env.LATEST_VERSION }})" |
|||
fi |
|||
|
|||
- name: Create Release tag ${{ env.LATEST_VERSION }} |
|||
uses: softprops/action-gh-release@v2 |
|||
with: |
|||
name: "MikroTik ${{ env.LATEST_VERSION }}" |
|||
body: "MikroTik ${{ env.LATEST_VERSION }}" |
|||
tag_name: ${{ env.LATEST_VERSION }} |
|||
make_latest: "true" |
|||
files: | |
|||
mikrotik-${{ env.LATEST_VERSION }}.iso |
|||
routeros-${{ env.LATEST_VERSION }}.npk |
|||
option-${{ env.LATEST_VERSION }}.npk |
|||
keygen.zip |
|||
|
|||
@ -0,0 +1,2 @@ |
|||
__pycache__/ |
|||
venv/ |
|||
@ -0,0 +1,15 @@ |
|||
# Patch MikroTik RouterOS |
|||
|
|||
### Download [Latest Patched](https://github.com/elseif/MikroTikPatch/releases/latest) iso file,install it and enjoy. |
|||
|
|||
 |
|||
 |
|||
|
|||
### Uses keygen to generate license key. |
|||
 |
|||
### all patches are applied automatically with [github workflow](https://github.com/elseif/MikroTikPatch/blob/main/.github/workflows/mikrotik_patch.yml). |
|||
|
|||
|
|||
|
|||
|
|||
|
|||
|
After Width: 639 | Height: 372 | Size: 18 KiB |
|
After Width: 523 | Height: 180 | Size: 17 KiB |
@ -0,0 +1,213 @@ |
|||
|
|||
import struct |
|||
from sha256 import SHA256 |
|||
from toyecc import AffineCurvePoint, getcurvebyname, FieldElement,ECPrivateKey,ECPublicKey,Tools |
|||
from toyecc.Random import secure_rand_int_between |
|||
|
|||
MIKRO_LICENSE_HEADER = '-----BEGIN MIKROTIK SOFTWARE KEY------------' |
|||
MIKRO_LICENSE_FOOTER = '-----END MIKROTIK SOFTWARE KEY--------------' |
|||
MIKRO_BASE64_CHARACTER_TABLE = b'ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789+/' |
|||
SOFTWARE_ID_CHARACTER_TABLE = b'TN0BYX18S5HZ4IA67DGF3LPCJQRUK9MW2VE' |
|||
|
|||
MIKRO_SHA256_K = ( |
|||
0x0548D563, 0x98308EAB, 0x37AF7CCC, 0xDFBC4E3C, |
|||
0xF125AAC9, 0xEC98ACB8, 0x8B540795, 0xD3E0EF0E, |
|||
0x4904D6E5, 0x0DA84981, 0x9A1F8452, 0x00EB7EAA, |
|||
0x96F8E3B3, 0xA6CDB655, 0xE7410F9E, 0x8EECB03D, |
|||
0x9C6A7C25, 0xD77B072F, 0x6E8F650A, 0x124E3640, |
|||
0x7E53785A, 0xE0150772, 0xC61EF4E0, 0xBC57E5E0, |
|||
0xC0F9A285, 0xDB342856, 0x190834C7, 0xFBEB7D8E, |
|||
0x251BED34, 0x0E9F2AAD, 0x256AB901, 0x0A5B7890, |
|||
0x9F124F09, 0xD84A9151, 0x427AF67A, 0x8059C9AA, |
|||
0x13EAB029, 0x3153CDF1, 0x262D405D, 0xA2105D87, |
|||
0x9C745F15, 0xD1613847, 0x294CE135, 0x20FB0F3C, |
|||
0x8424D8ED, 0x8F4201B6, 0x12CA1EA7, 0x2054B091, |
|||
0x463D8288, 0xC83253C3, 0x33EA314A, 0x9696DC92, |
|||
0xD041CE9A, 0xE5477160, 0xC7656BE8, 0x5179FE33, |
|||
0x1F4726F1, 0x5F393AF0, 0x26E2D004, 0x6D020245, |
|||
0x85FDF6D7, 0xB0237C56, 0xFF5FBD94, 0xA8B3F534 |
|||
) |
|||
def mikro_softwareid_decode(software_id:str)->int: |
|||
assert(isinstance(software_id, str)) |
|||
software_id = software_id.replace('-', '') |
|||
ret = 0 |
|||
for i in reversed(range(len(software_id))): |
|||
ret *= len(SOFTWARE_ID_CHARACTER_TABLE) |
|||
ret += SOFTWARE_ID_CHARACTER_TABLE.index(ord(software_id[i])) |
|||
return ret |
|||
|
|||
def mikro_softwareid_encode(id:int)->str: |
|||
assert(isinstance(id, int)) |
|||
ret = '' |
|||
for i in range(8): |
|||
ret += chr(SOFTWARE_ID_CHARACTER_TABLE[id % 0x23]) |
|||
id //= 0x23 |
|||
if i == 3: |
|||
ret += '-' |
|||
return ret |
|||
|
|||
def to32bits(v): |
|||
return (v + (1 << 32)) % (1 << 32) |
|||
|
|||
def rotl(n, d): |
|||
return (n << d) | (n >> (32 - d)) |
|||
|
|||
def mikro_encode(s:bytes)->bytes: |
|||
s = list(struct.unpack('>' + 'I' * (len(s) // 4), s)) |
|||
for i in reversed(range(16)): |
|||
s[(i+0) % 4] = to32bits(rotl(s[(i+3) % 4], MIKRO_SHA256_K[i*4+3] & 0x0F) ^ (s[(i+0) % 4] - s[(i+3) % 4])) |
|||
s[(i+3) % 4] = to32bits(s[(i+3) % 4] + s[(i+1) % 4] + MIKRO_SHA256_K[i*4+3]) |
|||
|
|||
s[(i+1) % 4] = to32bits(rotl(s[(i+2) % 4], MIKRO_SHA256_K[i*4+2] & 0x0F) ^ (s[(i+1) % 4] - s[(i+2) % 4])) |
|||
s[(i+0) % 4] = to32bits(s[(i+0) % 4] + s[(i+2) % 4] + MIKRO_SHA256_K[i*4+2]) |
|||
|
|||
s[(i+2) % 4] = to32bits(rotl(s[(i+1) % 4], MIKRO_SHA256_K[i*4+1] & 0x0F) ^ (s[(i+2) % 4] - s[(i+1) % 4])) |
|||
s[(i+1) % 4] = to32bits(s[(i+1) % 4] + s[(i+3) % 4] + MIKRO_SHA256_K[i*4+1]) |
|||
|
|||
s[(i+3) % 4] = to32bits(rotl(s[(i+0) % 4], MIKRO_SHA256_K[i*4+0] & 0x0F) ^ (s[(i+3) % 4] - s[(i+0) % 4])) |
|||
s[(i+2) % 4] = to32bits(s[(i+2) % 4] + s[(i+0) % 4] + MIKRO_SHA256_K[i*4+0]) |
|||
|
|||
encodedLicensePayload = b'' |
|||
for x in s: |
|||
encodedLicensePayload += x.to_bytes(4, 'big') |
|||
return encodedLicensePayload |
|||
|
|||
def mikro_decode(s:bytes)->bytes: |
|||
s = list(struct.unpack('>'+'I'*(len(s) // 4), s)) |
|||
for i in range(16): |
|||
s[(i+2) % 4] = to32bits(s[(i+2) % 4] - s[(i+0) % 4] - MIKRO_SHA256_K[i*4+0]) |
|||
s[(i+3) % 4] = to32bits((rotl(s[(i+0) % 4], MIKRO_SHA256_K[i*4+0] & 0x0F) ^ s[(i+3) % 4]) + s[(i+0) % 4]) |
|||
|
|||
s[(i+1) % 4] = to32bits(s[(i+1) % 4] - s[(i+3) % 4] - MIKRO_SHA256_K[i*4+1]) |
|||
s[(i+2) % 4] = to32bits((rotl(s[(i+1) % 4], MIKRO_SHA256_K[i*4+1] & 0x0F) ^ s[(i+2) % 4]) + s[(i+1) % 4]) |
|||
|
|||
s[(i+0) % 4] = to32bits(s[(i+0) % 4] - s[(i+2) % 4] - MIKRO_SHA256_K[i*4+2]) |
|||
s[(i+1) % 4] = to32bits((rotl(s[(i+2) % 4], MIKRO_SHA256_K[i*4+2] & 0x0F) ^ s[(i+1) % 4]) + s[(i+2) % 4]) |
|||
|
|||
s[(i+3) % 4] = to32bits(s[(i+3) % 4] - s[(i+1) % 4] - MIKRO_SHA256_K[i*4+3]) |
|||
s[(i+0) % 4] = to32bits((rotl(s[(i+3) % 4], MIKRO_SHA256_K[i*4+3] & 0x0F) ^ s[(i+0) % 4]) + s[(i+3) % 4]) |
|||
|
|||
ret = b'' |
|||
for x in s: |
|||
ret += x.to_bytes(4, 'big') |
|||
|
|||
return ret |
|||
|
|||
|
|||
def mikro_base64_encode(data:bytes, pad = False)->str: |
|||
encoded = '' |
|||
left = 0 |
|||
for i in range(0, len(data)): |
|||
if left == 0: |
|||
encoded += chr(MIKRO_BASE64_CHARACTER_TABLE[data[i] & 0x3F]) |
|||
left = 2 |
|||
else: |
|||
if left == 6: |
|||
encoded += chr(MIKRO_BASE64_CHARACTER_TABLE[data[i - 1] >> 2]) |
|||
encoded += chr(MIKRO_BASE64_CHARACTER_TABLE[data[i] & 0x3F]) |
|||
left = 2 |
|||
else: |
|||
index1 = data[i - 1] >> (8 - left) |
|||
index2 = data[i] << (left) |
|||
encoded += chr(MIKRO_BASE64_CHARACTER_TABLE[(index1 | index2) & 0x3F]) |
|||
left += 2 |
|||
|
|||
if left != 0: |
|||
encoded += chr(MIKRO_BASE64_CHARACTER_TABLE[data[len(data) - 1] >> (8 - left)]) |
|||
|
|||
if pad: |
|||
for i in range(0, (4 - len(encoded) % 4) % 4): |
|||
encoded += '=' |
|||
|
|||
return encoded |
|||
def mikro_base64_decode(data:str)->bytes: |
|||
ret = b"" |
|||
data = data.replace("=", "").encode() |
|||
left = 0 |
|||
for i in range(0, len(data)): |
|||
if left == 0: |
|||
left = 6 |
|||
else: |
|||
value1 = MIKRO_BASE64_CHARACTER_TABLE.index(data[i - 1]) >> (6 - left) |
|||
value2 = MIKRO_BASE64_CHARACTER_TABLE.index(data[i]) & (2 ** (8 - left) - 1) |
|||
value = value1 | (value2 << left) |
|||
ret += bytes([value]) |
|||
left -= 2 |
|||
return ret |
|||
|
|||
class MikroSHA256(SHA256): |
|||
K = MIKRO_SHA256_K |
|||
INITIAL_STATE = SHA256.State( |
|||
0x5B653932, 0x7B145F8F, 0x71FFB291, 0x38EF925F, |
|||
0x03E1AAF9, 0x4A2057CC, 0x4CAF4DD9, 0x643CC9EA |
|||
) |
|||
|
|||
def mikro_sha256(data:bytes)->bytes: |
|||
return MikroSHA256(data).digest() |
|||
|
|||
def mikro_eddsa_sign(data:bytes,private_key:bytes)->bytes: |
|||
assert(isinstance(data, bytes)) |
|||
assert(isinstance(private_key, bytes)) |
|||
curve = getcurvebyname('Ed25519') |
|||
private_key = ECPrivateKey.eddsa_decode(curve,private_key) |
|||
return private_key.eddsa_sign(data).encode() |
|||
|
|||
def mikro_eddsa_verify(data:bytes,signature:bytes,public_key:bytes): |
|||
assert(isinstance(data, bytes)) |
|||
assert(isinstance(signature, bytes)) |
|||
assert(isinstance(public_key, bytes)) |
|||
curve = getcurvebyname('Ed25519') |
|||
public_key = ECPublicKey.eddsa_decode(curve,public_key) |
|||
signature = ECPrivateKey.EDDSASignature.decode(curve,signature) |
|||
return public_key.eddsa_verify(data,signature) |
|||
|
|||
def mikro_kcdsa_sign(data:bytes,private_key:bytes)->bytes: |
|||
assert(isinstance(data, bytes)) |
|||
assert(isinstance(private_key, bytes)) |
|||
curve = getcurvebyname('Curve25519') |
|||
private_key:ECPrivateKey = ECPrivateKey(Tools.bytestoint_le(private_key), curve) |
|||
public_key:ECPublicKey = private_key.pubkey |
|||
while True: |
|||
nonce_secret = secure_rand_int_between(1, curve.n - 1) |
|||
nonce_point = nonce_secret * curve.G |
|||
nonce = int(nonce_point.x) % curve.n |
|||
nonce_hash = mikro_sha256(Tools.inttobytes_le(nonce,32)) |
|||
data_hash = bytearray(mikro_sha256(data)) |
|||
for i in range(16): |
|||
data_hash[8+i] ^= nonce_hash[i] |
|||
data_hash[0] &= 0xF8 |
|||
data_hash[31] &= 0x7F |
|||
data_hash[31] |= 0x40 |
|||
data_hash = Tools.bytestoint_le(data_hash) |
|||
signature = pow(private_key.scalar, -1, curve.n) * (nonce_secret - data_hash) |
|||
signature %= curve.n |
|||
if int((public_key.point * signature + curve.G * data_hash).x) == nonce: |
|||
return bytes(nonce_hash[:16]+Tools.inttobytes_le(signature,32)) |
|||
|
|||
def mikro_kcdsa_verify(data:bytes, signature:bytes, public_key:bytes)->bool: |
|||
assert(isinstance(data, bytes)) |
|||
assert(isinstance(signature, bytes)) |
|||
assert(isinstance(public_key, bytes)) |
|||
curve = getcurvebyname('Curve25519') |
|||
#y^2 = x^3 + ax^2 + x |
|||
x = FieldElement(Tools.bytestoint_le(public_key), curve.p) |
|||
YY = ((x**3) + (curve.a * x**2) + x).sqrt() |
|||
public_keys = [] |
|||
for y in YY: |
|||
public_keys += [AffineCurvePoint(x, int(y), curve)] |
|||
|
|||
data_hash = bytearray(mikro_sha256(data)) |
|||
nonce_hash = signature[:16] |
|||
signature = signature[16:] |
|||
for i in range(16): |
|||
data_hash[8+i] ^= nonce_hash[i] |
|||
data_hash[0] &= 0xF8 |
|||
data_hash[31] &= 0x7F |
|||
data_hash[31] |= 0x40 |
|||
data_hash = Tools.bytestoint_le(data_hash) |
|||
signature = Tools.bytestoint_le(signature) |
|||
for public_key in public_keys: |
|||
nonce = int((public_key * signature + curve.G * data_hash).x) |
|||
if mikro_sha256(Tools.inttobytes_le(nonce,32))[:len(nonce_hash)] == nonce_hash: |
|||
return True |
|||
return False |
|||
@ -0,0 +1,297 @@ |
|||
|
|||
import struct,zlib |
|||
from datetime import datetime |
|||
from dataclasses import dataclass |
|||
from enum import IntEnum |
|||
class NpkPartID(IntEnum): |
|||
NAME_INFO =0x01 # Package information: name, ver, etc. |
|||
DESCRIPTION =0x02 # Package description |
|||
DEPENDENCIES =0x03 # Package Dependencies |
|||
FILE_CONTAINER =0x04 # Files container zlib 1.2.3 |
|||
INSTALL_SCRIPT =0x07 # Install script |
|||
UNINSTALL_SCRIPT =0x08 # Uninstall script |
|||
SIGNATURE =0x09 # Package signature |
|||
ARCHITECTURE =0x10 # Package architecture (e.g. i386) |
|||
PKG_CONFLICTS =0x11 # Package conflicts |
|||
PKG_INFO =0x12 |
|||
FEATURES =0x13 |
|||
PKG_FEATURES =0x14 |
|||
SQUASHFS =0x15 # SquashFS |
|||
NULL_BLOCK =0X16 |
|||
GIT_COMMIT =0x17 # Git commit |
|||
CHANNEL =0x18 # Release type (e.g. stable, bugfix) |
|||
HEADER =0x19 |
|||
|
|||
@dataclass |
|||
class NpkPartItem: |
|||
id: NpkPartID |
|||
data: bytes|object |
|||
class NpkNameInfo: |
|||
_format = '<16s4sI12s' |
|||
def __init__(self,name:str,version:str,build_time=datetime.now(),_unknow=b'\x00'*12): |
|||
self._name = name[:16].encode().ljust(16,b'\x00') |
|||
self._version = self.encode_version(version) |
|||
self._build_time = int(build_time.timestamp()) |
|||
self._unknow = _unknow |
|||
def serialize(self)->bytes: |
|||
return struct.pack(self._format, self._name,self._version,self._build_time,self._unknow) |
|||
@staticmethod |
|||
def unserialize_from(data:bytes)->'NpkNameInfo': |
|||
assert len(data) == struct.calcsize(NpkNameInfo._format),'Invalid data length' |
|||
_name, _version,_build_time,_unknow = struct.unpack_from(NpkNameInfo._format,data) |
|||
return NpkNameInfo(_name.decode(),NpkNameInfo.decode_version(_version),datetime.fromtimestamp(_build_time),_unknow) |
|||
def __len__ (self)->int: |
|||
return struct.calcsize(self._format) |
|||
@property |
|||
def name(self)->str: |
|||
return self._name.decode().strip('\x00') |
|||
@name.setter |
|||
def name(self,value:str): |
|||
self._name = value[:16].encode().ljust(16,b'\x00') |
|||
@staticmethod |
|||
def decode_version(value:bytes): |
|||
revision,build,minor,major = struct.unpack_from('4B',value) |
|||
if build == 97: |
|||
build = 'alpha' |
|||
elif build == 98: |
|||
build = 'beta' |
|||
elif build == 99: |
|||
build = 'rc' |
|||
elif build == 102: |
|||
if revision & 0x80: |
|||
build = 'test' |
|||
revision &= 0x7f |
|||
else: |
|||
build = 'final' |
|||
else: |
|||
build = 'unknown' |
|||
return f'{major}.{minor}.{revision}.{build}' |
|||
@staticmethod |
|||
def encode_version(value:str): |
|||
s = value.split('.') |
|||
if 4 != len(s) and s[3] in [ 'alpha', 'beta', 'rc','final', 'test']: |
|||
raise ValueError('Invalid version string') |
|||
major = int(s[0]) |
|||
minor = int(s[1]) |
|||
revision = int(s[2]) |
|||
if s[3] == 'alpha': |
|||
build = 97 |
|||
elif s[3] == 'beta': |
|||
build = 98 |
|||
elif s[3] == 'rc': |
|||
build = 99 |
|||
elif s[3] == 'final': |
|||
build = 102 |
|||
revision &= 0x7f |
|||
else: #'test' |
|||
build = 102 |
|||
revision |= 0x80 |
|||
return struct.pack('4B',revision,build,minor,major) |
|||
@property |
|||
def version(self)->str: |
|||
return self.decode_version(self._version) |
|||
@version.setter |
|||
def version(self,value:str = '7.15.1.final'): |
|||
self._version = self.encode_version(value) |
|||
@property |
|||
def build_time(self): |
|||
return datetime.fromtimestamp(self._build_time) |
|||
@build_time.setter |
|||
def build_time(self,value:datetime): |
|||
self._build_time = int(value.timestamp()) |
|||
|
|||
class NpkFileContainer: |
|||
_format = '<BB6sIBBBBIIIH' |
|||
@dataclass |
|||
class NpkFileItem: |
|||
perm: int |
|||
type: int |
|||
usr_or_grp: int |
|||
modify_time: int |
|||
revision: int |
|||
rc: int |
|||
minor: int |
|||
major: int |
|||
create_time: int |
|||
unknow: int |
|||
name: bytes |
|||
data: bytes |
|||
def __init__(self,items:list['NpkFileContainer.NpkFileItem']=None): |
|||
self._items= items |
|||
def serialize(self)->bytes: |
|||
compressed_data = b'' |
|||
compressor = zlib.compressobj() |
|||
for item in self._items: |
|||
data = struct.pack(self._format, item.perm,item.type,item.usr_or_grp, item.modify_time,item.revision,item.rc,item.minor,item.major,item.create_time,item.unknow,len(item.data),len(item.name)) |
|||
data += item.name + item.data |
|||
compressed_data += compressor.compress(data) |
|||
return compressed_data + compressor.flush() |
|||
@staticmethod |
|||
def unserialize_from(data:bytes): |
|||
items:list['NpkFileContainer.NpkFileItem'] = [] |
|||
decompressed_data = zlib.decompress(data) |
|||
while len(decompressed_data): |
|||
offset = struct.calcsize(NpkFileContainer._format) |
|||
perm,type,usr_or_grp, modify_time,revision,rc,minor,major,create_time,unknow,data_size,name_size= struct.unpack_from(NpkFileContainer._format, decompressed_data) |
|||
name = decompressed_data[offset:offset+name_size] |
|||
data = decompressed_data[offset+name_size:offset+name_size+data_size] |
|||
items.append(NpkFileContainer.NpkFileItem(perm,type,usr_or_grp, modify_time,revision,rc,minor,major,create_time,unknow,name,data)) |
|||
decompressed_data = decompressed_data[offset+name_size+data_size:] |
|||
return NpkFileContainer(items) |
|||
|
|||
def __len__ (self)->int: |
|||
return len(self.serialize()) |
|||
def __getitem__(self,index:int)->'NpkFileContainer.NpkFileItem': |
|||
return self._items[index] |
|||
def __iter__(self): |
|||
for item in self._items: |
|||
yield item |
|||
|
|||
|
|||
class NovaPackage: |
|||
NPK_MAGIC = 0xbad0f11e |
|||
def __init__(self,data:bytes=b''): |
|||
self._parts:list[NpkPartItem] = [] |
|||
offset = 0 |
|||
while offset < len(data): |
|||
part_id,part_size = struct.unpack_from('<HI',data,offset) |
|||
offset += 6 |
|||
part_data = data[offset:offset+part_size] |
|||
offset += part_size |
|||
if part_id == NpkPartID.NAME_INFO: |
|||
self._parts.append(NpkPartItem(NpkPartID(part_id),NpkNameInfo.unserialize_from(part_data))) |
|||
# elif part_id == NpkPartID.FILE_CONTAINER: |
|||
# self._parts.append(NpkPartItem(NpkPartID(part_id),NpkFileContainer.unserialize_from(part_data))) |
|||
else: |
|||
self._parts.append(NpkPartItem(NpkPartID(part_id),part_data)) |
|||
|
|||
|
|||
def get_digest(self,hash_fnc)->bytes: |
|||
for part in self._parts: |
|||
data_header = struct.pack('<HI',part.id.value,len(part.data)) |
|||
if part.id == NpkPartID.HEADER: |
|||
continue |
|||
else: |
|||
hash_fnc.update(data_header) |
|||
if part.id == NpkPartID.SIGNATURE: |
|||
break |
|||
elif part.data: |
|||
if isinstance(part.data,bytes): |
|||
hash_fnc.update(part.data) |
|||
else: |
|||
hash_fnc.update(part.data.serialize()) |
|||
return hash_fnc.digest() |
|||
|
|||
def sign(self,kcdsa_private_key:bytes,eddsa_private_key:bytes): |
|||
import hashlib |
|||
from mikro import mikro_kcdsa_sign,mikro_eddsa_sign |
|||
self[NpkPartID.SIGNATURE].data = b'\0'*(20+48+64) |
|||
sha1_digest = self.get_digest(hashlib.new('SHA1')) |
|||
sha256_digest = self.get_digest(hashlib.new('SHA256')) |
|||
kcdsa_signature = mikro_kcdsa_sign(sha256_digest[:20],kcdsa_private_key) |
|||
eddsa_signature = mikro_eddsa_sign(sha256_digest,eddsa_private_key) |
|||
self[NpkPartID.SIGNATURE].data = sha1_digest + kcdsa_signature + eddsa_signature |
|||
|
|||
def verify(self,kcdsa_public_key:bytes,eddsa_public_key:bytes): |
|||
import hashlib |
|||
from mikro import mikro_kcdsa_verify,mikro_eddsa_verify |
|||
sha1_digest = self.get_digest(hashlib.new('SHA1')) |
|||
sha256_digest = self.get_digest(hashlib.new('SHA256')) |
|||
signature = self[NpkPartID.SIGNATURE].data |
|||
if sha1_digest != signature[:20]: |
|||
return False |
|||
if not mikro_kcdsa_verify(sha256_digest[:20],signature[20:68],kcdsa_public_key): |
|||
return False |
|||
if not mikro_eddsa_verify(sha256_digest,signature[68:132],eddsa_public_key): |
|||
return False |
|||
return True |
|||
|
|||
def __iter__(self): |
|||
for part in self._parts: |
|||
yield part |
|||
|
|||
def __getitem__(self, id:NpkPartID): |
|||
for part in self._parts: |
|||
if part.id == id: |
|||
return part |
|||
part = NpkPartItem(id,b'') |
|||
self._parts.append(part) |
|||
return part |
|||
|
|||
def save(self,file): |
|||
size = 0 |
|||
for part in self._parts: |
|||
size += 6 + len(part.data) |
|||
with open(file,'wb') as f: |
|||
f.write(struct.pack('<II', NovaPackage.NPK_MAGIC, size)) |
|||
for part in self._parts: |
|||
f.write(struct.pack('<HI',part.id.value ,len(part.data))) |
|||
if isinstance(part.data,bytes): |
|||
f.write(part.data) |
|||
else: |
|||
f.write(part.data.serialize()) |
|||
|
|||
@staticmethod |
|||
def load(file): |
|||
with open(file,'rb') as f: |
|||
data = f.read() |
|||
assert int.from_bytes(data[:4],'little') == NovaPackage.NPK_MAGIC, 'Invalid Nova Package Magic' |
|||
assert int.from_bytes(data[4:8],'little') == len(data) - 8, 'Invalid Nova Package Size' |
|||
return NovaPackage(data[8:]) |
|||
|
|||
def get_latest_version(channel:str) -> str: |
|||
import requests |
|||
response = requests.get(f'https://upgrade.mikrotik.com/routeros/NEWESTa7.{channel}') |
|||
return response.text.split(' ')[0] |
|||
|
|||
def create_option_npk(npk_file:str,squashfs_file:str)->NovaPackage: |
|||
|
|||
return option_npk |
|||
|
|||
if __name__=='__main__': |
|||
import argparse,os |
|||
parser = argparse.ArgumentParser(description='nova package creator and editor') |
|||
subparsers = parser.add_subparsers(dest="command") |
|||
sign_parser = subparsers.add_parser('sign',help='sign npk file') |
|||
sign_parser.add_argument('input',type=str, help='Input file') |
|||
sign_parser.add_argument('output',type=str,help='Output file') |
|||
verify_parser = subparsers.add_parser('verify',help='Verify npk file') |
|||
verify_parser.add_argument('input',type=str, help='Input file') |
|||
create_option_parser = subparsers.add_parser('create',help='Create option.npk file') |
|||
create_option_parser.add_argument('npk_file',type=str,help='From npk file') |
|||
create_option_parser.add_argument('squashfs',type=str,help='option squashfs file') |
|||
create_option_parser.add_argument('output',type=str,help='Output file') |
|||
|
|||
args = parser.parse_args() |
|||
if args.command =='sign': |
|||
print(f'Signing {args.input}') |
|||
npk = NovaPackage.load(args.input) |
|||
kcdsa_private_key = bytes.fromhex(os.environ['CUSTOM_LICENSE_PRIVATE_KEY']) |
|||
eddsa_private_key = bytes.fromhex(os.environ['CUSTOM_NPK_SIGN_PRIVATE_KEY']) |
|||
npk.sign(kcdsa_private_key,eddsa_private_key) |
|||
npk.save(args.output) |
|||
elif args.command == 'verify': |
|||
npk = NovaPackage.load(args.input) |
|||
print(f'Verifying {args.input} ',end="") |
|||
kcdsa_public_key = bytes.fromhex(os.environ['CUSTOM_LICENSE_PUBLIC_KEY']) |
|||
eddsa_public_key = bytes.fromhex(os.environ['CUSTOM_NPK_SIGN_PUBLIC_KEY']) |
|||
if npk.verify(kcdsa_public_key,eddsa_public_key): |
|||
print('Valid') |
|||
exit(0) |
|||
else: |
|||
print('Invalid') |
|||
exit(1) |
|||
elif args.command =='create': |
|||
print(f'Creating option.npk from {args.npk_file}') |
|||
kcdsa_private_key = bytes.fromhex(os.environ['CUSTOM_LICENSE_PRIVATE_KEY']) |
|||
eddsa_private_key = bytes.fromhex(os.environ['CUSTOM_NPK_SIGN_PRIVATE_KEY']) |
|||
option_npk = NovaPackage.load(args.npk_file) |
|||
option_npk[NpkPartID.NAME_INFO].data.name = 'option' |
|||
option_npk[NpkPartID.DESCRIPTION].data = b'option package has busybox ash' |
|||
option_npk[NpkPartID.NULL_BLOCK].data = b'' |
|||
option_npk[NpkPartID.SQUASHFS].data = open(args.squashfs,'rb').read() |
|||
option_npk.sign(kcdsa_private_key,eddsa_private_key) |
|||
option_npk.save(args.output) |
|||
print(f'Created {args.output}') |
|||
else: |
|||
parser.print_help() |
|||
@ -0,0 +1,111 @@ |
|||
import subprocess,lzma |
|||
import struct,os |
|||
from npk import NovaPackage,NpkPartID,NpkFileContainer |
|||
|
|||
def patch_bzimage(data:bytes,key_dict:dict): |
|||
print(f'bzImage size : {len(data)}') |
|||
PE_TEXT_SECTION_OFFSET = 414 |
|||
HEADER_PAYLOAD_OFFSET = 584 |
|||
HEADER_PAYLOAD_LENGTH_OFFSET = HEADER_PAYLOAD_OFFSET + 4 |
|||
text_section_raw_data = struct.unpack_from('<I',data,PE_TEXT_SECTION_OFFSET)[0] |
|||
payload_offset = text_section_raw_data +struct.unpack_from('<I',data,HEADER_PAYLOAD_OFFSET)[0] |
|||
payload_length = struct.unpack_from('<I',data,HEADER_PAYLOAD_LENGTH_OFFSET)[0] |
|||
payload_length = payload_length - 4 #last 4 bytes is uncompressed size(z_output_len) |
|||
print(f'vmlinux xz offset : {payload_offset}') |
|||
print(f'vmlinux xz size : {payload_length}') |
|||
z_output_len = struct.unpack_from('<I',data,payload_offset+payload_length)[0] |
|||
print(f'z_output_len : {z_output_len}') |
|||
vmlinux_xz = data[payload_offset:payload_offset+payload_length] |
|||
vmlinux = lzma.decompress(vmlinux_xz) |
|||
print(f'vmlinux size : {len(vmlinux)}') |
|||
assert z_output_len == len(vmlinux), 'vmlinux size is not equal to expected' |
|||
CPIO_HEADER_MAGIC = b'07070100' |
|||
CPIO_FOOTER_MAGIC = b'TRAILER!!!\x00\x00\x00\x00' #545241494C455221212100000000 |
|||
cpio_offset1 = vmlinux.index(CPIO_HEADER_MAGIC) |
|||
initramfs = vmlinux[cpio_offset1:] |
|||
cpio_offset2 = initramfs.index(CPIO_FOOTER_MAGIC)+len(CPIO_FOOTER_MAGIC) |
|||
initramfs = initramfs[:cpio_offset2] |
|||
new_initramfs = initramfs |
|||
for old_public_key,new_public_key in key_dict.items(): |
|||
if old_public_key in new_initramfs: |
|||
print(f'initramfs public key patched {old_public_key[:16].hex().upper()}...') |
|||
new_initramfs = new_initramfs.replace(old_public_key,new_public_key) |
|||
new_vmlinux = vmlinux.replace(initramfs,new_initramfs) |
|||
new_vmlinux_xz = lzma.compress(new_vmlinux,check=lzma.CHECK_CRC32,filters=[ |
|||
{"id": lzma.FILTER_X86}, |
|||
{"id": lzma.FILTER_LZMA2, "preset": 8,'dict_size': 32*1024*1024}, |
|||
]) |
|||
new_payload_length = len(new_vmlinux_xz) |
|||
print(f'new vmlinux xz size : {new_payload_length}') |
|||
assert new_payload_length <= payload_length , 'new vmlinux.xz size is too big' |
|||
new_payload_length = new_payload_length + 4 #last 4 bytes is uncompressed size(z_output_len) |
|||
new_data = bytearray(data) |
|||
struct.pack_into('<I',new_data,HEADER_PAYLOAD_LENGTH_OFFSET,new_payload_length) |
|||
vmlinux_xz += struct.pack('<I',z_output_len) |
|||
new_vmlinux_xz += struct.pack('<I',z_output_len) |
|||
new_vmlinux_xz = new_vmlinux_xz.ljust(len(vmlinux_xz),b'\0') |
|||
new_data = new_data.replace(vmlinux_xz,new_vmlinux_xz) |
|||
print(f'new bzImage size : {len(new_data)}') |
|||
return new_data |
|||
|
|||
def run_shell_command(command): |
|||
process = subprocess.run(command, shell=True, check=True, stdout=subprocess.PIPE, stderr=subprocess.PIPE) |
|||
return process.stdout, process.stderr |
|||
|
|||
def patch_squashfs(path,key_dict): |
|||
for root, dirs, files in os.walk(path): |
|||
for file in files: |
|||
file = os.path.join(root,file) |
|||
if os.path.isfile(file): |
|||
data = open(file,'rb').read() |
|||
for old_public_key,new_public_key in key_dict.items(): |
|||
if old_public_key in data: |
|||
print(f'{file} public key patched {old_public_key[:16].hex().upper()}...') |
|||
data = data.replace(old_public_key,new_public_key) |
|||
open(file,'wb').write(data) |
|||
|
|||
def patch_system_npk(npk_file,key_dict): |
|||
npk = NovaPackage.load(npk_file) |
|||
file_container = NpkFileContainer.unserialize_from(npk[NpkPartID.FILE_CONTAINER].data) |
|||
for item in file_container: |
|||
if item.name == b'boot/EFI/BOOT/BOOTX64.EFI': |
|||
print(f'patch {item.name} ...') |
|||
item.data = patch_bzimage(item.data,key_dict) |
|||
open('linux','wb').write(item.data) |
|||
break |
|||
npk[NpkPartID.FILE_CONTAINER].data = file_container.serialize() |
|||
try: |
|||
squashfs_file = 'squashfs.sfs' |
|||
extract_dir = 'squashfs-root' |
|||
open(squashfs_file,'wb').write(npk[NpkPartID.SQUASHFS].data) |
|||
print(f"extract {squashfs_file} ...") |
|||
_, stderr = run_shell_command(f"unsquashfs -d {extract_dir} {squashfs_file}") |
|||
print(stderr.decode()) |
|||
patch_squashfs(extract_dir,key_dict) |
|||
print(f"pack {extract_dir} ...") |
|||
run_shell_command(f"rm -f {squashfs_file}") |
|||
_, stderr = run_shell_command(f"mksquashfs {extract_dir} {squashfs_file} -quiet -comp xz -no-xattrs -b 256k") |
|||
print(stderr.decode()) |
|||
except Exception as e: |
|||
print(e) |
|||
print(f"clean ...") |
|||
run_shell_command(f"rm -rf {extract_dir}") |
|||
npk[NpkPartID.SQUASHFS].data = open(squashfs_file,'rb').read() |
|||
run_shell_command(f"rm -f {squashfs_file}") |
|||
kcdsa_private_key = bytes.fromhex(os.environ['CUSTOM_LICENSE_PRIVATE_KEY']) |
|||
eddsa_private_key = bytes.fromhex(os.environ['CUSTOM_NPK_SIGN_PRIVATE_KEY']) |
|||
npk.sign(kcdsa_private_key,eddsa_private_key) |
|||
npk.save(npk_file) |
|||
|
|||
if __name__ == '__main__': |
|||
import os,sys |
|||
key_dict = { |
|||
bytes.fromhex(os.environ['MIKRO_LICENSE_PUBLIC_KEY']):bytes.fromhex(os.environ['CUSTOM_LICENSE_PUBLIC_KEY']), |
|||
bytes.fromhex(os.environ['MIKRO_NPK_SIGN_PUBLIC_LKEY']):bytes.fromhex(os.environ['CUSTOM_NPK_SIGN_PUBLIC_KEY']) |
|||
} |
|||
if len(sys.argv) == 2: |
|||
print(f'patching {sys.argv[1]} ...') |
|||
patch_system_npk(sys.argv[1],key_dict) |
|||
else: |
|||
print('usage: python patch.py npk_file') |
|||
|
|||
|
After Width: 1589 | Height: 836 | Size: 91 KiB |
@ -0,0 +1,317 @@ |
|||
#!/usr/bin/env python |
|||
# |
|||
# Copyright (c) 2012 Dave Pifke. |
|||
# |
|||
# Permission is hereby granted, free of charge, to any person obtaining a copy |
|||
# of this software and associated documentation files (the "Software"), to |
|||
# deal in the Software without restriction, including without limitation the |
|||
# rights to use, copy, modify, merge, publish, distribute, sublicense, and/or |
|||
# sell copies of the Software, and to permit persons to whom the Software is |
|||
# furnished to do so, subject to the following conditions: |
|||
# |
|||
# The above copyright notice and this permission notice shall be included in |
|||
# all copies or substantial portions of the Software. |
|||
# |
|||
# THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR |
|||
# IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, |
|||
# FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE |
|||
# AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER |
|||
# LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING |
|||
# FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS |
|||
# IN THE SOFTWARE. |
|||
# |
|||
|
|||
"""SHA256 (FIPS 180-3) implementation for experimentation.""" |
|||
|
|||
import binascii |
|||
import codecs |
|||
import collections |
|||
import struct |
|||
import sys |
|||
|
|||
if sys.version > '3': |
|||
long = int |
|||
|
|||
|
|||
class SHA256(object): |
|||
""" |
|||
SHA256 (FIPS 180-3) implementation for experimentation. |
|||
|
|||
This is an implementation of the hash function designed not for |
|||
efficiency, but for clarity and ability to experiment. The details |
|||
of the algorithm are abstracted out with subclassing in mind. |
|||
|
|||
""" |
|||
|
|||
# Container for the state registers between rounds: |
|||
State = collections.namedtuple('State', 'a b c d e f g h') |
|||
|
|||
# From FIPS 180-3 section 5.3.3 (page 15): |
|||
INITIAL_STATE = State( |
|||
0x6a09e667, 0xbb67ae85, 0x3c6ef372, 0xa54ff53a, |
|||
0x510e527f, 0x9b05688c, 0x1f83d9ab, 0x5be0cd19 |
|||
) |
|||
|
|||
# From FIPS 180-3 section 4.2.2 (page 11): |
|||
K = ( |
|||
0x428a2f98, 0x71374491, 0xb5c0fbcf, 0xe9b5dba5, |
|||
0x3956c25b, 0x59f111f1, 0x923f82a4, 0xab1c5ed5, |
|||
0xd807aa98, 0x12835b01, 0x243185be, 0x550c7dc3, |
|||
0x72be5d74, 0x80deb1fe, 0x9bdc06a7, 0xc19bf174, |
|||
0xe49b69c1, 0xefbe4786, 0x0fc19dc6, 0x240ca1cc, |
|||
0x2de92c6f, 0x4a7484aa, 0x5cb0a9dc, 0x76f988da, |
|||
0x983e5152, 0xa831c66d, 0xb00327c8, 0xbf597fc7, |
|||
0xc6e00bf3, 0xd5a79147, 0x06ca6351, 0x14292967, |
|||
0x27b70a85, 0x2e1b2138, 0x4d2c6dfc, 0x53380d13, |
|||
0x650a7354, 0x766a0abb, 0x81c2c92e, 0x92722c85, |
|||
0xa2bfe8a1, 0xa81a664b, 0xc24b8b70, 0xc76c51a3, |
|||
0xd192e819, 0xd6990624, 0xf40e3585, 0x106aa070, |
|||
0x19a4c116, 0x1e376c08, 0x2748774c, 0x34b0bcb5, |
|||
0x391c0cb3, 0x4ed8aa4a, 0x5b9cca4f, 0x682e6ff3, |
|||
0x748f82ee, 0x78a5636f, 0x84c87814, 0x8cc70208, |
|||
0x90befffa, 0xa4506ceb, 0xbef9a3f7, 0xc67178f2 |
|||
) |
|||
|
|||
# Abstract bitwise operations, which can be overridden to provide tracing |
|||
# or alternate implementations: |
|||
@staticmethod |
|||
def _sum_mod32(*args): |
|||
return sum(args) & 0xffffffff |
|||
@classmethod |
|||
def _xor(cls, *args): |
|||
if len(args) == 2: |
|||
return args[0] ^ args[1] |
|||
else: |
|||
return args[0] ^ cls._xor(*args[1:]) |
|||
_and = staticmethod(lambda x, y: x & y) |
|||
_invert = staticmethod(lambda x: ~x) |
|||
|
|||
# Operations defined by FIPS 180-3 section 3.2 (page 8): |
|||
_rrot = staticmethod(lambda x, n: ((x & 0xffffffff) >> n) | (x << (32 - n)) & 0xffffffff) |
|||
_shr = staticmethod(lambda x, n: (x & 0xffffffff) >> n) |
|||
|
|||
# Operations defined by FIPS 180-3 section 4.1.2 (page 10): |
|||
_ch = classmethod(lambda cls, x, y, z: cls._xor(cls._and(x, y), cls._and(cls._invert(x), z))) |
|||
_maj = classmethod(lambda cls, x, y, z: cls._xor(cls._and(x, y), cls._and(x, z), cls._and(y, z))) |
|||
_S0 = classmethod(lambda cls, x: cls._xor(cls._rrot(x, 2), cls._rrot(x, 13), cls._rrot(x, 22))) |
|||
_S1 = classmethod(lambda cls, x: cls._xor(cls._rrot(x, 6), cls._rrot(x, 11), cls._rrot(x, 25))) |
|||
_s0 = classmethod(lambda cls, x: cls._xor(cls._rrot(x, 7), cls._rrot(x, 18), cls._shr(x, 3))) |
|||
_s1 = classmethod(lambda cls, x: cls._xor(cls._rrot(x, 17), cls._rrot(x, 19), cls._shr(x, 10))) |
|||
|
|||
# Operations defined by FIPS 180-3 section 6.2.2 (page 22): |
|||
_T1 = classmethod(lambda cls, prev, w, k: cls._sum_mod32(cls._S1(prev.e), cls._ch(prev.e, prev.f, prev.g), prev.h, w, k)) |
|||
_T2 = classmethod(lambda cls, prev: cls._sum_mod32(cls._S0(prev.a), cls._maj(prev.a, prev.b, prev.c))) |
|||
|
|||
@classmethod |
|||
def _round(cls, number, w, prev=INITIAL_STATE): |
|||
""" |
|||
Performs one round of SHA256 message transformation, returning the new |
|||
message state. See FIPS 180-3 section 6.2.2 step 3 (pages 21-22). |
|||
|
|||
:param number: |
|||
The round number. |
|||
|
|||
:param w: |
|||
The expanded word of the input for this round. |
|||
|
|||
:param prev: |
|||
Named tuple containing the working state from the previous round. |
|||
|
|||
""" |
|||
|
|||
t1 = cls._T1(prev, w, cls.K[number % 64]) |
|||
return cls.State( |
|||
a=cls._sum_mod32(t1, cls._T2(prev)), |
|||
b=prev.a, |
|||
c=prev.b, |
|||
d=prev.c, |
|||
e=cls._sum_mod32(prev.d, t1), |
|||
f=prev.e, |
|||
g=prev.f, |
|||
h=prev.g |
|||
) |
|||
|
|||
@classmethod |
|||
def _finalize(cls, state, initial_state=INITIAL_STATE): |
|||
""" |
|||
Returns the intermediate state after the final round for a given block |
|||
is complete. See FIPS 180-3 section 6.2.2 step 4 (page 22). |
|||
|
|||
:param state: |
|||
The digest state after the final round. |
|||
|
|||
:param initial_state: |
|||
The digest state from before the first round. |
|||
|
|||
""" |
|||
|
|||
return cls.State( |
|||
a=cls._sum_mod32(state.a, initial_state.a), |
|||
b=cls._sum_mod32(state.b, initial_state.b), |
|||
c=cls._sum_mod32(state.c, initial_state.c), |
|||
d=cls._sum_mod32(state.d, initial_state.d), |
|||
e=cls._sum_mod32(state.e, initial_state.e), |
|||
f=cls._sum_mod32(state.f, initial_state.f), |
|||
g=cls._sum_mod32(state.g, initial_state.g), |
|||
h=cls._sum_mod32(state.h, initial_state.h) |
|||
) |
|||
|
|||
@classmethod |
|||
def _expand_message(cls, message): |
|||
""" |
|||
Returns a list of 64 32-bit words based upon 16 32-bit words from the |
|||
message block being hashed. See FIPS 180-3 section 6.2.2 step 1 |
|||
(page 21). |
|||
|
|||
:param message: |
|||
Array of 16 32-bit values (512 bits total). |
|||
|
|||
""" |
|||
|
|||
assert len(message) == 16, '_expand_message() got %d words, expected 16' % len(message) |
|||
|
|||
w = list(message) |
|||
for i in range(16, 64): |
|||
w.append(cls._sum_mod32(w[i - 16], cls._s0(w[i - 15]), w[i - 7], cls._s1(w[i - 2]))) |
|||
|
|||
return w |
|||
|
|||
@classmethod |
|||
def _process_block(cls, message, state=INITIAL_STATE, round_offset=0): |
|||
""" |
|||
Processes a block of message data, returning the new digest state |
|||
(the intermediate hash value). See FIPS 180-3 section 6.2.2 (pages |
|||
21 and 22). |
|||
|
|||
:param message: |
|||
Byte string of length 64 containing the block data to hash. |
|||
|
|||
:param state: |
|||
The digest state from the previous block. |
|||
|
|||
:param round_offset: |
|||
The _round() method can be overridden to report intermediate hash |
|||
values, in which case it's useful to know how many rounds came |
|||
before. This argument allows the caller to specify as much. |
|||
|
|||
""" |
|||
|
|||
assert len(message) == 64, '_process_block() got %d bytes, expected 64' % len(message) |
|||
assert not round_offset % 64, 'round_offset should be a multiple of 64' |
|||
|
|||
w = cls._expand_message(struct.unpack('>LLLLLLLLLLLLLLLL', message)) |
|||
|
|||
midstate = state |
|||
for i in range(64): |
|||
midstate = cls._round(round_offset + i, w[i], midstate) |
|||
|
|||
return cls._finalize(midstate, state) |
|||
|
|||
@classmethod |
|||
def _pad_message(cls, message, length): |
|||
""" |
|||
Returns a list containing the final 1 or 2 message blocks, which |
|||
include the message padding per FIPS 180-3 section 5.1.1 (page 13). |
|||
|
|||
:param message: |
|||
Byte string containing the final block data to hash. Should be |
|||
less than a full block's worth (63 bytes or less). |
|||
|
|||
:param length: |
|||
Length of the message, in bits. |
|||
|
|||
""" |
|||
|
|||
assert len(message) < 64, 'Input to _pad_message() must be less than 512 bits' |
|||
|
|||
if len(message) <= 55: |
|||
# Append trailing 1 bit, then padding, then length |
|||
return [b''.join(( |
|||
message, |
|||
b'\x80', |
|||
b'\x00' * (55 - len(message)), |
|||
struct.pack('>LL', length >> 32, length & 0xffffffff), |
|||
))] |
|||
|
|||
else: |
|||
# Not enough room to append length, return two blocks: |
|||
return [ |
|||
# First is trailing 1 bit, then padding |
|||
b''.join(( |
|||
message, |
|||
b'\x80', |
|||
b'\x00' * (63 - len(message)), |
|||
)), |
|||
# Next is more padding, then length |
|||
b''.join(( |
|||
b'\x00' * 56, |
|||
struct.pack('>LL', length >> 32, length & 0xffffffff), |
|||
)), |
|||
] |
|||
|
|||
def __init__(self, message=b'', round_offset=0): |
|||
""" |
|||
Constructor. |
|||
|
|||
:param message: |
|||
Initial data to pass to update(). |
|||
|
|||
:param round_offset: |
|||
The _round() method can be overridden to report intermediate hash |
|||
values, in which case it's useful to know how many rounds came |
|||
before. For applications that perform double-hashing, you can |
|||
specify the number of rounds from the previous hash instance |
|||
using this parameter. |
|||
|
|||
""" |
|||
|
|||
self.state = self.INITIAL_STATE |
|||
self.length = long(0) |
|||
self.buffer = b'' |
|||
self.round_offset = round_offset |
|||
|
|||
self.update(message) |
|||
|
|||
def update(self, message): |
|||
""" |
|||
Updates the hash with the contents of *message*. |
|||
|
|||
Hashing uses 512-bit blocks, so the message is buffered until there's |
|||
enough data to process a complete block. When digest() is called, |
|||
any remaining data in the buffer will be padded and digested. |
|||
|
|||
:param message: |
|||
A byte string to digest. |
|||
|
|||
""" |
|||
|
|||
message = bytes(message) |
|||
self.length += len(message) * 8 |
|||
self.buffer = b''.join((self.buffer, message)) |
|||
|
|||
while len(self.buffer) >= 64: |
|||
self.state = self._process_block(self.buffer[:64], self.state, self.round_offset) |
|||
self.buffer = self.buffer[64:] |
|||
self.round_offset += 64 |
|||
|
|||
def digest(self): |
|||
""" |
|||
Returns the SHA256 digest of the message. |
|||
|
|||
The hash is based on all data passed thus far via the constructor and |
|||
update(). Any buffered data will be processed (along with the |
|||
terminating length), however the internal state is not modified. This |
|||
means that update() can safely be used again after digest(). |
|||
|
|||
""" |
|||
|
|||
final_state = self.state |
|||
for block in self._pad_message(self.buffer, self.length): |
|||
final_state = self._process_block(block, final_state, self.round_offset) |
|||
|
|||
return struct.pack('>LLLLLLLL', *final_state) |
|||
|
|||
def hexdigest(self): |
|||
"""Like digest(), but returns a hexadecimal string.""" |
|||
|
|||
return binascii.hexlify(self.digest()) |
|||
@ -0,0 +1,188 @@ |
|||
# |
|||
# toyecc - A small Elliptic Curve Cryptography Demonstration. |
|||
# Copyright (C) 2011-2022 Johannes Bauer |
|||
# |
|||
# This file is part of toyecc. |
|||
# |
|||
# toyecc is free software; you can redistribute it and/or modify |
|||
# it under the terms of the GNU General Public License as published by |
|||
# the Free Software Foundation; this program is ONLY licensed under |
|||
# version 3 of the License, later versions are explicitly excluded. |
|||
# |
|||
# toyecc is distributed in the hope that it will be useful, |
|||
# but WITHOUT ANY WARRANTY; without even the implied warranty of |
|||
# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the |
|||
# GNU General Public License for more details. |
|||
# |
|||
# You should have received a copy of the GNU General Public License |
|||
# along with toyecc; if not, write to the Free Software |
|||
# Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA |
|||
# |
|||
# Johannes Bauer <JohannesBauer@gmx.de> |
|||
# |
|||
|
|||
try: |
|||
from pyasn1.type import univ, namedtype, tag |
|||
import pyasn1.codec.ber.decoder |
|||
|
|||
class ECPVer(univ.Integer): |
|||
"""RFC 3279: Algorithms and Identifiers for the Internet X.509 Public Key Infrastructure Certificate and Certificate Revocation List (CRL) Profile |
|||
|
|||
ECPVer ::= INTEGER {ecpVer1(1)} |
|||
""" |
|||
pass |
|||
|
|||
class FieldElement(univ.OctetString): |
|||
"""RFC 3279: Algorithms and Identifiers for the Internet X.509 Public Key Infrastructure Certificate and Certificate Revocation List (CRL) Profile |
|||
|
|||
FieldElement ::= OCTET STRING |
|||
""" |
|||
pass |
|||
|
|||
class ECPoint(univ.OctetString): |
|||
"""RFC 3279: Algorithms and Identifiers for the Internet X.509 Public Key Infrastructure Certificate and Certificate Revocation List (CRL) Profile |
|||
|
|||
ECPoint ::= OCTET STRING |
|||
""" |
|||
pass |
|||
|
|||
class Curve(univ.Sequence): |
|||
"""RFC 3279: Algorithms and Identifiers for the Internet X.509 Public Key Infrastructure Certificate and Certificate Revocation List (CRL) Profile |
|||
|
|||
Curve ::= SEQUENCE { |
|||
a FieldElement, |
|||
b FieldElement, |
|||
seed BIT STRING OPTIONAL |
|||
} |
|||
""" |
|||
componentType = namedtype.NamedTypes( |
|||
namedtype.NamedType("a", FieldElement()), |
|||
namedtype.NamedType("b", FieldElement()), |
|||
namedtype.OptionalNamedType("seed", univ.BitString()), |
|||
) |
|||
|
|||
class FieldID(univ.Sequence): |
|||
"""RFC 3279: Algorithms and Identifiers for the Internet X.509 Public Key Infrastructure Certificate and Certificate Revocation List (CRL) Profile |
|||
|
|||
FieldID ::= SEQUENCE { |
|||
fieldType OBJECT IDENTIFIER, |
|||
parameters ANY DEFINED BY fieldType |
|||
} |
|||
""" |
|||
componentType = namedtype.NamedTypes( |
|||
namedtype.NamedType("fieldType", univ.ObjectIdentifier()), |
|||
namedtype.NamedType("parameters", univ.Any()), |
|||
) |
|||
|
|||
class SpecifiedECDomain(univ.Sequence): |
|||
"""RFC 3279: Algorithms and Identifiers for the Internet X.509 Public Key Infrastructure Certificate and Certificate Revocation List (CRL) Profile |
|||
|
|||
ECParameters ::= SEQUENCE { |
|||
version ECPVer, -- version is always 1 |
|||
fieldID FieldID, -- identifies the finite field over which the curve is defined |
|||
curve Curve, -- coefficients a and b of the elliptic curve |
|||
base ECPoint, -- specifies the base point P on the elliptic curve |
|||
order INTEGER, -- the order n of the base point |
|||
cofactor INTEGER OPTIONAL -- The integer h = #E(Fq)/n |
|||
} |
|||
""" |
|||
componentType = namedtype.NamedTypes( |
|||
namedtype.NamedType("version", ECPVer()), |
|||
namedtype.NamedType("fieldID", FieldID()), |
|||
namedtype.NamedType("curve", Curve()), |
|||
namedtype.NamedType("base", ECPoint()), |
|||
namedtype.NamedType("order", univ.Integer()), |
|||
namedtype.OptionalNamedType("cofactor", univ.Integer()), |
|||
) |
|||
|
|||
class ECParameters(univ.Choice): |
|||
"""RFC 5480: Elliptic Curve Cryptography Subject Public Key Information |
|||
|
|||
ECParameters ::= CHOICE { |
|||
namedCurve OBJECT IDENTIFIER |
|||
implicitCurve NULL |
|||
specifiedCurve SpecifiedECDomain |
|||
} |
|||
""" |
|||
componentType = namedtype.NamedTypes( |
|||
namedtype.NamedType("namedCurve", univ.ObjectIdentifier()), |
|||
namedtype.NamedType("implicitCurve", univ.Null()), |
|||
namedtype.NamedType("specifiedCurve", SpecifiedECDomain()), |
|||
) |
|||
|
|||
|
|||
class ECPrivateKey(univ.Sequence): |
|||
"""RFC 5915: Elliptic Curve Private Key Structure |
|||
|
|||
ECPrivateKey ::= SEQUENCE { |
|||
version INTEGER { ecPrivkeyVer1(1) } (ecPrivkeyVer1), |
|||
privateKey OCTET STRING, |
|||
parameters [0] ECParameters {{ NamedCurve }} OPTIONAL, |
|||
publicKey [1] BIT STRING OPTIONAL |
|||
} |
|||
""" |
|||
componentType = namedtype.NamedTypes( |
|||
namedtype.NamedType("version", univ.Integer()), |
|||
namedtype.NamedType("privateKey", univ.OctetString()), |
|||
namedtype.OptionalNamedType("parameters", ECParameters().subtype(implicitTag=tag.Tag(tag.tagClassContext, tag.tagFormatSimple, 0))), |
|||
namedtype.OptionalNamedType("publicKey", univ.BitString().subtype(implicitTag=tag.Tag(tag.tagClassContext, tag.tagFormatSimple, 1))), |
|||
) |
|||
|
|||
class AlgorithmIdentifier(univ.Sequence): |
|||
"""RFC 5480: Elliptic Curve Cryptography Subject Public Key Information |
|||
|
|||
AlgorithmIdentifier ::= SEQUENCE { |
|||
algorithm OBJECT IDENTIFIER, |
|||
parameters ANY DEFINED BY algorithm OPTIONAL |
|||
} |
|||
""" |
|||
componentType = namedtype.NamedTypes( |
|||
namedtype.NamedType("algorithm", univ.ObjectIdentifier()), |
|||
namedtype.NamedType("parameters", ECParameters()), |
|||
) |
|||
|
|||
class ECPublicKey(univ.Sequence): |
|||
"""RFC 5480: Elliptic Curve Cryptography Subject Public Key Information |
|||
|
|||
SubjectPublicKeyInfo ::= SEQUENCE { |
|||
algorithm AlgorithmIdentifier, |
|||
subjectPublicKey BIT STRING |
|||
} |
|||
""" |
|||
componentType = namedtype.NamedTypes( |
|||
namedtype.NamedType("algorithm", AlgorithmIdentifier()), |
|||
namedtype.NamedType("subjectPublicKey", univ.BitString()), |
|||
) |
|||
|
|||
class FieldFPParameters(univ.Integer): |
|||
"""For F_P fields, the field parameters is just the integer P.""" |
|||
pass |
|||
|
|||
__have_asn1 = True |
|||
except ImportError: |
|||
__have_asn1 = False |
|||
|
|||
def have_asn1_support(): |
|||
return __have_asn1 |
|||
|
|||
def __assert_asn1_support(): |
|||
if not have_asn1_support(): |
|||
raise Exception("ASN.1 support is required, but the pyasn1 library could not be imported. Functionality not available.") |
|||
|
|||
def parse_asn1_field_params_fp(derdata): |
|||
"""Parse an ASN.1 DER encoded field parameter for fields in F_P.""" |
|||
__assert_asn1_support() |
|||
(parsed, tail) = pyasn1.codec.ber.decoder.decode(derdata, asn1Spec = FieldFPParameters()) |
|||
return parsed |
|||
|
|||
def parse_asn1_public_key(derdata): |
|||
"""Parse an ASN.1 DER encoded EC public key.""" |
|||
__assert_asn1_support() |
|||
(parsed, tail) = pyasn1.codec.ber.decoder.decode(derdata, asn1Spec = ECPublicKey()) |
|||
return parsed |
|||
|
|||
def parse_asn1_private_key(derdata): |
|||
"""Parse an ASN.1 DER encoded EC private key.""" |
|||
__assert_asn1_support() |
|||
(parsed, tail) = pyasn1.codec.ber.decoder.decode(derdata, asn1Spec = ECPrivateKey()) |
|||
return parsed |
|||
@ -0,0 +1,132 @@ |
|||
# |
|||
# toyecc - A small Elliptic Curve Cryptography Demonstration. |
|||
# Copyright (C) 2011-2022 Johannes Bauer |
|||
# |
|||
# This file is part of toyecc. |
|||
# |
|||
# toyecc is free software; you can redistribute it and/or modify |
|||
# it under the terms of the GNU General Public License as published by |
|||
# the Free Software Foundation; this program is ONLY licensed under |
|||
# version 3 of the License, later versions are explicitly excluded. |
|||
# |
|||
# toyecc is distributed in the hope that it will be useful, |
|||
# but WITHOUT ANY WARRANTY; without even the implied warranty of |
|||
# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the |
|||
# GNU General Public License for more details. |
|||
# |
|||
# You should have received a copy of the GNU General Public License |
|||
# along with toyecc; if not, write to the Free Software |
|||
# Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA |
|||
# |
|||
# Johannes Bauer <JohannesBauer@gmx.de> |
|||
# |
|||
|
|||
import math |
|||
|
|||
from .FieldElement import FieldElement |
|||
from .PointOps import PointOpEDDSAEncoding, PointOpCurveConversion, PointOpNaiveOrderCalculation, PointOpSerialization, PointOpScalarMultiplicationXOnly |
|||
|
|||
class AffineCurvePoint(PointOpEDDSAEncoding, PointOpCurveConversion, PointOpNaiveOrderCalculation, PointOpSerialization, PointOpScalarMultiplicationXOnly): |
|||
"""Represents a point on a curve in affine (x, y) representation.""" |
|||
|
|||
def __init__(self, x, y, curve): |
|||
"""Generate a curve point (x, y) on the curve 'curve'. x and y have to |
|||
be integers. If the neutral element of the group O (for some curves, |
|||
this is a point at infinity) should be created, use the static method |
|||
'neutral', since representations of O differ on various curves (e.g. in |
|||
short Weierstrass curves, they have no explicit notation in affine |
|||
space while on twisted Edwards curves they do.""" |
|||
# Either x and y are None (Point at Infty) or both are defined |
|||
assert(((x is None) and (y is None)) or ((x is not None) and (y is not None))) |
|||
assert((x is None) or isinstance(x, int)) |
|||
assert((y is None) or isinstance(y, int)) |
|||
if x is None: |
|||
# Point at infinity |
|||
self._x = None |
|||
self._y = None |
|||
else: |
|||
self._x = FieldElement(x, curve.p) |
|||
self._y = FieldElement(y, curve.p) |
|||
self._curve = curve |
|||
|
|||
@staticmethod |
|||
def neutral(curve): |
|||
"""Returns the neutral element of the curve group.""" |
|||
return curve.neutral() |
|||
|
|||
@property |
|||
def is_neutral(self): |
|||
"""Indicates if the point is the neutral element O of the curve (point |
|||
at infinity for some curves).""" |
|||
return self.curve.is_neutral(self) |
|||
|
|||
@property |
|||
def x(self): |
|||
"""Affine X component of the point, field element of p.""" |
|||
return self._x |
|||
|
|||
@property |
|||
def y(self): |
|||
"""Affine Y component of the point, field element of p.""" |
|||
return self._y |
|||
|
|||
@property |
|||
def curve(self): |
|||
"""Curve that the point is located on.""" |
|||
return self._curve |
|||
|
|||
def __add__(self, other): |
|||
"""Returns the point addition.""" |
|||
assert(isinstance(other, AffineCurvePoint)) |
|||
return self.curve.point_addition(self, other) |
|||
|
|||
def __rmul__(self, other): |
|||
return self * other |
|||
|
|||
def __neg__(self): |
|||
"""Returns the conjugated point.""" |
|||
return self.curve.point_conjugate(self) |
|||
|
|||
def __mul__(self, scalar): |
|||
"""Returns the scalar point multiplication. The scalar needs to be an |
|||
integer value.""" |
|||
assert(isinstance(scalar, int)) |
|||
assert(scalar >= 0) |
|||
|
|||
result = self.curve.neutral() |
|||
n = self |
|||
if scalar > 0: |
|||
for bit in range(scalar.bit_length()): |
|||
if (scalar & (1 << bit)): |
|||
result = result + n |
|||
n = n + n |
|||
#assert(result.oncurve()) |
|||
return result |
|||
|
|||
def __eq__(self, other): |
|||
return (self.x, self.y) == (other.x, other.y) |
|||
|
|||
def __ne__(self, other): |
|||
return not (self == other) |
|||
|
|||
def __hash__(self): |
|||
return hash((self.x, self.y)) |
|||
|
|||
def oncurve(self): |
|||
"""Indicates if the given point is satisfying the curve equation (i.e. |
|||
if it is a point on the curve).""" |
|||
return self.curve.oncurve(self) |
|||
|
|||
def compress(self): |
|||
"""Returns the compressed point format (if this is possible on the |
|||
given curve).""" |
|||
return self.curve.compress(self) |
|||
|
|||
def __repr__(self): |
|||
return str(self) |
|||
|
|||
def __str__(self): |
|||
if self.is_neutral: |
|||
return "(neutral)" |
|||
else: |
|||
return "(0x%x, 0x%x)" % (int(self.x), int(self.y)) |
|||
@ -0,0 +1,59 @@ |
|||
# |
|||
# toyecc - A small Elliptic Curve Cryptography Demonstration. |
|||
# Copyright (C) 2011-2022 Johannes Bauer |
|||
# |
|||
# This file is part of toyecc. |
|||
# |
|||
# toyecc is free software; you can redistribute it and/or modify |
|||
# it under the terms of the GNU General Public License as published by |
|||
# the Free Software Foundation; this program is ONLY licensed under |
|||
# version 3 of the License, later versions are explicitly excluded. |
|||
# |
|||
# toyecc is distributed in the hope that it will be useful, |
|||
# but WITHOUT ANY WARRANTY; without even the implied warranty of |
|||
# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the |
|||
# GNU General Public License for more details. |
|||
# |
|||
# You should have received a copy of the GNU General Public License |
|||
# along with toyecc; if not, write to the Free Software |
|||
# Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA |
|||
# |
|||
# Johannes Bauer <JohannesBauer@gmx.de> |
|||
# |
|||
|
|||
from .FieldElement import FieldElement |
|||
|
|||
class CRT(): |
|||
"""Implements the Chinese Remainder Theorem algorithm where a number of |
|||
modular congruences are given that all need to be satisfied.""" |
|||
def __init__(self): |
|||
self._moduli = { } |
|||
|
|||
def add(self, value, modulus): |
|||
"""Adds a value that shall be returned when the result is taken modulo |
|||
the given modulus.""" |
|||
assert(modulus not in self._moduli) |
|||
assert(isinstance(value, int)) |
|||
assert(isinstance(modulus, int)) |
|||
self._moduli[modulus] = value |
|||
return self |
|||
|
|||
def solve(self): |
|||
"""Solve the Chinese Remainder Theorem for the given values and |
|||
moduli.""" |
|||
# Calculate product of all moduli |
|||
product = 1 |
|||
for modulus in self._moduli.keys(): |
|||
product *= modulus |
|||
|
|||
# Then determine the solution |
|||
solution = 0 |
|||
for modulus in self._moduli.keys(): |
|||
if self._moduli[modulus] == 0: |
|||
continue |
|||
|
|||
rem_product = product // modulus |
|||
one_value = int(FieldElement(rem_product, modulus).inverse()) |
|||
solution += rem_product * one_value * self._moduli[modulus] |
|||
|
|||
return solution % product |
|||
@ -0,0 +1,842 @@ |
|||
# |
|||
# toyecc - A small Elliptic Curve Cryptography Demonstration. |
|||
# Copyright (C) 2011-2022 Johannes Bauer |
|||
# |
|||
# This file is part of toyecc. |
|||
# |
|||
# toyecc is free software; you can redistribute it and/or modify |
|||
# it under the terms of the GNU General Public License as published by |
|||
# the Free Software Foundation; this program is ONLY licensed under |
|||
# version 3 of the License, later versions are explicitly excluded. |
|||
# |
|||
# toyecc is distributed in the hope that it will be useful, |
|||
# but WITHOUT ANY WARRANTY; without even the implied warranty of |
|||
# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the |
|||
# GNU General Public License for more details. |
|||
# |
|||
# You should have received a copy of the GNU General Public License |
|||
# along with toyecc; if not, write to the Free Software |
|||
# Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA |
|||
# |
|||
# Johannes Bauer <JohannesBauer@gmx.de> |
|||
# |
|||
|
|||
import enum |
|||
import collections |
|||
from .ShortWeierstrassCurve import ShortWeierstrassCurve |
|||
from .MontgomeryCurve import MontgomeryCurve |
|||
from .TwistedEdwardsCurve import TwistedEdwardsCurve |
|||
from .Singleton import singleton |
|||
from .FieldElement import FieldElement |
|||
from .Exceptions import DuplicateCurveException, NoSuchCurveException, UnsupportedFieldException |
|||
from .ASN1 import parse_asn1_field_params_fp |
|||
from .AffineCurvePoint import AffineCurvePoint |
|||
from .CurveQuirks import CurveQuirkEdDSASetPrivateKeyMSB, CurveQuirkEdDSAEnsurePrimeOrderSubgroup, CurveQuirkSigningHashFunction |
|||
from . import Tools |
|||
|
|||
@singleton |
|||
class CurveDB(object): |
|||
def __init__(self): |
|||
self._entries = { } |
|||
self._primary_names = set() |
|||
self._taken_names = set() |
|||
|
|||
def _checknames(self, curvenames): |
|||
if len(curvenames & self._taken_names) > 0: |
|||
taken_names = ", ".join(sorted(list(curvenames & self._taken_names))) |
|||
raise DuplicateCurveException("Curve(s) named %s already registered in curve DB." % (taken_names)) |
|||
|
|||
def register(self, entry): |
|||
"""Registers a curve in the curve database.""" |
|||
all_names = set(name.lower() for name in entry.all_aliases) |
|||
self._checknames(all_names) |
|||
self._taken_names |= all_names |
|||
self._primary_names.add(entry.name) |
|||
|
|||
self._entries[entry.primary_name.lower()] = entry |
|||
for aliasname in entry.aliases: |
|||
clone = entry.clone(secondary_name = aliasname) |
|||
self._entries[aliasname.lower()] = clone |
|||
|
|||
def curvenames(self): |
|||
"""Returns the primary names of all curves in the DB.""" |
|||
return (curve.name for curve in self._entries.values() if (curve.is_aka is False)) |
|||
|
|||
def allcurvenames(self): |
|||
"""Returns all names of all curves in the DB. This includes duplicate |
|||
AKAs such as secp224r1 which is also known as wap-wsg-idm-ecid-wtls12 |
|||
albeit under a different OID.""" |
|||
return (curve.name for curve in self._entries.values()) |
|||
|
|||
def find_duplicate_curves(self): |
|||
"""Returns curves in which the domain parameters (including the |
|||
coordinates of the generator point G) are identical. This can happen if |
|||
identical curves are registered under the same name.""" |
|||
params = collections.defaultdict(list) |
|||
for curve in self: |
|||
params[tuple(sorted(curve.domain_params))].append(curve.name) |
|||
return [ curves for (param, curves) in params.items() if (len(curves) > 1) ] |
|||
|
|||
def getentry(self, name): |
|||
"""Returns a specific curve entry by its case-insensitive name.""" |
|||
name = name.lower() |
|||
if name not in self._entries: |
|||
raise KeyError("Curve named '%s' is not known in curve database." % (name)) |
|||
return self._entries[name] |
|||
|
|||
def get_curve_from_asn1(self, asn1): |
|||
"""This function will take a parsed ASN.1 ECParameters class as input |
|||
and try to return the curve specified within. If the ECParameters |
|||
specify a named curve by its's OID then a lookup is performed against |
|||
the curve database and that named curve returned on success if |
|||
non-ambiguous. If the parameters are exclicitly stated, then an unnamed |
|||
ShortWeierstrassCurve is constructed.""" |
|||
|
|||
if asn1["namedCurve"] is not None: |
|||
# Curve is encoded as OID, look up from curve DB |
|||
curve_oid = str(asn1["namedCurve"]) |
|||
entries = [ entry for entry in self if (entry.oid == curve_oid) ] |
|||
if len(entries) == 0: |
|||
raise NoSuchCurveException("Trying to load curve with OID %s from curve DB, but no such curve is present in database." % (curve_oid)) |
|||
elif len(entries) > 1: |
|||
raise Exception("Trying to load curve with OID %s from curve DB, but found %d curves (refuse to guess in the face of ambiguity)." % (curve_oid, len(entries))) |
|||
curve = entries[0]() |
|||
elif asn1["specifiedCurve"] is not None: |
|||
field_type_oid = str(asn1["specifiedCurve"]["fieldID"]["fieldType"]) |
|||
if field_type_oid == "1.2.840.10045.1.1": |
|||
# F_P curve is encoded in explicit form |
|||
p = int(parse_asn1_field_params_fp(asn1["specifiedCurve"]["fieldID"]["parameters"])) |
|||
a = Tools.bytestoint(asn1["specifiedCurve"]["curve"]["a"]) |
|||
b = Tools.bytestoint(asn1["specifiedCurve"]["curve"]["b"]) |
|||
G = bytes(asn1["specifiedCurve"]["base"]) |
|||
(Gx, Gy) = AffineCurvePoint.deserialize_uncompressed(G) |
|||
n = int(asn1["specifiedCurve"]["order"]) |
|||
h = int(asn1["specifiedCurve"]["cofactor"]) |
|||
curve = ShortWeierstrassCurve(p = p, a = a, b = b, n = n, h = h, Gx = Gx, Gy = Gy) |
|||
else: |
|||
# Maybe F_2^N curve or some other, unsupported type |
|||
raise UnsupportedFieldException("Only supports elliptic curves in F_P are supported, but the requested field type OID was %s." % (field_type_oid)) |
|||
else: |
|||
raise NoSuchCurveException("Cannot load implicit curve.") |
|||
return curve |
|||
|
|||
def __iter__(self): |
|||
"""Iterates over the curve DB entries.""" |
|||
for name in self.curvenames(): |
|||
yield self._entries[name.lower()] |
|||
|
|||
def __getitem__(self, name): |
|||
"""Returns a curve (not a curve DB entry) by its name.""" |
|||
return self.getentry(name)() |
|||
|
|||
def __str__(self): |
|||
return "CurveDB<%d unique curves, %d total>" % (len(self._primary_names), len(self._entries)) |
|||
|
|||
|
|||
class _CurveDBEntry(object): |
|||
def __init__(self, primary_name, curve_class, domain_params, **kwargs): |
|||
allowed_kwargs = set(("oid", "alt_oids", "aliases", "origin", "secure", "quirks")) |
|||
wrong_args = kwargs.keys() - allowed_kwargs |
|||
if len(wrong_args) > 0: |
|||
raise Exception("Illegal keyword arguments: %s" % (", ".join(sorted(wrong_args)))) |
|||
|
|||
assert(primary_name is not None) |
|||
self._primary_name = primary_name |
|||
self._secondary_name = None |
|||
self._curve_class = curve_class |
|||
self._domain_params = domain_params |
|||
self._oid = kwargs.get("oid") |
|||
self._alt_oids = kwargs.get("alt_oids") |
|||
self._aliases = kwargs.get("aliases") |
|||
self._origin = kwargs.get("origin") |
|||
self._secure = kwargs.get("secure", True) |
|||
self._quirks = kwargs.get("quirks", [ ]) |
|||
self._instance = None |
|||
|
|||
def clone(self, secondary_name = None): |
|||
clone = _CurveDBEntry(primary_name = self._primary_name, curve_class = self._curve_class, domain_params = self._domain_params, oid = self._oid, alt_oids = self._alt_oids, aliases = self._aliases, origin = self._origin, secure = self._secure) |
|||
clone._instance = self._instance |
|||
clone._secondary_name = secondary_name |
|||
return clone |
|||
|
|||
@property |
|||
def is_aka(self): |
|||
"""Returns if this curve entry is an AKA ('also known as') for a |
|||
different curve (but maybe with a different OID). Example: prime192v1 |
|||
and secp192r1 refer to identical curves, but 'prime192v1' is the |
|||
internally considered primary name while 'secp192r1' is considered to |
|||
be an AKA.""" |
|||
return self._secondary_name is not None |
|||
|
|||
@property |
|||
def primary_name(self): |
|||
return self._primary_name |
|||
|
|||
@property |
|||
def name(self): |
|||
if self._secondary_name is not None: |
|||
return self._secondary_name |
|||
else: |
|||
return self._primary_name |
|||
|
|||
@property |
|||
def fieldsize_bits(self): |
|||
return self._domain_params["p"].bit_length() |
|||
|
|||
@property |
|||
def secure(self): |
|||
return self._secure |
|||
|
|||
@property |
|||
def origin(self): |
|||
return self._origin |
|||
|
|||
@property |
|||
def bits_security_estimate(self): |
|||
if not self.secure: |
|||
return 0 |
|||
else: |
|||
# Require instanciation of the class |
|||
self() |
|||
return self._instance.security_bit_estimate |
|||
|
|||
def get_alternative_oid(self, name): |
|||
"""Returns the alternative OID if it has one.""" |
|||
if self._alt_oids is not None: |
|||
return self._alt_oids.get(name) |
|||
|
|||
@property |
|||
def oid(self): |
|||
if (self._alt_oids is not None) and (self.name in self._alt_oids): |
|||
return self._alt_oids[self.name] |
|||
else: |
|||
return self._oid |
|||
|
|||
@property |
|||
def aliases(self): |
|||
if self._aliases is not None: |
|||
yield from self._aliases |
|||
|
|||
@property |
|||
def all_aliases(self): |
|||
yield self._primary_name |
|||
yield from self.aliases |
|||
|
|||
@property |
|||
def prettyname(self): |
|||
if self._instance is None: |
|||
return self._curve_class.pretty_name |
|||
else: |
|||
return self._instance.prettyname |
|||
|
|||
@property |
|||
def domain_params(self): |
|||
if self._instance is None: |
|||
return dict(self._domain_params) |
|||
else: |
|||
return self._instance.domainparamdict |
|||
|
|||
@property |
|||
def prettytitle(self): |
|||
return "%d bit %s Curve" % (self.fieldsize_bits, self.prettyname) |
|||
|
|||
def dump(self, domain = False): |
|||
print("%s: %s" % (self.name, self.prettytitle)) |
|||
if self._aliases is not None: |
|||
print("Aliases: %s" % (", ".join(sorted(list(self._aliases))))) |
|||
if self._oid is not None: |
|||
print("OID : %s" % (self._oid)) |
|||
if domain: |
|||
print("Domain parameters:") |
|||
for (key, value) in sorted(self.domain_params.items()): |
|||
if isinstance(value, FieldElement): |
|||
value = value.sigint() |
|||
print(" %-10s %s" % (key, value)) |
|||
|
|||
def __call__(self): |
|||
"""Instanciate the curve.""" |
|||
if self._instance is None: |
|||
# Instanciate actual curve |
|||
params = self._domain_params |
|||
params["name"] = self.name |
|||
params["quirks"] = self._quirks |
|||
self._instance = self._curve_class(**params) |
|||
return self._instance |
|||
|
|||
def __str__(self): |
|||
if self._secondary_name is not None: |
|||
return "CurveDBEntry<%s AKA %s>" % (self.primary_name, self._secondary_name) |
|||
else: |
|||
return "CurveDBEntry<%s>" % (self.name) |
|||
|
|||
cdb = CurveDB() |
|||
cdb.register(_CurveDBEntry("brainpoolP160r1", ShortWeierstrassCurve, { |
|||
"a": 0x340e7be2a280eb74e2be61bada745d97e8f7c300, |
|||
"b": 0x1e589a8595423412134faa2dbdec95c8d8675e58, |
|||
"p": 0xe95e4a5f737059dc60dfc7ad95b3d8139515620f, |
|||
"n": 0xe95e4a5f737059dc60df5991d45029409e60fc09, |
|||
"h": 1, |
|||
"Gx": 0xbed5af16ea3f6a4f62938c4631eb5af7bdbcdbc3, |
|||
"Gy": 0x1667cb477a1a8ec338f94741669c976316da6321, |
|||
}, oid = "1.3.36.3.3.2.8.1.1.1", origin = "ECC Brainpool")) |
|||
|
|||
cdb.register(_CurveDBEntry("brainpoolP160t1", ShortWeierstrassCurve, { |
|||
"a": 0xe95e4a5f737059dc60dfc7ad95b3d8139515620c, |
|||
"b": 0x7a556b6dae535b7b51ed2c4d7daa7a0b5c55f380, |
|||
"p": 0xe95e4a5f737059dc60dfc7ad95b3d8139515620f, |
|||
"n": 0xe95e4a5f737059dc60df5991d45029409e60fc09, |
|||
"h": 1, |
|||
"Gx": 0xb199b13b9b34efc1397e64baeb05acc265ff2378, |
|||
"Gy": 0xadd6718b7c7c1961f0991b842443772152c9e0ad, |
|||
}, oid = "1.3.36.3.3.2.8.1.1.2", origin = "ECC Brainpool")) |
|||
|
|||
cdb.register(_CurveDBEntry("brainpoolP192r1", ShortWeierstrassCurve, { |
|||
"a": 0x6a91174076b1e0e19c39c031fe8685c1cae040e5c69a28ef, |
|||
"b": 0x469a28ef7c28cca3dc721d044f4496bcca7ef4146fbf25c9, |
|||
"p": 0xc302f41d932a36cda7a3463093d18db78fce476de1a86297, |
|||
"n": 0xc302f41d932a36cda7a3462f9e9e916b5be8f1029ac4acc1, |
|||
"h": 1, |
|||
"Gx": 0xc0a0647eaab6a48753b033c56cb0f0900a2f5c4853375fd6, |
|||
"Gy": 0x14b690866abd5bb88b5f4828c1490002e6773fa2fa299b8f, |
|||
}, oid = "1.3.36.3.3.2.8.1.1.3", origin = "ECC Brainpool")) |
|||
|
|||
cdb.register(_CurveDBEntry("brainpoolP192t1", ShortWeierstrassCurve, { |
|||
"a": 0xc302f41d932a36cda7a3463093d18db78fce476de1a86294, |
|||
"b": 0x13d56ffaec78681e68f9deb43b35bec2fb68542e27897b79, |
|||
"p": 0xc302f41d932a36cda7a3463093d18db78fce476de1a86297, |
|||
"n": 0xc302f41d932a36cda7a3462f9e9e916b5be8f1029ac4acc1, |
|||
"h": 1, |
|||
"Gx": 0x3ae9e58c82f63c30282e1fe7bbf43fa72c446af6f4618129, |
|||
"Gy": 0x97e2c5667c2223a902ab5ca449d0084b7e5b3de7ccc01c9, |
|||
}, oid = "1.3.36.3.3.2.8.1.1.4", origin = "ECC Brainpool")) |
|||
|
|||
cdb.register(_CurveDBEntry("brainpoolP224r1", ShortWeierstrassCurve, { |
|||
"a": 0x68a5e62ca9ce6c1c299803a6c1530b514e182ad8b0042a59cad29f43, |
|||
"b": 0x2580f63ccfe44138870713b1a92369e33e2135d266dbb372386c400b, |
|||
"p": 0xd7c134aa264366862a18302575d1d787b09f075797da89f57ec8c0ff, |
|||
"n": 0xd7c134aa264366862a18302575d0fb98d116bc4b6ddebca3a5a7939f, |
|||
"h": 1, |
|||
"Gx": 0xd9029ad2c7e5cf4340823b2a87dc68c9e4ce3174c1e6efdee12c07d, |
|||
"Gy": 0x58aa56f772c0726f24c6b89e4ecdac24354b9e99caa3f6d3761402cd, |
|||
}, oid = "1.3.36.3.3.2.8.1.1.5", origin = "ECC Brainpool")) |
|||
|
|||
cdb.register(_CurveDBEntry("brainpoolP224t1", ShortWeierstrassCurve, { |
|||
"a": 0xd7c134aa264366862a18302575d1d787b09f075797da89f57ec8c0fc, |
|||
"b": 0x4b337d934104cd7bef271bf60ced1ed20da14c08b3bb64f18a60888d, |
|||
"p": 0xd7c134aa264366862a18302575d1d787b09f075797da89f57ec8c0ff, |
|||
"n": 0xd7c134aa264366862a18302575d0fb98d116bc4b6ddebca3a5a7939f, |
|||
"h": 1, |
|||
"Gx": 0x6ab1e344ce25ff3896424e7ffe14762ecb49f8928ac0c76029b4d580, |
|||
"Gy": 0x374e9f5143e568cd23f3f4d7c0d4b1e41c8cc0d1c6abd5f1a46db4c, |
|||
}, oid = "1.3.36.3.3.2.8.1.1.6", origin = "ECC Brainpool")) |
|||
|
|||
cdb.register(_CurveDBEntry("brainpoolP256r1", ShortWeierstrassCurve, { |
|||
"a": 0x7d5a0975fc2c3057eef67530417affe7fb8055c126dc5c6ce94a4b44f330b5d9, |
|||
"b": 0x26dc5c6ce94a4b44f330b5d9bbd77cbf958416295cf7e1ce6bccdc18ff8c07b6, |
|||
"p": 0xa9fb57dba1eea9bc3e660a909d838d726e3bf623d52620282013481d1f6e5377, |
|||
"n": 0xa9fb57dba1eea9bc3e660a909d838d718c397aa3b561a6f7901e0e82974856a7, |
|||
"h": 1, |
|||
"Gx": 0x8bd2aeb9cb7e57cb2c4b482ffc81b7afb9de27e1e3bd23c23a4453bd9ace3262, |
|||
"Gy": 0x547ef835c3dac4fd97f8461a14611dc9c27745132ded8e545c1d54c72f046997, |
|||
}, oid = "1.3.36.3.3.2.8.1.1.7", origin = "ECC Brainpool")) |
|||
|
|||
cdb.register(_CurveDBEntry("brainpoolP256t1", ShortWeierstrassCurve, { |
|||
"a": 0xa9fb57dba1eea9bc3e660a909d838d726e3bf623d52620282013481d1f6e5374, |
|||
"b": 0x662c61c430d84ea4fe66a7733d0b76b7bf93ebc4af2f49256ae58101fee92b04, |
|||
"p": 0xa9fb57dba1eea9bc3e660a909d838d726e3bf623d52620282013481d1f6e5377, |
|||
"n": 0xa9fb57dba1eea9bc3e660a909d838d718c397aa3b561a6f7901e0e82974856a7, |
|||
"h": 1, |
|||
"Gx": 0xa3e8eb3cc1cfe7b7732213b23a656149afa142c47aafbc2b79a191562e1305f4, |
|||
"Gy": 0x2d996c823439c56d7f7b22e14644417e69bcb6de39d027001dabe8f35b25c9be, |
|||
}, oid = "1.3.36.3.3.2.8.1.1.8", origin = "ECC Brainpool")) |
|||
|
|||
cdb.register(_CurveDBEntry("brainpoolP320r1", ShortWeierstrassCurve, { |
|||
"a": 0x3ee30b568fbab0f883ccebd46d3f3bb8a2a73513f5eb79da66190eb085ffa9f492f375a97d860eb4, |
|||
"b": 0x520883949dfdbc42d3ad198640688a6fe13f41349554b49acc31dccd884539816f5eb4ac8fb1f1a6, |
|||
"p": 0xd35e472036bc4fb7e13c785ed201e065f98fcfa6f6f40def4f92b9ec7893ec28fcd412b1f1b32e27, |
|||
"n": 0xd35e472036bc4fb7e13c785ed201e065f98fcfa5b68f12a32d482ec7ee8658e98691555b44c59311, |
|||
"h": 1, |
|||
"Gx": 0x43bd7e9afb53d8b85289bcc48ee5bfe6f20137d10a087eb6e7871e2a10a599c710af8d0d39e20611, |
|||
"Gy": 0x14fdd05545ec1cc8ab4093247f77275e0743ffed117182eaa9c77877aaac6ac7d35245d1692e8ee1, |
|||
}, oid = "1.3.36.3.3.2.8.1.1.9", origin = "ECC Brainpool")) |
|||
|
|||
cdb.register(_CurveDBEntry("brainpoolP320t1", ShortWeierstrassCurve, { |
|||
"a": 0xd35e472036bc4fb7e13c785ed201e065f98fcfa6f6f40def4f92b9ec7893ec28fcd412b1f1b32e24, |
|||
"b": 0xa7f561e038eb1ed560b3d147db782013064c19f27ed27c6780aaf77fb8a547ceb5b4fef422340353, |
|||
"p": 0xd35e472036bc4fb7e13c785ed201e065f98fcfa6f6f40def4f92b9ec7893ec28fcd412b1f1b32e27, |
|||
"n": 0xd35e472036bc4fb7e13c785ed201e065f98fcfa5b68f12a32d482ec7ee8658e98691555b44c59311, |
|||
"h": 1, |
|||
"Gx": 0x925be9fb01afc6fb4d3e7d4990010f813408ab106c4f09cb7ee07868cc136fff3357f624a21bed52, |
|||
"Gy": 0x63ba3a7a27483ebf6671dbef7abb30ebee084e58a0b077ad42a5a0989d1ee71b1b9bc0455fb0d2c3, |
|||
}, oid = "1.3.36.3.3.2.8.1.1.10", origin = "ECC Brainpool")) |
|||
|
|||
cdb.register(_CurveDBEntry("brainpoolP384r1", ShortWeierstrassCurve, { |
|||
"a": 0x7bc382c63d8c150c3c72080ace05afa0c2bea28e4fb22787139165efba91f90f8aa5814a503ad4eb04a8c7dd22ce2826, |
|||
"b": 0x4a8c7dd22ce28268b39b55416f0447c2fb77de107dcd2a62e880ea53eeb62d57cb4390295dbc9943ab78696fa504c11, |
|||
"p": 0x8cb91e82a3386d280f5d6f7e50e641df152f7109ed5456b412b1da197fb71123acd3a729901d1a71874700133107ec53, |
|||
"n": 0x8cb91e82a3386d280f5d6f7e50e641df152f7109ed5456b31f166e6cac0425a7cf3ab6af6b7fc3103b883202e9046565, |
|||
"h": 1, |
|||
"Gx": 0x1d1c64f068cf45ffa2a63a81b7c13f6b8847a3e77ef14fe3db7fcafe0cbd10e8e826e03436d646aaef87b2e247d4af1e, |
|||
"Gy": 0x8abe1d7520f9c2a45cb1eb8e95cfd55262b70b29feec5864e19c054ff99129280e4646217791811142820341263c5315, |
|||
}, oid = "1.3.36.3.3.2.8.1.1.11", origin = "ECC Brainpool")) |
|||
|
|||
cdb.register(_CurveDBEntry("brainpoolP384t1", ShortWeierstrassCurve, { |
|||
"a": 0x8cb91e82a3386d280f5d6f7e50e641df152f7109ed5456b412b1da197fb71123acd3a729901d1a71874700133107ec50, |
|||
"b": 0x7f519eada7bda81bd826dba647910f8c4b9346ed8ccdc64e4b1abd11756dce1d2074aa263b88805ced70355a33b471ee, |
|||
"p": 0x8cb91e82a3386d280f5d6f7e50e641df152f7109ed5456b412b1da197fb71123acd3a729901d1a71874700133107ec53, |
|||
"n": 0x8cb91e82a3386d280f5d6f7e50e641df152f7109ed5456b31f166e6cac0425a7cf3ab6af6b7fc3103b883202e9046565, |
|||
"h": 1, |
|||
"Gx": 0x18de98b02db9a306f2afcd7235f72a819b80ab12ebd653172476fecd462aabffc4ff191b946a5f54d8d0aa2f418808cc, |
|||
"Gy": 0x25ab056962d30651a114afd2755ad336747f93475b7a1fca3b88f2b6a208ccfe469408584dc2b2912675bf5b9e582928, |
|||
}, oid = "1.3.36.3.3.2.8.1.1.12", origin = "ECC Brainpool")) |
|||
|
|||
cdb.register(_CurveDBEntry("brainpoolP512r1", ShortWeierstrassCurve, { |
|||
"a": 0x7830a3318b603b89e2327145ac234cc594cbdd8d3df91610a83441caea9863bc2ded5d5aa8253aa10a2ef1c98b9ac8b57f1117a72bf2c7b9e7c1ac4d77fc94ca, |
|||
"b": 0x3df91610a83441caea9863bc2ded5d5aa8253aa10a2ef1c98b9ac8b57f1117a72bf2c7b9e7c1ac4d77fc94cadc083e67984050b75ebae5dd2809bd638016f723, |
|||
"p": 0xaadd9db8dbe9c48b3fd4e6ae33c9fc07cb308db3b3c9d20ed6639cca703308717d4d9b009bc66842aecda12ae6a380e62881ff2f2d82c68528aa6056583a48f3, |
|||
"n": 0xaadd9db8dbe9c48b3fd4e6ae33c9fc07cb308db3b3c9d20ed6639cca70330870553e5c414ca92619418661197fac10471db1d381085ddaddb58796829ca90069, |
|||
"h": 1, |
|||
"Gx": 0x81aee4bdd82ed9645a21322e9c4c6a9385ed9f70b5d916c1b43b62eef4d0098eff3b1f78e2d0d48d50d1687b93b97d5f7c6d5047406a5e688b352209bcb9f822, |
|||
"Gy": 0x7dde385d566332ecc0eabfa9cf7822fdf209f70024a57b1aa000c55b881f8111b2dcde494a5f485e5bca4bd88a2763aed1ca2b2fa8f0540678cd1e0f3ad80892, |
|||
}, oid = "1.3.36.3.3.2.8.1.1.13", origin = "ECC Brainpool")) |
|||
|
|||
cdb.register(_CurveDBEntry("brainpoolP512t1", ShortWeierstrassCurve, { |
|||
"a": 0xaadd9db8dbe9c48b3fd4e6ae33c9fc07cb308db3b3c9d20ed6639cca703308717d4d9b009bc66842aecda12ae6a380e62881ff2f2d82c68528aa6056583a48f0, |
|||
"b": 0x7cbbbcf9441cfab76e1890e46884eae321f70c0bcb4981527897504bec3e36a62bcdfa2304976540f6450085f2dae145c22553b465763689180ea2571867423e, |
|||
"p": 0xaadd9db8dbe9c48b3fd4e6ae33c9fc07cb308db3b3c9d20ed6639cca703308717d4d9b009bc66842aecda12ae6a380e62881ff2f2d82c68528aa6056583a48f3, |
|||
"n": 0xaadd9db8dbe9c48b3fd4e6ae33c9fc07cb308db3b3c9d20ed6639cca70330870553e5c414ca92619418661197fac10471db1d381085ddaddb58796829ca90069, |
|||
"h": 1, |
|||
"Gx": 0x640ece5c12788717b9c1ba06cbc2a6feba85842458c56dde9db1758d39c0313d82ba51735cdb3ea499aa77a7d6943a64f7a3f25fe26f06b51baa2696fa9035da, |
|||
"Gy": 0x5b534bd595f5af0fa2c892376c84ace1bb4e3019b71634c01131159cae03cee9d9932184beef216bd71df2dadf86a627306ecff96dbb8bace198b61e00f8b332, |
|||
}, oid = "1.3.36.3.3.2.8.1.1.14", origin = "ECC Brainpool")) |
|||
|
|||
cdb.register(_CurveDBEntry("prime192v1", ShortWeierstrassCurve, { |
|||
"a": 0xfffffffffffffffffffffffffffffffefffffffffffffffc, |
|||
"b": 0x64210519e59c80e70fa7e9ab72243049feb8deecc146b9b1, |
|||
"p": 0xfffffffffffffffffffffffffffffffeffffffffffffffff, |
|||
"n": 0xffffffffffffffffffffffff99def836146bc9b1b4d22831, |
|||
"h": 1, |
|||
"Gx": 0x188da80eb03090f67cbf20eb43a18800f4ff0afd82ff1012, |
|||
"Gy": 0x7192b95ffc8da78631011ed6b24cdd573f977a11e794811, |
|||
}, aliases = [ "secp192r1", "NIST P-192", "ansip192r1" ], oid = "1.2.840.10045.3.1.1", origin = "Certicom Standards for Efficient Cryptography (SEC) 2 / ANSI X9.62 / FIPS 186-2 / NIST Recommended Elliptic Curves for Federal Government Use")) |
|||
|
|||
cdb.register(_CurveDBEntry("prime192v2", ShortWeierstrassCurve, { |
|||
"a": 0xfffffffffffffffffffffffffffffffefffffffffffffffc, |
|||
"b": 0xcc22d6dfb95c6b25e49c0d6364a4e5980c393aa21668d953, |
|||
"p": 0xfffffffffffffffffffffffffffffffeffffffffffffffff, |
|||
"n": 0xfffffffffffffffffffffffe5fb1a724dc80418648d8dd31, |
|||
"h": 1, |
|||
"Gx": 0xeea2bae7e1497842f2de7769cfe9c989c072ad696f48034a, |
|||
"Gy": 0x6574d11d69b6ec7a672bb82a083df2f2b0847de970b2de15, |
|||
}, oid = "1.2.840.10045.3.1.2", origin = "ANSI X9.62")) |
|||
|
|||
cdb.register(_CurveDBEntry("prime192v3", ShortWeierstrassCurve, { |
|||
"a": 0xfffffffffffffffffffffffffffffffefffffffffffffffc, |
|||
"b": 0x22123dc2395a05caa7423daeccc94760a7d462256bd56916, |
|||
"p": 0xfffffffffffffffffffffffffffffffeffffffffffffffff, |
|||
"n": 0xffffffffffffffffffffffff7a62d031c83f4294f640ec13, |
|||
"h": 1, |
|||
"Gx": 0x7d29778100c65a1da1783716588dce2b8b4aee8e228f1896, |
|||
"Gy": 0x38a90f22637337334b49dcb66a6dc8f9978aca7648a943b0, |
|||
}, oid = "1.2.840.10045.3.1.3", origin = "ANSI X9.62")) |
|||
|
|||
cdb.register(_CurveDBEntry("prime239v1", ShortWeierstrassCurve, { |
|||
"a": 0x7fffffffffffffffffffffff7fffffffffff8000000000007ffffffffffc, |
|||
"b": 0x6b016c3bdcf18941d0d654921475ca71a9db2fb27d1d37796185c2942c0a, |
|||
"p": 0x7fffffffffffffffffffffff7fffffffffff8000000000007fffffffffff, |
|||
"n": 0x7fffffffffffffffffffffff7fffff9e5e9a9f5d9071fbd1522688909d0b, |
|||
"h": 1, |
|||
"Gx": 0xffa963cdca8816ccc33b8642bedf905c3d358573d3f27fbbd3b3cb9aaaf, |
|||
"Gy": 0x7debe8e4e90a5dae6e4054ca530ba04654b36818ce226b39fccb7b02f1ae, |
|||
}, oid = "1.2.840.10045.3.1.4", origin = "ANSI X9.62")) |
|||
|
|||
cdb.register(_CurveDBEntry("prime239v2", ShortWeierstrassCurve, { |
|||
"a": 0x7fffffffffffffffffffffff7fffffffffff8000000000007ffffffffffc, |
|||
"b": 0x617fab6832576cbbfed50d99f0249c3fee58b94ba0038c7ae84c8c832f2c, |
|||
"p": 0x7fffffffffffffffffffffff7fffffffffff8000000000007fffffffffff, |
|||
"n": 0x7fffffffffffffffffffffff800000cfa7e8594377d414c03821bc582063, |
|||
"h": 1, |
|||
"Gx": 0x38af09d98727705120c921bb5e9e26296a3cdcf2f35757a0eafd87b830e7, |
|||
"Gy": 0x5b0125e4dbea0ec7206da0fc01d9b081329fb555de6ef460237dff8be4ba, |
|||
}, oid = "1.2.840.10045.3.1.5", origin = "ANSI X9.62")) |
|||
|
|||
cdb.register(_CurveDBEntry("prime239v3", ShortWeierstrassCurve, { |
|||
"a": 0x7fffffffffffffffffffffff7fffffffffff8000000000007ffffffffffc, |
|||
"b": 0x255705fa2a306654b1f4cb03d6a750a30c250102d4988717d9ba15ab6d3e, |
|||
"p": 0x7fffffffffffffffffffffff7fffffffffff8000000000007fffffffffff, |
|||
"n": 0x7fffffffffffffffffffffff7fffff975deb41b3a6057c3c432146526551, |
|||
"h": 1, |
|||
"Gx": 0x6768ae8e18bb92cfcf005c949aa2c6d94853d0e660bbf854b1c9505fe95a, |
|||
"Gy": 0x1607e6898f390c06bc1d552bad226f3b6fcfe48b6e818499af18e3ed6cf3, |
|||
}, oid = "1.2.840.10045.3.1.6", origin = "ANSI X9.62")) |
|||
|
|||
cdb.register(_CurveDBEntry("prime256v1", ShortWeierstrassCurve, { |
|||
"a": 0xffffffff00000001000000000000000000000000fffffffffffffffffffffffc, |
|||
"b": 0x5ac635d8aa3a93e7b3ebbd55769886bc651d06b0cc53b0f63bce3c3e27d2604b, |
|||
"p": 0xffffffff00000001000000000000000000000000ffffffffffffffffffffffff, |
|||
"n": 0xffffffff00000000ffffffffffffffffbce6faada7179e84f3b9cac2fc632551, |
|||
"h": 1, |
|||
"Gx": 0x6b17d1f2e12c4247f8bce6e563a440f277037d812deb33a0f4a13945d898c296, |
|||
"Gy": 0x4fe342e2fe1a7f9b8ee7eb4a7c0f9e162bce33576b315ececbb6406837bf51f5, |
|||
}, aliases = [ "secp256r1", "NIST P-256" ], oid = "1.2.840.10045.3.1.7", origin = "Certicom Standards for Efficient Cryptography (SEC) 2 / ANSI X9.62 / FIPS 186-2 / NIST Recommended Elliptic Curves for Federal Government Use")) |
|||
|
|||
cdb.register(_CurveDBEntry("secp112r1", ShortWeierstrassCurve, { |
|||
"a": 0xdb7c2abf62e35e668076bead2088, |
|||
"b": 0x659ef8ba043916eede8911702b22, |
|||
"p": 0xdb7c2abf62e35e668076bead208b, |
|||
"n": 0xdb7c2abf62e35e7628dfac6561c5, |
|||
"h": 1, |
|||
"Gx": 0x9487239995a5ee76b55f9c2f098, |
|||
"Gy": 0xa89ce5af8724c0a23e0e0ff77500, |
|||
}, aliases = [ "wap-wsg-idm-ecid-wtls6" ], oid = "1.3.132.0.6", alt_oids = { "wap-wsg-idm-ecid-wtls6": "2.23.43.1.4.6" }, origin = "Certicom Standards for Efficient Cryptography (SEC) 2 / Wireless Application Protocol WAP-261-WTLS-20010406a")) |
|||
|
|||
cdb.register(_CurveDBEntry("secp112r2", ShortWeierstrassCurve, { |
|||
"a": 0x6127c24c05f38a0aaaf65c0ef02c, |
|||
"b": 0x51def1815db5ed74fcc34c85d709, |
|||
"p": 0xdb7c2abf62e35e668076bead208b, |
|||
"n": 0x36df0aafd8b8d7597ca10520d04b, |
|||
"h": 4, |
|||
"Gx": 0x4ba30ab5e892b4e1649dd0928643, |
|||
"Gy": 0xadcd46f5882e3747def36e956e97, |
|||
}, oid = "1.3.132.0.7", origin = "Certicom Standards for Efficient Cryptography (SEC) 2")) |
|||
|
|||
cdb.register(_CurveDBEntry("secp128r1", ShortWeierstrassCurve, { |
|||
"a": 0xfffffffdfffffffffffffffffffffffc, |
|||
"b": 0xe87579c11079f43dd824993c2cee5ed3, |
|||
"p": 0xfffffffdffffffffffffffffffffffff, |
|||
"n": 0xfffffffe0000000075a30d1b9038a115, |
|||
"h": 1, |
|||
"Gx": 0x161ff7528b899b2d0c28607ca52c5b86, |
|||
"Gy": 0xcf5ac8395bafeb13c02da292dded7a83, |
|||
}, oid = "1.3.132.0.28", origin = "Certicom Standards for Efficient Cryptography (SEC) 2")) |
|||
|
|||
cdb.register(_CurveDBEntry("secp128r2", ShortWeierstrassCurve, { |
|||
"a": 0xd6031998d1b3bbfebf59cc9bbff9aee1, |
|||
"b": 0x5eeefca380d02919dc2c6558bb6d8a5d, |
|||
"p": 0xfffffffdffffffffffffffffffffffff, |
|||
"n": 0x3fffffff7fffffffbe0024720613b5a3, |
|||
"h": 4, |
|||
"Gx": 0x7b6aa5d85e572983e6fb32a7cdebc140, |
|||
"Gy": 0x27b6916a894d3aee7106fe805fc34b44, |
|||
}, oid = "1.3.132.0.29", origin = "Certicom Standards for Efficient Cryptography (SEC) 2")) |
|||
|
|||
cdb.register(_CurveDBEntry("secp160k1", ShortWeierstrassCurve, { |
|||
"a": 0, |
|||
"b": 7, |
|||
"p": 0x0fffffffffffffffffffffffffffffffeffffac73, |
|||
"n": 0x100000000000000000001b8fa16dfab9aca16b6b3, |
|||
"h": 1, |
|||
"Gx": 0x03b4c382ce37aa192a4019e763036f4f5dd4d7ebb, |
|||
"Gy": 0x0938cf935318fdced6bc28286531733c3f03c4fee, |
|||
}, aliases = [ "ansip160k1" ], oid = "1.3.132.0.9", origin = "Certicom Standards for Efficient Cryptography (SEC) 2")) |
|||
|
|||
cdb.register(_CurveDBEntry("secp160r1", ShortWeierstrassCurve, { |
|||
"a": 0x0ffffffffffffffffffffffffffffffff7ffffffc, |
|||
"b": 0x01c97befc54bd7a8b65acf89f81d4d4adc565fa45, |
|||
"p": 0x0ffffffffffffffffffffffffffffffff7fffffff, |
|||
"n": 0x100000000000000000001f4c8f927aed3ca752257, |
|||
"h": 1, |
|||
"Gx": 0x04a96b5688ef573284664698968c38bb913cbfc82, |
|||
"Gy": 0x023a628553168947d59dcc912042351377ac5fb32, |
|||
}, aliases = [ "ansip160r1" ], oid = "1.3.132.0.8", origin = "Certicom Standards for Efficient Cryptography (SEC) 2")) |
|||
|
|||
cdb.register(_CurveDBEntry("secp160r2", ShortWeierstrassCurve, { |
|||
"a": 0x0fffffffffffffffffffffffffffffffeffffac70, |
|||
"b": 0x0b4e134d3fb59eb8bab57274904664d5af50388ba, |
|||
"p": 0x0fffffffffffffffffffffffffffffffeffffac73, |
|||
"n": 0x100000000000000000000351ee786a818f3a1a16b, |
|||
"h": 1, |
|||
"Gx": 0x052dcb034293a117e1f4ff11b30f7199d3144ce6d, |
|||
"Gy": 0x0feaffef2e331f296e071fa0df9982cfea7d43f2e, |
|||
}, aliases = [ "ansip160r2", "wap-wsg-idm-ecid-wtls7" ], oid = "1.3.132.0.30", alt_oids = { "wap-wsg-idm-ecid-wtls7": "2.23.43.1.4.7" }, origin = "Certicom Standards for Efficient Cryptography (SEC) 2 / Wireless Application Protocol WAP-261-WTLS-20010406a")) |
|||
|
|||
cdb.register(_CurveDBEntry("secp192k1", ShortWeierstrassCurve, { |
|||
"a": 0, |
|||
"b": 3, |
|||
"p": 0xfffffffffffffffffffffffffffffffffffffffeffffee37, |
|||
"n": 0xfffffffffffffffffffffffe26f2fc170f69466a74defd8d, |
|||
"h": 1, |
|||
"Gx": 0xdb4ff10ec057e9ae26b07d0280b7f4341da5d1b1eae06c7d, |
|||
"Gy": 0x9b2f2f6d9c5628a7844163d015be86344082aa88d95e2f9d, |
|||
}, aliases = [ "ansip192k1" ], oid = "1.3.132.0.31", origin = "Certicom Standards for Efficient Cryptography (SEC) 2")) |
|||
|
|||
cdb.register(_CurveDBEntry("secp224k1", ShortWeierstrassCurve, { |
|||
"a": 0, |
|||
"b": 5, |
|||
"p": 0x0fffffffffffffffffffffffffffffffffffffffffffffffeffffe56d, |
|||
"n": 0x10000000000000000000000000001dce8d2ec6184caf0a971769fb1f7, |
|||
"h": 1, |
|||
"Gx": 0x0a1455b334df099df30fc28a169a467e9e47075a90f7e650eb6b7a45c, |
|||
"Gy": 0x07e089fed7fba344282cafbd6f7e319f7c0b0bd59e2ca4bdb556d61a5, |
|||
}, aliases = [ "ansip224k1" ], oid = "1.3.132.0.32", origin = "Certicom Standards for Efficient Cryptography (SEC) 2")) |
|||
|
|||
cdb.register(_CurveDBEntry("secp224r1", ShortWeierstrassCurve, { |
|||
"a": 0xfffffffffffffffffffffffffffffffefffffffffffffffffffffffe, |
|||
"b": 0xb4050a850c04b3abf54132565044b0b7d7bfd8ba270b39432355ffb4, |
|||
"p": 0xffffffffffffffffffffffffffffffff000000000000000000000001, |
|||
"n": 0xffffffffffffffffffffffffffff16a2e0b8f03e13dd29455c5c2a3d, |
|||
"h": 1, |
|||
"Gx": 0xb70e0cbd6bb4bf7f321390b94a03c1d356c21122343280d6115c1d21, |
|||
"Gy": 0xbd376388b5f723fb4c22dfe6cd4375a05a07476444d5819985007e34, |
|||
}, aliases = [ "ansip224r1", "NIST P-224", "wap-wsg-idm-ecid-wtls12" ], oid = "1.3.132.0.33", alt_oids = { "wap-wsg-idm-ecid-wtls12": "2.23.43.1.4.12" }, origin = "Certicom Standards for Efficient Cryptography (SEC) 2 / FIPS 186-2 / NIST Recommended Elliptic Curves for Federal Government Use / Wireless Application Protocol WAP-261-WTLS-20010406a")) |
|||
|
|||
cdb.register(_CurveDBEntry("secp256k1", ShortWeierstrassCurve, { |
|||
"a": 0, |
|||
"b": 7, |
|||
"p": 0xfffffffffffffffffffffffffffffffffffffffffffffffffffffffefffffc2f, |
|||
"n": 0xfffffffffffffffffffffffffffffffebaaedce6af48a03bbfd25e8cd0364141, |
|||
"h": 1, |
|||
"Gx": 0x79be667ef9dcbbac55a06295ce870b07029bfcdb2dce28d959f2815b16f81798, |
|||
"Gy": 0x483ada7726a3c4655da4fbfc0e1108a8fd17b448a68554199c47d08ffb10d4b8, |
|||
}, aliases = [ "ansip256k1" ], oid = "1.3.132.0.10", origin = "Certicom Standards for Efficient Cryptography (SEC) 2")) |
|||
|
|||
cdb.register(_CurveDBEntry("secp384r1", ShortWeierstrassCurve, { |
|||
"a": 0xfffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffeffffffff0000000000000000fffffffc, |
|||
"b": 0xb3312fa7e23ee7e4988e056be3f82d19181d9c6efe8141120314088f5013875ac656398d8a2ed19d2a85c8edd3ec2aef, |
|||
"p": 0xfffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffeffffffff0000000000000000ffffffff, |
|||
"n": 0xffffffffffffffffffffffffffffffffffffffffffffffffc7634d81f4372ddf581a0db248b0a77aecec196accc52973, |
|||
"h": 1, |
|||
"Gx": 0xaa87ca22be8b05378eb1c71ef320ad746e1d3b628ba79b9859f741e082542a385502f25dbf55296c3a545e3872760ab7, |
|||
"Gy": 0x3617de4a96262c6f5d9e98bf9292dc29f8f41dbd289a147ce9da3113b5f0b8c00a60b1ce1d7e819d7a431d7c90ea0e5f, |
|||
}, aliases = [ "ansip384r1", "NIST P-384" ], oid = "1.3.132.0.34", origin = "Certicom Standards for Efficient Cryptography (SEC) 2 / FIPS 186-2 / NIST Recommended Elliptic Curves for Federal Government Use")) |
|||
|
|||
cdb.register(_CurveDBEntry("secp521r1", ShortWeierstrassCurve, { |
|||
"a": 0x1fffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffc, |
|||
"b": 0x051953eb9618e1c9a1f929a21a0b68540eea2da725b99b315f3b8b489918ef109e156193951ec7e937b1652c0bd3bb1bf073573df883d2c34f1ef451fd46b503f00, |
|||
"p": 0x1ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff, |
|||
"n": 0x1fffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffa51868783bf2f966b7fcc0148f709a5d03bb5c9b8899c47aebb6fb71e91386409, |
|||
"h": 1, |
|||
"Gx": 0x0c6858e06b70404e9cd9e3ecb662395b4429c648139053fb521f828af606b4d3dbaa14b5e77efe75928fe1dc127a2ffa8de3348b3c1856a429bf97e7e31c2e5bd66, |
|||
"Gy": 0x11839296a789a3bc0045c8a5fb42c7d1bd998f54449579b446817afbd17273e662c97ee72995ef42640c550b9013fad0761353c7086a272c24088be94769fd16650, |
|||
}, aliases = [ "NIST P-521", "ansip521r1" ], oid = "1.3.132.0.35", origin = "Certicom Standards for Efficient Cryptography (SEC) 2 / FIPS 186-2 / NIST Recommended Elliptic Curves for Federal Government Use")) |
|||
|
|||
cdb.register(_CurveDBEntry("wap-wsg-idm-ecid-wtls8", ShortWeierstrassCurve, { |
|||
"a": 0, |
|||
"b": 3, |
|||
"p": 0x0fffffffffffffffffffffffffde7, |
|||
"n": 0x100000000000001ecea551ad837e9, |
|||
"h": 1, |
|||
"Gx": 1, |
|||
"Gy": 2, |
|||
}, oid = "2.23.43.1.4.8", origin = "Wireless Application Protocol WAP-261-WTLS-20010406a")) |
|||
|
|||
cdb.register(_CurveDBEntry("wap-wsg-idm-ecid-wtls9", ShortWeierstrassCurve, { |
|||
"a": 0, |
|||
"b": 3, |
|||
"p": 0x0fffffffffffffffffffffffffffffffffffc808f, |
|||
"n": 0x100000000000000000001cdc98ae0e2de574abf33, |
|||
"h": 1, |
|||
"Gx": 1, |
|||
"Gy": 2, |
|||
}, oid = "2.23.43.1.4.9", origin = "Wireless Application Protocol WAP-261-WTLS-20010406a")) |
|||
|
|||
cdb.register(_CurveDBEntry("Curve25519", MontgomeryCurve, { |
|||
"a": 486662, |
|||
"b": 1, |
|||
"p": (2 ** 255) - 19, |
|||
"n": (2 ** 252) + 27742317777372353535851937790883648493, |
|||
"h": 8, |
|||
"Gx": 0x9, |
|||
"Gy": 0x5f51e65e475f794b1fe122d388b72eb36dc2b28192839e4dd6163a5d81312c14, |
|||
}, origin = "2006 Bernstein")) |
|||
|
|||
# Curve imported from IETF https://tools.ietf.org/html/rfc7748 |
|||
cdb.register(_CurveDBEntry("Curve448", MontgomeryCurve, { |
|||
"a": 156326, |
|||
"b": 1, |
|||
"p": (2 ** 448) - (2 ** 224) - 1, |
|||
"n": (2 ** 446) - 0x8335dc163bb124b65129c96fde933d8d723a70aadc873d6d54a7bb0d, |
|||
"h": 4, |
|||
"Gx": 0x5, |
|||
"Gy": 0x7d235d1295f5b1f66c98ab6e58326fcecbae5d34f55545d060f75dc28df3f6edb8027e2346430d211312c4b150677af76fd7223d457b5b1a, |
|||
}, origin = "2006 Bernstein")) |
|||
|
|||
cdb.register(_CurveDBEntry("Ed25519", TwistedEdwardsCurve, { |
|||
"a": -1, |
|||
"d": 37095705934669439343138083508754565189542113879843219016388785533085940283555, |
|||
"p": (2 ** 255) - 19, |
|||
"n": (2 ** 252) + 27742317777372353535851937790883648493, |
|||
"h": 8, |
|||
"Gx": 0x216936d3cd6e53fec0a4e231fdd6dc5c692cc7609525a7b2c9562d608f25d51a, |
|||
"Gy": 0x6666666666666666666666666666666666666666666666666666666666666658, |
|||
}, origin = "2011 Bernstein-Duif-Lange-Schwabe-Yang", quirks = [ CurveQuirkEdDSASetPrivateKeyMSB(), CurveQuirkEdDSAEnsurePrimeOrderSubgroup(), CurveQuirkSigningHashFunction("sha512") ])) |
|||
|
|||
# Curve imported from SafeCurves http://safecurves.cr.yp.to |
|||
cdb.register(_CurveDBEntry("Anomalous", ShortWeierstrassCurve, { |
|||
"a": 0x98d0fac687d6343eb1a1f595283eb1a1f58d0fac687d635f5e4, |
|||
"b": 0x4a1f58d0fac687d6343eb1a5e2d6343eb1a1f58d0fac688ab3f, |
|||
"p": 0xb0000000000000000000000953000000000000000000001f9d7, |
|||
"n": 0xb0000000000000000000000953000000000000000000001f9d7, |
|||
"h": 1, |
|||
"Gx": 0x101efb35fd1963c4871a2d17edaafa7e249807f58f8705126c6, |
|||
"Gy": 0x22389a3954375834304ba1d509a97de6c07148ea7f5951b20e7, |
|||
}, secure = False, origin = "Bernstein http://safecurves.cr.yp.to illustration of additive transfer and small discriminant")) |
|||
|
|||
# Curve imported from SafeCurves http://safecurves.cr.yp.to |
|||
cdb.register(_CurveDBEntry("M-221", MontgomeryCurve, { |
|||
"a": 117050, |
|||
"b": 1, |
|||
"p": 0x1ffffffffffffffffffffffffffffffffffffffffffffffffffffffd, |
|||
"n": 0x40000000000000000000000000015a08ed730e8a2f77f005042605b, |
|||
"h": 8, |
|||
"Gx": 4, |
|||
"Gy": 0xf7acdd2a4939571d1cef14eca37c228e61dbff10707dc6c08c5056d, |
|||
}, aliases = [ "Curve2213" ], origin = "2013 Aranha-Barreto-Pereira-Ricardini")) |
|||
|
|||
# Curve imported from SafeCurves http://safecurves.cr.yp.to |
|||
cdb.register(_CurveDBEntry("E-222", TwistedEdwardsCurve, { |
|||
"a": 1, |
|||
"d": 160102, |
|||
"p": 0x3fffffffffffffffffffffffffffffffffffffffffffffffffffff8b, |
|||
"n": 0xffffffffffffffffffffffffffff70cbc95e932f802f31423598cbf, |
|||
"h": 4, |
|||
"Gx": 0x19b12bb156a389e55c9768c303316d07c23adab3736eb2bc3eb54e51, |
|||
"Gy": 28, |
|||
}, origin = "2013 Aranha-Barreto-Pereira-Ricardini")) |
|||
|
|||
# Curve imported from SafeCurves http://safecurves.cr.yp.to |
|||
cdb.register(_CurveDBEntry("Curve1174", TwistedEdwardsCurve, { |
|||
"a": 1, |
|||
"d": -1174, |
|||
"p": 0x7fffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff7, |
|||
"n": 0x1fffffffffffffffffffffffffffffff77965c4dfd307348944d45fd166c971, |
|||
"h": 4, |
|||
"Gx": 0x37fbb0cea308c479343aee7c029a190c021d96a492ecd6516123f27bce29eda, |
|||
"Gy": 0x6b72f82d47fb7cc6656841169840e0c4fe2dee2af3f976ba4ccb1bf9b46360e, |
|||
}, origin = "2013 Bernstein-Hamburg-Krasnova-Lange")) |
|||
|
|||
# Curve imported from SafeCurves http://safecurves.cr.yp.to |
|||
cdb.register(_CurveDBEntry("BN(2,254)", ShortWeierstrassCurve, { |
|||
"a": 0, |
|||
"b": 2, |
|||
"p": 0x2523648240000001ba344d80000000086121000000000013a700000000000013, |
|||
"n": 0x2523648240000001ba344d8000000007ff9f800000000010a10000000000000d, |
|||
"h": 1, |
|||
"Gx": -1, |
|||
"Gy": 1, |
|||
}, origin = "2011 Pereira-Simplicio-Naehrig-Barreto")) |
|||
|
|||
# Curve imported from SafeCurves http://safecurves.cr.yp.to |
|||
cdb.register(_CurveDBEntry("ANSSI FRP256v1", ShortWeierstrassCurve, { |
|||
"a": -3, |
|||
"b": 0xee353fca5428a9300d4aba754a44c00fdfec0c9ae4b1a1803075ed967b7bb73f, |
|||
"p": 0xf1fd178c0b3ad58f10126de8ce42435b3961adbcabc8ca6de8fcf353d86e9c03, |
|||
"n": 0xf1fd178c0b3ad58f10126de8ce42435b53dc67e140d2bf941ffdd459c6d655e1, |
|||
"h": 1, |
|||
"Gx": 0xb6b3d4c356c139eb31183d4749d423958c27d2dcaf98b70164c97a2dd98f5cff, |
|||
"Gy": 0x6142e0f7c8b204911f9271f0f3ecef8c2701c307e8e4c9e183115a1554062cfb, |
|||
}, oid = "1.2.250.1.223.101.256.1", origin = "Agence nationale de la sécurité des systèmes d'information")) |
|||
|
|||
# Curve imported from SafeCurves http://safecurves.cr.yp.to |
|||
cdb.register(_CurveDBEntry("E-382", TwistedEdwardsCurve, { |
|||
"a": 1, |
|||
"d": -67254, |
|||
"p": 0x3fffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff97, |
|||
"n": 0xfffffffffffffffffffffffffffffffffffffffffffffffd5fb21f21e95eee17c5e69281b102d2773e27e13fd3c9719, |
|||
"h": 4, |
|||
"Gx": 0x196f8dd0eab20391e5f05be96e8d20ae68f840032b0b64352923bab85364841193517dbce8105398ebc0cc9470f79603, |
|||
"Gy": 17, |
|||
}, origin = "2013 Aranha-Barreto-Pereira-Ricardini")) |
|||
|
|||
# Curve imported from SafeCurves http://safecurves.cr.yp.to |
|||
cdb.register(_CurveDBEntry("M-383", MontgomeryCurve, { |
|||
"a": 2065150, |
|||
"b": 1, |
|||
"p": 0x7fffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff45, |
|||
"n": 0x10000000000000000000000000000000000000000000000006c79673ac36ba6e7a32576f7b1b249e46bbc225be9071d7, |
|||
"h": 8, |
|||
"Gx": 12, |
|||
"Gy": 0x1ec7ed04aaf834af310e304b2da0f328e7c165f0e8988abd3992861290f617aa1f1b2e7d0b6e332e969991b62555e77e, |
|||
}, origin = "2013 Aranha-Barreto-Pereira-Ricardini")) |
|||
|
|||
# Curve imported from SafeCurves http://safecurves.cr.yp.to |
|||
cdb.register(_CurveDBEntry("Curve383187", MontgomeryCurve, { |
|||
"a": 229969, |
|||
"b": 1, |
|||
"p": 0x7fffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff45, |
|||
"n": 0x1000000000000000000000000000000000000000000000000e85a85287a1488acd41ae84b2b7030446f72088b00a0e21, |
|||
"h": 8, |
|||
"Gx": 5, |
|||
"Gy": 0x1eebe07dc1871896732b12d5504a32370471965c7a11f2c89865f855ab3cbd7c224e3620c31af3370788457dd5ce46df, |
|||
}, origin = "2013 Aranha-Barreto-Pereira-Ricardini")) |
|||
|
|||
# Curve imported from SafeCurves http://safecurves.cr.yp.to |
|||
cdb.register(_CurveDBEntry("Curve41417", TwistedEdwardsCurve, { |
|||
"a": 1, |
|||
"d": 3617, |
|||
"p": 0x3fffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffef, |
|||
"n": 0x7ffffffffffffffffffffffffffffffffffffffffffffffffffeb3cc92414cf706022b36f1c0338ad63cf181b0e71a5e106af79, |
|||
"h": 8, |
|||
"Gx": 0x1a334905141443300218c0631c326e5fcd46369f44c03ec7f57ff35498a4ab4d6d6ba111301a73faa8537c64c4fd3812f3cbc595, |
|||
"Gy": 34, |
|||
}, aliases = [ "Curve3617" ], origin = "2013 Bernstein-Lange")) |
|||
|
|||
# Curve imported from SafeCurves http://safecurves.cr.yp.to |
|||
cdb.register(_CurveDBEntry("Ed448-Goldilocks", TwistedEdwardsCurve, { |
|||
"a": 1, |
|||
"d": -39081, |
|||
"p": 0xfffffffffffffffffffffffffffffffffffffffffffffffffffffffeffffffffffffffffffffffffffffffffffffffffffffffffffffffff, |
|||
"n": 0x3fffffffffffffffffffffffffffffffffffffffffffffffffffffff7cca23e9c44edb49aed63690216cc2728dc58f552378c292ab5844f3, |
|||
"h": 4, |
|||
"Gx": 0x297ea0ea2692ff1b4faff46098453a6a26adf733245f065c3c59d0709cecfa96147eaaf3932d94c63d96c170033f4ba0c7f0de840aed939f, |
|||
"Gy": 19, |
|||
}, origin = "2014 Hamburg", quirks = [ CurveQuirkEdDSASetPrivateKeyMSB(), CurveQuirkEdDSAEnsurePrimeOrderSubgroup(), CurveQuirkSigningHashFunction("shake256-114") ])) |
|||
|
|||
# Curve imported from https://tools.ietf.org/html/rfc8032 |
|||
cdb.register(_CurveDBEntry("Ed448", TwistedEdwardsCurve, { |
|||
"a": 1, |
|||
"d": -39081, |
|||
"p": 0xfffffffffffffffffffffffffffffffffffffffffffffffffffffffeffffffffffffffffffffffffffffffffffffffffffffffffffffffff, |
|||
"n": 0x3fffffffffffffffffffffffffffffffffffffffffffffffffffffff7cca23e9c44edb49aed63690216cc2728dc58f552378c292ab5844f3, |
|||
"h": 4, |
|||
"Gx": 0x4f1970c66bed0ded221d15a622bf36da9e146570470f1767ea6de324a3d3a46412ae1af72ab66511433b80e18b00938e2626a82bc70cc05e, |
|||
"Gy": 0x693f46716eb6bc248876203756c9c7624bea73736ca3984087789c1e05a0c2d73ad3ff1ce67c39c4fdbd132c4ed7c8ad9808795bf230fa14, |
|||
}, origin = "https://tools.ietf.org/html/rfc8032", quirks = [ CurveQuirkEdDSASetPrivateKeyMSB(), CurveQuirkEdDSAEnsurePrimeOrderSubgroup(), CurveQuirkSigningHashFunction("shake256-114") ])) |
|||
|
|||
# Curve imported from SafeCurves http://safecurves.cr.yp.to |
|||
cdb.register(_CurveDBEntry("M-511", MontgomeryCurve, { |
|||
"a": 530438, |
|||
"b": 1, |
|||
"p": 0x7fffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff45, |
|||
"n": 0x100000000000000000000000000000000000000000000000000000000000000017b5feff30c7f5677ab2aeebd13779a2ac125042a6aa10bfa54c15bab76baf1b, |
|||
"h": 8, |
|||
"Gx": 5, |
|||
"Gy": 0x2fbdc0ad8530803d28fdbad354bb488d32399ac1cf8f6e01ee3f96389b90c809422b9429e8a43dbf49308ac4455940abe9f1dbca542093a895e30a64af056fa5, |
|||
}, aliases = [ "Curve511187" ], origin = "2013 Aranha-Barreto-Pereira-Ricardini")) |
|||
|
|||
# Curve imported from SafeCurves http://safecurves.cr.yp.to |
|||
cdb.register(_CurveDBEntry("E-521", TwistedEdwardsCurve, { |
|||
"a": 1, |
|||
"d": -376014, |
|||
"p": 0x1ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff, |
|||
"n": 0x7ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffd15b6c64746fc85f736b8af5e7ec53f04fbd8c4569a8f1f4540ea2435f5180d6b, |
|||
"h": 4, |
|||
"Gx": 0x752cb45c48648b189df90cb2296b2878a3bfd9f42fc6c818ec8bf3c9c0c6203913f6ecc5ccc72434b1ae949d568fc99c6059d0fb13364838aa302a940a2f19ba6c, |
|||
"Gy": 12, |
|||
}, origin = "2013 Bernstein-Lange / 2013 Hamburg / 2013 Aranha-Barreto-Pereira-Ricardini")) |
|||
|
|||
cdb.register(_CurveDBEntry("rigol", ShortWeierstrassCurve, { |
|||
"a": 0x2982, |
|||
"b": 0x3408, |
|||
"p": 0xaebf94cee3e707, |
|||
"n": 0xaebf94d5c6aa71, |
|||
"h": 1, |
|||
"Gx": 0x7a3e808599a525, |
|||
"Gy": 0x28be7fafd2a052, |
|||
}, origin = "Rigol DS2xxx feature activation curve")) |
|||
|
|||
def getcurvedb(): |
|||
"""Returns an instance of the curve database singleton object.""" |
|||
return CurveDB() |
|||
|
|||
def getcurvenames(): |
|||
"""Returns the names of all curves known to toyecc.""" |
|||
return CurveDB().curvenames() |
|||
|
|||
def getcurveentry(name): |
|||
"""Returns a curve entry by its name.""" |
|||
return CurveDB().getentry(name) |
|||
|
|||
def getcurvebyname(name): |
|||
"""Returns a curve by its name.""" |
|||
return CurveDB()[name] |
|||
@ -0,0 +1,140 @@ |
|||
# |
|||
# toyecc - A small Elliptic Curve Cryptography Demonstration. |
|||
# Copyright (C) 2011-2022 Johannes Bauer |
|||
# |
|||
# This file is part of toyecc. |
|||
# |
|||
# toyecc is free software; you can redistribute it and/or modify |
|||
# it under the terms of the GNU General Public License as published by |
|||
# the Free Software Foundation; this program is ONLY licensed under |
|||
# version 3 of the License, later versions are explicitly excluded. |
|||
# |
|||
# toyecc is distributed in the hope that it will be useful, |
|||
# but WITHOUT ANY WARRANTY; without even the implied warranty of |
|||
# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the |
|||
# GNU General Public License for more details. |
|||
# |
|||
# You should have received a copy of the GNU General Public License |
|||
# along with toyecc; if not, write to the Free Software |
|||
# Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA |
|||
# |
|||
# Johannes Bauer <JohannesBauer@gmx.de> |
|||
# |
|||
|
|||
from .FieldElement import FieldElement |
|||
from .Exceptions import NoSuchCurveException |
|||
|
|||
class CurveOpIsomorphism(object): |
|||
def _twist(self, d = None, sqrt_d = None): |
|||
"""Returns the twisted curve with the twist coefficient d. If d is a |
|||
quadratic non-residue mod p then this function will yield a curve that |
|||
is isomorphous on the field extension GF(sqrt(d)). If it is a quadratic |
|||
residue, it will return an GF(p)-isomorphous curve.""" |
|||
assert(self.curvetype == "shortweierstrass") |
|||
ShortWeierstrassCurve = self.__class__ |
|||
|
|||
if sqrt_d is not None: |
|||
# If a square root is given, then it must be a correct square root |
|||
assert(sqrt_d ** 2 == d) |
|||
|
|||
a = self.a * (d ** 2) |
|||
b = self.b * (d ** 3) |
|||
if d.is_qr and self.hasgenerator: |
|||
# Quadratic twist will return an GF(p)-isomorphous curve -> convert |
|||
# generator point as well |
|||
if sqrt_d is None: |
|||
sqrt_d = d.sqrt()[0] |
|||
Gx = int(self.G.x * d) |
|||
Gy = int(self.G.y * (sqrt_d ** 3)) |
|||
|
|||
n = self.n |
|||
h = self.h |
|||
else: |
|||
# Quadratic twist will return an isomorphous curve on the |
|||
# GF(sqrt(d)) field extension -> no generator point conversion for |
|||
# now |
|||
# Gx = int(self.G.x * d) |
|||
# Gy = int(self.G.y * d) |
|||
Gx = None |
|||
Gy = None |
|||
|
|||
# If the original curve had q + 1 - t points, then its twist will |
|||
# have q + 1 + t points. TODO: Does this help us to find the order |
|||
# of the generator point G? I don't think it does :-( Leave n and h |
|||
# therefore unset for the moment. |
|||
n = None |
|||
h = None |
|||
|
|||
return ShortWeierstrassCurve(a = int(a), b = int(b), p = self.p, n = n, h = h, Gx = Gx, Gy = Gy) |
|||
|
|||
def twist(self, d = None): |
|||
"""If the twist coefficient d is omitted, the function will |
|||
automatically look for an arbitrary quadratic non-residue in F_P.""" |
|||
if d == 0: |
|||
raise Exception("Domain error: d must be nonzero.") |
|||
elif d is None: |
|||
# Search for a QNR in F_P |
|||
d = FieldElement.any_qnr(self.p) |
|||
else: |
|||
d = FieldElement(d, self.p) |
|||
if d.is_qr: |
|||
raise Exception("Twist requested, but twist coefficient d is a quadratic-residue mod p. Refusing to return a GF(p)-isomorphic curve; if you want this behavior, use twist_fp_isomorphic()") |
|||
return self._twist(d) |
|||
|
|||
def twist_fp_isomorphic(self, u): |
|||
"""Returns a GF(p)-isomorphous curve by applying the substituting |
|||
transformation x = u^2 x' and y = u^3 y' on the curve equation. The |
|||
function therefore returns a quadratic twist with d = u^2, i.e. it |
|||
ensures that the twist coefficient d is a quadratic residue mod p..""" |
|||
if u == 0: |
|||
raise Exception("Domain error: u must be nonzero.") |
|||
return self._twist(FieldElement(u ** 2, self.p), FieldElement(u, self.p)) |
|||
|
|||
def twist_fp_isomorphic_fixed_a(self, a): |
|||
"""Tries to find an GF(p)-isomorphous curve which has a particular |
|||
given value for the curve coefficient 'a'.""" |
|||
|
|||
# anew = a * u^4 -> u = quartic_root(anew / a) |
|||
scalar = a // self.a |
|||
u = scalar.quartic_root() |
|||
if u is None: |
|||
raise NoSuchCurveException("Cannot find an isomorphism so that a = %d because %s has no quartic root in F_P" % (a, scalar)) |
|||
return self.twist_fp_isomorphic(int(u)) |
|||
|
|||
def is_isomorphous_curve(self, other): |
|||
"""Returns if the given curve 'other' is isomorphous in the same field |
|||
as the given curve curve.""" |
|||
if other.p != self.p: |
|||
return False |
|||
|
|||
try: |
|||
iso = self.twist_fp_isomorphic_fixed_a(other.a) |
|||
except NoSuchCurveException: |
|||
# No isomorphous curve with this value for a exists |
|||
return False |
|||
|
|||
# The curves should be identical after the transformation if they're |
|||
# isomorphous to each other |
|||
return (iso.a == other.a) and (iso.b == other.b) |
|||
|
|||
class CurveOpExportSage(object): |
|||
def export_sage(self, varname = "curve"): |
|||
"""Exports the elliptic curve to statements that can be used within the |
|||
SAGE computer algebra system.""" |
|||
|
|||
# EllipticCurve([a1,a2,a3,a4,a6]) means in Sage: |
|||
# y² + a1 x y + a3 y = x³ + a2 x² + a4 x + a6 |
|||
# i.e. for Short Weierstrass a4 = A, a6 = B |
|||
|
|||
statements = [ ] |
|||
statements.append("# %s" % (str(self))) |
|||
statements.append("%s_p = 0x%x" % (varname, int(self.p))) |
|||
statements.append("%s_F = GF(%s_p)" % (varname, varname)) |
|||
if self.curvetype == "shortweierstrass": |
|||
statements.append("%s_a = 0x%x" % (varname, int(self.a))) |
|||
statements.append("%s_b = 0x%x" % (varname, int(self.b))) |
|||
statements.append("%s = EllipticCurve(%s_F, [ %s_a, %s_b ])" % (varname, varname, varname, varname)) |
|||
else: |
|||
raise Exception(NotImplemented) |
|||
|
|||
return statements |
|||
@ -0,0 +1,77 @@ |
|||
# |
|||
# toyecc - A small Elliptic Curve Cryptography Demonstration. |
|||
# Copyright (C) 2011-2022 Johannes Bauer |
|||
# |
|||
# This file is part of toyecc. |
|||
# |
|||
# toyecc is free software; you can redistribute it and/or modify |
|||
# it under the terms of the GNU General Public License as published by |
|||
# the Free Software Foundation; this program is ONLY licensed under |
|||
# version 3 of the License, later versions are explicitly excluded. |
|||
# |
|||
# toyecc is distributed in the hope that it will be useful, |
|||
# but WITHOUT ANY WARRANTY; without even the implied warranty of |
|||
# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the |
|||
# GNU General Public License for more details. |
|||
# |
|||
# You should have received a copy of the GNU General Public License |
|||
# along with toyecc; if not, write to the Free Software |
|||
# Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA |
|||
# |
|||
# Johannes Bauer <JohannesBauer@gmx.de> |
|||
# |
|||
|
|||
import hashlib |
|||
|
|||
class CurveQuirk(object): |
|||
identifier = None |
|||
|
|||
@property |
|||
def identity(self): |
|||
return (self.identifier, ) |
|||
|
|||
def __eq__(self, other): |
|||
return self.identity == other.identity |
|||
|
|||
def __ne__(self, other): |
|||
return not (self == other) |
|||
|
|||
def __lt__(self, other): |
|||
return self.identity < other.identity |
|||
|
|||
def __hash__(self): |
|||
return hash(self.identity) |
|||
|
|||
def __str__(self): |
|||
return self.identifier |
|||
|
|||
class CurveQuirkEdDSASetPrivateKeyMSB(CurveQuirk): |
|||
"""Set the highest significant bit of the private key during EdDSA |
|||
signature generation. For example, for EdDSA signatures on Ed25519, this |
|||
would bitwise or the value 'a' with 2^254.""" |
|||
identifier = "EdDSA_set_private_key_MSB" |
|||
|
|||
class CurveQuirkEdDSAEnsurePrimeOrderSubgroup(CurveQuirk): |
|||
"""Ensures during EdDSA signature generation that the private key is on a |
|||
prime-order subgroup. This is done by clearing the amount of bits that is |
|||
required by the cofactor of the curve (which has to be a power of two for |
|||
this quirk to work, otherwise it'll fail at runtime). Concretely, for EdDSA |
|||
on Ed25519 this means that the least significant three bits would be set to |
|||
zero because the curve cofactor is 8.""" |
|||
identifier = "EdDSA_use_prime_order_subgroup" |
|||
|
|||
class CurveQuirkSigningHashFunction(CurveQuirk): |
|||
"""For some curves, the signing hash function is implicitly given. In |
|||
particular for the Ed448 and Ed25519 variants, this is true. Encode these |
|||
as a curve quirk.""" |
|||
identifier = "signing_hash_function" |
|||
|
|||
def __init__(self, sig_fnc_name): |
|||
self._sig_fnc_name = sig_fnc_name |
|||
|
|||
def hashdata(self, data): |
|||
hash_fnc = { |
|||
"sha512": lambda x: hashlib.sha512(data).digest(), |
|||
"shake256-114": lambda x: hashlib.shake_256(data).digest(114), |
|||
} |
|||
return hash_fnc[self._sig_fnc_name](data) |
|||
@ -0,0 +1,67 @@ |
|||
# |
|||
# toyecc - A small Elliptic Curve Cryptography Demonstration. |
|||
# Copyright (C) 2011-2022 Johannes Bauer |
|||
# |
|||
# This file is part of toyecc. |
|||
# |
|||
# toyecc is free software; you can redistribute it and/or modify |
|||
# it under the terms of the GNU General Public License as published by |
|||
# the Free Software Foundation; this program is ONLY licensed under |
|||
# version 3 of the License, later versions are explicitly excluded. |
|||
# |
|||
# toyecc is distributed in the hope that it will be useful, |
|||
# but WITHOUT ANY WARRANTY; without even the implied warranty of |
|||
# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the |
|||
# GNU General Public License for more details. |
|||
# |
|||
# You should have received a copy of the GNU General Public License |
|||
# along with toyecc; if not, write to the Free Software |
|||
# Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA |
|||
# |
|||
# Johannes Bauer <JohannesBauer@gmx.de> |
|||
# |
|||
|
|||
from .Polynomial import Polynomial |
|||
|
|||
class DivisionPolynomial(object): |
|||
def __init__(self, curve): |
|||
"""Creates a division polynomial generator which returns \psi_i for the |
|||
given curve in Weierstrass form.""" |
|||
self._curve = curve |
|||
assert(self._curve.curvetype == "shortweierstrass") |
|||
self._cache = { } |
|||
self._curvepoly = None |
|||
self._initcache() |
|||
|
|||
def _initcache(self): |
|||
(a, b) = (self.curve.a, self.curve.b) |
|||
x = Polynomial(self.curve.p) |
|||
self._cache[0] = Polynomial(self.curve.p, 0) |
|||
self._cache[1] = Polynomial(self.curve.p, 1) |
|||
self._cache[2] = Polynomial(self.curve.p, 2) |
|||
self._cache[3] = (3 * x**4) + (6 * a * x**2) + (12 * b * x) - (a**2) |
|||
self._cache[4] = 4 * (x**6 + (5 * a * x**4) + (20 * b * x**3) - (5 * a**2 * x**2) - (4 * a * b * x) - (8 * b**2) - (a**3)) |
|||
self._curvepoly = x**3 + (a * x) + b |
|||
|
|||
@property |
|||
def curve(self): |
|||
return self._curve |
|||
|
|||
def __getitem__(self, index): |
|||
if index not in self._cache: |
|||
m = index // 2 |
|||
if (index % 2) == 1: |
|||
# The paper says this would be correct: |
|||
# result = (self[m + 2] * self[m]**3) - (self[m - 1] * self[m + 1] ** 3) |
|||
# But MIRACL does it differently. Use the MIRACL approach: |
|||
if (m % 2) == 0: |
|||
result = (self._curvepoly**2 * self[m + 2] * self[m]**3) - (self[m - 1] * self[m + 1]**3) |
|||
else: |
|||
result = (self[m + 2] * self[m]**3) - (self._curvepoly**2 * self[m - 1] * self[m + 1]**3) |
|||
else: |
|||
result = (self[m] // 2) * ((self[m + 2] * self[m - 1]**2) - (self[m - 2] * self[m + 1]**2)) |
|||
self._cache[index] = result |
|||
return self._cache[index] |
|||
|
|||
def __str__(self): |
|||
return "DivisionPolys<%s, %d cached>" % (str(self.curve), len(self._cache)) |
|||
@ -0,0 +1,41 @@ |
|||
# |
|||
# toyecc - A small Elliptic Curve Cryptography Demonstration. |
|||
# Copyright (C) 2011-2016 Johannes Bauer |
|||
# |
|||
# This file is part of toyecc. |
|||
# |
|||
# toyecc is free software; you can redistribute it and/or modify |
|||
# it under the terms of the GNU General Public License as published by |
|||
# the Free Software Foundation; this program is ONLY licensed under |
|||
# version 3 of the License, later versions are explicitly excluded. |
|||
# |
|||
# toyecc is distributed in the hope that it will be useful, |
|||
# but WITHOUT ANY WARRANTY; without even the implied warranty of |
|||
# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the |
|||
# GNU General Public License for more details. |
|||
# |
|||
# You should have received a copy of the GNU General Public License |
|||
# along with toyecc; if not, write to the Free Software |
|||
# Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA |
|||
# |
|||
# Johannes Bauer <JohannesBauer@gmx.de> |
|||
# |
|||
|
|||
def doc_inherit(superclass): |
|||
"""Inherit the docstring of a parent method. The super class needs to be |
|||
given as the decorator's parameter. Does not overwrite the docstring if |
|||
there is already one present.""" |
|||
def decorator(decoree): |
|||
method_name = decoree.__name__ |
|||
if decoree.__doc__ is None: |
|||
parent_method = getattr(superclass, method_name, None) |
|||
if parent_method is None: |
|||
raise Exception("Tried to inherit docstring of method '%s' from class '%s', but the latter does not offer a method by that name." % (method_name, str(superclass))) |
|||
docstr = parent_method.__doc__ |
|||
if docstr is None: |
|||
raise Exception("Tried to inherit docstring of method '%s' from class '%s', but that method also does not have a docstring." % (method_name, str(superclass))) |
|||
decoree.__doc__ = docstr |
|||
else: |
|||
raise Exception("Tried to overwrite an already present docstring of %s" % (str(decoree))) |
|||
return decoree |
|||
return decorator |
|||
@ -0,0 +1,79 @@ |
|||
# |
|||
# toyecc - A small Elliptic Curve Cryptography Demonstration. |
|||
# Copyright (C) 2011-2022 Johannes Bauer |
|||
# |
|||
# This file is part of toyecc. |
|||
# |
|||
# toyecc is free software; you can redistribute it and/or modify |
|||
# it under the terms of the GNU General Public License as published by |
|||
# the Free Software Foundation; this program is ONLY licensed under |
|||
# version 3 of the License, later versions are explicitly excluded. |
|||
# |
|||
# toyecc is distributed in the hope that it will be useful, |
|||
# but WITHOUT ANY WARRANTY; without even the implied warranty of |
|||
# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the |
|||
# GNU General Public License for more details. |
|||
# |
|||
# You should have received a copy of the GNU General Public License |
|||
# along with toyecc; if not, write to the Free Software |
|||
# Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA |
|||
# |
|||
# Johannes Bauer <JohannesBauer@gmx.de> |
|||
# |
|||
|
|||
from .PrivKeyOps import PrivKeyOpECDSASign, PrivKeyOpECIESDecrypt, PrivKeyOpEDDSASign, PrivKeyOpEDDSAKeyGen, PrivKeyOpEDDSAEncode, PrivKeyOpECDH, PrivKeyOpLoad |
|||
from .ECPublicKey import ECPublicKey |
|||
from .Random import secure_rand_int_between |
|||
|
|||
class ECPrivateKey(PrivKeyOpECDSASign, PrivKeyOpECIESDecrypt, PrivKeyOpEDDSASign, PrivKeyOpEDDSAKeyGen, PrivKeyOpEDDSAEncode, PrivKeyOpECDH, PrivKeyOpLoad): |
|||
"""Represents an elliptic curve private key.""" |
|||
|
|||
def __init__(self, scalar, curve): |
|||
"""Initialize the private key with the given scalar on the given |
|||
curve.""" |
|||
self._seed = None |
|||
self._scalar = scalar |
|||
self._curve = curve |
|||
self._pubkey = ECPublicKey(self._scalar * self._curve.G) |
|||
|
|||
@property |
|||
def scalar(self): |
|||
"""Returns the private scalar d of the key.""" |
|||
return self._scalar |
|||
|
|||
@property |
|||
def curve(self): |
|||
"""Returns the group which is used for EC computations.""" |
|||
return self._curve |
|||
|
|||
@property |
|||
def pubkey(self): |
|||
"""Returns the public key that is the counterpart to this private key.""" |
|||
return self._pubkey |
|||
|
|||
@property |
|||
def seed(self): |
|||
"""Returns the seed or None if there wasn't one. A seed is used for |
|||
schemes like EdDSA; it basically is a binary string that is hashed to |
|||
yield that actual private scalar d.""" |
|||
return self._seed |
|||
|
|||
def set_seed(self, seed): |
|||
"""Sets the seed of the private key. This operation can only performed |
|||
if no scalar has previously been set for this key.""" |
|||
assert(self._seed is None) |
|||
self._seed = seed |
|||
return self |
|||
|
|||
@staticmethod |
|||
def generate(curve): |
|||
"""Generate a random private key on a given curve.""" |
|||
scalar = secure_rand_int_between(1, curve.n - 1) |
|||
return ECPrivateKey(scalar, curve) |
|||
|
|||
def __str__(self): |
|||
if self._seed is None: |
|||
return "PrivateKey<d = 0x%x>" % (self.scalar) |
|||
else: |
|||
seedstr = "".join("%02x" % (c) for c in self._seed) |
|||
return "PrivateKey<d = 0x%x, seed = %s>" % (self.scalar, seedstr) |
|||
@ -0,0 +1,44 @@ |
|||
# |
|||
# toyecc - A small Elliptic Curve Cryptography Demonstration. |
|||
# Copyright (C) 2011-2016 Johannes Bauer |
|||
# |
|||
# This file is part of toyecc. |
|||
# |
|||
# toyecc is free software; you can redistribute it and/or modify |
|||
# it under the terms of the GNU General Public License as published by |
|||
# the Free Software Foundation; this program is ONLY licensed under |
|||
# version 3 of the License, later versions are explicitly excluded. |
|||
# |
|||
# toyecc is distributed in the hope that it will be useful, |
|||
# but WITHOUT ANY WARRANTY; without even the implied warranty of |
|||
# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the |
|||
# GNU General Public License for more details. |
|||
# |
|||
# You should have received a copy of the GNU General Public License |
|||
# along with toyecc; if not, write to the Free Software |
|||
# Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA |
|||
# |
|||
# Johannes Bauer <JohannesBauer@gmx.de> |
|||
# |
|||
|
|||
from .PubKeyOps import PubKeyOpECDSAVerify, PubKeyOpECDSAExploitReusedNonce, PubKeyOpEDDSAVerify, PubKeyOpEDDSAEncode, PubKeyOpECIESEncrypt, PubKeyOpLoad |
|||
|
|||
class ECPublicKey(PubKeyOpECDSAVerify, PubKeyOpECDSAExploitReusedNonce, PubKeyOpEDDSAVerify, PubKeyOpEDDSAEncode, PubKeyOpECIESEncrypt, PubKeyOpLoad): |
|||
"""Elliptic curve public key abstraction. An EC public key is just a point |
|||
on the curve, which is why the constructor only takes this (public) point |
|||
as a parameter. The public key abstraction allows this point to be used in |
|||
various meaningful purposes (ECDSA signature verification, etc.).""" |
|||
|
|||
def __init__(self, point): |
|||
self._point = point |
|||
|
|||
@property |
|||
def curve(self): |
|||
return self._point.curve |
|||
|
|||
@property |
|||
def point(self): |
|||
return self._point |
|||
|
|||
def __str__(self): |
|||
return "PublicKey<%s>" % (str(self.point)) |
|||
@ -0,0 +1,202 @@ |
|||
# |
|||
# toyecc - A small Elliptic Curve Cryptography Demonstration. |
|||
# Copyright (C) 2011-2022 Johannes Bauer |
|||
# |
|||
# This file is part of toyecc. |
|||
# |
|||
# toyecc is free software; you can redistribute it and/or modify |
|||
# it under the terms of the GNU General Public License as published by |
|||
# the Free Software Foundation; this program is ONLY licensed under |
|||
# version 3 of the License, later versions are explicitly excluded. |
|||
# |
|||
# toyecc is distributed in the hope that it will be useful, |
|||
# but WITHOUT ANY WARRANTY; without even the implied warranty of |
|||
# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the |
|||
# GNU General Public License for more details. |
|||
# |
|||
# You should have received a copy of the GNU General Public License |
|||
# along with toyecc; if not, write to the Free Software |
|||
# Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA |
|||
# |
|||
# Johannes Bauer <JohannesBauer@gmx.de> |
|||
# |
|||
|
|||
from .AffineCurvePoint import AffineCurvePoint |
|||
|
|||
class EllipticCurve(object): |
|||
"""Elliptic curve base class. Provides functionality which all curves have |
|||
in common.""" |
|||
def __init__(self, p, n, h, Gx, Gy, **kwargs): |
|||
assert(isinstance(p, int)) # Modulus |
|||
assert((n is None) or isinstance(n, int)) # Order |
|||
assert((h is None) or isinstance(h, int)) # Cofactor |
|||
assert((Gx is None) or isinstance(Gx, int)) # Generator Point X |
|||
assert((Gy is None) or isinstance(Gy, int)) # Generator Point Y |
|||
assert((Gx is None) == (Gy is None)) # Either both X and Y of G are set or none |
|||
self._p = p |
|||
self._n = n |
|||
self._h = h |
|||
if (Gx is not None) and (Gy is not None): |
|||
self._G = AffineCurvePoint(Gx, Gy, self) |
|||
else: |
|||
self._G = None |
|||
|
|||
if "quirks" in kwargs: |
|||
self._quirks = { quirk.identifier: quirk for quirk in kwargs["quirks"] } |
|||
else: |
|||
self._quirks = { } |
|||
@property |
|||
def p(self): |
|||
"""Returns the prime modulus which constitutes the finite field in |
|||
which the curve lies.""" |
|||
return self._p |
|||
|
|||
@property |
|||
def n(self): |
|||
"""Returns the order of the subgroup that is created by the generator |
|||
G.""" |
|||
return self._n |
|||
|
|||
@property |
|||
def h(self): |
|||
"""Returns the cofactor of the generator subgroup, i.e. h = #E(F_p) / |
|||
n. This will always be an integer according to Lagrange's Theorem.""" |
|||
return self._h |
|||
|
|||
@property |
|||
def G(self): |
|||
"""Returns the generator point G of the curve or None if no such point |
|||
was set. The generator point generates a subgroup over #E(F_p).""" |
|||
return self._G |
|||
|
|||
@property |
|||
def curve_order(self): |
|||
"""Returns the order of the curve in the underlying field, i.e. |
|||
#E(F_p). Intuitively, this is the total number of points on the curve |
|||
(plus maybe points at ininity, depending on the curve type) that |
|||
satisfy the curve equation.""" |
|||
if (self.h is None) or (self.n is None): |
|||
raise Exception("#E(F_p) is unknown for this curve") |
|||
return self.h * self.n |
|||
|
|||
@property |
|||
def frobenius_trace(self): |
|||
"""Returns the Frobenius trace 't' of the curve. Since |
|||
#E(F_p) = p + 1 - t it follows that t = p + 1 - #E(F_p).""" |
|||
return self.p + 1 - self.curve_order |
|||
|
|||
@property |
|||
def domainparams(self): |
|||
"""Returns the curve parameters as a named tuple.""" |
|||
raise Exception(NotImplemented) |
|||
|
|||
@property |
|||
def hasgenerator(self): |
|||
"""Returns if a generator point was supplied for the curve.""" |
|||
return self.G is not None |
|||
|
|||
@property |
|||
def hasname(self): |
|||
"""Returns if the curve is named (i.e. its name is not None).""" |
|||
return self.name is not None |
|||
|
|||
@property |
|||
def name(self): |
|||
"""Returns the name of the curve, if it was given one during |
|||
construction. Purely informational.""" |
|||
return self._name |
|||
|
|||
@property |
|||
def prettyname(self): |
|||
"""Returns the pretty name of the curve type. This might depend on the |
|||
actual curve, since it may also vary on the actual domain parameters to |
|||
include if the curve is a Koblitz curve or not.""" |
|||
return self.pretty_name |
|||
|
|||
@property |
|||
def curvetype(self): |
|||
"""Returns a string that corresponds to the curve type. For example, |
|||
this string can be 'shortweierstrass', 'twistededwards' or |
|||
'montgomery'.""" |
|||
raise Exception(NotImplemented) |
|||
|
|||
@property |
|||
def domainparamdict(self): |
|||
"""Returns the domain parameters of the curve as a dictionary.""" |
|||
return dict(self.domainparams._asdict()) |
|||
|
|||
@property |
|||
def security_bit_estimate(self): |
|||
"""Gives a haphazard estimate of the security of the underlying field, |
|||
in bits. For most curves, this will be half the bitsize of n (but might |
|||
be less, for example for Koblitz curves some bits might be |
|||
subtracted).""" |
|||
return self.n.bit_length() // 2 |
|||
|
|||
def enumerate_points(self): |
|||
"""Enumerates all points on the curve, including the point at infinity |
|||
(if the curve has such a special point).""" |
|||
raise Exception(NotImplemented) |
|||
|
|||
def naive_order_calculation(self): |
|||
"""Naively calculates the order #E(F_p) of the curve by enumerating and |
|||
counting all points which fulfull the curve equation. Note that this |
|||
implementation only works for the smallest of curves and is |
|||
computationally infeasible for all practical applications.""" |
|||
order = 0 |
|||
for pt in self.enumerate_points(): |
|||
order += 1 |
|||
return order |
|||
|
|||
def neutral(self): |
|||
"""Returns the neutral element of the curve group (for some curves, |
|||
this will be the point at infinity).""" |
|||
return AffineCurvePoint(None, None, self) |
|||
|
|||
def is_neutral(self, P): |
|||
"""Checks if a given point P is the neutral element of the group.""" |
|||
return P.x is None |
|||
|
|||
def oncurve(self, P): |
|||
"""Checks is a given point P is on the curve.""" |
|||
raise Exception(NotImplemented) |
|||
|
|||
def point_addition(self, P, Q): |
|||
"""Returns the sum of two points P and Q on the curve.""" |
|||
raise Exception(NotImplemented) |
|||
|
|||
def point_conjugate(self, P): |
|||
"""Returns the negated point -P to a given point P.""" |
|||
raise Exception(NotImplemented) |
|||
|
|||
def compress(self, P): |
|||
"""Returns the compressed representation of the point P on the |
|||
curve. Not all curves may support this operation.""" |
|||
raise Exception(NotImplemented) |
|||
|
|||
def uncompress(self, compressed): |
|||
"""Returns the uncompressed representation of a point on the curve. Not |
|||
all curves may support this operation.""" |
|||
raise Exception(NotImplemented) |
|||
|
|||
def has_quirk(self, quirk_class): |
|||
"""Some elliptic curves may have quirks or tweaks for certain |
|||
algorithms. These are attached to the curve using the 'quirks' kwarg of |
|||
the constructor. Code that wants to query if a specific quirk is |
|||
present may do so by calling 'has_quirk' with the according quirk class |
|||
(not a quirk class instance!).""" |
|||
return quirk_class.identifier in self._quirks |
|||
|
|||
def get_quirk(self, quirk_class): |
|||
"""If a quirk is present for a given elliptic curve, this quirk may |
|||
have been parametrized during instanciation. The get_quirk() method |
|||
returns that quirk instance when given a specific quirk class as input. |
|||
It raises a KeyError if the requested quirk is not present for the |
|||
elliptic curve.""" |
|||
return self._quirks[quirk_class.identifier] |
|||
|
|||
def __eq__(self, other): |
|||
return self.domainparams == other.domainparams |
|||
|
|||
def __ne__(self, other): |
|||
return not (self == other) |
|||
@ -0,0 +1,34 @@ |
|||
# |
|||
# toyecc - A small Elliptic Curve Cryptography Demonstration. |
|||
# Copyright (C) 2011-2022 Johannes Bauer |
|||
# |
|||
# This file is part of toyecc. |
|||
# |
|||
# toyecc is free software; you can redistribute it and/or modify |
|||
# it under the terms of the GNU General Public License as published by |
|||
# the Free Software Foundation; this program is ONLY licensed under |
|||
# version 3 of the License, later versions are explicitly excluded. |
|||
# |
|||
# toyecc is distributed in the hope that it will be useful, |
|||
# but WITHOUT ANY WARRANTY; without even the implied warranty of |
|||
# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the |
|||
# GNU General Public License for more details. |
|||
# |
|||
# You should have received a copy of the GNU General Public License |
|||
# along with toyecc; if not, write to the Free Software |
|||
# Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA |
|||
# |
|||
# Johannes Bauer <JohannesBauer@gmx.de> |
|||
# |
|||
|
|||
class DuplicateCurveException(Exception): |
|||
pass |
|||
|
|||
class NoSuchCurveException(Exception): |
|||
pass |
|||
|
|||
class UnsupportedPointFormatException(Exception): |
|||
pass |
|||
|
|||
class UnsupportedFieldException(Exception): |
|||
pass |
|||
@ -0,0 +1,249 @@ |
|||
# |
|||
# toyecc - A small Elliptic Curve Cryptography Demonstration. |
|||
# Copyright (C) 2011-2022 Johannes Bauer |
|||
# |
|||
# This file is part of toyecc. |
|||
# |
|||
# toyecc is free software; you can redistribute it and/or modify |
|||
# it under the terms of the GNU General Public License as published by |
|||
# the Free Software Foundation; this program is ONLY licensed under |
|||
# version 3 of the License, later versions are explicitly excluded. |
|||
# |
|||
# toyecc is distributed in the hope that it will be useful, |
|||
# but WITHOUT ANY WARRANTY; without even the implied warranty of |
|||
# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the |
|||
# GNU General Public License for more details. |
|||
# |
|||
# You should have received a copy of the GNU General Public License |
|||
# along with toyecc; if not, write to the Free Software |
|||
# Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA |
|||
# |
|||
# Johannes Bauer <JohannesBauer@gmx.de> |
|||
# |
|||
|
|||
import random |
|||
|
|||
class FieldElement(object): |
|||
"""Represents an element in a finite field over a (prime) modulus.""" |
|||
|
|||
def __init__(self, intvalue, modulus): |
|||
assert(isinstance(intvalue, int)) |
|||
assert(isinstance(modulus, int)) |
|||
self._intvalue = intvalue % modulus |
|||
self._modulus = modulus |
|||
self._qnr = None |
|||
|
|||
@property |
|||
def modulus(self): |
|||
"""Returns the field's modulus.""" |
|||
return self._modulus |
|||
|
|||
@staticmethod |
|||
def _eea(a, b): |
|||
"""Extended euclidian algorithm. Returns the gcd of (a, b) and the |
|||
Bezout-coefficients.""" |
|||
assert(isinstance(a, int)) |
|||
assert(isinstance(b, int)) |
|||
(s, t, u, v) = (1, 0, 0, 1) |
|||
while b != 0: |
|||
(q, r) = (a // b, a % b) |
|||
(unew, vnew) = (s, t) |
|||
s = u - (q * s) |
|||
t = v - (q * t) |
|||
(a, b) = (b, r) |
|||
(u, v) = (unew, vnew) |
|||
return (a, u, v) |
|||
|
|||
def inverse(self): |
|||
if int(self) == 0: |
|||
raise Exception("Trying to invert zero") |
|||
(gcd, u, v) = self._eea(int(self), self.modulus) |
|||
return FieldElement(v, self.modulus) |
|||
|
|||
@property |
|||
def is_qr(self): |
|||
"""Returns if the number is a quadratic residue according to Euler's |
|||
criterion.""" |
|||
return not self.is_qnr |
|||
|
|||
@property |
|||
def is_qnr(self): |
|||
"""Returns if the number is a quadratic non-residue according to |
|||
Euler's criterion.""" |
|||
if self._qnr is None: |
|||
self._qnr = int(self ** ((self._modulus - 1) // 2)) != 1 |
|||
return self._qnr |
|||
|
|||
@property |
|||
def legrende_symbol(self): |
|||
"""Returns the Legrende symbol of the field element, i.e. 0 if the |
|||
element is 0 mod p, 1 if it is a quadratic residue mod p or -1 if it is |
|||
a quadratic non-residue mod p.""" |
|||
if self == 0: |
|||
return 0 |
|||
elif self.is_qr: |
|||
return 1 |
|||
else: |
|||
return -1 |
|||
|
|||
def _tonelli_shanks_sqrt(self): |
|||
"""Performs the Tonelli-Shanks algorithm to determine the square root |
|||
on an element. Note that the algorithm only works if the value it is |
|||
performed on is a quadratic residue mod p.""" |
|||
q = self._modulus - 1 |
|||
s = 0 |
|||
while (q % 2) == 0: |
|||
s += 1 |
|||
q >>= 1 |
|||
assert(q * (2 ** s) == self.modulus - 1) |
|||
|
|||
while True: |
|||
z = FieldElement(random.randint(1, self.modulus - 1), self.modulus) |
|||
if z.is_qnr: |
|||
break |
|||
assert(z.is_qnr) |
|||
c = z ** q |
|||
|
|||
r = self ** ((q + 1) // 2) |
|||
t = self ** q |
|||
m = s |
|||
while int(t) != 1: |
|||
for i in range(1, m): |
|||
if int(t ** (1 << i)) == 1: |
|||
break |
|||
|
|||
b = c ** (1 << (m - i - 1)) |
|||
r = r * b |
|||
t = t * (b ** 2) |
|||
c = b ** 2 |
|||
m = i |
|||
|
|||
return r |
|||
|
|||
def sqr(self): |
|||
"""Return the squared value.""" |
|||
return self * self |
|||
|
|||
def sqrt(self): |
|||
"""Returns the square root of the value or None if the value is a |
|||
quadratic non-residue mod p.""" |
|||
if self.is_qnr: |
|||
return None |
|||
|
|||
if (self._modulus % 4) == 3: |
|||
root = self ** ((self._modulus + 1) // 4) |
|||
assert(root * root == self) |
|||
else: |
|||
root = self._tonelli_shanks_sqrt() |
|||
|
|||
if (int(root) & 1) == 0: |
|||
return (root, -root) |
|||
else: |
|||
return (-root, root) |
|||
|
|||
def quartic_root(self): |
|||
"""Returns the quartic root of the value or None if no such value |
|||
explicitly exists mod p.""" |
|||
root = self.sqrt() |
|||
if root is not None: |
|||
r1 = root[0].sqrt() or list() |
|||
r2 = root[1].sqrt() or list() |
|||
for candidate in list(r1) + list(r2): |
|||
if (candidate ** 4) == self: |
|||
return candidate |
|||
|
|||
def __checktype(self, value): |
|||
if isinstance(value, int): |
|||
return value |
|||
elif isinstance(value, FieldElement): |
|||
if value.modulus == self.modulus: |
|||
return int(value) |
|||
else: |
|||
raise Exception("Cannot perform meaningful arithmetic operations on field elements in different fields.") |
|||
|
|||
def sigint(self): |
|||
"""Returns a signed integer if the negative value is less than 10 |
|||
decimal digits and the absolute negated value is smaller than the |
|||
absolute positive value.""" |
|||
neg = abs(int(-self)) |
|||
if (neg < int(self)) and (neg < 1000000000): |
|||
return -neg |
|||
else: |
|||
return int(self) |
|||
|
|||
@classmethod |
|||
def any_qnr(cls, modulus): |
|||
"""Returns any quadratic non-residue in F(modulus).""" |
|||
for i in range(1000): |
|||
candidate = cls(random.randint(2, modulus - 1), modulus) |
|||
if candidate.is_qnr: |
|||
return candidate |
|||
raise Exception("Could not find a QNR in F_%d with a reasonable amount of tries." % (modulus)) |
|||
|
|||
def __int__(self): |
|||
return self._intvalue |
|||
|
|||
def __add__(self, value): |
|||
value = self.__checktype(value) |
|||
if value is None: |
|||
return NotImplemented |
|||
return FieldElement(int(self) + value, self.modulus) |
|||
|
|||
def __sub__(self, value): |
|||
value = self.__checktype(value) |
|||
if value is None: |
|||
return NotImplemented |
|||
return FieldElement(int(self) - value, self.modulus) |
|||
|
|||
def __mul__(self, value): |
|||
value = self.__checktype(value) |
|||
if value is None: |
|||
return NotImplemented |
|||
return FieldElement(int(self) * value, self.modulus) |
|||
|
|||
def __floordiv__(self, value): |
|||
value = self.__checktype(value) |
|||
if value is None: |
|||
return NotImplemented |
|||
return self * FieldElement(value, self.modulus).inverse() |
|||
|
|||
def __pow__(self, exponent): |
|||
assert(isinstance(exponent, int)) |
|||
return FieldElement(pow(int(self), exponent, self.modulus), self.modulus) |
|||
|
|||
def __neg__(self): |
|||
return FieldElement(-int(self), self.modulus) |
|||
|
|||
def __radd__(self, value): |
|||
return self + value |
|||
|
|||
def __rsub__(self, value): |
|||
return -self + value |
|||
|
|||
def __rmul__(self, value): |
|||
return self * value |
|||
|
|||
def __rfloordiv__(self, value): |
|||
return self.inverse() * value |
|||
|
|||
def __eq__(self, value): |
|||
if value is None: |
|||
return False |
|||
value = self.__checktype(value) |
|||
return int(self) == (value % self.modulus) |
|||
|
|||
def __ne__(self, other): |
|||
return not (self == other) |
|||
|
|||
def __lt__(self, value): |
|||
value = self.__checktype(value) |
|||
return int(self) < value |
|||
|
|||
def __hash__(self): |
|||
return hash((self._intvalue, self._modulus)) |
|||
|
|||
def __repr__(self): |
|||
return str(self) |
|||
|
|||
def __str__(self): |
|||
return "{0x%x}" % (int(self)) |
|||
@ -0,0 +1,164 @@ |
|||
# |
|||
# toyecc - A small Elliptic Curve Cryptography Demonstration. |
|||
# Copyright (C) 2011-2022 Johannes Bauer |
|||
# |
|||
# This file is part of toyecc. |
|||
# |
|||
# toyecc is free software; you can redistribute it and/or modify |
|||
# it under the terms of the GNU General Public License as published by |
|||
# the Free Software Foundation; this program is ONLY licensed under |
|||
# version 3 of the License, later versions are explicitly excluded. |
|||
# |
|||
# toyecc is distributed in the hope that it will be useful, |
|||
# but WITHOUT ANY WARRANTY; without even the implied warranty of |
|||
# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the |
|||
# GNU General Public License for more details. |
|||
# |
|||
# You should have received a copy of the GNU General Public License |
|||
# along with toyecc; if not, write to the Free Software |
|||
# Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA |
|||
# |
|||
# Johannes Bauer <JohannesBauer@gmx.de> |
|||
# |
|||
|
|||
import collections |
|||
from .FieldElement import FieldElement |
|||
from .AffineCurvePoint import AffineCurvePoint |
|||
from .EllipticCurve import EllipticCurve |
|||
from .DocInherit import doc_inherit |
|||
import toyecc.TwistedEdwardsCurve |
|||
|
|||
_MontgomeryCurveDomainParameters = collections.namedtuple("MontgomeryCurveDomainParameters", [ "curvetype", "a", "b", "p", "n", "G" ]) |
|||
|
|||
class MontgomeryCurve(EllipticCurve): |
|||
"""Represents an elliptic curve over a finite field F_P that satisfies the |
|||
Montgomery equation by^2 = x^3 + ax^2 + x.""" |
|||
pretty_name = "Montgomery" |
|||
|
|||
def __init__(self, a, b, p, n, h, Gx, Gy, **kwargs): |
|||
"""Create an elliptic Montgomery curve given the equation coefficients |
|||
a and b, the curve modulus p, the order of the curve n, the cofactor of |
|||
the curve h and the generator point G's X and Y coordinates in affine |
|||
representation, Gx and Gy.""" |
|||
EllipticCurve.__init__(self, p, n, h, Gx, Gy, **kwargs) |
|||
assert(isinstance(a, int)) # Curve coefficent A |
|||
assert(isinstance(b, int)) # Curve coefficent B |
|||
self._a = FieldElement(a, p) |
|||
self._b = FieldElement(b, p) |
|||
self._name = kwargs.get("name") |
|||
|
|||
# Check that the curve is not singular |
|||
assert(self.b * ((self.a ** 2) - 4) != 0) |
|||
|
|||
if self._G is not None: |
|||
# Check that the generator G is on the curve |
|||
assert(self._G.oncurve()) |
|||
|
|||
# Check that the generator G is of curve order |
|||
assert((self.n * self.G).is_neutral) |
|||
|
|||
@property |
|||
@doc_inherit(EllipticCurve) |
|||
def domainparams(self): |
|||
return _MontgomeryCurveDomainParameters(curvetype = self.curvetype, a = self.a, b = self.b, p = self.p, n = self.n, G = self.G) |
|||
|
|||
@property |
|||
@doc_inherit(EllipticCurve) |
|||
def curvetype(self): |
|||
return "montgomery" |
|||
|
|||
@property |
|||
def a(self): |
|||
"""Returns the coefficient a of the curve equation by^2 = x^3 + ax^2 + x.""" |
|||
return self._a |
|||
|
|||
@property |
|||
def b(self): |
|||
"""Returns the coefficient b of the curve equation by^2 = x^3 + ax^2 + x.""" |
|||
return self._b |
|||
|
|||
@doc_inherit(EllipticCurve) |
|||
def oncurve(self, P): |
|||
return (P.is_neutral) or ((self.b * P.y ** 2) == (P.x ** 3) + (self.a * (P.x ** 2)) + P.x) |
|||
|
|||
@doc_inherit(EllipticCurve) |
|||
def point_conjugate(self, P): |
|||
return AffineCurvePoint(int(P.x), int(-P.y), self) |
|||
|
|||
@doc_inherit(EllipticCurve) |
|||
def point_addition(self, P, Q): |
|||
if P.is_neutral: |
|||
# P is at infinity, O + Q = Q |
|||
result = Q |
|||
elif P == -Q: |
|||
# P == -Q, return O (point at infinity) |
|||
result = AffineCurvePoint.neutral(self) |
|||
elif P == Q: |
|||
# P == Q, point doubling |
|||
newx = -2 * P.x - self.a + (3 * P.x**2 + 2 * P.x * self.a + 1)**2 // (4 * P.y**2 * self.b) |
|||
newy = -P.y + (3 * P.x**2 + 2 * P.x * self.a + 1) * (3 * P.x + self.a) // (2 * P.y * self.b) - (3 * P.x**2 + 2 * P.x * self.a + 1)**3 // (8 * P.y**3 * self.b**2) |
|||
result = AffineCurvePoint(int(newx), int(newy), self) |
|||
else: |
|||
# P != Q, point addition |
|||
newx = -P.x - Q.x - self.a + (P.y - Q.y)**2 * self.b // (P.x - Q.x)**2 |
|||
newy = (2 * P.x + Q.x + self.a) * (P.y - Q.y) // (P.x - Q.x) - P.y - (P.y - Q.y)**3 * self.b // (P.x - Q.x)**3 |
|||
result = AffineCurvePoint(int(newx), int(newy), self) |
|||
return result |
|||
|
|||
def to_twistededwards(self, a = None): |
|||
"""Converts the domain parameters of this curve to domain parameters of |
|||
a birationally equivalent twisted Edwards curve. The user may select a |
|||
desired a coefficient that the resulting Edwards curve shall have or |
|||
leave it at None to accept an arbitrary one.""" |
|||
assert((a is None) or isinstance(a, int)) |
|||
|
|||
# For the Montgomery curve, B can always be arbitrarily chosen as long |
|||
# as the surrogate B coeffients are identical in their quadratic |
|||
# residue property mod p. This means an Montgomery curve where B is a |
|||
# quadratic residue mod p is isomorphous to all other Montgomery curves |
|||
# with identical A, p and where B is also a quadratic residue mod p. We |
|||
# use this property to get the curve we want if there is a desired "a" |
|||
# outcome and choose B appropriately. |
|||
if a is None: |
|||
# No special wish for a, just do the normal conversion |
|||
conversion_b = self.b |
|||
a = (self.a + 2) // conversion_b |
|||
else: |
|||
# We desire a special a and calculate the B we want |
|||
conversion_b = (self.a + 2) // a |
|||
|
|||
# And assure that it's QR property is the same as the original |
|||
assert(conversion_b.is_qr == self.b.is_qr) |
|||
d = (self.a - 2) // conversion_b |
|||
|
|||
# Then construct a curve with no generator first |
|||
raw_curve = toyecc.TwistedEdwardsCurve.TwistedEdwardsCurve( |
|||
a = int(a), |
|||
d = int(d), |
|||
p = self.p, |
|||
n = self.n, |
|||
h = self.h, |
|||
Gx = None, |
|||
Gy = None, |
|||
) |
|||
|
|||
# Convert the generator point to the new curve |
|||
G_twed = self.G.convert(raw_curve) |
|||
|
|||
# And recreate the curve with this new generator |
|||
twed_curve = toyecc.TwistedEdwardsCurve.TwistedEdwardsCurve( |
|||
a = int(a), |
|||
d = int(d), |
|||
p = self.p, |
|||
n = self.n, |
|||
h = self.h, |
|||
Gx = int(G_twed.x), |
|||
Gy = int(G_twed.y), |
|||
) |
|||
return twed_curve |
|||
|
|||
def __str__(self): |
|||
if self.hasname: |
|||
return "MontgomeryCurve<%s>" % (self.name) |
|||
else: |
|||
return "MontgomeryCurve<0x%x y^2 = x^3 + 0x%x x^2 + x mod 0x%x>" % (int(self.b), int(self.a), int(self.p)) |
|||
@ -0,0 +1,258 @@ |
|||
# |
|||
# toyecc - A small Elliptic Curve Cryptography Demonstration. |
|||
# Copyright (C) 2011-2022 Johannes Bauer |
|||
# |
|||
# This file is part of toyecc. |
|||
# |
|||
# toyecc is free software; you can redistribute it and/or modify |
|||
# it under the terms of the GNU General Public License as published by |
|||
# the Free Software Foundation; this program is ONLY licensed under |
|||
# version 3 of the License, later versions are explicitly excluded. |
|||
# |
|||
# toyecc is distributed in the hope that it will be useful, |
|||
# but WITHOUT ANY WARRANTY; without even the implied warranty of |
|||
# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the |
|||
# GNU General Public License for more details. |
|||
# |
|||
# You should have received a copy of the GNU General Public License |
|||
# along with toyecc; if not, write to the Free Software |
|||
# Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA |
|||
# |
|||
# Johannes Bauer <JohannesBauer@gmx.de> |
|||
# |
|||
|
|||
from . import Tools |
|||
from .FieldElement import FieldElement |
|||
from .Exceptions import UnsupportedPointFormatException |
|||
|
|||
class PointOpEDDSAEncoding(object): |
|||
def eddsa_encode(self): |
|||
"""Performs serialization of the point as required by EdDSA.""" |
|||
coordlen = (self.curve.B + 7) // 8 |
|||
bitlen = (coordlen * 8) - 1 |
|||
enc_value = int(self.y) |
|||
enc_value &= ((1 << bitlen) - 1) |
|||
enc_value |= (int(self.x) & 1) << bitlen |
|||
return Tools.inttobytes_le(enc_value, (self.curve.B + 7) // 8) |
|||
|
|||
@staticmethod |
|||
def __eddsa_recoverx(curve, y): |
|||
x = 0 |
|||
xx = (y * y - 1) // (curve.d * y * y - curve.a) |
|||
if curve.p % 8 == 5: |
|||
x = xx ** ((curve.p + 3) // 8) |
|||
if x * x == -xx: |
|||
x = x * (FieldElement(2, curve.p) ** ((curve.p - 1) // 4)) |
|||
elif curve.p % 4 == 3: |
|||
x = xx ** ((curve.p + 1) // 4) |
|||
if x * x != xx: |
|||
x = 0 |
|||
return int(x) |
|||
|
|||
@classmethod |
|||
def eddsa_decode(cls, curve, data): |
|||
"""Performs deserialization of the point as required by EdDSA.""" |
|||
assert(curve.curvetype == "twistededwards") |
|||
coordlen = (curve.B + 7) // 8 |
|||
bitlen = (coordlen * 8) - 1 |
|||
enc_value = int.from_bytes(data, byteorder = "little") |
|||
y = enc_value & ((1 << bitlen) - 1) |
|||
x = PointOpEDDSAEncoding.__eddsa_recoverx(curve, y) |
|||
hibit = (enc_value >> bitlen) & 1 |
|||
if (x & 1) != hibit: |
|||
x = curve.p - x |
|||
return cls(x, y, curve) |
|||
|
|||
class PointOpCurveConversion(object): |
|||
@staticmethod |
|||
def __pconv_twed_mont_scalefactor(twedcurve, montcurve): |
|||
native_b = 4 // (twedcurve.a - twedcurve.d) |
|||
if native_b == montcurve.b: |
|||
# Scaling is not necessary, already native curve format |
|||
scale_factor = 1 |
|||
else: |
|||
# Scaling of montgomery y component (v) is needed |
|||
if twedcurve.hasgenerator and montcurve.hasgenerator: |
|||
# Convert the generator point of the twisted edwards source |
|||
# point to unscaled Montgomery space |
|||
Gv = (1 + twedcurve.G.y) // ((1 - twedcurve.G.y) * twedcurve.G.x) |
|||
|
|||
# And calculate a multiplicative scaling factor so that the |
|||
# point will result in the target curve's generator point Y |
|||
scale_factor = montcurve.G.y // Gv |
|||
|
|||
elif native_b.is_qr: |
|||
# If b is a quadradic residue mod p then any other |
|||
# quadratic residue can serve as a surrgate b coefficient |
|||
# to yield an isomorphous curve. Only y coordinate of the |
|||
# resulting points needs to be scaled. Calculate a scaling |
|||
# ratio. |
|||
scale_factors = (montcurve.b // native_b).sqrt() |
|||
|
|||
# At least one of the curves lacks a generator point, |
|||
# select just any scale factor |
|||
scale_factor = scale_factors[0].inverse() |
|||
|
|||
else: |
|||
# Native B is a quadratic non-residue module B; Not sure |
|||
# how to handle this case |
|||
# TODO: Implement this |
|||
raise Exception(NotImplemented) |
|||
return scale_factor |
|||
|
|||
def convert(self, targetcurve): |
|||
"""Convert the affine curve point to a point on a birationally |
|||
equivalent target curve.""" |
|||
|
|||
if self.is_neutral: |
|||
return targetcurve.neutral() |
|||
|
|||
if (self.curve.curvetype == "twistededwards") and (targetcurve.curvetype == "montgomery"): |
|||
# (x, y) are Edwards coordinates |
|||
# (u, v) are Montgomery coordonates |
|||
(x, y) = (self.x, self.y) |
|||
u = (1 + y) // (1 - y) |
|||
v = (1 + y) // ((1 - y) * x) |
|||
|
|||
# Montgomery coordinates are unscaled to the actual B coefficient |
|||
# of the curve right now. Calculate scaling factor and scale v |
|||
# appropriately |
|||
scaling_factor = self.__pconv_twed_mont_scalefactor(self.curve, targetcurve) |
|||
v = v * scaling_factor |
|||
|
|||
point = self.__class__(int(u), int(v), targetcurve) |
|||
elif (self.curve.curvetype == "montgomery") and (targetcurve.curvetype == "twistededwards"): |
|||
# (x, y) are Edwards coordinates |
|||
# (u, v) are Montgomery coordonates |
|||
(u, v) = (self.x, self.y) |
|||
y = (u - 1) // (u + 1) |
|||
x = -(1 + y) // (v * (y - 1)) |
|||
|
|||
# Twisted Edwards coordinates are unscaled to the actual B |
|||
# coefficient of the curve right now. Calculate scaling factor and |
|||
# scale x appropriately |
|||
scaling_factor = self.__pconv_twed_mont_scalefactor(targetcurve, self.curve) |
|||
x = x * scaling_factor |
|||
|
|||
point = self.__class__(int(x), int(y), targetcurve) |
|||
else: |
|||
raise Exception(NotImplemented) |
|||
|
|||
assert(point.oncurve()) |
|||
return point |
|||
|
|||
class PointOpNaiveOrderCalculation(object): |
|||
def naive_order_calculation(self): |
|||
"""Calculates the order of the point naively, i.e. by walking through |
|||
all points until the given neutral element is hit. Note that this only |
|||
works for smallest of curves and is not computationally feasible for |
|||
anything else.""" |
|||
curpt = self |
|||
order = 1 |
|||
while not curpt.is_neutral: |
|||
order += 1 |
|||
curpt += self |
|||
return order |
|||
|
|||
|
|||
class PointOpSerialization(object): |
|||
def serialize_uncompressed(self): |
|||
"""Serializes the point into a bytes object in uncompressed form.""" |
|||
length = (self.curve.p.bit_length() + 7) // 8 |
|||
serialized = bytes([ 0x04 ]) + Tools.inttobytes(int(self.x), length) + Tools.inttobytes(int(self.y), length) |
|||
return serialized |
|||
|
|||
@classmethod |
|||
def deserialize_uncompressed(cls, data, curve = None): |
|||
"""Deserializes a curve point which is given in uncompressed form. A |
|||
curve may be passed with the 'curve' argument in which case an |
|||
AffineCurvePoint is returned from this method. Otherwise the affine X |
|||
and Y coordinates are returned as a tuple.""" |
|||
if data[0] != 0x04: |
|||
raise UnsupportedPointFormatException("Generator point of explicitly encoded curve is given in unsupported form (0x%x)." % (data[0])) |
|||
data = data[1:] |
|||
assert((len(data) % 2) == 0) |
|||
Px = Tools.bytestoint(data[ : len(data) // 2]) |
|||
Py = Tools.bytestoint(data[len(data) // 2 : ]) |
|||
if curve is not None: |
|||
return cls(Px, Py, curve) |
|||
else: |
|||
return (Px, Py) |
|||
|
|||
class PointOpScalarMultiplicationXOnly(): |
|||
"""Compute an X-only ladder scalar multiplication of the private key and |
|||
the X coordinate of a given point.""" |
|||
def _x_double(self, x): |
|||
"""Doubling of point with coordinate x.""" |
|||
if x is None: |
|||
return None |
|||
|
|||
den = 4 * (x**3 + self.curve.a * x + self.curve.b) |
|||
if den == 0: |
|||
# Point at infinity |
|||
return None |
|||
num = (x**2 - self.curve.a)**2 - (8 * self.curve.b * x) |
|||
return num // den |
|||
|
|||
def _x_add_multiplicative(self, x1, x2, x3prime): |
|||
"""Multiplicative formula addition of x1 + x2, where x3' is the |
|||
difference in X of P1 - P2. Using this function only makes sense where |
|||
(P1 - P2) is fixed, as it is in the ladder implementation.""" |
|||
if x1 is None: |
|||
return x2 |
|||
elif x2 is None: |
|||
return x1 |
|||
elif x1 == x2: |
|||
return None |
|||
num = -4 * self.curve.b * (x1 + x2) + (x1 * x2 - self.curve.a)**2 |
|||
den = x3prime * (x1 - x2)**2 |
|||
result = num // den |
|||
return result |
|||
|
|||
def _x_add_additive(self, x1, x2, x3prime): |
|||
"""Additive formula addition of x1 + x2, where x3' is the difference in |
|||
X of P1 - P2. Using this function only makes sense where (P1 - P2) is |
|||
fixed, as it is in the ladder implementation.""" |
|||
if x1 is None: |
|||
return x2 |
|||
elif x2 is None: |
|||
return x1 |
|||
elif x1 == x2: |
|||
return None |
|||
num = 2 * (x1 + x2) * (x1 * x2 + self.curve.a) + 4 * self.curve.b |
|||
den = (x1 - x2) ** 2 |
|||
result = num // den - x3prime |
|||
return result |
|||
|
|||
def _x_add(self, x1, x2, x3prime): |
|||
"""There are two equivalent implementations, one using the |
|||
multiplicative and the other using the additive representation. Both |
|||
should work equally well.""" |
|||
return self._x_add_multiplicative(x1, x2, x3prime) |
|||
#return self._x_add_additive(x1, x2, x3prime) |
|||
|
|||
def scalar_mul_xonly(self, scalar): |
|||
"""This implements the X-coordinate-only multiplication algorithm of a |
|||
Short Weierstrass curve with the X coordinate of a given point. |
|||
Reference is "Izu and Takagi: A Fast Parallel Elliptic Curve |
|||
Multiplication Resistant against Side Channel Attacks" (2002)""" |
|||
if self.curve.curvetype != "shortweierstrass": |
|||
raise NotImplementedError("X-only ladder multiplication is only implemented for Short Weierstrass curves") |
|||
if self.is_neutral: |
|||
# Point at infinity is input |
|||
return None |
|||
elif scalar == 0: |
|||
# Multiplication with zero -> point at infinity is output |
|||
return None |
|||
|
|||
x_coordinate = int(self.x) |
|||
if not isinstance(x_coordinate, FieldElement): |
|||
x_coordinate = FieldElement(x_coordinate, self.curve.p) |
|||
Q = [ x_coordinate, self._x_double(x_coordinate), None ] |
|||
for bitno in reversed(range(scalar.bit_length() - 1)): |
|||
bit = (scalar >> bitno) & 1 |
|||
Q[2] = self._x_double(Q[bit]) |
|||
Q[1] = self._x_add(Q[0], Q[1], x_coordinate) |
|||
Q[0] = Q[2 - bit] |
|||
Q[1] = Q[1 + bit] |
|||
return Q[0] |
|||
@ -0,0 +1,331 @@ |
|||
# |
|||
# toyecc - A small Elliptic Curve Cryptography Demonstration. |
|||
# Copyright (C) 2011-2022 Johannes Bauer |
|||
# |
|||
# This file is part of toyecc. |
|||
# |
|||
# toyecc is free software; you can redistribute it and/or modify |
|||
# it under the terms of the GNU General Public License as published by |
|||
# the Free Software Foundation; this program is ONLY licensed under |
|||
# version 3 of the License, later versions are explicitly excluded. |
|||
# |
|||
# toyecc is distributed in the hope that it will be useful, |
|||
# but WITHOUT ANY WARRANTY; without even the implied warranty of |
|||
# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the |
|||
# GNU General Public License for more details. |
|||
# |
|||
# You should have received a copy of the GNU General Public License |
|||
# along with toyecc; if not, write to the Free Software |
|||
# Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA |
|||
# |
|||
# Johannes Bauer <JohannesBauer@gmx.de> |
|||
# |
|||
|
|||
import re |
|||
import collections |
|||
|
|||
from .FieldElement import FieldElement |
|||
|
|||
class _CoeffDict(object): |
|||
def __init__(self): |
|||
self._coeffs = { } |
|||
|
|||
def clone(self): |
|||
clone = _CoeffDict() |
|||
clone._coeffs = dict(self._coeffs) |
|||
return clone |
|||
|
|||
@property |
|||
def degree(self): |
|||
if len(self._coeffs) > 0: |
|||
return max(self._coeffs.keys()) |
|||
else: |
|||
return 0 |
|||
|
|||
def clone(self): |
|||
clone = _CoeffDict() |
|||
clone._coeffs = dict(self._coeffs) |
|||
return clone |
|||
|
|||
def __eq__(self, other): |
|||
return self._coeffs == other._coeffs |
|||
|
|||
def __neq__(self, other): |
|||
return not (self == other) |
|||
|
|||
def __iter__(self): |
|||
return iter(self._coeffs.items()) |
|||
|
|||
def __len__(self): |
|||
return len(self._coeffs) |
|||
|
|||
def __getitem__(self, key): |
|||
return self._coeffs.get(key, 0) |
|||
|
|||
def __setitem__(self, key, value): |
|||
if (value == 0) and (key in self._coeffs): |
|||
del self._coeffs[key] |
|||
else: |
|||
self._coeffs[key] = value |
|||
|
|||
def __str__(self): |
|||
return "CoeffDict<%s>" % (str(self._coeffs)) |
|||
|
|||
class Polynomial(object): |
|||
_TERM_RE = re.compile("^((?P<coeff>-?\d+)\*)?x(\^(?P<exponent>\d+))?$") |
|||
_CACHE_EXPONENTS = [ 2, 3 ] |
|||
|
|||
def __init__(self, modulus, initvalue = None): |
|||
self._modulus = modulus |
|||
self._terms = _CoeffDict() |
|||
if initvalue is None: |
|||
self._terms[1] = FieldElement(1, self._modulus) |
|||
else: |
|||
if initvalue != 0: |
|||
self._terms[0] = FieldElement(initvalue, self._modulus) |
|||
self._expcache = { } |
|||
|
|||
@property |
|||
def degree(self): |
|||
return self._terms.degree |
|||
|
|||
@property |
|||
def modulus(self): |
|||
return self._modulus |
|||
|
|||
@property |
|||
def is_constant(self): |
|||
return self.degree == 0 |
|||
|
|||
def get_constant(self): |
|||
assert(self.is_constant) |
|||
return self[0] |
|||
|
|||
def _clone(self): |
|||
clone = Polynomial(self.modulus, 0) |
|||
clone._terms = self._terms.clone() |
|||
return clone |
|||
|
|||
def substitute(self, value): |
|||
result = 0 |
|||
for (exponent, coefficient) in self._terms: |
|||
result += coefficient * (value ** exponent) |
|||
return result |
|||
|
|||
def gcd(self, other): |
|||
"""Returns the greatest common divisor polynomial of this object and |
|||
the other polynomial.""" |
|||
assert(isinstance(other, Polynomial)) |
|||
assert(self.modulus == other.modulus) |
|||
assert((self != 0) or (other != 0)) |
|||
(a, b) = (self, other) |
|||
if a == 0: |
|||
return b |
|||
elif b == 0: |
|||
return a |
|||
|
|||
while b != 0: |
|||
(a, b) = (b, a % b) |
|||
|
|||
highest_coefficient = a._terms[a.degree] |
|||
a = a // highest_coefficient |
|||
return a |
|||
|
|||
def __and__(self, other): |
|||
"""Returns the greatest common divisor polynomial of this object and |
|||
the other polynomial.""" |
|||
return self.gcd(other) |
|||
|
|||
def __add__(self, value): |
|||
if isinstance(value, int) or isinstance(value, FieldElement): |
|||
result = self._clone() |
|||
result._terms[0] += value |
|||
return result |
|||
elif isinstance(value, Polynomial): |
|||
result = self._clone() |
|||
for (exponent, coefficient) in value: |
|||
result._terms[exponent] += coefficient |
|||
return result |
|||
else: |
|||
raise Exception(NotImplemented) |
|||
|
|||
def __sub__(self, value): |
|||
if isinstance(value, int) or isinstance(value, FieldElement): |
|||
result = self._clone() |
|||
result._terms[0] -= value |
|||
return result |
|||
elif isinstance(value, Polynomial): |
|||
result = self._clone() |
|||
for (exponent, coefficient) in value: |
|||
result._terms[exponent] -= coefficient |
|||
return result |
|||
else: |
|||
raise Exception(NotImplemented) |
|||
|
|||
def __pow__(self, value): |
|||
if value in self._expcache: |
|||
return self._expcache[value] |
|||
if isinstance(value, int): |
|||
if len(self._terms) == 1: |
|||
result = Polynomial(self.modulus, 0) |
|||
for (exponent, coefficient) in self: |
|||
result._terms[exponent * value] = coefficient ** value |
|||
else: |
|||
exponent = value |
|||
result = Polynomial(self.modulus, 1) |
|||
multiplier = self |
|||
for bit in range(exponent.bit_length()): |
|||
if exponent & (1 << bit): |
|||
result = result * multiplier |
|||
multiplier = multiplier * multiplier |
|||
else: |
|||
raise Exception(NotImplemented) |
|||
|
|||
if value in self._CACHE_EXPONENTS: |
|||
self._expcache[value] = result |
|||
|
|||
return result |
|||
|
|||
def powmod(self, exponent, modulus): |
|||
"""Returns the result of (self^exponent) % modulus. Exponent must be an |
|||
integer and modulus another Polynomial.""" |
|||
assert(isinstance(exponent, int)) |
|||
assert((modulus is None) or isinstance(modulus, Polynomial)) |
|||
assert(exponent >= 0) |
|||
result = Polynomial(self.modulus, 1) |
|||
multiplier = self |
|||
for bit in range(exponent.bit_length()): |
|||
if exponent & (1 << bit): |
|||
result = (result * multiplier) % modulus |
|||
multiplier = (multiplier * multiplier) % modulus |
|||
return result |
|||
|
|||
@classmethod |
|||
def parse_poly(cls, polystr, modulus): |
|||
poly = Polynomial(modulus, 0) |
|||
|
|||
polystr = polystr.replace(" - ", " + -") |
|||
terms = polystr.split(" + ") |
|||
for term in terms: |
|||
if term.isnumeric(): |
|||
poly._terms[0] += int(term) |
|||
else: |
|||
result = cls._TERM_RE.match(term) |
|||
if result is None: |
|||
raise Exception("Cannot parse polynomial term: '%s'" % (term)) |
|||
result = result.groupdict() |
|||
|
|||
result = { key: int(value) for (key, value) in result.items() if (value is not None) } |
|||
coeff = result.get("coeff", 1) |
|||
exponent = result.get("exponent", 1) |
|||
poly._terms[exponent] += FieldElement(coeff, modulus) |
|||
|
|||
return poly |
|||
|
|||
def __floordiv__(self, value): |
|||
if isinstance(value, int) or isinstance(value, FieldElement): |
|||
result = Polynomial(self.modulus, 0) |
|||
for (exponent, coefficient) in self: |
|||
result._terms[exponent] = coefficient // value |
|||
return result |
|||
elif isinstance(value, Polynomial): |
|||
if value.degree == 0: |
|||
return self // value[0] |
|||
|
|||
result = Polynomial(self.modulus, 0) |
|||
numerator = self._clone() |
|||
while numerator.degree >= value.degree: |
|||
shift = numerator.degree - value.degree |
|||
multiplier = numerator[numerator.degree] // value[value.degree] |
|||
|
|||
result._terms[shift] += multiplier |
|||
for (exponent, coefficient) in value: |
|||
numerator._terms[exponent + shift] -= multiplier * coefficient |
|||
return result |
|||
|
|||
else: |
|||
raise Exception(NotImplemented) |
|||
|
|||
def __mul__(self, value): |
|||
if isinstance(value, int) or isinstance(value, FieldElement): |
|||
result = Polynomial(self.modulus, 0) |
|||
for (exponent, coefficient) in self: |
|||
result._terms[exponent] = coefficient * value |
|||
return result |
|||
elif isinstance(value, Polynomial): |
|||
result = Polynomial(self.modulus, 0) |
|||
for (exponent1, coefficient1) in self: |
|||
for (exponent2, coefficient2) in value: |
|||
result._terms[exponent1 + exponent2] += coefficient1 * coefficient2 |
|||
return result |
|||
else: |
|||
raise Exception(NotImplemented) |
|||
|
|||
def __mod__(self, value): |
|||
if isinstance(value, Polynomial): |
|||
if value.degree == 0: |
|||
return Polynomial(self.modulus, 0) |
|||
|
|||
result = self._clone() |
|||
while result.degree >= value.degree: |
|||
shift = result.degree - value.degree |
|||
multiplier = result[result.degree] // value[value.degree] |
|||
|
|||
for (exponent, coefficient) in value: |
|||
result._terms[exponent + shift] -= multiplier * coefficient |
|||
return result |
|||
else: |
|||
raise Exception(NotImplemented) |
|||
|
|||
def __rmul__(self, value): |
|||
return self * value |
|||
|
|||
def __radd__(self, value): |
|||
return self + value |
|||
|
|||
def __getitem__(self, exponent): |
|||
return self._terms[exponent] |
|||
|
|||
def __iter__(self): |
|||
yield from iter(self._terms) |
|||
|
|||
def __eq__(self, value): |
|||
if isinstance(value, int) or isinstance(value, FieldElement): |
|||
return self.is_constant and (self.get_constant() == value) |
|||
elif isinstance(value, Polynomial): |
|||
return (self.modulus == value.modulus) and (self._terms == value._terms) |
|||
else: |
|||
raise Exception(NotImplemented) |
|||
|
|||
def __ne__(self, value): |
|||
return not (self == value) |
|||
|
|||
def __repr__(self): |
|||
return str(self) |
|||
|
|||
def __str__(self): |
|||
terms = [ ] |
|||
for (exponent, coefficient) in sorted(self, reverse = True): |
|||
if coefficient == 0: |
|||
continue |
|||
|
|||
if exponent == 0: |
|||
terms.append("%d" % (int(coefficient))) |
|||
continue |
|||
|
|||
elif coefficient == 1: |
|||
coeffstr = "" |
|||
else: |
|||
coeffstr = "%d*" % (int(coefficient)) |
|||
|
|||
if exponent == 1: |
|||
termstr = "x" |
|||
else: |
|||
termstr = "x^%d" % (int(exponent)) |
|||
|
|||
terms.append(coeffstr + termstr) |
|||
|
|||
if len(terms) == 0: |
|||
return "0" |
|||
else: |
|||
return " + ".join(terms) |
|||
@ -0,0 +1,228 @@ |
|||
# |
|||
# toyecc - A small Elliptic Curve Cryptography Demonstration. |
|||
# Copyright (C) 2011-2022 Johannes Bauer |
|||
# |
|||
# This file is part of toyecc. |
|||
# |
|||
# toyecc is free software; you can redistribute it and/or modify |
|||
# it under the terms of the GNU General Public License as published by |
|||
# the Free Software Foundation; this program is ONLY licensed under |
|||
# version 3 of the License, later versions are explicitly excluded. |
|||
# |
|||
# toyecc is distributed in the hope that it will be useful, |
|||
# but WITHOUT ANY WARRANTY; without even the implied warranty of |
|||
# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the |
|||
# GNU General Public License for more details. |
|||
# |
|||
# You should have received a copy of the GNU General Public License |
|||
# along with toyecc; if not, write to the Free Software |
|||
# Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA |
|||
# |
|||
# Johannes Bauer <JohannesBauer@gmx.de> |
|||
# |
|||
|
|||
import hashlib |
|||
import collections |
|||
|
|||
from .FieldElement import FieldElement |
|||
from .Random import secure_rand, secure_rand_int_between |
|||
from .AffineCurvePoint import AffineCurvePoint |
|||
from .CurveDB import CurveDB |
|||
from .ShortWeierstrassCurve import ShortWeierstrassCurve |
|||
from .ASN1 import parse_asn1_private_key, parse_asn1_field_params_fp |
|||
from . import Tools |
|||
from .CurveQuirks import CurveQuirkEdDSASetPrivateKeyMSB, CurveQuirkEdDSAEnsurePrimeOrderSubgroup, CurveQuirkSigningHashFunction |
|||
|
|||
class PrivKeyOpECDSASign(object): |
|||
ECDSASignature = collections.namedtuple("ECDSASignature", [ "hashalg", "r", "s" ]) |
|||
|
|||
def ecdsa_sign_hash(self, message_digest, k = None, digestname = None): |
|||
"""Signs a given messagedigest, given as bytes, using ECDSA. |
|||
Optionally a nonce k can be supplied which should usually be unqiuely |
|||
chosen for every ECDSA signature. This way it is possible to |
|||
deliberately create broken signatures which can be exploited later on. |
|||
If k is not supplied, it is randomly chosen. If a digestname is |
|||
supplied the name of this digest eventually ends up in the |
|||
ECDSASignature object.""" |
|||
assert(isinstance(message_digest, bytes)) |
|||
assert((k is None) or isinstance(k, int)) |
|||
|
|||
# Convert message digest to integer value |
|||
e = Tools.ecdsa_msgdigest_to_int(message_digest, self.curve.n) |
|||
|
|||
# Select a random integer (if None is supplied!) |
|||
if k is None: |
|||
k = secure_rand_int_between(1, self.curve.n - 1) |
|||
|
|||
# r = (k * G)_x mod n |
|||
Rmodp = k * self.curve.G |
|||
r = int(Rmodp.x) % self.curve.n |
|||
assert(r != 0) |
|||
|
|||
s = FieldElement(e + self.scalar * r, self.curve.n) // k |
|||
|
|||
return self.ECDSASignature(r = r, s = int(s), hashalg = digestname) |
|||
|
|||
def ecdsa_sign(self, message, digestname, k = None): |
|||
"""Signs a given message with the digest that is given as a string. |
|||
Optionally a nonce k can be supplied which should usually be unqiuely |
|||
chosen for every ECDSA signature. This way it is possible to |
|||
deliberately create broken signatures which can be exploited later |
|||
on. If k is not supplied, it is randomly chosen.""" |
|||
assert(isinstance(message, bytes)) |
|||
assert(isinstance(digestname, str)) |
|||
digest_fnc = hashlib.new(digestname) |
|||
digest_fnc.update(message) |
|||
message_digest = digest_fnc.digest() |
|||
return self.ecdsa_sign_hash(message_digest, k = k, digestname = digestname) |
|||
|
|||
|
|||
class PrivKeyOpECIESDecrypt(object): |
|||
def ecies_decrypt(self, R): |
|||
"""Takes the transmitted point R and reconstructs the shared secret |
|||
point S using the private key.""" |
|||
# Transmitted R is given, restore the symmetric key S |
|||
return self._scalar * R |
|||
|
|||
|
|||
class PrivKeyOpEDDSASign(object): |
|||
class EDDSASignature(object): |
|||
def __init__(self, curve, R, s): |
|||
self._curve = curve |
|||
self._R = R |
|||
self._s = s |
|||
|
|||
@property |
|||
def curve(self): |
|||
return self._curve |
|||
|
|||
@property |
|||
def R(self): |
|||
return self._R |
|||
|
|||
@property |
|||
def s(self): |
|||
return self._s |
|||
|
|||
def encode(self): |
|||
"""Performs serialization of the signature as used by EdDSA.""" |
|||
return self.R.eddsa_encode() + Tools.inttobytes_le(self.s, (self.curve.B + 7) // 8) |
|||
|
|||
@classmethod |
|||
def decode(cls, curve, encoded_signature): |
|||
"""Performs deserialization of the signature as used by EdDSA.""" |
|||
assert(isinstance(encoded_signature, bytes)) |
|||
coordlen = (curve.B + 7) // 8 |
|||
assert(len(encoded_signature) == 2 * coordlen) |
|||
encoded_R = encoded_signature[:coordlen] |
|||
encoded_s = encoded_signature[coordlen:] |
|||
R = AffineCurvePoint.eddsa_decode(curve, encoded_R) |
|||
s = Tools.bytestoint_le(encoded_s) |
|||
return cls(curve, R, s) |
|||
|
|||
def __eq__(self, other): |
|||
return (self.R, self.s) == (other.R, other.s) |
|||
|
|||
def __str__(self): |
|||
return "EDDSASignature<R = %s, s = %s>" % (self.R, self.s) |
|||
|
|||
def eddsa_sign(self, message): |
|||
"""Performs an EdDSA signature of the message. For this to work the |
|||
curve has to be a twisted Edwards curve and the private key scalar has |
|||
to be generated from a hashed seed. This hashed seed is automatically |
|||
generated when a keypair is generated using, for example, the |
|||
eddsa_generate() function instead of the regular key generation |
|||
function generate().""" |
|||
assert(self.curve.curvetype == "twistededwards") |
|||
if self._seed is None: |
|||
raise Exception("EdDSA requires a seed which is the source for calculation of the private key scalar.") |
|||
if not self.curve.has_quirk(CurveQuirkSigningHashFunction): |
|||
raise Exception("Unable to determine EdDSA signature function.") |
|||
|
|||
quirk = self.curve.get_quirk(CurveQuirkSigningHashFunction) |
|||
h = quirk.hashdata(self._seed) |
|||
|
|||
coordlen = (self.curve.B + 7) // 8 |
|||
r = Tools.bytestoint_le(quirk.hashdata(h[coordlen : 2 * coordlen] + message)) |
|||
R = r * self.curve.G |
|||
s = (r + Tools.bytestoint_le(quirk.hashdata(R.eddsa_encode() + self.pubkey.point.eddsa_encode() + message)) * self.scalar) % self.curve.n |
|||
sig = self.EDDSASignature(self.curve, R, s) |
|||
return sig |
|||
|
|||
|
|||
class PrivKeyOpEDDSAKeyGen(object): |
|||
@classmethod |
|||
def eddsa_generate(cls, curve, seed = None): |
|||
"""Generates a randomly selected seed value. This seed value is then |
|||
hashed using the EdDSA hash function (SHA512 for Ed2556 and |
|||
Shake256-114 for Ed448) and the resulting value is (slightly modified) |
|||
used as the private key scalar. Since for EdDSA signing operations |
|||
this seed value is needed, it is also stored within the private key.""" |
|||
coordlen = (curve.B + 7) // 8 |
|||
if seed is None: |
|||
seed = secure_rand(coordlen) |
|||
assert(isinstance(seed, bytes)) |
|||
assert(len(seed) == coordlen) |
|||
|
|||
# Calculate hash over seed and generate scalar from hash over seed |
|||
if not curve.has_quirk(CurveQuirkSigningHashFunction): |
|||
raise Exception("Unable to determine EdDSA signature function.") |
|||
quirk = curve.get_quirk(CurveQuirkSigningHashFunction) |
|||
h = quirk.hashdata(seed) |
|||
a = int.from_bytes(h[:coordlen], byteorder = "little") & ((1 << (curve.B - 1)) - 1) |
|||
|
|||
# Do we need to mask out lower significant bits to ensure that we use a |
|||
# prime order subgroup? |
|||
if curve.has_quirk(CurveQuirkEdDSAEnsurePrimeOrderSubgroup): |
|||
if not Tools.is_power_of_two(curve.h): |
|||
raise Exception("Can only ensure prime order subgroup by masking 'a' when curve cofactor is a power of two, h = %d isn't." % (curve.h)) |
|||
a &= ~(curve.h - 1) |
|||
|
|||
# Is the MSB of the curve always set to ensure constant runtime of the |
|||
# Montgomery ladder? |
|||
if curve.has_quirk(CurveQuirkEdDSASetPrivateKeyMSB): |
|||
bit = curve.n.bit_length() + 1 |
|||
a |= (1 << bit) |
|||
privkey = cls(a, curve) |
|||
privkey.set_seed(seed) |
|||
return privkey |
|||
|
|||
|
|||
class PrivKeyOpEDDSAEncode(object): |
|||
def eddsa_encode(self): |
|||
"""Performs serialization of a private key that is used for EdDSA.""" |
|||
return self.seed |
|||
|
|||
@classmethod |
|||
def eddsa_decode(cls, curve, encoded_privkey): |
|||
"""Performs decoding of a serialized private key as it is used for EdDSA.""" |
|||
return cls.eddsa_generate(curve, encoded_privkey) |
|||
|
|||
|
|||
class PrivKeyOpECDH(object): |
|||
def ecdh_compute(self, peer_pubkey): |
|||
"""Compute the shared secret point using our own private key and the |
|||
public key of our peer.""" |
|||
return self.scalar * peer_pubkey.point |
|||
|
|||
|
|||
class PrivKeyOpLoad(object): |
|||
@classmethod |
|||
def load_derdata(cls, derdata): |
|||
"""Loads an EC private key from a DER-encoded ASN.1 bytes object.""" |
|||
asn1 = parse_asn1_private_key(derdata) |
|||
private_key_scalar = Tools.bytestoint(asn1["privateKey"]) |
|||
curve = CurveDB().get_curve_from_asn1(asn1["parameters"]) |
|||
return cls(private_key_scalar, curve) |
|||
|
|||
@classmethod |
|||
def load_pem(cls, pemfilename): |
|||
"""Loads an EC private key from a PEM-encoded 'EC PRIVATE KEY' file.""" |
|||
return cls.load_derdata(Tools.load_pem_data(pemfilename, "EC PRIVATE KEY")) |
|||
|
|||
@classmethod |
|||
def load_der(cls, derfilename): |
|||
"""Loads an EC private key from a DER-encoded ASN.1 file.""" |
|||
with open(derfilename, "rb") as f: |
|||
data = f.read() |
|||
return cls.load_derdata(data) |
|||
@ -0,0 +1,166 @@ |
|||
# |
|||
# toyecc - A small Elliptic Curve Cryptography Demonstration. |
|||
# Copyright (C) 2011-2022 Johannes Bauer |
|||
# |
|||
# This file is part of toyecc. |
|||
# |
|||
# toyecc is free software; you can redistribute it and/or modify |
|||
# it under the terms of the GNU General Public License as published by |
|||
# the Free Software Foundation; this program is ONLY licensed under |
|||
# version 3 of the License, later versions are explicitly excluded. |
|||
# |
|||
# toyecc is distributed in the hope that it will be useful, |
|||
# but WITHOUT ANY WARRANTY; without even the implied warranty of |
|||
# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the |
|||
# GNU General Public License for more details. |
|||
# |
|||
# You should have received a copy of the GNU General Public License |
|||
# along with toyecc; if not, write to the Free Software |
|||
# Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA |
|||
# |
|||
# Johannes Bauer <JohannesBauer@gmx.de> |
|||
# |
|||
|
|||
import hashlib |
|||
|
|||
from .FieldElement import FieldElement |
|||
from .AffineCurvePoint import AffineCurvePoint |
|||
from .Random import secure_rand_int_between |
|||
from . import Tools |
|||
from .PrivKeyOps import PrivKeyOpLoad |
|||
from .ASN1 import parse_asn1_public_key |
|||
from .CurveDB import CurveDB |
|||
from .CurveQuirks import CurveQuirkSigningHashFunction |
|||
|
|||
class PubKeyOpECDSAExploitReusedNonce(object): |
|||
def ecdsa_exploit_reused_nonce(self, msg1, sig1, msg2, sig2): |
|||
"""Given two different messages msg1 and msg2 and their corresponding |
|||
signatures sig1, sig2, try to calculate the private key that was used |
|||
for signing if during signature generation no unique nonces were |
|||
used.""" |
|||
assert(isinstance(msg1, bytes)) |
|||
assert(isinstance(msg2, bytes)) |
|||
assert(msg1 != msg2) |
|||
assert(sig1.r == sig2.r) |
|||
|
|||
# Hash the messages |
|||
dig1 = hashlib.new(sig1.hashalg) |
|||
dig1.update(msg1) |
|||
dig1 = dig1.digest() |
|||
dig2 = hashlib.new(sig2.hashalg) |
|||
dig2.update(msg2) |
|||
dig2 = dig2.digest() |
|||
|
|||
# Calculate hashes of messages |
|||
e1 = Tools.ecdsa_msgdigest_to_int(dig1, self.point.curve.n) |
|||
e2 = Tools.ecdsa_msgdigest_to_int(dig2, self.point.curve.n) |
|||
|
|||
# Take them modulo n |
|||
e1 = FieldElement(e1, self.point.curve.n) |
|||
e2 = FieldElement(e2, self.point.curve.n) |
|||
|
|||
(s1, s2) = (FieldElement(sig1.s, self.point.curve.n), FieldElement(sig2.s, self.point.curve.n)) |
|||
r = sig1.r |
|||
|
|||
# Recover (supposedly) random nonce |
|||
nonce = (e1 - e2) // (s1 - s2) |
|||
|
|||
# Recover private key |
|||
priv = ((nonce * s1) - e1) // r |
|||
|
|||
return { "nonce": nonce, "privatekey": priv } |
|||
|
|||
|
|||
class PubKeyOpECDSAVerify(object): |
|||
def ecdsa_verify_hash(self, message_digest, signature): |
|||
"""Verify ECDSA signature over the hash of a message (the message |
|||
digest).""" |
|||
assert(isinstance(message_digest, bytes)) |
|||
assert(0 < signature.r < self.curve.n) |
|||
assert(0 < signature.s < self.curve.n) |
|||
|
|||
# Convert message digest to integer value |
|||
e = Tools.ecdsa_msgdigest_to_int(message_digest, self.curve.n) |
|||
|
|||
(r, s) = (signature.r, FieldElement(signature.s, self.curve.n)) |
|||
w = s.inverse() |
|||
u1 = int(e * w) |
|||
u2 = int(r * w) |
|||
|
|||
pt = (u1 * self.curve.G) + (u2 * self.point) |
|||
x1 = int(pt.x) % self.curve.n |
|||
return x1 == r |
|||
|
|||
def ecdsa_verify(self, message, signature): |
|||
"""Verify an ECDSA signature over a message.""" |
|||
assert(isinstance(message, bytes)) |
|||
digest_fnc = hashlib.new(signature.hashalg) |
|||
digest_fnc.update(message) |
|||
message_digest = digest_fnc.digest() |
|||
return self.ecdsa_verify_hash(message_digest, signature) |
|||
|
|||
|
|||
class PubKeyOpEDDSAVerify(object): |
|||
def eddsa_verify(self, message, signature): |
|||
"""Verify an EdDSA signature over a message.""" |
|||
if not self.curve.has_quirk(CurveQuirkSigningHashFunction): |
|||
raise Exception("Unable to determine EdDSA signature function.") |
|||
quirk = self.curve.get_quirk(CurveQuirkSigningHashFunction) |
|||
h = Tools.bytestoint_le(quirk.hashdata(signature.R.eddsa_encode() + self.point.eddsa_encode() + message)) |
|||
return (signature.s * self.curve.G) == signature.R + (h * self.point) |
|||
|
|||
|
|||
class PubKeyOpEDDSAEncode(object): |
|||
def eddsa_encode(self): |
|||
"""Encodes a EdDSA-encoded public key to its serialized (bytes) |
|||
form.""" |
|||
return self.point.eddsa_encode() |
|||
|
|||
@classmethod |
|||
def eddsa_decode(cls, curve, encoded_pubkey): |
|||
"""Decodes a EdDSA-encoded public key from its serialized (bytes) |
|||
form.""" |
|||
pubkey = AffineCurvePoint.eddsa_decode(curve, encoded_pubkey) |
|||
return cls(pubkey) |
|||
|
|||
class PubKeyOpECIESEncrypt(object): |
|||
def ecies_encrypt(self, r = None): |
|||
"""Generates a shared secret which can be used to symetrically encrypt |
|||
data that only the holder of the corresponding private key can read. |
|||
The output are two points, R and S: R is the public point that is |
|||
transmitted together with the message while S is the point which |
|||
resembles the shared secret. The receiver can use R together with her |
|||
private key to reconstruct S. A random nonce r can be supplied for this |
|||
function. If it isn't supplied, it is randomly chosen.""" |
|||
|
|||
# Chose a random number |
|||
if r is None: |
|||
r = secure_rand_int_between(1, self.curve.n - 1) |
|||
|
|||
R = r * self.curve.G |
|||
S = r * self.point |
|||
|
|||
# Return the publicly transmitted R and the symmetric key S |
|||
return { "R": R, "S": S } |
|||
|
|||
|
|||
class PubKeyOpLoad(object): |
|||
@classmethod |
|||
def load_derdata(cls, derdata): |
|||
"""Loads an EC public key from a DER-encoded ASN.1 bytes object.""" |
|||
asn1 = parse_asn1_public_key(derdata) |
|||
curve = CurveDB().get_curve_from_asn1(asn1["algorithm"]["parameters"]) |
|||
point = AffineCurvePoint.deserialize_uncompressed(Tools.bits_to_bytes(asn1["subjectPublicKey"]), curve) |
|||
return cls(point) |
|||
|
|||
@classmethod |
|||
def load_pem(cls, pemfilename): |
|||
"""Loads an EC public key from a PEM-encoded 'PUBLIC KEY' file.""" |
|||
return cls.load_derdata(Tools.load_pem_data(pemfilename, "PUBLIC KEY")) |
|||
|
|||
@classmethod |
|||
def load_der(cls, derfilename): |
|||
"""Loads an EC public key from a DER-encoded ASN.1 file.""" |
|||
with open(derfilename, "rb") as f: |
|||
data = f.read() |
|||
return cls.load_derdata(data) |
|||
@ -0,0 +1,48 @@ |
|||
# |
|||
# toyecc - A small Elliptic Curve Cryptography Demonstration. |
|||
# Copyright (C) 2011-2022 Johannes Bauer |
|||
# |
|||
# This file is part of toyecc. |
|||
# |
|||
# toyecc is free software; you can redistribute it and/or modify |
|||
# it under the terms of the GNU General Public License as published by |
|||
# the Free Software Foundation; this program is ONLY licensed under |
|||
# version 3 of the License, later versions are explicitly excluded. |
|||
# |
|||
# toyecc is distributed in the hope that it will be useful, |
|||
# but WITHOUT ANY WARRANTY; without even the implied warranty of |
|||
# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the |
|||
# GNU General Public License for more details. |
|||
# |
|||
# You should have received a copy of the GNU General Public License |
|||
# along with toyecc; if not, write to the Free Software |
|||
# Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA |
|||
# |
|||
# Johannes Bauer <JohannesBauer@gmx.de> |
|||
# |
|||
|
|||
def secure_rand(length): |
|||
import os |
|||
"""Returns a secure random bytes() object of the length 'length' bytes.""" |
|||
data = os.urandom(length) |
|||
assert(len(data) == length) |
|||
return data |
|||
|
|||
|
|||
def secure_rand_int(max_value): |
|||
"""Yields a value 0 <= return < maxvalue.""" |
|||
assert(max_value >= 2) |
|||
bytecnt = ((max_value - 1).bit_length() + 7) // 8 |
|||
max_bin_value = 256 ** bytecnt |
|||
wholecnt = max_bin_value // max_value |
|||
cutoff = wholecnt * max_value |
|||
while True: |
|||
rnd = sum((value << (8 * bytepos)) for (bytepos, value) in enumerate(secure_rand(bytecnt))) |
|||
if rnd < cutoff: |
|||
break |
|||
return rnd % max_value |
|||
|
|||
def secure_rand_int_between(min_value, max_value): |
|||
"""Yields a random number which goes from min_value (inclusive) to |
|||
max_value (inclusive).""" |
|||
return secure_rand_int(max_value - min_value + 1) + min_value |
|||
@ -0,0 +1,203 @@ |
|||
# |
|||
# toyecc - A small Elliptic Curve Cryptography Demonstration. |
|||
# Copyright (C) 2011-2016 Johannes Bauer |
|||
# |
|||
# This file is part of toyecc. |
|||
# |
|||
# toyecc is free software; you can redistribute it and/or modify |
|||
# it under the terms of the GNU General Public License as published by |
|||
# the Free Software Foundation; this program is ONLY licensed under |
|||
# version 3 of the License, later versions are explicitly excluded. |
|||
# |
|||
# toyecc is distributed in the hope that it will be useful, |
|||
# but WITHOUT ANY WARRANTY; without even the implied warranty of |
|||
# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the |
|||
# GNU General Public License for more details. |
|||
# |
|||
# You should have received a copy of the GNU General Public License |
|||
# along with toyecc; if not, write to the Free Software |
|||
# Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA |
|||
# |
|||
# Johannes Bauer <JohannesBauer@gmx.de> |
|||
# |
|||
|
|||
import collections |
|||
from .FieldElement import FieldElement |
|||
from .AffineCurvePoint import AffineCurvePoint |
|||
from .EllipticCurve import EllipticCurve |
|||
from .DocInherit import doc_inherit |
|||
from .CurveOps import CurveOpIsomorphism, CurveOpExportSage |
|||
|
|||
_ShortWeierstrassCurveDomainParameters = collections.namedtuple("ShortWeierstrassCurveDomainParameters", [ "curvetype", "a", "b", "p", "n", "h", "G" ]) |
|||
|
|||
class ShortWeierstrassCurve(EllipticCurve, CurveOpIsomorphism, CurveOpExportSage): |
|||
"""Represents an elliptic curve over a finite field F_P that satisfies the |
|||
short Weierstrass equation y^2 = x^3 + ax + b.""" |
|||
pretty_name = "Short Weierstrass" |
|||
|
|||
def __init__(self, a, b, p, n, h, Gx, Gy, **kwargs): |
|||
"""Create an elliptic curve given the equation coefficients a and b, |
|||
the curve modulus p, the order of the curve n, the cofactor of the |
|||
curve h and the generator point G's X and Y coordinates in affine |
|||
representation, Gx and Gy.""" |
|||
EllipticCurve.__init__(self, p, n, h, Gx, Gy, **kwargs) |
|||
assert(isinstance(a, int)) # Curve coefficent A |
|||
assert(isinstance(b, int)) # Curve coefficent B |
|||
self._a = FieldElement(a, p) |
|||
self._b = FieldElement(b, p) |
|||
self._name = kwargs.get("name") |
|||
|
|||
# Check that the curve is not singular |
|||
assert((4 * (self.a ** 3)) + (27 * (self.b ** 2)) != 0) |
|||
|
|||
if self._G is not None: |
|||
# Check that the generator G is on the curve |
|||
assert(self._G.oncurve()) |
|||
|
|||
if self.n is not None: |
|||
# Check that the generator G is of curve order if a order was |
|||
# passed as well |
|||
assert((self.n * self.G).is_neutral) |
|||
|
|||
@classmethod |
|||
def init_rawcurve(cls, a, b, p): |
|||
"""Returns a raw curve which has an undiscovered amount of points |
|||
#E(F_p) (i.e. the domain parameters n and h are not set). This function |
|||
can be used to create a curve which is later completed by counting |
|||
#E(F_p) using Schoof's algorithm.""" |
|||
return cls(a = a, b = b, p = p, n = None, h = None, Gx = None, Gy = None) |
|||
|
|||
@property |
|||
def is_anomalous(self): |
|||
"""Returns if the curve is anomalous, i.e. if #F(p) == p. If this is |
|||
the case then there is an efficient method to solve the ECDLP. |
|||
Therefore the curve is not suitable for cryptographic use.""" |
|||
return self.jinv in [ 0, 1728 ] |
|||
|
|||
@property |
|||
@doc_inherit(EllipticCurve) |
|||
def domainparams(self): |
|||
return _ShortWeierstrassCurveDomainParameters(curvetype = self.curvetype, a = self.a, b = self.b, p = self.p, n = self.n, h = self.h, G = self.G) |
|||
|
|||
@property |
|||
@doc_inherit(EllipticCurve) |
|||
def curvetype(self): |
|||
return "shortweierstrass" |
|||
|
|||
@property |
|||
def is_koblitz(self): |
|||
"""Returns whether the curve allows for efficient computation of a map |
|||
\phi in the field (i.e. that the curve is commonly known as a 'Koblitz |
|||
Curve'). This corresponds to examples 3 and 4 of the paper "Faster |
|||
Point Multiplication on Elliptic Curves with Efficient Endomorphisms" |
|||
by Gallant, Lambert and Vanstone.""" |
|||
return ((self.b == 0) and ((self.p % 4) == 1)) or ((self.a == 0) and ((self.p % 3) == 1)) |
|||
|
|||
@property |
|||
def security_bit_estimate(self): |
|||
"""Returns the bit security estimate of the curve. Subtracts four bits |
|||
security margin for Koblitz curves.""" |
|||
security_bits = self.n.bit_length() // 2 |
|||
if self.is_koblitz: |
|||
security_bits -= 4 |
|||
return security_bits |
|||
|
|||
@property |
|||
@doc_inherit(EllipticCurve) |
|||
def prettyname(self): |
|||
name = [ ] |
|||
name.append(self.pretty_name) |
|||
if self.is_koblitz: |
|||
name.append("(Koblitz)") |
|||
return " ".join(name) |
|||
|
|||
@property |
|||
def a(self): |
|||
"""Returns the coefficient a of the curve equation y^2 = x^3 + ax + b.""" |
|||
return self._a |
|||
|
|||
@property |
|||
def b(self): |
|||
"""Returns the coefficient b of the curve equation y^2 = x^3 + ax + b.""" |
|||
return self._b |
|||
|
|||
@property |
|||
def jinv(self): |
|||
"""Returns the j-invariant of the curve, i.e. 1728 * 4 * a^3 / (4 * a^3 |
|||
+ 27 * b^2).""" |
|||
return 1728 * (4 * self.a ** 3) // ((4 * self.a ** 3) + (27 * self.b ** 2)) |
|||
|
|||
def getpointwithx(self, x): |
|||
"""Returns a tuple of two points which fulfill the curve equation or |
|||
None if not such points exist.""" |
|||
assert(isinstance(x, int)) |
|||
yy = ((FieldElement(x, self._p) ** 3) + (self._a * x) + self._b) |
|||
y = yy.sqrt() |
|||
if y: |
|||
return (AffineCurvePoint(x, int(y[0]), self), AffineCurvePoint(x, int(y[1]), self)) |
|||
else: |
|||
return None |
|||
|
|||
@doc_inherit(EllipticCurve) |
|||
def oncurve(self, P): |
|||
return P.is_neutral or ((P.y ** 2) == (P.x ** 3) + (self.a * P.x) + self.b) |
|||
|
|||
@doc_inherit(EllipticCurve) |
|||
def point_conjugate(self, P): |
|||
return AffineCurvePoint(int(P.x), int(-P.y), self) |
|||
|
|||
@doc_inherit(EllipticCurve) |
|||
def point_addition(self, P, Q): |
|||
if P.is_neutral: |
|||
# P is at infinity, O + Q = Q |
|||
result = Q |
|||
elif Q.is_neutral: |
|||
# Q is at infinity, P + O = P |
|||
result = P |
|||
elif P == -Q: |
|||
# P == -Q, return O (point at infinity) |
|||
result = self.neutral() |
|||
elif P == Q: |
|||
# P == Q, point doubling |
|||
s = ((3 * P.x ** 2) + self.a) // (2 * P.y) |
|||
newx = s * s - (2 * P.x) |
|||
newy = s * (P.x - newx) - P.y |
|||
result = AffineCurvePoint(int(newx), int(newy), self) |
|||
else: |
|||
# P != Q, point addition |
|||
s = (P.y - Q.y) // (P.x - Q.x) |
|||
newx = (s ** 2) - P.x - Q.x |
|||
newy = s * (P.x - newx) - P.y |
|||
result = AffineCurvePoint(int(newx), int(newy), self) |
|||
return result |
|||
|
|||
@doc_inherit(EllipticCurve) |
|||
def compress(self, P): |
|||
return (int(P.x), int(P.y) % 2) |
|||
|
|||
@doc_inherit(EllipticCurve) |
|||
def uncompress(self, compressed): |
|||
(x, ybit) = compressed |
|||
x = FieldElement(x, self.p) |
|||
alpha = (x ** 3) + (self.a * x) + self.b |
|||
(beta1, beta2) = alpha.sqrt() |
|||
if (int(beta1) % 2) == ybit: |
|||
y = beta1 |
|||
else: |
|||
y = beta2 |
|||
return AffineCurvePoint(int(x), int(y), self) |
|||
|
|||
@doc_inherit(EllipticCurve) |
|||
def enumerate_points(self): |
|||
yield self.neutral() |
|||
for x in range(self.p): |
|||
points = self.getpointwithx(x) |
|||
if points is not None: |
|||
yield points[0] |
|||
yield points[1] |
|||
|
|||
def __str__(self): |
|||
if self.hasname: |
|||
return "ShortWeierstrassCurve<%s>" % (self.name) |
|||
else: |
|||
return "ShortWeierstrassCurve<y^2 = x^3 + 0x%x x + 0x%x mod 0x%x>" % (int(self.a), int(self.b), int(self.p)) |
|||
@ -0,0 +1,69 @@ |
|||
#!/usr/bin/python3 |
|||
# |
|||
# Singleton - Singleton decorator, taken from PEP318 |
|||
# Copyright (C) 2011-2022 Johannes Bauer |
|||
# |
|||
# This file is part of jpycommon. |
|||
# |
|||
# jpycommon is free software; you can redistribute it and/or modify |
|||
# it under the terms of the GNU General Public License as published by |
|||
# the Free Software Foundation; this program is ONLY licensed under |
|||
# version 3 of the License, later versions are explicitly excluded. |
|||
# |
|||
# jpycommon is distributed in the hope that it will be useful, |
|||
# but WITHOUT ANY WARRANTY; without even the implied warranty of |
|||
# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the |
|||
# GNU General Public License for more details. |
|||
# |
|||
# You should have received a copy of the GNU General Public License |
|||
# along with jpycommon; if not, write to the Free Software |
|||
# Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA |
|||
# |
|||
# Johannes Bauer <JohannesBauer@gmx.de> |
|||
# |
|||
# File UUID a24a2230-7bd4-4737-98ab-8aae62d1bd57 |
|||
|
|||
def singleton(cls): |
|||
class InnerClass(cls): |
|||
_instance = None |
|||
|
|||
def __new__(cls, *args, **kwargs): |
|||
if InnerClass._instance is None: |
|||
InnerClass._instance = super(InnerClass, cls).__new__(cls, *args, **kwargs) |
|||
InnerClass._instance._initialized = False |
|||
return InnerClass._instance |
|||
|
|||
def __init__(self, *args, **kwargs): |
|||
if self._initialized: |
|||
return |
|||
super(InnerClass, self).__init__(*args, **kwargs) |
|||
self._initialized = True |
|||
|
|||
InnerClass.__name__ = cls.__name__ |
|||
return InnerClass |
|||
|
|||
|
|||
if __name__ == "__main__": |
|||
print("start") |
|||
|
|||
@singleton |
|||
class FooSingleton(): |
|||
_barkoo = -1 |
|||
|
|||
def __init__(self): |
|||
print("init called") |
|||
|
|||
def getid(self): |
|||
return id(self) * FooSingleton._barkoo |
|||
|
|||
print("pre init") |
|||
x = FooSingleton() |
|||
print(x, x.getid()) |
|||
|
|||
y = FooSingleton() |
|||
print(y, y.getid()) |
|||
|
|||
z = FooSingleton() |
|||
print(z, z.getid()) |
|||
|
|||
assert(x is y) |
|||
@ -0,0 +1,98 @@ |
|||
# |
|||
# toyecc - A small Elliptic Curve Cryptography Demonstration. |
|||
# Copyright (C) 2011-2022 Johannes Bauer |
|||
# |
|||
# This file is part of toyecc. |
|||
# |
|||
# toyecc is free software; you can redistribute it and/or modify |
|||
# it under the terms of the GNU General Public License as published by |
|||
# the Free Software Foundation; this program is ONLY licensed under |
|||
# version 3 of the License, later versions are explicitly excluded. |
|||
# |
|||
# toyecc is distributed in the hope that it will be useful, |
|||
# but WITHOUT ANY WARRANTY; without even the implied warranty of |
|||
# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the |
|||
# GNU General Public License for more details. |
|||
# |
|||
# You should have received a copy of the GNU General Public License |
|||
# along with toyecc; if not, write to the Free Software |
|||
# Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA |
|||
# |
|||
# Johannes Bauer <JohannesBauer@gmx.de> |
|||
# |
|||
|
|||
import hashlib |
|||
import base64 |
|||
import inspect |
|||
|
|||
def bytestoint_le(data): |
|||
"""Converts given bytes to a little-endian integer value.""" |
|||
return sum(value << (8 * index) for (index, value) in enumerate(data)) |
|||
|
|||
def inttobytes_le(value, length): |
|||
"""Converts a little-endian integer value into a bytes object.""" |
|||
return bytes((value >> (8 * i)) & 0xff for i in range(length)) |
|||
|
|||
def bytestoint(data): |
|||
"""Converts given bytes to a big-endian integer value.""" |
|||
return bytestoint_le(reversed(data)) |
|||
|
|||
def inttobytes(value, length): |
|||
"""Converts a big-endian integer value into a bytes object.""" |
|||
return bytes((value >> (8 * i)) & 0xff for i in reversed(range(length))) |
|||
|
|||
def bits_to_bytes(bitarray): |
|||
"""Converts a tuple of bits (e.g. a ASN.1 BitString) to a bytes object. |
|||
Only works when number of bits is a multiple of 8.""" |
|||
|
|||
def bit_word_to_value(word): |
|||
assert(len(word) == 8) |
|||
return sum(value << i for (i, value) in enumerate(reversed(word))) |
|||
|
|||
assert((len(bitarray) % 8) == 0) |
|||
return bytes(bit_word_to_value(bitarray[i : i + 8]) for i in range(0, len(bitarray), 8)) |
|||
|
|||
def ecdsa_msgdigest_to_int(message_digest, curveorder): |
|||
"""Performs truncation of a message digest to the bitlength of the curve |
|||
order.""" |
|||
# Convert message digest to integer value |
|||
e = bytestoint(message_digest) |
|||
|
|||
# Truncate hash value if necessary |
|||
msg_digest_bits = 8 * len(message_digest) |
|||
if msg_digest_bits > curveorder.bit_length(): |
|||
shift = msg_digest_bits - curveorder.bit_length() |
|||
e >>= shift |
|||
|
|||
return e |
|||
|
|||
def load_pem_data(filename, specifier): |
|||
"""Loads the PEM payload, designated with a BEGIN and END specifier, from a |
|||
file given by its filename.""" |
|||
data = None |
|||
with open(filename, "r") as f: |
|||
spec_begin = "-----BEGIN " + specifier + "-----" |
|||
spec_end = "-----END " + specifier + "-----" |
|||
for line in f: |
|||
line = line.rstrip() |
|||
if (data is None) and (line == spec_begin): |
|||
data = [ ] |
|||
elif (data is not None) and (line == spec_end): |
|||
break |
|||
elif data is not None: |
|||
data.append(line) |
|||
if data is None: |
|||
raise Exception("Trying to parse PEM file with specifier '%s', but no such block in file found." % (specifier)) |
|||
data = base64.b64decode("".join(data).encode("utf-8")) |
|||
return data |
|||
|
|||
def is_power_of_two(value): |
|||
"""Returns True if the given value is a positive power of two, False |
|||
otherwise.""" |
|||
while value > 0: |
|||
if value == 1: |
|||
return True |
|||
elif (value & 1) == 1: |
|||
return False |
|||
value >>= 1 |
|||
return False |
|||
@ -0,0 +1,171 @@ |
|||
# |
|||
# toyecc - A small Elliptic Curve Cryptography Demonstration. |
|||
# Copyright (C) 2011-2022 Johannes Bauer |
|||
# |
|||
# This file is part of toyecc. |
|||
# |
|||
# toyecc is free software; you can redistribute it and/or modify |
|||
# it under the terms of the GNU General Public License as published by |
|||
# the Free Software Foundation; this program is ONLY licensed under |
|||
# version 3 of the License, later versions are explicitly excluded. |
|||
# |
|||
# toyecc is distributed in the hope that it will be useful, |
|||
# but WITHOUT ANY WARRANTY; without even the implied warranty of |
|||
# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the |
|||
# GNU General Public License for more details. |
|||
# |
|||
# You should have received a copy of the GNU General Public License |
|||
# along with toyecc; if not, write to the Free Software |
|||
# Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA |
|||
# |
|||
# Johannes Bauer <JohannesBauer@gmx.de> |
|||
# |
|||
|
|||
import collections |
|||
from .FieldElement import FieldElement |
|||
from .AffineCurvePoint import AffineCurvePoint |
|||
from .EllipticCurve import EllipticCurve |
|||
from .DocInherit import doc_inherit |
|||
import toyecc.MontgomeryCurve |
|||
|
|||
_TwistedEdwardsCurveDomainParameters = collections.namedtuple("TwistedEdwardsCurveDomainParameters", [ "curvetype", "a", "d", "p", "n", "G" ]) |
|||
|
|||
class TwistedEdwardsCurve(EllipticCurve): |
|||
"""Represents an elliptic curve over a finite field F_P that satisfies the |
|||
Twisted Edwards equation a x^2 + y^2 = 1 + d x^2 y^2.""" |
|||
pretty_name = "Twisted Edwards" |
|||
|
|||
def __init__(self, a, d, p, n, h, Gx, Gy, **kwargs): |
|||
"""Create an elliptic Twisted Edwards curve given the equation |
|||
coefficients a and d, the curve field's modulus p, the order of the |
|||
curve n and the generator point G's X and Y coordinates in affine |
|||
representation, Gx and Gy.""" |
|||
EllipticCurve.__init__(self, p, n, h, Gx, Gy, **kwargs) |
|||
assert(isinstance(a, int)) # Curve coefficent A |
|||
assert(isinstance(d, int)) # Curve coefficent D |
|||
self._a = FieldElement(a, p) |
|||
self._d = FieldElement(d, p) |
|||
self._name = kwargs.get("name") |
|||
|
|||
# Check that the curve is not singular |
|||
assert(self.d * (1 - self.d) != 0) |
|||
|
|||
if self._G is not None: |
|||
# Check that the generator G is on the curve |
|||
assert(self._G.oncurve()) |
|||
|
|||
# Check that the generator G is of curve order |
|||
assert((self.n * self.G).is_neutral) |
|||
|
|||
@property |
|||
@doc_inherit(EllipticCurve) |
|||
def domainparams(self): |
|||
return _TwistedEdwardsCurveDomainParameters(curvetype = self.curvetype, a = self.a, d = self.d, p = self.p, n = self.n, G = self.G) |
|||
|
|||
@property |
|||
@doc_inherit(EllipticCurve) |
|||
def curvetype(self): |
|||
return "twistededwards" |
|||
|
|||
@property |
|||
def a(self): |
|||
"""Returns the coefficient a of the curve equation a x^2 + y^2 = 1 + |
|||
d x^2 y^2.""" |
|||
return self._a |
|||
|
|||
@property |
|||
def d(self): |
|||
"""Returns the coefficient d of the curve equation a x^2 + y^2 = 1 + |
|||
d x^2 y^2.""" |
|||
return self._d |
|||
|
|||
@property |
|||
def B(self): |
|||
"""Returns the length of the curve's field modulus in bits plus one.""" |
|||
return self._p.bit_length() + 1 |
|||
|
|||
@property |
|||
def is_complete(self): |
|||
"""Returns if the twisted Edwards curve is complete. This is the case |
|||
exactly when d is a quadratic non-residue modulo p.""" |
|||
return self.d.is_qnr |
|||
|
|||
@doc_inherit(EllipticCurve) |
|||
def neutral(self): |
|||
return AffineCurvePoint(0, 1, self) |
|||
|
|||
@doc_inherit(EllipticCurve) |
|||
def is_neutral(self, P): |
|||
return (P.x == 0) and (P.y == 1) |
|||
|
|||
@doc_inherit(EllipticCurve) |
|||
def oncurve(self, P): |
|||
return (self.a * P.x ** 2) + P.y ** 2 == 1 + self.d * P.x ** 2 * P.y ** 2 |
|||
|
|||
@doc_inherit(EllipticCurve) |
|||
def point_conjugate(self, P): |
|||
return AffineCurvePoint(int(-P.x), int(P.y), self) |
|||
|
|||
@doc_inherit(EllipticCurve) |
|||
def point_addition(self, P, Q): |
|||
x = (P.x * Q.y + Q.x * P.y) // (1 + self.d * P.x * Q.x * P.y * Q.y) |
|||
y = (P.y * Q.y - self.a * P.x * Q.x) // (1 - self.d * P.x * Q.x * P.y * Q.y) |
|||
return AffineCurvePoint(int(x), int(y), self) |
|||
|
|||
def to_montgomery(self, b = None): |
|||
"""Converts the twisted Edwards curve domain parameters to Montgomery |
|||
domain parameters. For this conversion, b can be chosen semi-freely. |
|||
If the native b coefficient is a quadratic residue modulo p, then the |
|||
freely chosen b value must also be. If it is a quadratic non-residue, |
|||
then so must be the surrogate b coefficient. If b is omitted, the |
|||
native b value is used. The generator point of the twisted Edwards |
|||
curve is also converted to Montgomery form. For this conversion, |
|||
there's an invariant (one of two possible outcomes). An arbitrary |
|||
bijection is used for this.""" |
|||
assert((b is None) or isinstance(b, int)) |
|||
|
|||
# Calculate the native montgomery coefficents a, b first |
|||
a = 2 * (self.a + self.d) // (self.a - self.d) |
|||
native_b = 4 // (self.a - self.d) |
|||
if b is None: |
|||
b = native_b |
|||
else: |
|||
# If a b value was supplied, make sure is is either a QR or QNR mod |
|||
# p, depending on what the native b value was |
|||
b = FieldElement(b, self.p) |
|||
if native_b.is_qr != b.is_qr: |
|||
raise Exception("The b coefficient of the resulting curve must be a quadratic %s modulo p, %s is not." % ([ "non-residue", "residue" ][native_b.is_qr], str(b))) |
|||
|
|||
# Generate the raw curve without a generator yet |
|||
raw_curve = toyecc.MontgomeryCurve.MontgomeryCurve( |
|||
a = int(a), |
|||
b = int(b), |
|||
p = self.p, |
|||
n = self.n, |
|||
h = self.h, |
|||
Gx = None, |
|||
Gy = None, |
|||
) |
|||
|
|||
# Then convert the original generator point using the raw curve to |
|||
# yield a birationally equivalent generator point |
|||
G_m = self.G.convert(raw_curve) |
|||
|
|||
# And create the curve again, setting this generator |
|||
montgomery_curve = toyecc.MontgomeryCurve.MontgomeryCurve( |
|||
a = int(a), |
|||
b = int(b), |
|||
p = self.p, |
|||
n = self.n, |
|||
h = self.h, |
|||
Gx = int(G_m.x), |
|||
Gy = int(G_m.y), |
|||
) |
|||
|
|||
return montgomery_curve |
|||
|
|||
def __str__(self): |
|||
if self.hasname: |
|||
return "TwistedEdwardsCurve<%s>" % (self.name) |
|||
else: |
|||
return "TwistedEdwardsCurve<0x%x x^2 + y^2 = 1 + 0x%x x^2 y^2 mod 0x%x>" % (int(self.a), int(self.d), int(self.p)) |
|||
@ -0,0 +1,95 @@ |
|||
"""toyecc - Elliptic Curve Cryptography demonstration library |
|||
|
|||
toyecc is a library that is supposed to demonstrate and teach how Elliptic |
|||
Curve Cryptography (ECC) works. It is implemented in pure Python and neither |
|||
aims to be feature-complete not side-channel resistant nor secure in any way. |
|||
Please keep this in mind when using toyecc. Certain algorithms have been |
|||
deliberately implemented exactly without precautions against side-channel |
|||
attacks in order to cleanly demonstrate the concepts. |
|||
|
|||
There is a curve database included in toyecc which already knows lots of |
|||
interesting elliptic curves by name: |
|||
|
|||
import toyecc |
|||
curve = toyecc.getcurvebyname("secp112r1") |
|||
print(curve) |
|||
ShortWeierstrassCurve<secp112r1> |
|||
|
|||
On this curve you can now create a public/private keypair: |
|||
|
|||
privkey = toyecc.ECPrivateKey.generate(curve) |
|||
print(privkey) |
|||
PrivateKey<d = 0x89fb9821aa5154c9934b3e0268ef> |
|||
print(privkey.pubkey) |
|||
PublicKey<(0x69976db41f5e487928463b9f8a38, 0xda1fdba3de89c58683bd2d635430)> |
|||
|
|||
You can also regenerate this keypair later on if you wish to remain it static |
|||
and not change with every invocation: |
|||
|
|||
privkey = toyecc.ECPrivateKey(0x89fb9821aa5154c9934b3e0268ef, curve) |
|||
print(privkey) |
|||
PrivateKey<d = 0x89fb9821aa5154c9934b3e0268ef> |
|||
|
|||
You can use this keypair to perform actions like ECDSA signing: |
|||
|
|||
signature = privkey.ecdsa_sign(b"Foobar", "sha1") |
|||
print(signature) |
|||
ECDSASignature(hashalg='sha1', r=1762251013383878369191057972852867, s=3691758261134002001156831324480002) |
|||
|
|||
If you want to recreate a public key anew, you can also do so by first creating |
|||
the public point: |
|||
|
|||
pubkeypt = toyecc.AffineCurvePoint(0x69976db41f5e487928463b9f8a38, 0xda1fdba3de89c58683bd2d635430, curve) |
|||
print(pubkeypt) |
|||
(0x69976db41f5e487928463b9f8a38, 0xda1fdba3de89c58683bd2d635430) |
|||
|
|||
Then you can just wrap the point in a ECPublicKey object to have access to |
|||
methods like ECDSA verification and such: |
|||
|
|||
pubkey = toyecc.ECPublicKey(pubkeypt) |
|||
print(pubkey) |
|||
PublicKey<(0x69976db41f5e487928463b9f8a38, 0xda1fdba3de89c58683bd2d635430)> |
|||
|
|||
Lastly, you can verify the signature you created earlier: |
|||
|
|||
pubkey.ecdsa_verify(b"Foobar", signature) |
|||
True |
|||
|
|||
And change the message so the signature would become invalid: |
|||
|
|||
pubkey.ecdsa_verify(b"Barfoo", signature) |
|||
False |
|||
""" |
|||
|
|||
# |
|||
# toyecc - A small Elliptic Curve Cryptography Demonstration. |
|||
# Copyright (C) 2011-2022 Johannes Bauer |
|||
# |
|||
# This file is part of toyecc. |
|||
# |
|||
# toyecc is free software; you can redistribute it and/or modify |
|||
# it under the terms of the GNU General Public License as published by |
|||
# the Free Software Foundation; this program is ONLY licensed under |
|||
# version 3 of the License, later versions are explicitly excluded. |
|||
# |
|||
# toyecc is distributed in the hope that it will be useful, |
|||
# but WITHOUT ANY WARRANTY; without even the implied warranty of |
|||
# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the |
|||
# GNU General Public License for more details. |
|||
# |
|||
# You should have received a copy of the GNU General Public License |
|||
# along with toyecc; if not, write to the Free Software |
|||
# Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA |
|||
# |
|||
# Johannes Bauer <JohannesBauer@gmx.de> |
|||
# |
|||
|
|||
from .FieldElement import FieldElement |
|||
from .AffineCurvePoint import AffineCurvePoint |
|||
from .CurveDB import getcurvedb, getcurveentry, getcurvebyname, getcurvenames |
|||
from .ECPrivateKey import ECPrivateKey |
|||
from .ECPublicKey import ECPublicKey |
|||
from .ShortWeierstrassCurve import ShortWeierstrassCurve |
|||
from .CRT import CRT |
|||
|
|||
VERSION = "0.0.9rc0" |
|||
Write
Preview
Loading…
Cancel
Save
Reference in new issue