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