Amir Taaki
13 years ago
22 changed files with 172 additions and 3408 deletions
@ -1,656 +0,0 @@ |
|||
#!/usr/bin/python |
|||
# |
|||
# aes.py: implements AES - Advanced Encryption Standard |
|||
# from the SlowAES project, http://code.google.com/p/slowaes/ |
|||
# |
|||
# Copyright (c) 2008 Josh Davis ( http://www.josh-davis.org ), |
|||
# Alex Martelli ( http://www.aleax.it ) |
|||
# |
|||
# Ported from C code written by Laurent Haan ( http://www.progressive-coding.com ) |
|||
# |
|||
# Licensed under the Apache License, Version 2.0 |
|||
# http://www.apache.org/licenses/ |
|||
# |
|||
import os |
|||
import sys |
|||
import math |
|||
|
|||
def append_PKCS7_padding(s): |
|||
"""return s padded to a multiple of 16-bytes by PKCS7 padding""" |
|||
numpads = 16 - (len(s)%16) |
|||
return s + numpads*chr(numpads) |
|||
|
|||
def strip_PKCS7_padding(s): |
|||
"""return s stripped of PKCS7 padding""" |
|||
if len(s)%16 or not s: |
|||
raise ValueError("String of len %d can't be PCKS7-padded" % len(s)) |
|||
numpads = ord(s[-1]) |
|||
if numpads > 16: |
|||
raise ValueError("String ending with %r can't be PCKS7-padded" % s[-1]) |
|||
return s[:-numpads] |
|||
|
|||
class AES(object): |
|||
# valid key sizes |
|||
keySize = dict(SIZE_128=16, SIZE_192=24, SIZE_256=32) |
|||
|
|||
# Rijndael S-box |
|||
sbox = [0x63, 0x7c, 0x77, 0x7b, 0xf2, 0x6b, 0x6f, 0xc5, 0x30, 0x01, 0x67, |
|||
0x2b, 0xfe, 0xd7, 0xab, 0x76, 0xca, 0x82, 0xc9, 0x7d, 0xfa, 0x59, |
|||
0x47, 0xf0, 0xad, 0xd4, 0xa2, 0xaf, 0x9c, 0xa4, 0x72, 0xc0, 0xb7, |
|||
0xfd, 0x93, 0x26, 0x36, 0x3f, 0xf7, 0xcc, 0x34, 0xa5, 0xe5, 0xf1, |
|||
0x71, 0xd8, 0x31, 0x15, 0x04, 0xc7, 0x23, 0xc3, 0x18, 0x96, 0x05, |
|||
0x9a, 0x07, 0x12, 0x80, 0xe2, 0xeb, 0x27, 0xb2, 0x75, 0x09, 0x83, |
|||
0x2c, 0x1a, 0x1b, 0x6e, 0x5a, 0xa0, 0x52, 0x3b, 0xd6, 0xb3, 0x29, |
|||
0xe3, 0x2f, 0x84, 0x53, 0xd1, 0x00, 0xed, 0x20, 0xfc, 0xb1, 0x5b, |
|||
0x6a, 0xcb, 0xbe, 0x39, 0x4a, 0x4c, 0x58, 0xcf, 0xd0, 0xef, 0xaa, |
|||
0xfb, 0x43, 0x4d, 0x33, 0x85, 0x45, 0xf9, 0x02, 0x7f, 0x50, 0x3c, |
|||
0x9f, 0xa8, 0x51, 0xa3, 0x40, 0x8f, 0x92, 0x9d, 0x38, 0xf5, 0xbc, |
|||
0xb6, 0xda, 0x21, 0x10, 0xff, 0xf3, 0xd2, 0xcd, 0x0c, 0x13, 0xec, |
|||
0x5f, 0x97, 0x44, 0x17, 0xc4, 0xa7, 0x7e, 0x3d, 0x64, 0x5d, 0x19, |
|||
0x73, 0x60, 0x81, 0x4f, 0xdc, 0x22, 0x2a, 0x90, 0x88, 0x46, 0xee, |
|||
0xb8, 0x14, 0xde, 0x5e, 0x0b, 0xdb, 0xe0, 0x32, 0x3a, 0x0a, 0x49, |
|||
0x06, 0x24, 0x5c, 0xc2, 0xd3, 0xac, 0x62, 0x91, 0x95, 0xe4, 0x79, |
|||
0xe7, 0xc8, 0x37, 0x6d, 0x8d, 0xd5, 0x4e, 0xa9, 0x6c, 0x56, 0xf4, |
|||
0xea, 0x65, 0x7a, 0xae, 0x08, 0xba, 0x78, 0x25, 0x2e, 0x1c, 0xa6, |
|||
0xb4, 0xc6, 0xe8, 0xdd, 0x74, 0x1f, 0x4b, 0xbd, 0x8b, 0x8a, 0x70, |
|||
0x3e, 0xb5, 0x66, 0x48, 0x03, 0xf6, 0x0e, 0x61, 0x35, 0x57, 0xb9, |
|||
0x86, 0xc1, 0x1d, 0x9e, 0xe1, 0xf8, 0x98, 0x11, 0x69, 0xd9, 0x8e, |
|||
0x94, 0x9b, 0x1e, 0x87, 0xe9, 0xce, 0x55, 0x28, 0xdf, 0x8c, 0xa1, |
|||
0x89, 0x0d, 0xbf, 0xe6, 0x42, 0x68, 0x41, 0x99, 0x2d, 0x0f, 0xb0, |
|||
0x54, 0xbb, 0x16] |
|||
|
|||
# Rijndael Inverted S-box |
|||
rsbox = [0x52, 0x09, 0x6a, 0xd5, 0x30, 0x36, 0xa5, 0x38, 0xbf, 0x40, 0xa3, |
|||
0x9e, 0x81, 0xf3, 0xd7, 0xfb , 0x7c, 0xe3, 0x39, 0x82, 0x9b, 0x2f, |
|||
0xff, 0x87, 0x34, 0x8e, 0x43, 0x44, 0xc4, 0xde, 0xe9, 0xcb , 0x54, |
|||
0x7b, 0x94, 0x32, 0xa6, 0xc2, 0x23, 0x3d, 0xee, 0x4c, 0x95, 0x0b, |
|||
0x42, 0xfa, 0xc3, 0x4e , 0x08, 0x2e, 0xa1, 0x66, 0x28, 0xd9, 0x24, |
|||
0xb2, 0x76, 0x5b, 0xa2, 0x49, 0x6d, 0x8b, 0xd1, 0x25 , 0x72, 0xf8, |
|||
0xf6, 0x64, 0x86, 0x68, 0x98, 0x16, 0xd4, 0xa4, 0x5c, 0xcc, 0x5d, |
|||
0x65, 0xb6, 0x92 , 0x6c, 0x70, 0x48, 0x50, 0xfd, 0xed, 0xb9, 0xda, |
|||
0x5e, 0x15, 0x46, 0x57, 0xa7, 0x8d, 0x9d, 0x84 , 0x90, 0xd8, 0xab, |
|||
0x00, 0x8c, 0xbc, 0xd3, 0x0a, 0xf7, 0xe4, 0x58, 0x05, 0xb8, 0xb3, |
|||
0x45, 0x06 , 0xd0, 0x2c, 0x1e, 0x8f, 0xca, 0x3f, 0x0f, 0x02, 0xc1, |
|||
0xaf, 0xbd, 0x03, 0x01, 0x13, 0x8a, 0x6b , 0x3a, 0x91, 0x11, 0x41, |
|||
0x4f, 0x67, 0xdc, 0xea, 0x97, 0xf2, 0xcf, 0xce, 0xf0, 0xb4, 0xe6, |
|||
0x73 , 0x96, 0xac, 0x74, 0x22, 0xe7, 0xad, 0x35, 0x85, 0xe2, 0xf9, |
|||
0x37, 0xe8, 0x1c, 0x75, 0xdf, 0x6e , 0x47, 0xf1, 0x1a, 0x71, 0x1d, |
|||
0x29, 0xc5, 0x89, 0x6f, 0xb7, 0x62, 0x0e, 0xaa, 0x18, 0xbe, 0x1b , |
|||
0xfc, 0x56, 0x3e, 0x4b, 0xc6, 0xd2, 0x79, 0x20, 0x9a, 0xdb, 0xc0, |
|||
0xfe, 0x78, 0xcd, 0x5a, 0xf4 , 0x1f, 0xdd, 0xa8, 0x33, 0x88, 0x07, |
|||
0xc7, 0x31, 0xb1, 0x12, 0x10, 0x59, 0x27, 0x80, 0xec, 0x5f , 0x60, |
|||
0x51, 0x7f, 0xa9, 0x19, 0xb5, 0x4a, 0x0d, 0x2d, 0xe5, 0x7a, 0x9f, |
|||
0x93, 0xc9, 0x9c, 0xef , 0xa0, 0xe0, 0x3b, 0x4d, 0xae, 0x2a, 0xf5, |
|||
0xb0, 0xc8, 0xeb, 0xbb, 0x3c, 0x83, 0x53, 0x99, 0x61 , 0x17, 0x2b, |
|||
0x04, 0x7e, 0xba, 0x77, 0xd6, 0x26, 0xe1, 0x69, 0x14, 0x63, 0x55, |
|||
0x21, 0x0c, 0x7d] |
|||
|
|||
def getSBoxValue(self,num): |
|||
"""Retrieves a given S-Box Value""" |
|||
return self.sbox[num] |
|||
|
|||
def getSBoxInvert(self,num): |
|||
"""Retrieves a given Inverted S-Box Value""" |
|||
return self.rsbox[num] |
|||
|
|||
def rotate(self, word): |
|||
""" Rijndael's key schedule rotate operation. |
|||
|
|||
Rotate a word eight bits to the left: eg, rotate(1d2c3a4f) == 2c3a4f1d |
|||
Word is an char list of size 4 (32 bits overall). |
|||
""" |
|||
return word[1:] + word[:1] |
|||
|
|||
# Rijndael Rcon |
|||
Rcon = [0x8d, 0x01, 0x02, 0x04, 0x08, 0x10, 0x20, 0x40, 0x80, 0x1b, 0x36, |
|||
0x6c, 0xd8, 0xab, 0x4d, 0x9a, 0x2f, 0x5e, 0xbc, 0x63, 0xc6, 0x97, |
|||
0x35, 0x6a, 0xd4, 0xb3, 0x7d, 0xfa, 0xef, 0xc5, 0x91, 0x39, 0x72, |
|||
0xe4, 0xd3, 0xbd, 0x61, 0xc2, 0x9f, 0x25, 0x4a, 0x94, 0x33, 0x66, |
|||
0xcc, 0x83, 0x1d, 0x3a, 0x74, 0xe8, 0xcb, 0x8d, 0x01, 0x02, 0x04, |
|||
0x08, 0x10, 0x20, 0x40, 0x80, 0x1b, 0x36, 0x6c, 0xd8, 0xab, 0x4d, |
|||
0x9a, 0x2f, 0x5e, 0xbc, 0x63, 0xc6, 0x97, 0x35, 0x6a, 0xd4, 0xb3, |
|||
0x7d, 0xfa, 0xef, 0xc5, 0x91, 0x39, 0x72, 0xe4, 0xd3, 0xbd, 0x61, |
|||
0xc2, 0x9f, 0x25, 0x4a, 0x94, 0x33, 0x66, 0xcc, 0x83, 0x1d, 0x3a, |
|||
0x74, 0xe8, 0xcb, 0x8d, 0x01, 0x02, 0x04, 0x08, 0x10, 0x20, 0x40, |
|||
0x80, 0x1b, 0x36, 0x6c, 0xd8, 0xab, 0x4d, 0x9a, 0x2f, 0x5e, 0xbc, |
|||
0x63, 0xc6, 0x97, 0x35, 0x6a, 0xd4, 0xb3, 0x7d, 0xfa, 0xef, 0xc5, |
|||
0x91, 0x39, 0x72, 0xe4, 0xd3, 0xbd, 0x61, 0xc2, 0x9f, 0x25, 0x4a, |
|||
0x94, 0x33, 0x66, 0xcc, 0x83, 0x1d, 0x3a, 0x74, 0xe8, 0xcb, 0x8d, |
|||
0x01, 0x02, 0x04, 0x08, 0x10, 0x20, 0x40, 0x80, 0x1b, 0x36, 0x6c, |
|||
0xd8, 0xab, 0x4d, 0x9a, 0x2f, 0x5e, 0xbc, 0x63, 0xc6, 0x97, 0x35, |
|||
0x6a, 0xd4, 0xb3, 0x7d, 0xfa, 0xef, 0xc5, 0x91, 0x39, 0x72, 0xe4, |
|||
0xd3, 0xbd, 0x61, 0xc2, 0x9f, 0x25, 0x4a, 0x94, 0x33, 0x66, 0xcc, |
|||
0x83, 0x1d, 0x3a, 0x74, 0xe8, 0xcb, 0x8d, 0x01, 0x02, 0x04, 0x08, |
|||
0x10, 0x20, 0x40, 0x80, 0x1b, 0x36, 0x6c, 0xd8, 0xab, 0x4d, 0x9a, |
|||
0x2f, 0x5e, 0xbc, 0x63, 0xc6, 0x97, 0x35, 0x6a, 0xd4, 0xb3, 0x7d, |
|||
0xfa, 0xef, 0xc5, 0x91, 0x39, 0x72, 0xe4, 0xd3, 0xbd, 0x61, 0xc2, |
|||
0x9f, 0x25, 0x4a, 0x94, 0x33, 0x66, 0xcc, 0x83, 0x1d, 0x3a, 0x74, |
|||
0xe8, 0xcb ] |
|||
|
|||
def getRconValue(self, num): |
|||
"""Retrieves a given Rcon Value""" |
|||
return self.Rcon[num] |
|||
|
|||
def core(self, word, iteration): |
|||
"""Key schedule core.""" |
|||
# rotate the 32-bit word 8 bits to the left |
|||
word = self.rotate(word) |
|||
# apply S-Box substitution on all 4 parts of the 32-bit word |
|||
for i in range(4): |
|||
word[i] = self.getSBoxValue(word[i]) |
|||
# XOR the output of the rcon operation with i to the first part |
|||
# (leftmost) only |
|||
word[0] = word[0] ^ self.getRconValue(iteration) |
|||
return word |
|||
|
|||
def expandKey(self, key, size, expandedKeySize): |
|||
"""Rijndael's key expansion. |
|||
|
|||
Expands an 128,192,256 key into an 176,208,240 bytes key |
|||
|
|||
expandedKey is a char list of large enough size, |
|||
key is the non-expanded key. |
|||
""" |
|||
# current expanded keySize, in bytes |
|||
currentSize = 0 |
|||
rconIteration = 1 |
|||
expandedKey = [0] * expandedKeySize |
|||
|
|||
# set the 16, 24, 32 bytes of the expanded key to the input key |
|||
for j in range(size): |
|||
expandedKey[j] = key[j] |
|||
currentSize += size |
|||
|
|||
while currentSize < expandedKeySize: |
|||
# assign the previous 4 bytes to the temporary value t |
|||
t = expandedKey[currentSize-4:currentSize] |
|||
|
|||
# every 16,24,32 bytes we apply the core schedule to t |
|||
# and increment rconIteration afterwards |
|||
if currentSize % size == 0: |
|||
t = self.core(t, rconIteration) |
|||
rconIteration += 1 |
|||
# For 256-bit keys, we add an extra sbox to the calculation |
|||
if size == self.keySize["SIZE_256"] and ((currentSize % size) == 16): |
|||
for l in range(4): t[l] = self.getSBoxValue(t[l]) |
|||
|
|||
# We XOR t with the four-byte block 16,24,32 bytes before the new |
|||
# expanded key. This becomes the next four bytes in the expanded |
|||
# key. |
|||
for m in range(4): |
|||
expandedKey[currentSize] = expandedKey[currentSize - size] ^ \ |
|||
t[m] |
|||
currentSize += 1 |
|||
|
|||
return expandedKey |
|||
|
|||
def addRoundKey(self, state, roundKey): |
|||
"""Adds (XORs) the round key to the state.""" |
|||
for i in range(16): |
|||
state[i] ^= roundKey[i] |
|||
return state |
|||
|
|||
def createRoundKey(self, expandedKey, roundKeyPointer): |
|||
"""Create a round key. |
|||
Creates a round key from the given expanded key and the |
|||
position within the expanded key. |
|||
""" |
|||
roundKey = [0] * 16 |
|||
for i in range(4): |
|||
for j in range(4): |
|||
roundKey[j*4+i] = expandedKey[roundKeyPointer + i*4 + j] |
|||
return roundKey |
|||
|
|||
def galois_multiplication(self, a, b): |
|||
"""Galois multiplication of 8 bit characters a and b.""" |
|||
p = 0 |
|||
for counter in range(8): |
|||
if b & 1: p ^= a |
|||
hi_bit_set = a & 0x80 |
|||
a <<= 1 |
|||
# keep a 8 bit |
|||
a &= 0xFF |
|||
if hi_bit_set: |
|||
a ^= 0x1b |
|||
b >>= 1 |
|||
return p |
|||
|
|||
# |
|||
# substitute all the values from the state with the value in the SBox |
|||
# using the state value as index for the SBox |
|||
# |
|||
def subBytes(self, state, isInv): |
|||
if isInv: getter = self.getSBoxInvert |
|||
else: getter = self.getSBoxValue |
|||
for i in range(16): state[i] = getter(state[i]) |
|||
return state |
|||
|
|||
# iterate over the 4 rows and call shiftRow() with that row |
|||
def shiftRows(self, state, isInv): |
|||
for i in range(4): |
|||
state = self.shiftRow(state, i*4, i, isInv) |
|||
return state |
|||
|
|||
# each iteration shifts the row to the left by 1 |
|||
def shiftRow(self, state, statePointer, nbr, isInv): |
|||
for i in range(nbr): |
|||
if isInv: |
|||
state[statePointer:statePointer+4] = \ |
|||
state[statePointer+3:statePointer+4] + \ |
|||
state[statePointer:statePointer+3] |
|||
else: |
|||
state[statePointer:statePointer+4] = \ |
|||
state[statePointer+1:statePointer+4] + \ |
|||
state[statePointer:statePointer+1] |
|||
return state |
|||
|
|||
# galois multiplication of the 4x4 matrix |
|||
def mixColumns(self, state, isInv): |
|||
# iterate over the 4 columns |
|||
for i in range(4): |
|||
# construct one column by slicing over the 4 rows |
|||
column = state[i:i+16:4] |
|||
# apply the mixColumn on one column |
|||
column = self.mixColumn(column, isInv) |
|||
# put the values back into the state |
|||
state[i:i+16:4] = column |
|||
|
|||
return state |
|||
|
|||
# galois multiplication of 1 column of the 4x4 matrix |
|||
def mixColumn(self, column, isInv): |
|||
if isInv: mult = [14, 9, 13, 11] |
|||
else: mult = [2, 1, 1, 3] |
|||
cpy = list(column) |
|||
g = self.galois_multiplication |
|||
|
|||
column[0] = g(cpy[0], mult[0]) ^ g(cpy[3], mult[1]) ^ \ |
|||
g(cpy[2], mult[2]) ^ g(cpy[1], mult[3]) |
|||
column[1] = g(cpy[1], mult[0]) ^ g(cpy[0], mult[1]) ^ \ |
|||
g(cpy[3], mult[2]) ^ g(cpy[2], mult[3]) |
|||
column[2] = g(cpy[2], mult[0]) ^ g(cpy[1], mult[1]) ^ \ |
|||
g(cpy[0], mult[2]) ^ g(cpy[3], mult[3]) |
|||
column[3] = g(cpy[3], mult[0]) ^ g(cpy[2], mult[1]) ^ \ |
|||
g(cpy[1], mult[2]) ^ g(cpy[0], mult[3]) |
|||
return column |
|||
|
|||
# applies the 4 operations of the forward round in sequence |
|||
def aes_round(self, state, roundKey): |
|||
state = self.subBytes(state, False) |
|||
state = self.shiftRows(state, False) |
|||
state = self.mixColumns(state, False) |
|||
state = self.addRoundKey(state, roundKey) |
|||
return state |
|||
|
|||
# applies the 4 operations of the inverse round in sequence |
|||
def aes_invRound(self, state, roundKey): |
|||
state = self.shiftRows(state, True) |
|||
state = self.subBytes(state, True) |
|||
state = self.addRoundKey(state, roundKey) |
|||
state = self.mixColumns(state, True) |
|||
return state |
|||
|
|||
# Perform the initial operations, the standard round, and the final |
|||
# operations of the forward aes, creating a round key for each round |
|||
def aes_main(self, state, expandedKey, nbrRounds): |
|||
state = self.addRoundKey(state, self.createRoundKey(expandedKey, 0)) |
|||
i = 1 |
|||
while i < nbrRounds: |
|||
state = self.aes_round(state, |
|||
self.createRoundKey(expandedKey, 16*i)) |
|||
i += 1 |
|||
state = self.subBytes(state, False) |
|||
state = self.shiftRows(state, False) |
|||
state = self.addRoundKey(state, |
|||
self.createRoundKey(expandedKey, 16*nbrRounds)) |
|||
return state |
|||
|
|||
# Perform the initial operations, the standard round, and the final |
|||
# operations of the inverse aes, creating a round key for each round |
|||
def aes_invMain(self, state, expandedKey, nbrRounds): |
|||
state = self.addRoundKey(state, |
|||
self.createRoundKey(expandedKey, 16*nbrRounds)) |
|||
i = nbrRounds - 1 |
|||
while i > 0: |
|||
state = self.aes_invRound(state, |
|||
self.createRoundKey(expandedKey, 16*i)) |
|||
i -= 1 |
|||
state = self.shiftRows(state, True) |
|||
state = self.subBytes(state, True) |
|||
state = self.addRoundKey(state, self.createRoundKey(expandedKey, 0)) |
|||
return state |
|||
|
|||
# encrypts a 128 bit input block against the given key of size specified |
|||
def encrypt(self, iput, key, size): |
|||
output = [0] * 16 |
|||
# the number of rounds |
|||
nbrRounds = 0 |
|||
# the 128 bit block to encode |
|||
block = [0] * 16 |
|||
# set the number of rounds |
|||
if size == self.keySize["SIZE_128"]: nbrRounds = 10 |
|||
elif size == self.keySize["SIZE_192"]: nbrRounds = 12 |
|||
elif size == self.keySize["SIZE_256"]: nbrRounds = 14 |
|||
else: return None |
|||
|
|||
# the expanded keySize |
|||
expandedKeySize = 16*(nbrRounds+1) |
|||
|
|||
# Set the block values, for the block: |
|||
# a0,0 a0,1 a0,2 a0,3 |
|||
# a1,0 a1,1 a1,2 a1,3 |
|||
# a2,0 a2,1 a2,2 a2,3 |
|||
# a3,0 a3,1 a3,2 a3,3 |
|||
# the mapping order is a0,0 a1,0 a2,0 a3,0 a0,1 a1,1 ... a2,3 a3,3 |
|||
# |
|||
# iterate over the columns |
|||
for i in range(4): |
|||
# iterate over the rows |
|||
for j in range(4): |
|||
block[(i+(j*4))] = iput[(i*4)+j] |
|||
|
|||
# expand the key into an 176, 208, 240 bytes key |
|||
# the expanded key |
|||
expandedKey = self.expandKey(key, size, expandedKeySize) |
|||
|
|||
# encrypt the block using the expandedKey |
|||
block = self.aes_main(block, expandedKey, nbrRounds) |
|||
|
|||
# unmap the block again into the output |
|||
for k in range(4): |
|||
# iterate over the rows |
|||
for l in range(4): |
|||
output[(k*4)+l] = block[(k+(l*4))] |
|||
return output |
|||
|
|||
# decrypts a 128 bit input block against the given key of size specified |
|||
def decrypt(self, iput, key, size): |
|||
output = [0] * 16 |
|||
# the number of rounds |
|||
nbrRounds = 0 |
|||
# the 128 bit block to decode |
|||
block = [0] * 16 |
|||
# set the number of rounds |
|||
if size == self.keySize["SIZE_128"]: nbrRounds = 10 |
|||
elif size == self.keySize["SIZE_192"]: nbrRounds = 12 |
|||
elif size == self.keySize["SIZE_256"]: nbrRounds = 14 |
|||
else: return None |
|||
|
|||
# the expanded keySize |
|||
expandedKeySize = 16*(nbrRounds+1) |
|||
|
|||
# Set the block values, for the block: |
|||
# a0,0 a0,1 a0,2 a0,3 |
|||
# a1,0 a1,1 a1,2 a1,3 |
|||
# a2,0 a2,1 a2,2 a2,3 |
|||
# a3,0 a3,1 a3,2 a3,3 |
|||
# the mapping order is a0,0 a1,0 a2,0 a3,0 a0,1 a1,1 ... a2,3 a3,3 |
|||
|
|||
# iterate over the columns |
|||
for i in range(4): |
|||
# iterate over the rows |
|||
for j in range(4): |
|||
block[(i+(j*4))] = iput[(i*4)+j] |
|||
# expand the key into an 176, 208, 240 bytes key |
|||
expandedKey = self.expandKey(key, size, expandedKeySize) |
|||
# decrypt the block using the expandedKey |
|||
block = self.aes_invMain(block, expandedKey, nbrRounds) |
|||
# unmap the block again into the output |
|||
for k in range(4): |
|||
# iterate over the rows |
|||
for l in range(4): |
|||
output[(k*4)+l] = block[(k+(l*4))] |
|||
return output |
|||
|
|||
|
|||
class AESModeOfOperation(object): |
|||
|
|||
aes = AES() |
|||
|
|||
# structure of supported modes of operation |
|||
modeOfOperation = dict(OFB=0, CFB=1, CBC=2) |
|||
|
|||
# converts a 16 character string into a number array |
|||
def convertString(self, string, start, end, mode): |
|||
if end - start > 16: end = start + 16 |
|||
if mode == self.modeOfOperation["CBC"]: ar = [0] * 16 |
|||
else: ar = [] |
|||
|
|||
i = start |
|||
j = 0 |
|||
while len(ar) < end - start: |
|||
ar.append(0) |
|||
while i < end: |
|||
ar[j] = ord(string[i]) |
|||
j += 1 |
|||
i += 1 |
|||
return ar |
|||
|
|||
# Mode of Operation Encryption |
|||
# stringIn - Input String |
|||
# mode - mode of type modeOfOperation |
|||
# hexKey - a hex key of the bit length size |
|||
# size - the bit length of the key |
|||
# hexIV - the 128 bit hex Initilization Vector |
|||
def encrypt(self, stringIn, mode, key, size, IV): |
|||
if len(key) % size: |
|||
return None |
|||
if len(IV) % 16: |
|||
return None |
|||
# the AES input/output |
|||
plaintext = [] |
|||
iput = [0] * 16 |
|||
output = [] |
|||
ciphertext = [0] * 16 |
|||
# the output cipher string |
|||
cipherOut = [] |
|||
# char firstRound |
|||
firstRound = True |
|||
if stringIn != None: |
|||
for j in range(int(math.ceil(float(len(stringIn))/16))): |
|||
start = j*16 |
|||
end = j*16+16 |
|||
if end > len(stringIn): |
|||
end = len(stringIn) |
|||
plaintext = self.convertString(stringIn, start, end, mode) |
|||
# print 'PT@%s:%s' % (j, plaintext) |
|||
if mode == self.modeOfOperation["CFB"]: |
|||
if firstRound: |
|||
output = self.aes.encrypt(IV, key, size) |
|||
firstRound = False |
|||
else: |
|||
output = self.aes.encrypt(iput, key, size) |
|||
for i in range(16): |
|||
if len(plaintext)-1 < i: |
|||
ciphertext[i] = 0 ^ output[i] |
|||
elif len(output)-1 < i: |
|||
ciphertext[i] = plaintext[i] ^ 0 |
|||
elif len(plaintext)-1 < i and len(output) < i: |
|||
ciphertext[i] = 0 ^ 0 |
|||
else: |
|||
ciphertext[i] = plaintext[i] ^ output[i] |
|||
for k in range(end-start): |
|||
cipherOut.append(ciphertext[k]) |
|||
iput = ciphertext |
|||
elif mode == self.modeOfOperation["OFB"]: |
|||
if firstRound: |
|||
output = self.aes.encrypt(IV, key, size) |
|||
firstRound = False |
|||
else: |
|||
output = self.aes.encrypt(iput, key, size) |
|||
for i in range(16): |
|||
if len(plaintext)-1 < i: |
|||
ciphertext[i] = 0 ^ output[i] |
|||
elif len(output)-1 < i: |
|||
ciphertext[i] = plaintext[i] ^ 0 |
|||
elif len(plaintext)-1 < i and len(output) < i: |
|||
ciphertext[i] = 0 ^ 0 |
|||
else: |
|||
ciphertext[i] = plaintext[i] ^ output[i] |
|||
for k in range(end-start): |
|||
cipherOut.append(ciphertext[k]) |
|||
iput = output |
|||
elif mode == self.modeOfOperation["CBC"]: |
|||
for i in range(16): |
|||
if firstRound: |
|||
iput[i] = plaintext[i] ^ IV[i] |
|||
else: |
|||
iput[i] = plaintext[i] ^ ciphertext[i] |
|||
# print 'IP@%s:%s' % (j, iput) |
|||
firstRound = False |
|||
ciphertext = self.aes.encrypt(iput, key, size) |
|||
# always 16 bytes because of the padding for CBC |
|||
for k in range(16): |
|||
cipherOut.append(ciphertext[k]) |
|||
return mode, len(stringIn), cipherOut |
|||
|
|||
# Mode of Operation Decryption |
|||
# cipherIn - Encrypted String |
|||
# originalsize - The unencrypted string length - required for CBC |
|||
# mode - mode of type modeOfOperation |
|||
# key - a number array of the bit length size |
|||
# size - the bit length of the key |
|||
# IV - the 128 bit number array Initilization Vector |
|||
def decrypt(self, cipherIn, originalsize, mode, key, size, IV): |
|||
# cipherIn = unescCtrlChars(cipherIn) |
|||
if len(key) % size: |
|||
return None |
|||
if len(IV) % 16: |
|||
return None |
|||
# the AES input/output |
|||
ciphertext = [] |
|||
iput = [] |
|||
output = [] |
|||
plaintext = [0] * 16 |
|||
# the output plain text string |
|||
stringOut = '' |
|||
# char firstRound |
|||
firstRound = True |
|||
if cipherIn != None: |
|||
for j in range(int(math.ceil(float(len(cipherIn))/16))): |
|||
start = j*16 |
|||
end = j*16+16 |
|||
if j*16+16 > len(cipherIn): |
|||
end = len(cipherIn) |
|||
ciphertext = cipherIn[start:end] |
|||
if mode == self.modeOfOperation["CFB"]: |
|||
if firstRound: |
|||
output = self.aes.encrypt(IV, key, size) |
|||
firstRound = False |
|||
else: |
|||
output = self.aes.encrypt(iput, key, size) |
|||
for i in range(16): |
|||
if len(output)-1 < i: |
|||
plaintext[i] = 0 ^ ciphertext[i] |
|||
elif len(ciphertext)-1 < i: |
|||
plaintext[i] = output[i] ^ 0 |
|||
elif len(output)-1 < i and len(ciphertext) < i: |
|||
plaintext[i] = 0 ^ 0 |
|||
else: |
|||
plaintext[i] = output[i] ^ ciphertext[i] |
|||
for k in range(end-start): |
|||
stringOut += chr(plaintext[k]) |
|||
iput = ciphertext |
|||
elif mode == self.modeOfOperation["OFB"]: |
|||
if firstRound: |
|||
output = self.aes.encrypt(IV, key, size) |
|||
firstRound = False |
|||
else: |
|||
output = self.aes.encrypt(iput, key, size) |
|||
for i in range(16): |
|||
if len(output)-1 < i: |
|||
plaintext[i] = 0 ^ ciphertext[i] |
|||
elif len(ciphertext)-1 < i: |
|||
plaintext[i] = output[i] ^ 0 |
|||
elif len(output)-1 < i and len(ciphertext) < i: |
|||
plaintext[i] = 0 ^ 0 |
|||
else: |
|||
plaintext[i] = output[i] ^ ciphertext[i] |
|||
for k in range(end-start): |
|||
stringOut += chr(plaintext[k]) |
|||
iput = output |
|||
elif mode == self.modeOfOperation["CBC"]: |
|||
output = self.aes.decrypt(ciphertext, key, size) |
|||
for i in range(16): |
|||
if firstRound: |
|||
plaintext[i] = IV[i] ^ output[i] |
|||
else: |
|||
plaintext[i] = iput[i] ^ output[i] |
|||
firstRound = False |
|||
if originalsize is not None and originalsize < end: |
|||
for k in range(originalsize-start): |
|||
stringOut += chr(plaintext[k]) |
|||
else: |
|||
for k in range(end-start): |
|||
stringOut += chr(plaintext[k]) |
|||
iput = ciphertext |
|||
return stringOut |
|||
|
|||
|
|||
def encryptData(key, data, mode=AESModeOfOperation.modeOfOperation["CBC"]): |
|||
"""encrypt `data` using `key` |
|||
|
|||
`key` should be a string of bytes. |
|||
|
|||
returned cipher is a string of bytes prepended with the initialization |
|||
vector. |
|||
|
|||
""" |
|||
key = map(ord, key) |
|||
if mode == AESModeOfOperation.modeOfOperation["CBC"]: |
|||
data = append_PKCS7_padding(data) |
|||
keysize = len(key) |
|||
assert keysize in AES.keySize.values(), 'invalid key size: %s' % keysize |
|||
# create a new iv using random data |
|||
iv = [ord(i) for i in os.urandom(16)] |
|||
moo = AESModeOfOperation() |
|||
(mode, length, ciph) = moo.encrypt(data, mode, key, keysize, iv) |
|||
# With padding, the original length does not need to be known. It's a bad |
|||
# idea to store the original message length. |
|||
# prepend the iv. |
|||
return ''.join(map(chr, iv)) + ''.join(map(chr, ciph)) |
|||
|
|||
def decryptData(key, data, mode=AESModeOfOperation.modeOfOperation["CBC"]): |
|||
"""decrypt `data` using `key` |
|||
|
|||
`key` should be a string of bytes. |
|||
|
|||
`data` should have the initialization vector prepended as a string of |
|||
ordinal values. |
|||
|
|||
""" |
|||
|
|||
key = map(ord, key) |
|||
keysize = len(key) |
|||
assert keysize in AES.keySize.values(), 'invalid key size: %s' % keysize |
|||
# iv is first 16 bytes |
|||
iv = map(ord, data[:16]) |
|||
data = map(ord, data[16:]) |
|||
moo = AESModeOfOperation() |
|||
decr = moo.decrypt(data, None, mode, key, keysize, iv) |
|||
if mode == AESModeOfOperation.modeOfOperation["CBC"]: |
|||
decr = strip_PKCS7_padding(decr) |
|||
return decr |
|||
|
|||
def generateRandomKey(keysize): |
|||
"""Generates a key from random data of length `keysize`. |
|||
|
|||
The returned key is a string of bytes. |
|||
|
|||
""" |
|||
if keysize not in (16, 24, 32): |
|||
emsg = 'Invalid keysize, %s. Should be one of (16, 24, 32).' |
|||
raise ValueError, emsg % keysize |
|||
return os.urandom(keysize) |
|||
|
|||
if __name__ == "__main__": |
|||
moo = AESModeOfOperation() |
|||
cleartext = "This is a test!" |
|||
cypherkey = [143,194,34,208,145,203,230,143,177,246,97,206,145,92,255,84] |
|||
iv = [103,35,148,239,76,213,47,118,255,222,123,176,106,134,98,92] |
|||
mode, orig_len, ciph = moo.encrypt(cleartext, moo.modeOfOperation["CBC"], |
|||
cypherkey, moo.aes.keySize["SIZE_128"], iv) |
|||
print 'm=%s, ol=%s (%s), ciph=%s' % (mode, orig_len, len(cleartext), ciph) |
|||
decr = moo.decrypt(ciph, orig_len, mode, cypherkey, |
|||
moo.aes.keySize["SIZE_128"], iv) |
|||
print decr |
@ -1,16 +0,0 @@ |
|||
|
|||
from keys import SigningKey, VerifyingKey, BadSignatureError, BadDigestError |
|||
from curves import NIST192p, NIST224p, NIST256p, NIST384p, NIST521p |
|||
|
|||
_hush_pyflakes = [SigningKey, VerifyingKey, BadSignatureError, BadDigestError, |
|||
NIST192p, NIST224p, NIST256p, NIST384p, NIST521p] |
|||
del _hush_pyflakes |
|||
|
|||
# This code comes from http://github.com/warner/python-ecdsa |
|||
|
|||
try: |
|||
from _version import __version__ as v |
|||
__version__ = v |
|||
del v |
|||
except ImportError: |
|||
__version__ = "UNKNOWN" |
@ -1,41 +0,0 @@ |
|||
import der, ecdsa |
|||
|
|||
class UnknownCurveError(Exception): |
|||
pass |
|||
|
|||
def orderlen(order): |
|||
return (1+len("%x"%order))//2 # bytes |
|||
|
|||
# the NIST curves |
|||
class Curve: |
|||
def __init__(self, name, curve, generator, oid): |
|||
self.name = name |
|||
self.curve = curve |
|||
self.generator = generator |
|||
self.order = generator.order() |
|||
self.baselen = orderlen(self.order) |
|||
self.verifying_key_length = 2*self.baselen |
|||
self.signature_length = 2*self.baselen |
|||
self.oid = oid |
|||
self.encoded_oid = der.encode_oid(*oid) |
|||
|
|||
NIST192p = Curve("NIST192p", ecdsa.curve_192, ecdsa.generator_192, |
|||
(1, 2, 840, 10045, 3, 1, 1)) |
|||
NIST224p = Curve("NIST224p", ecdsa.curve_224, ecdsa.generator_224, |
|||
(1, 3, 132, 0, 33)) |
|||
NIST256p = Curve("NIST256p", ecdsa.curve_256, ecdsa.generator_256, |
|||
(1, 2, 840, 10045, 3, 1, 7)) |
|||
NIST384p = Curve("NIST384p", ecdsa.curve_384, ecdsa.generator_384, |
|||
(1, 3, 132, 0, 34)) |
|||
NIST521p = Curve("NIST521p", ecdsa.curve_521, ecdsa.generator_521, |
|||
(1, 3, 132, 0, 35)) |
|||
|
|||
curves = [NIST192p, NIST224p, NIST256p, NIST384p, NIST521p] |
|||
|
|||
def find_curve(oid_curve): |
|||
for c in curves: |
|||
if c.oid == oid_curve: |
|||
return c |
|||
raise UnknownCurveError("I don't know about the curve with oid %s." |
|||
"I only know about these: %s" % |
|||
(oid_curve, [c.name for c in curves])) |
@ -1,190 +0,0 @@ |
|||
import binascii |
|||
import base64 |
|||
|
|||
class UnexpectedDER(Exception): |
|||
pass |
|||
|
|||
def encode_constructed(tag, value): |
|||
return chr(0xa0+tag) + encode_length(len(value)) + value |
|||
def encode_integer(r): |
|||
assert r >= 0 # can't support negative numbers yet |
|||
h = "%x" % r |
|||
if len(h)%2: |
|||
h = "0" + h |
|||
s = binascii.unhexlify(h) |
|||
if ord(s[0]) <= 0x7f: |
|||
return "\x02" + chr(len(s)) + s |
|||
else: |
|||
# DER integers are two's complement, so if the first byte is |
|||
# 0x80-0xff then we need an extra 0x00 byte to prevent it from |
|||
# looking negative. |
|||
return "\x02" + chr(len(s)+1) + "\x00" + s |
|||
|
|||
def encode_bitstring(s): |
|||
return "\x03" + encode_length(len(s)) + s |
|||
def encode_octet_string(s): |
|||
return "\x04" + encode_length(len(s)) + s |
|||
def encode_oid(first, second, *pieces): |
|||
assert first <= 2 |
|||
assert second <= 39 |
|||
encoded_pieces = [chr(40*first+second)] + [encode_number(p) |
|||
for p in pieces] |
|||
body = "".join(encoded_pieces) |
|||
return "\x06" + encode_length(len(body)) + body |
|||
def encode_sequence(*encoded_pieces): |
|||
total_len = sum([len(p) for p in encoded_pieces]) |
|||
return "\x30" + encode_length(total_len) + "".join(encoded_pieces) |
|||
def encode_number(n): |
|||
b128_digits = [] |
|||
while n: |
|||
b128_digits.insert(0, (n & 0x7f) | 0x80) |
|||
n = n >> 7 |
|||
if not b128_digits: |
|||
b128_digits.append(0) |
|||
b128_digits[-1] &= 0x7f |
|||
return "".join([chr(d) for d in b128_digits]) |
|||
|
|||
def remove_constructed(string): |
|||
s0 = ord(string[0]) |
|||
if (s0 & 0xe0) != 0xa0: |
|||
raise UnexpectedDER("wanted constructed tag (0xa0-0xbf), got 0x%02x" |
|||
% s0) |
|||
tag = s0 & 0x1f |
|||
length, llen = read_length(string[1:]) |
|||
body = string[1+llen:1+llen+length] |
|||
rest = string[1+llen+length:] |
|||
return tag, body, rest |
|||
|
|||
def remove_sequence(string): |
|||
if not string.startswith("\x30"): |
|||
raise UnexpectedDER("wanted sequence (0x30), got 0x%02x" % |
|||
ord(string[0])) |
|||
length, lengthlength = read_length(string[1:]) |
|||
endseq = 1+lengthlength+length |
|||
return string[1+lengthlength:endseq], string[endseq:] |
|||
|
|||
def remove_octet_string(string): |
|||
if not string.startswith("\x04"): |
|||
raise UnexpectedDER("wanted octetstring (0x04), got 0x%02x" % |
|||
ord(string[0])) |
|||
length, llen = read_length(string[1:]) |
|||
body = string[1+llen:1+llen+length] |
|||
rest = string[1+llen+length:] |
|||
return body, rest |
|||
|
|||
def remove_object(string): |
|||
if not string.startswith("\x06"): |
|||
raise UnexpectedDER("wanted object (0x06), got 0x%02x" % |
|||
ord(string[0])) |
|||
length, lengthlength = read_length(string[1:]) |
|||
body = string[1+lengthlength:1+lengthlength+length] |
|||
rest = string[1+lengthlength+length:] |
|||
numbers = [] |
|||
while body: |
|||
n, ll = read_number(body) |
|||
numbers.append(n) |
|||
body = body[ll:] |
|||
n0 = numbers.pop(0) |
|||
first = n0//40 |
|||
second = n0-(40*first) |
|||
numbers.insert(0, first) |
|||
numbers.insert(1, second) |
|||
return tuple(numbers), rest |
|||
|
|||
def remove_integer(string): |
|||
if not string.startswith("\x02"): |
|||
raise UnexpectedDER("wanted integer (0x02), got 0x%02x" % |
|||
ord(string[0])) |
|||
length, llen = read_length(string[1:]) |
|||
numberbytes = string[1+llen:1+llen+length] |
|||
rest = string[1+llen+length:] |
|||
assert ord(numberbytes[0]) < 0x80 # can't support negative numbers yet |
|||
return int(binascii.hexlify(numberbytes), 16), rest |
|||
|
|||
def read_number(string): |
|||
number = 0 |
|||
llen = 0 |
|||
# base-128 big endian, with b7 set in all but the last byte |
|||
while True: |
|||
if llen > len(string): |
|||
raise UnexpectedDER("ran out of length bytes") |
|||
number = number << 7 |
|||
d = ord(string[llen]) |
|||
number += (d & 0x7f) |
|||
llen += 1 |
|||
if not d & 0x80: |
|||
break |
|||
return number, llen |
|||
|
|||
def encode_length(l): |
|||
assert l >= 0 |
|||
if l < 0x80: |
|||
return chr(l) |
|||
s = "%x" % l |
|||
if len(s)%2: |
|||
s = "0"+s |
|||
s = binascii.unhexlify(s) |
|||
llen = len(s) |
|||
return chr(0x80|llen) + s |
|||
|
|||
def read_length(string): |
|||
if not (ord(string[0]) & 0x80): |
|||
# short form |
|||
return (ord(string[0]) & 0x7f), 1 |
|||
# else long-form: b0&0x7f is number of additional base256 length bytes, |
|||
# big-endian |
|||
llen = ord(string[0]) & 0x7f |
|||
if llen > len(string)-1: |
|||
raise UnexpectedDER("ran out of length bytes") |
|||
return int(binascii.hexlify(string[1:1+llen]), 16), 1+llen |
|||
|
|||
def remove_bitstring(string): |
|||
if not string.startswith("\x03"): |
|||
raise UnexpectedDER("wanted bitstring (0x03), got 0x%02x" % |
|||
ord(string[0])) |
|||
length, llen = read_length(string[1:]) |
|||
body = string[1+llen:1+llen+length] |
|||
rest = string[1+llen+length:] |
|||
return body, rest |
|||
|
|||
# SEQUENCE([1, STRING(secexp), cont[0], OBJECT(curvename), cont[1], BINTSTRING) |
|||
|
|||
|
|||
# signatures: (from RFC3279) |
|||
# ansi-X9-62 OBJECT IDENTIFIER ::= { |
|||
# iso(1) member-body(2) us(840) 10045 } |
|||
# |
|||
# id-ecSigType OBJECT IDENTIFIER ::= { |
|||
# ansi-X9-62 signatures(4) } |
|||
# ecdsa-with-SHA1 OBJECT IDENTIFIER ::= { |
|||
# id-ecSigType 1 } |
|||
## so 1,2,840,10045,4,1 |
|||
## so 0x42, .. .. |
|||
|
|||
# Ecdsa-Sig-Value ::= SEQUENCE { |
|||
# r INTEGER, |
|||
# s INTEGER } |
|||
|
|||
# id-public-key-type OBJECT IDENTIFIER ::= { ansi-X9.62 2 } |
|||
# |
|||
# id-ecPublicKey OBJECT IDENTIFIER ::= { id-publicKeyType 1 } |
|||
|
|||
# I think the secp224r1 identifier is (t=06,l=05,v=2b81040021) |
|||
# secp224r1 OBJECT IDENTIFIER ::= { |
|||
# iso(1) identified-organization(3) certicom(132) curve(0) 33 } |
|||
# and the secp384r1 is (t=06,l=05,v=2b81040022) |
|||
# secp384r1 OBJECT IDENTIFIER ::= { |
|||
# iso(1) identified-organization(3) certicom(132) curve(0) 34 } |
|||
|
|||
def unpem(pem): |
|||
d = "".join([l.strip() for l in pem.split("\n") |
|||
if l and not l.startswith("-----")]) |
|||
return base64.b64decode(d) |
|||
def topem(der, name): |
|||
b64 = base64.b64encode(der) |
|||
lines = ["-----BEGIN %s-----\n" % name] |
|||
lines.extend([b64[start:start+64]+"\n" |
|||
for start in range(0, len(b64), 64)]) |
|||
lines.append("-----END %s-----\n" % name) |
|||
return "".join(lines) |
|||
|
@ -1,560 +0,0 @@ |
|||
#! /usr/bin/env python |
|||
""" |
|||
Implementation of Elliptic-Curve Digital Signatures. |
|||
|
|||
Classes and methods for elliptic-curve signatures: |
|||
private keys, public keys, signatures, |
|||
NIST prime-modulus curves with modulus lengths of |
|||
192, 224, 256, 384, and 521 bits. |
|||
|
|||
Example: |
|||
|
|||
# (In real-life applications, you would probably want to |
|||
# protect against defects in SystemRandom.) |
|||
from random import SystemRandom |
|||
randrange = SystemRandom().randrange |
|||
|
|||
# Generate a public/private key pair using the NIST Curve P-192: |
|||
|
|||
g = generator_192 |
|||
n = g.order() |
|||
secret = randrange( 1, n ) |
|||
pubkey = Public_key( g, g * secret ) |
|||
privkey = Private_key( pubkey, secret ) |
|||
|
|||
# Signing a hash value: |
|||
|
|||
hash = randrange( 1, n ) |
|||
signature = privkey.sign( hash, randrange( 1, n ) ) |
|||
|
|||
# Verifying a signature for a hash value: |
|||
|
|||
if pubkey.verifies( hash, signature ): |
|||
print "Demo verification succeeded." |
|||
else: |
|||
print "*** Demo verification failed." |
|||
|
|||
# Verification fails if the hash value is modified: |
|||
|
|||
if pubkey.verifies( hash-1, signature ): |
|||
print "**** Demo verification failed to reject tampered hash." |
|||
else: |
|||
print "Demo verification correctly rejected tampered hash." |
|||
|
|||
Version of 2009.05.16. |
|||
|
|||
Revision history: |
|||
2005.12.31 - Initial version. |
|||
2008.11.25 - Substantial revisions introducing new classes. |
|||
2009.05.16 - Warn against using random.randrange in real applications. |
|||
2009.05.17 - Use random.SystemRandom by default. |
|||
|
|||
Written in 2005 by Peter Pearson and placed in the public domain. |
|||
""" |
|||
|
|||
|
|||
import ellipticcurve |
|||
import numbertheory |
|||
import random |
|||
|
|||
|
|||
|
|||
class Signature( object ): |
|||
"""ECDSA signature. |
|||
""" |
|||
def __init__( self, r, s ): |
|||
self.r = r |
|||
self.s = s |
|||
|
|||
|
|||
|
|||
class Public_key( object ): |
|||
"""Public key for ECDSA. |
|||
""" |
|||
|
|||
def __init__( self, generator, point ): |
|||
"""generator is the Point that generates the group, |
|||
point is the Point that defines the public key. |
|||
""" |
|||
|
|||
self.curve = generator.curve() |
|||
self.generator = generator |
|||
self.point = point |
|||
n = generator.order() |
|||
if not n: |
|||
raise RuntimeError, "Generator point must have order." |
|||
if not n * point == ellipticcurve.INFINITY: |
|||
raise RuntimeError, "Generator point order is bad." |
|||
if point.x() < 0 or n <= point.x() or point.y() < 0 or n <= point.y(): |
|||
raise RuntimeError, "Generator point has x or y out of range." |
|||
|
|||
|
|||
def verifies( self, hash, signature ): |
|||
"""Verify that signature is a valid signature of hash. |
|||
Return True if the signature is valid. |
|||
""" |
|||
|
|||
# From X9.62 J.3.1. |
|||
|
|||
G = self.generator |
|||
n = G.order() |
|||
r = signature.r |
|||
s = signature.s |
|||
if r < 1 or r > n-1: return False |
|||
if s < 1 or s > n-1: return False |
|||
c = numbertheory.inverse_mod( s, n ) |
|||
u1 = ( hash * c ) % n |
|||
u2 = ( r * c ) % n |
|||
xy = u1 * G + u2 * self.point |
|||
v = xy.x() % n |
|||
return v == r |
|||
|
|||
|
|||
|
|||
class Private_key( object ): |
|||
"""Private key for ECDSA. |
|||
""" |
|||
|
|||
def __init__( self, public_key, secret_multiplier ): |
|||
"""public_key is of class Public_key; |
|||
secret_multiplier is a large integer. |
|||
""" |
|||
|
|||
self.public_key = public_key |
|||
self.secret_multiplier = secret_multiplier |
|||
|
|||
def sign( self, hash, random_k ): |
|||
"""Return a signature for the provided hash, using the provided |
|||
random nonce. It is absolutely vital that random_k be an unpredictable |
|||
number in the range [1, self.public_key.point.order()-1]. If |
|||
an attacker can guess random_k, he can compute our private key from a |
|||
single signature. Also, if an attacker knows a few high-order |
|||
bits (or a few low-order bits) of random_k, he can compute our private |
|||
key from many signatures. The generation of nonces with adequate |
|||
cryptographic strength is very difficult and far beyond the scope |
|||
of this comment. |
|||
|
|||
May raise RuntimeError, in which case retrying with a new |
|||
random value k is in order. |
|||
""" |
|||
|
|||
G = self.public_key.generator |
|||
n = G.order() |
|||
k = random_k % n |
|||
p1 = k * G |
|||
r = p1.x() |
|||
if r == 0: raise RuntimeError, "amazingly unlucky random number r" |
|||
s = ( numbertheory.inverse_mod( k, n ) * \ |
|||
( hash + ( self.secret_multiplier * r ) % n ) ) % n |
|||
if s == 0: raise RuntimeError, "amazingly unlucky random number s" |
|||
return Signature( r, s ) |
|||
|
|||
|
|||
|
|||
def int_to_string( x ): |
|||
"""Convert integer x into a string of bytes, as per X9.62.""" |
|||
assert x >= 0 |
|||
if x == 0: return chr(0) |
|||
result = "" |
|||
while x > 0: |
|||
q, r = divmod( x, 256 ) |
|||
result = chr( r ) + result |
|||
x = q |
|||
return result |
|||
|
|||
|
|||
def string_to_int( s ): |
|||
"""Convert a string of bytes into an integer, as per X9.62.""" |
|||
result = 0L |
|||
for c in s: result = 256 * result + ord( c ) |
|||
return result |
|||
|
|||
|
|||
def digest_integer( m ): |
|||
"""Convert an integer into a string of bytes, compute |
|||
its SHA-1 hash, and convert the result to an integer.""" |
|||
# |
|||
# I don't expect this function to be used much. I wrote |
|||
# it in order to be able to duplicate the examples |
|||
# in ECDSAVS. |
|||
# |
|||
from hashlib import sha1 |
|||
return string_to_int( sha1( int_to_string( m ) ).digest() ) |
|||
|
|||
|
|||
def point_is_valid( generator, x, y ): |
|||
"""Is (x,y) a valid public key based on the specified generator?""" |
|||
|
|||
# These are the tests specified in X9.62. |
|||
|
|||
n = generator.order() |
|||
curve = generator.curve() |
|||
if x < 0 or n <= x or y < 0 or n <= y: |
|||
return False |
|||
if not curve.contains_point( x, y ): |
|||
return False |
|||
if not n*ellipticcurve.Point( curve, x, y ) == \ |
|||
ellipticcurve.INFINITY: |
|||
return False |
|||
return True |
|||
|
|||
|
|||
|
|||
# NIST Curve P-192: |
|||
_p = 6277101735386680763835789423207666416083908700390324961279L |
|||
_r = 6277101735386680763835789423176059013767194773182842284081L |
|||
# s = 0x3045ae6fc8422f64ed579528d38120eae12196d5L |
|||
# c = 0x3099d2bbbfcb2538542dcd5fb078b6ef5f3d6fe2c745de65L |
|||
_b = 0x64210519e59c80e70fa7e9ab72243049feb8deecc146b9b1L |
|||
_Gx = 0x188da80eb03090f67cbf20eb43a18800f4ff0afd82ff1012L |
|||
_Gy = 0x07192b95ffc8da78631011ed6b24cdd573f977a11e794811L |
|||
|
|||
curve_192 = ellipticcurve.CurveFp( _p, -3, _b ) |
|||
generator_192 = ellipticcurve.Point( curve_192, _Gx, _Gy, _r ) |
|||
|
|||
|
|||
# NIST Curve P-224: |
|||
_p = 26959946667150639794667015087019630673557916260026308143510066298881L |
|||
_r = 26959946667150639794667015087019625940457807714424391721682722368061L |
|||
# s = 0xbd71344799d5c7fcdc45b59fa3b9ab8f6a948bc5L |
|||
# c = 0x5b056c7e11dd68f40469ee7f3c7a7d74f7d121116506d031218291fbL |
|||
_b = 0xb4050a850c04b3abf54132565044b0b7d7bfd8ba270b39432355ffb4L |
|||
_Gx =0xb70e0cbd6bb4bf7f321390b94a03c1d356c21122343280d6115c1d21L |
|||
_Gy = 0xbd376388b5f723fb4c22dfe6cd4375a05a07476444d5819985007e34L |
|||
|
|||
curve_224 = ellipticcurve.CurveFp( _p, -3, _b ) |
|||
generator_224 = ellipticcurve.Point( curve_224, _Gx, _Gy, _r ) |
|||
|
|||
# NIST Curve P-256: |
|||
_p = 115792089210356248762697446949407573530086143415290314195533631308867097853951L |
|||
_r = 115792089210356248762697446949407573529996955224135760342422259061068512044369L |
|||
# s = 0xc49d360886e704936a6678e1139d26b7819f7e90L |
|||
# c = 0x7efba1662985be9403cb055c75d4f7e0ce8d84a9c5114abcaf3177680104fa0dL |
|||
_b = 0x5ac635d8aa3a93e7b3ebbd55769886bc651d06b0cc53b0f63bce3c3e27d2604bL |
|||
_Gx = 0x6b17d1f2e12c4247f8bce6e563a440f277037d812deb33a0f4a13945d898c296L |
|||
_Gy = 0x4fe342e2fe1a7f9b8ee7eb4a7c0f9e162bce33576b315ececbb6406837bf51f5L |
|||
|
|||
curve_256 = ellipticcurve.CurveFp( _p, -3, _b ) |
|||
generator_256 = ellipticcurve.Point( curve_256, _Gx, _Gy, _r ) |
|||
|
|||
# NIST Curve P-384: |
|||
_p = 39402006196394479212279040100143613805079739270465446667948293404245721771496870329047266088258938001861606973112319L |
|||
_r = 39402006196394479212279040100143613805079739270465446667946905279627659399113263569398956308152294913554433653942643L |
|||
# s = 0xa335926aa319a27a1d00896a6773a4827acdac73L |
|||
# c = 0x79d1e655f868f02fff48dcdee14151ddb80643c1406d0ca10dfe6fc52009540a495e8042ea5f744f6e184667cc722483L |
|||
_b = 0xb3312fa7e23ee7e4988e056be3f82d19181d9c6efe8141120314088f5013875ac656398d8a2ed19d2a85c8edd3ec2aefL |
|||
_Gx = 0xaa87ca22be8b05378eb1c71ef320ad746e1d3b628ba79b9859f741e082542a385502f25dbf55296c3a545e3872760ab7L |
|||
_Gy = 0x3617de4a96262c6f5d9e98bf9292dc29f8f41dbd289a147ce9da3113b5f0b8c00a60b1ce1d7e819d7a431d7c90ea0e5fL |
|||
|
|||
curve_384 = ellipticcurve.CurveFp( _p, -3, _b ) |
|||
generator_384 = ellipticcurve.Point( curve_384, _Gx, _Gy, _r ) |
|||
|
|||
# NIST Curve P-521: |
|||
_p = 6864797660130609714981900799081393217269435300143305409394463459185543183397656052122559640661454554977296311391480858037121987999716643812574028291115057151L |
|||
_r = 6864797660130609714981900799081393217269435300143305409394463459185543183397655394245057746333217197532963996371363321113864768612440380340372808892707005449L |
|||
# s = 0xd09e8800291cb85396cc6717393284aaa0da64baL |
|||
# c = 0x0b48bfa5f420a34949539d2bdfc264eeeeb077688e44fbf0ad8f6d0edb37bd6b533281000518e19f1b9ffbe0fe9ed8a3c2200b8f875e523868c70c1e5bf55bad637L |
|||
_b = 0x051953eb9618e1c9a1f929a21a0b68540eea2da725b99b315f3b8b489918ef109e156193951ec7e937b1652c0bd3bb1bf073573df883d2c34f1ef451fd46b503f00L |
|||
_Gx = 0xc6858e06b70404e9cd9e3ecb662395b4429c648139053fb521f828af606b4d3dbaa14b5e77efe75928fe1dc127a2ffa8de3348b3c1856a429bf97e7e31c2e5bd66L |
|||
_Gy = 0x11839296a789a3bc0045c8a5fb42c7d1bd998f54449579b446817afbd17273e662c97ee72995ef42640c550b9013fad0761353c7086a272c24088be94769fd16650L |
|||
|
|||
curve_521 = ellipticcurve.CurveFp( _p, -3, _b ) |
|||
generator_521 = ellipticcurve.Point( curve_521, _Gx, _Gy, _r ) |
|||
|
|||
|
|||
|
|||
def __main__(): |
|||
class TestFailure(Exception): pass |
|||
|
|||
def test_point_validity( generator, x, y, expected ): |
|||
"""generator defines the curve; is (x,y) a point on |
|||
this curve? "expected" is True if the right answer is Yes.""" |
|||
if point_is_valid( generator, x, y ) == expected: |
|||
print "Point validity tested as expected." |
|||
else: |
|||
raise TestFailure("*** Point validity test gave wrong result.") |
|||
|
|||
def test_signature_validity( Msg, Qx, Qy, R, S, expected ): |
|||
"""Msg = message, Qx and Qy represent the base point on |
|||
elliptic curve c192, R and S are the signature, and |
|||
"expected" is True iff the signature is expected to be valid.""" |
|||
pubk = Public_key( generator_192, |
|||
ellipticcurve.Point( curve_192, Qx, Qy ) ) |
|||
got = pubk.verifies( digest_integer( Msg ), Signature( R, S ) ) |
|||
if got == expected: |
|||
print "Signature tested as expected: got %s, expected %s." % \ |
|||
( got, expected ) |
|||
else: |
|||
raise TestFailure("*** Signature test failed: got %s, expected %s." % \ |
|||
( got, expected )) |
|||
|
|||
print "NIST Curve P-192:" |
|||
|
|||
p192 = generator_192 |
|||
|
|||
# From X9.62: |
|||
|
|||
d = 651056770906015076056810763456358567190100156695615665659L |
|||
Q = d * p192 |
|||
if Q.x() != 0x62B12D60690CDCF330BABAB6E69763B471F994DD702D16A5L: |
|||
raise TestFailure("*** p192 * d came out wrong.") |
|||
else: |
|||
print "p192 * d came out right." |
|||
|
|||
k = 6140507067065001063065065565667405560006161556565665656654L |
|||
R = k * p192 |
|||
if R.x() != 0x885052380FF147B734C330C43D39B2C4A89F29B0F749FEADL \ |
|||
or R.y() != 0x9CF9FA1CBEFEFB917747A3BB29C072B9289C2547884FD835L: |
|||
raise TestFailure("*** k * p192 came out wrong.") |
|||
else: |
|||
print "k * p192 came out right." |
|||
|
|||
u1 = 2563697409189434185194736134579731015366492496392189760599L |
|||
u2 = 6266643813348617967186477710235785849136406323338782220568L |
|||
temp = u1 * p192 + u2 * Q |
|||
if temp.x() != 0x885052380FF147B734C330C43D39B2C4A89F29B0F749FEADL \ |
|||
or temp.y() != 0x9CF9FA1CBEFEFB917747A3BB29C072B9289C2547884FD835L: |
|||
raise TestFailure("*** u1 * p192 + u2 * Q came out wrong.") |
|||
else: |
|||
print "u1 * p192 + u2 * Q came out right." |
|||
|
|||
e = 968236873715988614170569073515315707566766479517L |
|||
pubk = Public_key( generator_192, generator_192 * d ) |
|||
privk = Private_key( pubk, d ) |
|||
sig = privk.sign( e, k ) |
|||
r, s = sig.r, sig.s |
|||
if r != 3342403536405981729393488334694600415596881826869351677613L \ |
|||
or s != 5735822328888155254683894997897571951568553642892029982342L: |
|||
raise TestFailure("*** r or s came out wrong.") |
|||
else: |
|||
print "r and s came out right." |
|||
|
|||
valid = pubk.verifies( e, sig ) |
|||
if valid: print "Signature verified OK." |
|||
else: raise TestFailure("*** Signature failed verification.") |
|||
|
|||
valid = pubk.verifies( e-1, sig ) |
|||
if not valid: print "Forgery was correctly rejected." |
|||
else: raise TestFailure("*** Forgery was erroneously accepted.") |
|||
|
|||
print "Testing point validity, as per ECDSAVS.pdf B.2.2:" |
|||
|
|||
test_point_validity( \ |
|||
p192, \ |
|||
0xcd6d0f029a023e9aaca429615b8f577abee685d8257cc83aL, \ |
|||
0x00019c410987680e9fb6c0b6ecc01d9a2647c8bae27721bacdfcL, \ |
|||
False ) |
|||
|
|||
test_point_validity( |
|||
p192, \ |
|||
0x00017f2fce203639e9eaf9fb50b81fc32776b30e3b02af16c73bL, \ |
|||
0x95da95c5e72dd48e229d4748d4eee658a9a54111b23b2adbL, \ |
|||
False ) |
|||
|
|||
test_point_validity( |
|||
p192, \ |
|||
0x4f77f8bc7fccbadd5760f4938746d5f253ee2168c1cf2792L, \ |
|||
0x000147156ff824d131629739817edb197717c41aab5c2a70f0f6L, \ |
|||
False ) |
|||
|
|||
test_point_validity( |
|||
p192, \ |
|||
0xc58d61f88d905293bcd4cd0080bcb1b7f811f2ffa41979f6L, \ |
|||
0x8804dc7a7c4c7f8b5d437f5156f3312ca7d6de8a0e11867fL, \ |
|||
True ) |
|||
|
|||
test_point_validity( |
|||
p192, \ |
|||
0xcdf56c1aa3d8afc53c521adf3ffb96734a6a630a4a5b5a70L, \ |
|||
0x97c1c44a5fb229007b5ec5d25f7413d170068ffd023caa4eL, \ |
|||
True ) |
|||
|
|||
test_point_validity( |
|||
p192, \ |
|||
0x89009c0dc361c81e99280c8e91df578df88cdf4b0cdedcedL, \ |
|||
0x27be44a529b7513e727251f128b34262a0fd4d8ec82377b9L, \ |
|||
True ) |
|||
|
|||
test_point_validity( |
|||
p192, \ |
|||
0x6a223d00bd22c52833409a163e057e5b5da1def2a197dd15L, \ |
|||
0x7b482604199367f1f303f9ef627f922f97023e90eae08abfL, \ |
|||
True ) |
|||
|
|||
test_point_validity( |
|||
p192, \ |
|||
0x6dccbde75c0948c98dab32ea0bc59fe125cf0fb1a3798edaL, \ |
|||
0x0001171a3e0fa60cf3096f4e116b556198de430e1fbd330c8835L, \ |
|||
False ) |
|||
|
|||
test_point_validity( |
|||
p192, \ |
|||
0xd266b39e1f491fc4acbbbc7d098430931cfa66d55015af12L, \ |
|||
0x193782eb909e391a3148b7764e6b234aa94e48d30a16dbb2L, \ |
|||
False ) |
|||
|
|||
test_point_validity( |
|||
p192, \ |
|||
0x9d6ddbcd439baa0c6b80a654091680e462a7d1d3f1ffeb43L, \ |
|||
0x6ad8efc4d133ccf167c44eb4691c80abffb9f82b932b8caaL, \ |
|||
False ) |
|||
|
|||
test_point_validity( |
|||
p192, \ |
|||
0x146479d944e6bda87e5b35818aa666a4c998a71f4e95edbcL, \ |
|||
0xa86d6fe62bc8fbd88139693f842635f687f132255858e7f6L, \ |
|||
False ) |
|||
|
|||
test_point_validity( |
|||
p192, \ |
|||
0xe594d4a598046f3598243f50fd2c7bd7d380edb055802253L, \ |
|||
0x509014c0c4d6b536e3ca750ec09066af39b4c8616a53a923L, \ |
|||
False ) |
|||
|
|||
print "Trying signature-verification tests from ECDSAVS.pdf B.2.4:" |
|||
print "P-192:" |
|||
Msg = 0x84ce72aa8699df436059f052ac51b6398d2511e49631bcb7e71f89c499b9ee425dfbc13a5f6d408471b054f2655617cbbaf7937b7c80cd8865cf02c8487d30d2b0fbd8b2c4e102e16d828374bbc47b93852f212d5043c3ea720f086178ff798cc4f63f787b9c2e419efa033e7644ea7936f54462dc21a6c4580725f7f0e7d158L |
|||
Qx = 0xd9dbfb332aa8e5ff091e8ce535857c37c73f6250ffb2e7acL |
|||
Qy = 0x282102e364feded3ad15ddf968f88d8321aa268dd483ebc4L |
|||
R = 0x64dca58a20787c488d11d6dd96313f1b766f2d8efe122916L |
|||
S = 0x1ecba28141e84ab4ecad92f56720e2cc83eb3d22dec72479L |
|||
test_signature_validity( Msg, Qx, Qy, R, S, True ) |
|||
|
|||
Msg = 0x94bb5bacd5f8ea765810024db87f4224ad71362a3c28284b2b9f39fab86db12e8beb94aae899768229be8fdb6c4f12f28912bb604703a79ccff769c1607f5a91450f30ba0460d359d9126cbd6296be6d9c4bb96c0ee74cbb44197c207f6db326ab6f5a659113a9034e54be7b041ced9dcf6458d7fb9cbfb2744d999f7dfd63f4L |
|||
Qx = 0x3e53ef8d3112af3285c0e74842090712cd324832d4277ae7L |
|||
Qy = 0xcc75f8952d30aec2cbb719fc6aa9934590b5d0ff5a83adb7L |
|||
R = 0x8285261607283ba18f335026130bab31840dcfd9c3e555afL |
|||
S = 0x356d89e1b04541afc9704a45e9c535ce4a50929e33d7e06cL |
|||
test_signature_validity( Msg, Qx, Qy, R, S, True ) |
|||
|
|||
Msg = 0xf6227a8eeb34afed1621dcc89a91d72ea212cb2f476839d9b4243c66877911b37b4ad6f4448792a7bbba76c63bdd63414b6facab7dc71c3396a73bd7ee14cdd41a659c61c99b779cecf07bc51ab391aa3252386242b9853ea7da67fd768d303f1b9b513d401565b6f1eb722dfdb96b519fe4f9bd5de67ae131e64b40e78c42ddL |
|||
Qx = 0x16335dbe95f8e8254a4e04575d736befb258b8657f773cb7L |
|||
Qy = 0x421b13379c59bc9dce38a1099ca79bbd06d647c7f6242336L |
|||
R = 0x4141bd5d64ea36c5b0bd21ef28c02da216ed9d04522b1e91L |
|||
S = 0x159a6aa852bcc579e821b7bb0994c0861fb08280c38daa09L |
|||
test_signature_validity( Msg, Qx, Qy, R, S, False ) |
|||
|
|||
Msg = 0x16b5f93afd0d02246f662761ed8e0dd9504681ed02a253006eb36736b563097ba39f81c8e1bce7a16c1339e345efabbc6baa3efb0612948ae51103382a8ee8bc448e3ef71e9f6f7a9676694831d7f5dd0db5446f179bcb737d4a526367a447bfe2c857521c7f40b6d7d7e01a180d92431fb0bbd29c04a0c420a57b3ed26ccd8aL |
|||
Qx = 0xfd14cdf1607f5efb7b1793037b15bdf4baa6f7c16341ab0bL |
|||
Qy = 0x83fa0795cc6c4795b9016dac928fd6bac32f3229a96312c4L |
|||
R = 0x8dfdb832951e0167c5d762a473c0416c5c15bc1195667dc1L |
|||
S = 0x1720288a2dc13fa1ec78f763f8fe2ff7354a7e6fdde44520L |
|||
test_signature_validity( Msg, Qx, Qy, R, S, False ) |
|||
|
|||
Msg = 0x08a2024b61b79d260e3bb43ef15659aec89e5b560199bc82cf7c65c77d39192e03b9a895d766655105edd9188242b91fbde4167f7862d4ddd61e5d4ab55196683d4f13ceb90d87aea6e07eb50a874e33086c4a7cb0273a8e1c4408f4b846bceae1ebaac1b2b2ea851a9b09de322efe34cebe601653efd6ddc876ce8c2f2072fbL |
|||
Qx = 0x674f941dc1a1f8b763c9334d726172d527b90ca324db8828L |
|||
Qy = 0x65adfa32e8b236cb33a3e84cf59bfb9417ae7e8ede57a7ffL |
|||
R = 0x9508b9fdd7daf0d8126f9e2bc5a35e4c6d800b5b804d7796L |
|||
S = 0x36f2bf6b21b987c77b53bb801b3435a577e3d493744bfab0L |
|||
test_signature_validity( Msg, Qx, Qy, R, S, False ) |
|||
|
|||
Msg = 0x1843aba74b0789d4ac6b0b8923848023a644a7b70afa23b1191829bbe4397ce15b629bf21a8838298653ed0c19222b95fa4f7390d1b4c844d96e645537e0aae98afb5c0ac3bd0e4c37f8daaff25556c64e98c319c52687c904c4de7240a1cc55cd9756b7edaef184e6e23b385726e9ffcba8001b8f574987c1a3fedaaa83ca6dL |
|||
Qx = 0x10ecca1aad7220b56a62008b35170bfd5e35885c4014a19fL |
|||
Qy = 0x04eb61984c6c12ade3bc47f3c629ece7aa0a033b9948d686L |
|||
R = 0x82bfa4e82c0dfe9274169b86694e76ce993fd83b5c60f325L |
|||
S = 0xa97685676c59a65dbde002fe9d613431fb183e8006d05633L |
|||
test_signature_validity( Msg, Qx, Qy, R, S, False ) |
|||
|
|||
Msg = 0x5a478f4084ddd1a7fea038aa9732a822106385797d02311aeef4d0264f824f698df7a48cfb6b578cf3da416bc0799425bb491be5b5ecc37995b85b03420a98f2c4dc5c31a69a379e9e322fbe706bbcaf0f77175e05cbb4fa162e0da82010a278461e3e974d137bc746d1880d6eb02aa95216014b37480d84b87f717bb13f76e1L |
|||
Qx = 0x6636653cb5b894ca65c448277b29da3ad101c4c2300f7c04L |
|||
Qy = 0xfdf1cbb3fc3fd6a4f890b59e554544175fa77dbdbeb656c1L |
|||
R = 0xeac2ddecddfb79931a9c3d49c08de0645c783a24cb365e1cL |
|||
S = 0x3549fee3cfa7e5f93bc47d92d8ba100e881a2a93c22f8d50L |
|||
test_signature_validity( Msg, Qx, Qy, R, S, False ) |
|||
|
|||
Msg = 0xc598774259a058fa65212ac57eaa4f52240e629ef4c310722088292d1d4af6c39b49ce06ba77e4247b20637174d0bd67c9723feb57b5ead232b47ea452d5d7a089f17c00b8b6767e434a5e16c231ba0efa718a340bf41d67ea2d295812ff1b9277daacb8bc27b50ea5e6443bcf95ef4e9f5468fe78485236313d53d1c68f6ba2L |
|||
Qx = 0xa82bd718d01d354001148cd5f69b9ebf38ff6f21898f8aaaL |
|||
Qy = 0xe67ceede07fc2ebfafd62462a51e4b6c6b3d5b537b7caf3eL |
|||
R = 0x4d292486c620c3de20856e57d3bb72fcde4a73ad26376955L |
|||
S = 0xa85289591a6081d5728825520e62ff1c64f94235c04c7f95L |
|||
test_signature_validity( Msg, Qx, Qy, R, S, False ) |
|||
|
|||
Msg = 0xca98ed9db081a07b7557f24ced6c7b9891269a95d2026747add9e9eb80638a961cf9c71a1b9f2c29744180bd4c3d3db60f2243c5c0b7cc8a8d40a3f9a7fc910250f2187136ee6413ffc67f1a25e1c4c204fa9635312252ac0e0481d89b6d53808f0c496ba87631803f6c572c1f61fa049737fdacce4adff757afed4f05beb658L |
|||
Qx = 0x7d3b016b57758b160c4fca73d48df07ae3b6b30225126c2fL |
|||
Qy = 0x4af3790d9775742bde46f8da876711be1b65244b2b39e7ecL |
|||
R = 0x95f778f5f656511a5ab49a5d69ddd0929563c29cbc3a9e62L |
|||
S = 0x75c87fc358c251b4c83d2dd979faad496b539f9f2ee7a289L |
|||
test_signature_validity( Msg, Qx, Qy, R, S, False ) |
|||
|
|||
Msg = 0x31dd9a54c8338bea06b87eca813d555ad1850fac9742ef0bbe40dad400e10288acc9c11ea7dac79eb16378ebea9490e09536099f1b993e2653cd50240014c90a9c987f64545abc6a536b9bd2435eb5e911fdfde2f13be96ea36ad38df4ae9ea387b29cced599af777338af2794820c9cce43b51d2112380a35802ab7e396c97aL |
|||
Qx = 0x9362f28c4ef96453d8a2f849f21e881cd7566887da8beb4aL |
|||
Qy = 0xe64d26d8d74c48a024ae85d982ee74cd16046f4ee5333905L |
|||
R = 0xf3923476a296c88287e8de914b0b324ad5a963319a4fe73bL |
|||
S = 0xf0baeed7624ed00d15244d8ba2aede085517dbdec8ac65f5L |
|||
test_signature_validity( Msg, Qx, Qy, R, S, True ) |
|||
|
|||
Msg = 0xb2b94e4432267c92f9fdb9dc6040c95ffa477652761290d3c7de312283f6450d89cc4aabe748554dfb6056b2d8e99c7aeaad9cdddebdee9dbc099839562d9064e68e7bb5f3a6bba0749ca9a538181fc785553a4000785d73cc207922f63e8ce1112768cb1de7b673aed83a1e4a74592f1268d8e2a4e9e63d414b5d442bd0456dL |
|||
Qx = 0xcc6fc032a846aaac25533eb033522824f94e670fa997ecefL |
|||
Qy = 0xe25463ef77a029eccda8b294fd63dd694e38d223d30862f1L |
|||
R = 0x066b1d07f3a40e679b620eda7f550842a35c18b80c5ebe06L |
|||
S = 0xa0b0fb201e8f2df65e2c4508ef303bdc90d934016f16b2dcL |
|||
test_signature_validity( Msg, Qx, Qy, R, S, False ) |
|||
|
|||
Msg = 0x4366fcadf10d30d086911de30143da6f579527036937007b337f7282460eae5678b15cccda853193ea5fc4bc0a6b9d7a31128f27e1214988592827520b214eed5052f7775b750b0c6b15f145453ba3fee24a085d65287e10509eb5d5f602c440341376b95c24e5c4727d4b859bfe1483d20538acdd92c7997fa9c614f0f839d7L |
|||
Qx = 0x955c908fe900a996f7e2089bee2f6376830f76a19135e753L |
|||
Qy = 0xba0c42a91d3847de4a592a46dc3fdaf45a7cc709b90de520L |
|||
R = 0x1f58ad77fc04c782815a1405b0925e72095d906cbf52a668L |
|||
S = 0xf2e93758b3af75edf784f05a6761c9b9a6043c66b845b599L |
|||
test_signature_validity( Msg, Qx, Qy, R, S, False ) |
|||
|
|||
Msg = 0x543f8af57d750e33aa8565e0cae92bfa7a1ff78833093421c2942cadf9986670a5ff3244c02a8225e790fbf30ea84c74720abf99cfd10d02d34377c3d3b41269bea763384f372bb786b5846f58932defa68023136cd571863b304886e95e52e7877f445b9364b3f06f3c28da12707673fecb4b8071de06b6e0a3c87da160cef3L |
|||
Qx = 0x31f7fa05576d78a949b24812d4383107a9a45bb5fccdd835L |
|||
Qy = 0x8dc0eb65994a90f02b5e19bd18b32d61150746c09107e76bL |
|||
R = 0xbe26d59e4e883dde7c286614a767b31e49ad88789d3a78ffL |
|||
S = 0x8762ca831c1ce42df77893c9b03119428e7a9b819b619068L |
|||
test_signature_validity( Msg, Qx, Qy, R, S, False ) |
|||
|
|||
Msg = 0xd2e8454143ce281e609a9d748014dcebb9d0bc53adb02443a6aac2ffe6cb009f387c346ecb051791404f79e902ee333ad65e5c8cb38dc0d1d39a8dc90add5023572720e5b94b190d43dd0d7873397504c0c7aef2727e628eb6a74411f2e400c65670716cb4a815dc91cbbfeb7cfe8c929e93184c938af2c078584da045e8f8d1L |
|||
Qx = 0x66aa8edbbdb5cf8e28ceb51b5bda891cae2df84819fe25c0L |
|||
Qy = 0x0c6bc2f69030a7ce58d4a00e3b3349844784a13b8936f8daL |
|||
R = 0xa4661e69b1734f4a71b788410a464b71e7ffe42334484f23L |
|||
S = 0x738421cf5e049159d69c57a915143e226cac8355e149afe9L |
|||
test_signature_validity( Msg, Qx, Qy, R, S, False ) |
|||
|
|||
Msg = 0x6660717144040f3e2f95a4e25b08a7079c702a8b29babad5a19a87654bc5c5afa261512a11b998a4fb36b5d8fe8bd942792ff0324b108120de86d63f65855e5461184fc96a0a8ffd2ce6d5dfb0230cbbdd98f8543e361b3205f5da3d500fdc8bac6db377d75ebef3cb8f4d1ff738071ad0938917889250b41dd1d98896ca06fbL |
|||
Qx = 0xbcfacf45139b6f5f690a4c35a5fffa498794136a2353fc77L |
|||
Qy = 0x6f4a6c906316a6afc6d98fe1f0399d056f128fe0270b0f22L |
|||
R = 0x9db679a3dafe48f7ccad122933acfe9da0970b71c94c21c1L |
|||
S = 0x984c2db99827576c0a41a5da41e07d8cc768bc82f18c9da9L |
|||
test_signature_validity( Msg, Qx, Qy, R, S, False ) |
|||
|
|||
|
|||
|
|||
print "Testing the example code:" |
|||
|
|||
# Building a public/private key pair from the NIST Curve P-192: |
|||
|
|||
g = generator_192 |
|||
n = g.order() |
|||
|
|||
# (random.SystemRandom is supposed to provide |
|||
# crypto-quality random numbers, but as Debian recently |
|||
# illustrated, a systems programmer can accidentally |
|||
# demolish this security, so in serious applications |
|||
# further precautions are appropriate.) |
|||
|
|||
randrange = random.SystemRandom().randrange |
|||
|
|||
secret = randrange( 1, n ) |
|||
pubkey = Public_key( g, g * secret ) |
|||
privkey = Private_key( pubkey, secret ) |
|||
|
|||
# Signing a hash value: |
|||
|
|||
hash = randrange( 1, n ) |
|||
signature = privkey.sign( hash, randrange( 1, n ) ) |
|||
|
|||
# Verifying a signature for a hash value: |
|||
|
|||
if pubkey.verifies( hash, signature ): |
|||
print "Demo verification succeeded." |
|||
else: |
|||
raise TestFailure("*** Demo verification failed.") |
|||
|
|||
if pubkey.verifies( hash-1, signature ): |
|||
raise TestFailure( "**** Demo verification failed to reject tampered hash.") |
|||
else: |
|||
print "Demo verification correctly rejected tampered hash." |
|||
|
|||
if __name__ == "__main__": |
|||
__main__() |
@ -1,290 +0,0 @@ |
|||
#! /usr/bin/env python |
|||
# |
|||
# Implementation of elliptic curves, for cryptographic applications. |
|||
# |
|||
# This module doesn't provide any way to choose a random elliptic |
|||
# curve, nor to verify that an elliptic curve was chosen randomly, |
|||
# because one can simply use NIST's standard curves. |
|||
# |
|||
# Notes from X9.62-1998 (draft): |
|||
# Nomenclature: |
|||
# - Q is a public key. |
|||
# The "Elliptic Curve Domain Parameters" include: |
|||
# - q is the "field size", which in our case equals p. |
|||
# - p is a big prime. |
|||
# - G is a point of prime order (5.1.1.1). |
|||
# - n is the order of G (5.1.1.1). |
|||
# Public-key validation (5.2.2): |
|||
# - Verify that Q is not the point at infinity. |
|||
# - Verify that X_Q and Y_Q are in [0,p-1]. |
|||
# - Verify that Q is on the curve. |
|||
# - Verify that nQ is the point at infinity. |
|||
# Signature generation (5.3): |
|||
# - Pick random k from [1,n-1]. |
|||
# Signature checking (5.4.2): |
|||
# - Verify that r and s are in [1,n-1]. |
|||
# |
|||
# Version of 2008.11.25. |
|||
# |
|||
# Revision history: |
|||
# 2005.12.31 - Initial version. |
|||
# 2008.11.25 - Change CurveFp.is_on to contains_point. |
|||
# |
|||
# Written in 2005 by Peter Pearson and placed in the public domain. |
|||
|
|||
import numbertheory |
|||
|
|||
class CurveFp( object ): |
|||
"""Elliptic Curve over the field of integers modulo a prime.""" |
|||
def __init__( self, p, a, b ): |
|||
"""The curve of points satisfying y^2 = x^3 + a*x + b (mod p).""" |
|||
self.__p = p |
|||
self.__a = a |
|||
self.__b = b |
|||
|
|||
def p( self ): |
|||
return self.__p |
|||
|
|||
def a( self ): |
|||
return self.__a |
|||
|
|||
def b( self ): |
|||
return self.__b |
|||
|
|||
def contains_point( self, x, y ): |
|||
"""Is the point (x,y) on this curve?""" |
|||
return ( y * y - ( x * x * x + self.__a * x + self.__b ) ) % self.__p == 0 |
|||
|
|||
|
|||
|
|||
class Point( object ): |
|||
"""A point on an elliptic curve. Altering x and y is forbidding, |
|||
but they can be read by the x() and y() methods.""" |
|||
def __init__( self, curve, x, y, order = None ): |
|||
"""curve, x, y, order; order (optional) is the order of this point.""" |
|||
self.__curve = curve |
|||
self.__x = x |
|||
self.__y = y |
|||
self.__order = order |
|||
# self.curve is allowed to be None only for INFINITY: |
|||
if self.__curve: assert self.__curve.contains_point( x, y ) |
|||
if order: assert self * order == INFINITY |
|||
|
|||
def __cmp__( self, other ): |
|||
"""Return 0 if the points are identical, 1 otherwise.""" |
|||
if self.__curve == other.__curve \ |
|||
and self.__x == other.__x \ |
|||
and self.__y == other.__y: |
|||
return 0 |
|||
else: |
|||
return 1 |
|||
|
|||
def __add__( self, other ): |
|||
"""Add one point to another point.""" |
|||
|
|||
# X9.62 B.3: |
|||
|
|||
if other == INFINITY: return self |
|||
if self == INFINITY: return other |
|||
assert self.__curve == other.__curve |
|||
if self.__x == other.__x: |
|||
if ( self.__y + other.__y ) % self.__curve.p() == 0: |
|||
return INFINITY |
|||
else: |
|||
return self.double() |
|||
|
|||
p = self.__curve.p() |
|||
|
|||
l = ( ( other.__y - self.__y ) * \ |
|||
numbertheory.inverse_mod( other.__x - self.__x, p ) ) % p |
|||
|
|||
x3 = ( l * l - self.__x - other.__x ) % p |
|||
y3 = ( l * ( self.__x - x3 ) - self.__y ) % p |
|||
|
|||
return Point( self.__curve, x3, y3 ) |
|||
|
|||
def __mul__( self, other ): |
|||
"""Multiply a point by an integer.""" |
|||
|
|||
def leftmost_bit( x ): |
|||
assert x > 0 |
|||
result = 1L |
|||
while result <= x: result = 2 * result |
|||
return result // 2 |
|||
|
|||
e = other |
|||
if self.__order: e = e % self.__order |
|||
if e == 0: return INFINITY |
|||
if self == INFINITY: return INFINITY |
|||
assert e > 0 |
|||
|
|||
# From X9.62 D.3.2: |
|||
|
|||
e3 = 3 * e |
|||
negative_self = Point( self.__curve, self.__x, -self.__y, self.__order ) |
|||
i = leftmost_bit( e3 ) // 2 |
|||
result = self |
|||
# print "Multiplying %s by %d (e3 = %d):" % ( self, other, e3 ) |
|||
while i > 1: |
|||
result = result.double() |
|||
if ( e3 & i ) != 0 and ( e & i ) == 0: result = result + self |
|||
if ( e3 & i ) == 0 and ( e & i ) != 0: result = result + negative_self |
|||
# print ". . . i = %d, result = %s" % ( i, result ) |
|||
i = i // 2 |
|||
|
|||
return result |
|||
|
|||
def __rmul__( self, other ): |
|||
"""Multiply a point by an integer.""" |
|||
|
|||
return self * other |
|||
|
|||
def __str__( self ): |
|||
if self == INFINITY: return "infinity" |
|||
return "(%d,%d)" % ( self.__x, self.__y ) |
|||
|
|||
def double( self ): |
|||
"""Return a new point that is twice the old.""" |
|||
|
|||
if self == INFINITY: |
|||
return INFINITY |
|||
|
|||
# X9.62 B.3: |
|||
|
|||
p = self.__curve.p() |
|||
a = self.__curve.a() |
|||
|
|||
l = ( ( 3 * self.__x * self.__x + a ) * \ |
|||
numbertheory.inverse_mod( 2 * self.__y, p ) ) % p |
|||
|
|||
x3 = ( l * l - 2 * self.__x ) % p |
|||
y3 = ( l * ( self.__x - x3 ) - self.__y ) % p |
|||
|
|||
return Point( self.__curve, x3, y3 ) |
|||
|
|||
def x( self ): |
|||
return self.__x |
|||
|
|||
def y( self ): |
|||
return self.__y |
|||
|
|||
def curve( self ): |
|||
return self.__curve |
|||
|
|||
def order( self ): |
|||
return self.__order |
|||
|
|||
|
|||
# This one point is the Point At Infinity for all purposes: |
|||
INFINITY = Point( None, None, None ) |
|||
|
|||
def __main__(): |
|||
|
|||
class FailedTest(Exception): pass |
|||
def test_add( c, x1, y1, x2, y2, x3, y3 ): |
|||
"""We expect that on curve c, (x1,y1) + (x2, y2 ) = (x3, y3).""" |
|||
p1 = Point( c, x1, y1 ) |
|||
p2 = Point( c, x2, y2 ) |
|||
p3 = p1 + p2 |
|||
print "%s + %s = %s" % ( p1, p2, p3 ), |
|||
if p3.x() != x3 or p3.y() != y3: |
|||
raise FailedTest("Failure: should give (%d,%d)." % ( x3, y3 )) |
|||
else: |
|||
print " Good." |
|||
|
|||
def test_double( c, x1, y1, x3, y3 ): |
|||
"""We expect that on curve c, 2*(x1,y1) = (x3, y3).""" |
|||
p1 = Point( c, x1, y1 ) |
|||
p3 = p1.double() |
|||
print "%s doubled = %s" % ( p1, p3 ), |
|||
if p3.x() != x3 or p3.y() != y3: |
|||
raise FailedTest("Failure: should give (%d,%d)." % ( x3, y3 )) |
|||
else: |
|||
print " Good." |
|||
|
|||
def test_double_infinity( c ): |
|||
"""We expect that on curve c, 2*INFINITY = INFINITY.""" |
|||
p1 = INFINITY |
|||
p3 = p1.double() |
|||
print "%s doubled = %s" % ( p1, p3 ), |
|||
if p3.x() != INFINITY.x() or p3.y() != INFINITY.y(): |
|||
raise FailedTest("Failure: should give (%d,%d)." % ( INFINITY.x(), INFINITY.y() )) |
|||
else: |
|||
print " Good." |
|||
|
|||
def test_multiply( c, x1, y1, m, x3, y3 ): |
|||
"""We expect that on curve c, m*(x1,y1) = (x3,y3).""" |
|||
p1 = Point( c, x1, y1 ) |
|||
p3 = p1 * m |
|||
print "%s * %d = %s" % ( p1, m, p3 ), |
|||
if p3.x() != x3 or p3.y() != y3: |
|||
raise FailedTest("Failure: should give (%d,%d)." % ( x3, y3 )) |
|||
else: |
|||
print " Good." |
|||
|
|||
|
|||
# A few tests from X9.62 B.3: |
|||
|
|||
c = CurveFp( 23, 1, 1 ) |
|||
test_add( c, 3, 10, 9, 7, 17, 20 ) |
|||
test_double( c, 3, 10, 7, 12 ) |
|||
test_add( c, 3, 10, 3, 10, 7, 12 ) # (Should just invoke double.) |
|||
test_multiply( c, 3, 10, 2, 7, 12 ) |
|||
|
|||
test_double_infinity(c) |
|||
|
|||
# From X9.62 I.1 (p. 96): |
|||
|
|||
g = Point( c, 13, 7, 7 ) |
|||
|
|||
check = INFINITY |
|||
for i in range( 7 + 1 ): |
|||
p = ( i % 7 ) * g |
|||
print "%s * %d = %s, expected %s . . ." % ( g, i, p, check ), |
|||
if p == check: |
|||
print " Good." |
|||
else: |
|||
raise FailedTest("Bad.") |
|||
check = check + g |
|||
|
|||
# NIST Curve P-192: |
|||
p = 6277101735386680763835789423207666416083908700390324961279L |
|||
r = 6277101735386680763835789423176059013767194773182842284081L |
|||
#s = 0x3045ae6fc8422f64ed579528d38120eae12196d5L |
|||
c = 0x3099d2bbbfcb2538542dcd5fb078b6ef5f3d6fe2c745de65L |
|||
b = 0x64210519e59c80e70fa7e9ab72243049feb8deecc146b9b1L |
|||
Gx = 0x188da80eb03090f67cbf20eb43a18800f4ff0afd82ff1012L |
|||
Gy = 0x07192b95ffc8da78631011ed6b24cdd573f977a11e794811L |
|||
|
|||
c192 = CurveFp( p, -3, b ) |
|||
p192 = Point( c192, Gx, Gy, r ) |
|||
|
|||
# Checking against some sample computations presented |
|||
# in X9.62: |
|||
|
|||
d = 651056770906015076056810763456358567190100156695615665659L |
|||
Q = d * p192 |
|||
if Q.x() != 0x62B12D60690CDCF330BABAB6E69763B471F994DD702D16A5L: |
|||
raise FailedTest("p192 * d came out wrong.") |
|||
else: |
|||
print "p192 * d came out right." |
|||
|
|||
k = 6140507067065001063065065565667405560006161556565665656654L |
|||
R = k * p192 |
|||
if R.x() != 0x885052380FF147B734C330C43D39B2C4A89F29B0F749FEADL \ |
|||
or R.y() != 0x9CF9FA1CBEFEFB917747A3BB29C072B9289C2547884FD835L: |
|||
raise FailedTest("k * p192 came out wrong.") |
|||
else: |
|||
print "k * p192 came out right." |
|||
|
|||
u1 = 2563697409189434185194736134579731015366492496392189760599L |
|||
u2 = 6266643813348617967186477710235785849136406323338782220568L |
|||
temp = u1 * p192 + u2 * Q |
|||
if temp.x() != 0x885052380FF147B734C330C43D39B2C4A89F29B0F749FEADL \ |
|||
or temp.y() != 0x9CF9FA1CBEFEFB917747A3BB29C072B9289C2547884FD835L: |
|||
raise FailedTest("u1 * p192 + u2 * Q came out wrong.") |
|||
else: |
|||
print "u1 * p192 + u2 * Q came out right." |
|||
|
|||
if __name__ == "__main__": |
|||
__main__() |
@ -1,252 +0,0 @@ |
|||
import binascii |
|||
|
|||
import ecdsa |
|||
import der |
|||
from curves import NIST192p, find_curve |
|||
from util import string_to_number, number_to_string, randrange |
|||
from util import sigencode_string, sigdecode_string |
|||
from util import oid_ecPublicKey, encoded_oid_ecPublicKey |
|||
from hashlib import sha1 |
|||
|
|||
class BadSignatureError(Exception): |
|||
pass |
|||
class BadDigestError(Exception): |
|||
pass |
|||
|
|||
class VerifyingKey: |
|||
def __init__(self, _error__please_use_generate=None): |
|||
if not _error__please_use_generate: |
|||
raise TypeError("Please use SigningKey.generate() to construct me") |
|||
|
|||
@classmethod |
|||
def from_public_point(klass, point, curve=NIST192p, hashfunc=sha1): |
|||
self = klass(_error__please_use_generate=True) |
|||
self.curve = curve |
|||
self.default_hashfunc = hashfunc |
|||
self.pubkey = ecdsa.Public_key(curve.generator, point) |
|||
self.pubkey.order = curve.order |
|||
return self |
|||
|
|||
@classmethod |
|||
def from_string(klass, string, curve=NIST192p, hashfunc=sha1): |
|||
order = curve.order |
|||
assert len(string) == curve.verifying_key_length, \ |
|||
(len(string), curve.verifying_key_length) |
|||
xs = string[:curve.baselen] |
|||
ys = string[curve.baselen:] |
|||
assert len(xs) == curve.baselen, (len(xs), curve.baselen) |
|||
assert len(ys) == curve.baselen, (len(ys), curve.baselen) |
|||
x = string_to_number(xs) |
|||
y = string_to_number(ys) |
|||
assert ecdsa.point_is_valid(curve.generator, x, y) |
|||
import ellipticcurve |
|||
point = ellipticcurve.Point(curve.curve, x, y, order) |
|||
return klass.from_public_point(point, curve, hashfunc) |
|||
|
|||
@classmethod |
|||
def from_pem(klass, string): |
|||
return klass.from_der(der.unpem(string)) |
|||
|
|||
@classmethod |
|||
def from_der(klass, string): |
|||
# [[oid_ecPublicKey,oid_curve], point_str_bitstring] |
|||
s1,empty = der.remove_sequence(string) |
|||
if empty != "": |
|||
raise der.UnexpectedDER("trailing junk after DER pubkey: %s" % |
|||
binascii.hexlify(empty)) |
|||
s2,point_str_bitstring = der.remove_sequence(s1) |
|||
# s2 = oid_ecPublicKey,oid_curve |
|||
oid_pk, rest = der.remove_object(s2) |
|||
oid_curve, empty = der.remove_object(rest) |
|||
if empty != "": |
|||
raise der.UnexpectedDER("trailing junk after DER pubkey objects: %s" % |
|||
binascii.hexlify(empty)) |
|||
assert oid_pk == oid_ecPublicKey, (oid_pk, oid_ecPublicKey) |
|||
curve = find_curve(oid_curve) |
|||
point_str, empty = der.remove_bitstring(point_str_bitstring) |
|||
if empty != "": |
|||
raise der.UnexpectedDER("trailing junk after pubkey pointstring: %s" % |
|||
binascii.hexlify(empty)) |
|||
assert point_str.startswith("\x00\x04") |
|||
return klass.from_string(point_str[2:], curve) |
|||
|
|||
def to_string(self): |
|||
# VerifyingKey.from_string(vk.to_string()) == vk as long as the |
|||
# curves are the same: the curve itself is not included in the |
|||
# serialized form |
|||
order = self.pubkey.order |
|||
x_str = number_to_string(self.pubkey.point.x(), order) |
|||
y_str = number_to_string(self.pubkey.point.y(), order) |
|||
return x_str + y_str |
|||
|
|||
def to_pem(self): |
|||
return der.topem(self.to_der(), "PUBLIC KEY") |
|||
|
|||
def to_der(self): |
|||
order = self.pubkey.order |
|||
x_str = number_to_string(self.pubkey.point.x(), order) |
|||
y_str = number_to_string(self.pubkey.point.y(), order) |
|||
point_str = "\x00\x04" + x_str + y_str |
|||
return der.encode_sequence(der.encode_sequence(encoded_oid_ecPublicKey, |
|||
self.curve.encoded_oid), |
|||
der.encode_bitstring(point_str)) |
|||
|
|||
def verify(self, signature, data, hashfunc=None, sigdecode=sigdecode_string): |
|||
hashfunc = hashfunc or self.default_hashfunc |
|||
digest = hashfunc(data).digest() |
|||
return self.verify_digest(signature, digest, sigdecode) |
|||
|
|||
def verify_digest(self, signature, digest, sigdecode=sigdecode_string): |
|||
if len(digest) > self.curve.baselen: |
|||
raise BadDigestError("this curve (%s) is too short " |
|||
"for your digest (%d)" % (self.curve.name, |
|||
8*len(digest))) |
|||
number = string_to_number(digest) |
|||
r, s = sigdecode(signature, self.pubkey.order) |
|||
sig = ecdsa.Signature(r, s) |
|||
if self.pubkey.verifies(number, sig): |
|||
return True |
|||
raise BadSignatureError |
|||
|
|||
class SigningKey: |
|||
def __init__(self, _error__please_use_generate=None): |
|||
if not _error__please_use_generate: |
|||
raise TypeError("Please use SigningKey.generate() to construct me") |
|||
|
|||
@classmethod |
|||
def generate(klass, curve=NIST192p, entropy=None, hashfunc=sha1): |
|||
secexp = randrange(curve.order, entropy) |
|||
return klass.from_secret_exponent(secexp, curve, hashfunc) |
|||
|
|||
# to create a signing key from a short (arbitrary-length) seed, convert |
|||
# that seed into an integer with something like |
|||
# secexp=util.randrange_from_seed__X(seed, curve.order), and then pass |
|||
# that integer into SigningKey.from_secret_exponent(secexp, curve) |
|||
|
|||
@classmethod |
|||
def from_secret_exponent(klass, secexp, curve=NIST192p, hashfunc=sha1): |
|||
self = klass(_error__please_use_generate=True) |
|||
self.curve = curve |
|||
self.default_hashfunc = hashfunc |
|||
self.baselen = curve.baselen |
|||
n = curve.order |
|||
assert 1 <= secexp < n |
|||
pubkey_point = curve.generator*secexp |
|||
pubkey = ecdsa.Public_key(curve.generator, pubkey_point) |
|||
pubkey.order = n |
|||
self.verifying_key = VerifyingKey.from_public_point(pubkey_point, curve, |
|||
hashfunc) |
|||
self.privkey = ecdsa.Private_key(pubkey, secexp) |
|||
self.privkey.order = n |
|||
return self |
|||
|
|||
@classmethod |
|||
def from_string(klass, string, curve=NIST192p, hashfunc=sha1): |
|||
assert len(string) == curve.baselen, (len(string), curve.baselen) |
|||
secexp = string_to_number(string) |
|||
return klass.from_secret_exponent(secexp, curve, hashfunc) |
|||
|
|||
@classmethod |
|||
def from_pem(klass, string, hashfunc=sha1): |
|||
# the privkey pem file has two sections: "EC PARAMETERS" and "EC |
|||
# PRIVATE KEY". The first is redundant. |
|||
privkey_pem = string[string.index("-----BEGIN EC PRIVATE KEY-----"):] |
|||
return klass.from_der(der.unpem(privkey_pem), hashfunc) |
|||
@classmethod |
|||
def from_der(klass, string, hashfunc=sha1): |
|||
# SEQ([int(1), octetstring(privkey),cont[0], oid(secp224r1), |
|||
# cont[1],bitstring]) |
|||
s, empty = der.remove_sequence(string) |
|||
if empty != "": |
|||
raise der.UnexpectedDER("trailing junk after DER privkey: %s" % |
|||
binascii.hexlify(empty)) |
|||
one, s = der.remove_integer(s) |
|||
if one != 1: |
|||
raise der.UnexpectedDER("expected '1' at start of DER privkey," |
|||
" got %d" % one) |
|||
privkey_str, s = der.remove_octet_string(s) |
|||
tag, curve_oid_str, s = der.remove_constructed(s) |
|||
if tag != 0: |
|||
raise der.UnexpectedDER("expected tag 0 in DER privkey," |
|||
" got %d" % tag) |
|||
curve_oid, empty = der.remove_object(curve_oid_str) |
|||
if empty != "": |
|||
raise der.UnexpectedDER("trailing junk after DER privkey " |
|||
"curve_oid: %s" % binascii.hexlify(empty)) |
|||
curve = find_curve(curve_oid) |
|||
|
|||
# we don't actually care about the following fields |
|||
# |
|||
#tag, pubkey_bitstring, s = der.remove_constructed(s) |
|||
#if tag != 1: |
|||
# raise der.UnexpectedDER("expected tag 1 in DER privkey, got %d" |
|||
# % tag) |
|||
#pubkey_str = der.remove_bitstring(pubkey_bitstring) |
|||
#if empty != "": |
|||
# raise der.UnexpectedDER("trailing junk after DER privkey " |
|||
# "pubkeystr: %s" % binascii.hexlify(empty)) |
|||
|
|||
# our from_string method likes fixed-length privkey strings |
|||
if len(privkey_str) < curve.baselen: |
|||
privkey_str = "\x00"*(curve.baselen-len(privkey_str)) + privkey_str |
|||
return klass.from_string(privkey_str, curve, hashfunc) |
|||
|
|||
def to_string(self): |
|||
secexp = self.privkey.secret_multiplier |
|||
s = number_to_string(secexp, self.privkey.order) |
|||
return s |
|||
|
|||
def to_pem(self): |
|||
# TODO: "BEGIN ECPARAMETERS" |
|||
return der.topem(self.to_der(), "EC PRIVATE KEY") |
|||
|
|||
def to_der(self): |
|||
# SEQ([int(1), octetstring(privkey),cont[0], oid(secp224r1), |
|||
# cont[1],bitstring]) |
|||
encoded_vk = "\x00\x04" + self.get_verifying_key().to_string() |
|||
return der.encode_sequence(der.encode_integer(1), |
|||
der.encode_octet_string(self.to_string()), |
|||
der.encode_constructed(0, self.curve.encoded_oid), |
|||
der.encode_constructed(1, der.encode_bitstring(encoded_vk)), |
|||
) |
|||
|
|||
def get_verifying_key(self): |
|||
return self.verifying_key |
|||
|
|||
def sign(self, data, entropy=None, hashfunc=None, sigencode=sigencode_string): |
|||
""" |
|||
hashfunc= should behave like hashlib.sha1 . The output length of the |
|||
hash (in bytes) must not be longer than the length of the curve order |
|||
(rounded up to the nearest byte), so using SHA256 with nist256p is |
|||
ok, but SHA256 with nist192p is not. (In the 2**-96ish unlikely event |
|||
of a hash output larger than the curve order, the hash will |
|||
effectively be wrapped mod n). |
|||
|
|||
Use hashfunc=hashlib.sha1 to match openssl's -ecdsa-with-SHA1 mode, |
|||
or hashfunc=hashlib.sha256 for openssl-1.0.0's -ecdsa-with-SHA256. |
|||
""" |
|||
|
|||
hashfunc = hashfunc or self.default_hashfunc |
|||
h = hashfunc(data).digest() |
|||
return self.sign_digest(h, entropy, sigencode) |
|||
|
|||
def sign_digest(self, digest, entropy=None, sigencode=sigencode_string): |
|||
if len(digest) > self.curve.baselen: |
|||
raise BadDigestError("this curve (%s) is too short " |
|||
"for your digest (%d)" % (self.curve.name, |
|||
8*len(digest))) |
|||
number = string_to_number(digest) |
|||
r, s = self.sign_number(number, entropy) |
|||
return sigencode(r, s, self.privkey.order) |
|||
|
|||
def sign_number(self, number, entropy=None): |
|||
# returns a pair of numbers |
|||
order = self.privkey.order |
|||
# privkey.sign() may raise RuntimeError in the amazingly unlikely |
|||
# (2**-192) event that r=0 or s=0, because that would leak the key. |
|||
# We could re-try with a different 'k', but we couldn't test that |
|||
# code, so I choose to allow the signature to fail instead. |
|||
k = randrange(order, entropy) |
|||
assert 1 <= k < order |
|||
sig = self.privkey.sign(number, k) |
|||
return sig.r, sig.s |
@ -1,614 +0,0 @@ |
|||
#! /usr/bin/env python |
|||
# |
|||
# Provide some simple capabilities from number theory. |
|||
# |
|||
# Version of 2008.11.14. |
|||
# |
|||
# Written in 2005 and 2006 by Peter Pearson and placed in the public domain. |
|||
# Revision history: |
|||
# 2008.11.14: Use pow( base, exponent, modulus ) for modular_exp. |
|||
# Make gcd and lcm accept arbitrarly many arguments. |
|||
|
|||
|
|||
|
|||
import math |
|||
import types |
|||
|
|||
|
|||
class Error( Exception ): |
|||
"""Base class for exceptions in this module.""" |
|||
pass |
|||
|
|||
class SquareRootError( Error ): |
|||
pass |
|||
|
|||
class NegativeExponentError( Error ): |
|||
pass |
|||
|
|||
|
|||
def modular_exp( base, exponent, modulus ): |
|||
"Raise base to exponent, reducing by modulus" |
|||
if exponent < 0: |
|||
raise NegativeExponentError( "Negative exponents (%d) not allowed" \ |
|||
% exponent ) |
|||
return pow( base, exponent, modulus ) |
|||
# result = 1L |
|||
# x = exponent |
|||
# b = base + 0L |
|||
# while x > 0: |
|||
# if x % 2 > 0: result = (result * b) % modulus |
|||
# x = x // 2 |
|||
# b = ( b * b ) % modulus |
|||
# return result |
|||
|
|||
|
|||
def polynomial_reduce_mod( poly, polymod, p ): |
|||
"""Reduce poly by polymod, integer arithmetic modulo p. |
|||
|
|||
Polynomials are represented as lists of coefficients |
|||
of increasing powers of x.""" |
|||
|
|||
# This module has been tested only by extensive use |
|||
# in calculating modular square roots. |
|||
|
|||
# Just to make this easy, require a monic polynomial: |
|||
assert polymod[-1] == 1 |
|||
|
|||
assert len( polymod ) > 1 |
|||
|
|||
while len( poly ) >= len( polymod ): |
|||
if poly[-1] != 0: |
|||
for i in range( 2, len( polymod ) + 1 ): |
|||
poly[-i] = ( poly[-i] - poly[-1] * polymod[-i] ) % p |
|||
poly = poly[0:-1] |
|||
|
|||
return poly |
|||
|
|||
|
|||
|
|||
def polynomial_multiply_mod( m1, m2, polymod, p ): |
|||
"""Polynomial multiplication modulo a polynomial over ints mod p. |
|||
|
|||
Polynomials are represented as lists of coefficients |
|||
of increasing powers of x.""" |
|||
|
|||
# This is just a seat-of-the-pants implementation. |
|||
|
|||
# This module has been tested only by extensive use |
|||
# in calculating modular square roots. |
|||
|
|||
# Initialize the product to zero: |
|||
|
|||
prod = ( len( m1 ) + len( m2 ) - 1 ) * [0] |
|||
|
|||
# Add together all the cross-terms: |
|||
|
|||
for i in range( len( m1 ) ): |
|||
for j in range( len( m2 ) ): |
|||
prod[i+j] = ( prod[i+j] + m1[i] * m2[j] ) % p |
|||
|
|||
return polynomial_reduce_mod( prod, polymod, p ) |
|||
|
|||
|
|||
|
|||
|
|||
def polynomial_exp_mod( base, exponent, polymod, p ): |
|||
"""Polynomial exponentiation modulo a polynomial over ints mod p. |
|||
|
|||
Polynomials are represented as lists of coefficients |
|||
of increasing powers of x.""" |
|||
|
|||
# Based on the Handbook of Applied Cryptography, algorithm 2.227. |
|||
|
|||
# This module has been tested only by extensive use |
|||
# in calculating modular square roots. |
|||
|
|||
assert exponent < p |
|||
|
|||
if exponent == 0: return [ 1 ] |
|||
|
|||
G = base |
|||
k = exponent |
|||
if k%2 == 1: s = G |
|||
else: s = [ 1 ] |
|||
|
|||
while k > 1: |
|||
k = k // 2 |
|||
G = polynomial_multiply_mod( G, G, polymod, p ) |
|||
if k%2 == 1: s = polynomial_multiply_mod( G, s, polymod, p ) |
|||
|
|||
return s |
|||
|
|||
|
|||
|
|||
def jacobi( a, n ): |
|||
"""Jacobi symbol""" |
|||
|
|||
# Based on the Handbook of Applied Cryptography (HAC), algorithm 2.149. |
|||
|
|||
# This function has been tested by comparison with a small |
|||
# table printed in HAC, and by extensive use in calculating |
|||
# modular square roots. |
|||
|
|||
assert n >= 3 |
|||
assert n%2 == 1 |
|||
a = a % n |
|||
if a == 0: return 0 |
|||
if a == 1: return 1 |
|||
a1, e = a, 0 |
|||
while a1%2 == 0: |
|||
a1, e = a1//2, e+1 |
|||
if e%2 == 0 or n%8 == 1 or n%8 == 7: s = 1 |
|||
else: s = -1 |
|||
if a1 == 1: return s |
|||
if n%4 == 3 and a1%4 == 3: s = -s |
|||
return s * jacobi( n % a1, a1 ) |
|||
|
|||
|
|||
|
|||
|
|||
def square_root_mod_prime( a, p ): |
|||
"""Modular square root of a, mod p, p prime.""" |
|||
|
|||
# Based on the Handbook of Applied Cryptography, algorithms 3.34 to 3.39. |
|||
|
|||
# This module has been tested for all values in [0,p-1] for |
|||
# every prime p from 3 to 1229. |
|||
|
|||
assert 0 <= a < p |
|||
assert 1 < p |
|||
|
|||
if a == 0: return 0 |
|||
if p == 2: return a |
|||
|
|||
jac = jacobi( a, p ) |
|||
if jac == -1: raise SquareRootError( "%d has no square root modulo %d" \ |
|||
% ( a, p ) ) |
|||
|
|||
if p % 4 == 3: return modular_exp( a, (p+1)//4, p ) |
|||
|
|||
if p % 8 == 5: |
|||
d = modular_exp( a, (p-1)//4, p ) |
|||
if d == 1: return modular_exp( a, (p+3)//8, p ) |
|||
if d == p-1: return ( 2 * a * modular_exp( 4*a, (p-5)//8, p ) ) % p |
|||
raise RuntimeError, "Shouldn't get here." |
|||
|
|||
for b in range( 2, p ): |
|||
if jacobi( b*b-4*a, p ) == -1: |
|||
f = ( a, -b, 1 ) |
|||
ff = polynomial_exp_mod( ( 0, 1 ), (p+1)//2, f, p ) |
|||
assert ff[1] == 0 |
|||
return ff[0] |
|||
raise RuntimeError, "No b found." |
|||
|
|||
|
|||
|
|||
def inverse_mod( a, m ): |
|||
"""Inverse of a mod m.""" |
|||
|
|||
if a < 0 or m <= a: a = a % m |
|||
|
|||
# From Ferguson and Schneier, roughly: |
|||
|
|||
c, d = a, m |
|||
uc, vc, ud, vd = 1, 0, 0, 1 |
|||
while c != 0: |
|||
q, c, d = divmod( d, c ) + ( c, ) |
|||
uc, vc, ud, vd = ud - q*uc, vd - q*vc, uc, vc |
|||
|
|||
# At this point, d is the GCD, and ud*a+vd*m = d. |
|||
# If d == 1, this means that ud is a inverse. |
|||
|
|||
assert d == 1 |
|||
if ud > 0: return ud |
|||
else: return ud + m |
|||
|
|||
|
|||
def gcd2(a, b): |
|||
"""Greatest common divisor using Euclid's algorithm.""" |
|||
while a: |
|||
a, b = b%a, a |
|||
return b |
|||
|
|||
|
|||
def gcd( *a ): |
|||
"""Greatest common divisor. |
|||
|
|||
Usage: gcd( [ 2, 4, 6 ] ) |
|||
or: gcd( 2, 4, 6 ) |
|||
""" |
|||
|
|||
if len( a ) > 1: return reduce( gcd2, a ) |
|||
if hasattr( a[0], "__iter__" ): return reduce( gcd2, a[0] ) |
|||
return a[0] |
|||
|
|||
|
|||
def lcm2(a,b): |
|||
"""Least common multiple of two integers.""" |
|||
|
|||
return (a*b)//gcd(a,b) |
|||
|
|||
|
|||
def lcm( *a ): |
|||
"""Least common multiple. |
|||
|
|||
Usage: lcm( [ 3, 4, 5 ] ) |
|||
or: lcm( 3, 4, 5 ) |
|||
""" |
|||
|
|||
if len( a ) > 1: return reduce( lcm2, a ) |
|||
if hasattr( a[0], "__iter__" ): return reduce( lcm2, a[0] ) |
|||
return a[0] |
|||
|
|||
|
|||
|
|||
def factorization( n ): |
|||
"""Decompose n into a list of (prime,exponent) pairs.""" |
|||
|
|||
assert isinstance( n, types.IntType ) or isinstance( n, types.LongType ) |
|||
|
|||
if n < 2: return [] |
|||
|
|||
result = [] |
|||
d = 2 |
|||
|
|||
# Test the small primes: |
|||
|
|||
for d in smallprimes: |
|||
if d > n: break |
|||
q, r = divmod( n, d ) |
|||
if r == 0: |
|||
count = 1 |
|||
while d <= n: |
|||
n = q |
|||
q, r = divmod( n, d ) |
|||
if r != 0: break |
|||
count = count + 1 |
|||
result.append( ( d, count ) ) |
|||
|
|||
# If n is still greater than the last of our small primes, |
|||
# it may require further work: |
|||
|
|||
if n > smallprimes[-1]: |
|||
if is_prime( n ): # If what's left is prime, it's easy: |
|||
result.append( ( n, 1 ) ) |
|||
else: # Ugh. Search stupidly for a divisor: |
|||
d = smallprimes[-1] |
|||
while 1: |
|||
d = d + 2 # Try the next divisor. |
|||
q, r = divmod( n, d ) |
|||
if q < d: break # n < d*d means we're done, n = 1 or prime. |
|||
if r == 0: # d divides n. How many times? |
|||
count = 1 |
|||
n = q |
|||
while d <= n: # As long as d might still divide n, |
|||
q, r = divmod( n, d ) # see if it does. |
|||
if r != 0: break |
|||
n = q # It does. Reduce n, increase count. |
|||
count = count + 1 |
|||
result.append( ( d, count ) ) |
|||
if n > 1: result.append( ( n, 1 ) ) |
|||
|
|||
return result |
|||
|
|||
|
|||
|
|||
def phi( n ): |
|||
"""Return the Euler totient function of n.""" |
|||
|
|||
assert isinstance( n, types.IntType ) or isinstance( n, types.LongType ) |
|||
|
|||
if n < 3: return 1 |
|||
|
|||
result = 1 |
|||
ff = factorization( n ) |
|||
for f in ff: |
|||
e = f[1] |
|||
if e > 1: |
|||
result = result * f[0] ** (e-1) * ( f[0] - 1 ) |
|||
else: |
|||
result = result * ( f[0] - 1 ) |
|||
return result |
|||
|
|||
|
|||
def carmichael( n ): |
|||
"""Return Carmichael function of n. |
|||
|
|||
Carmichael(n) is the smallest integer x such that |
|||
m**x = 1 mod n for all m relatively prime to n. |
|||
""" |
|||
|
|||
return carmichael_of_factorized( factorization( n ) ) |
|||
|
|||
|
|||
def carmichael_of_factorized( f_list ): |
|||
"""Return the Carmichael function of a number that is |
|||
represented as a list of (prime,exponent) pairs. |
|||
""" |
|||
|
|||
if len( f_list ) < 1: return 1 |
|||
|
|||
result = carmichael_of_ppower( f_list[0] ) |
|||
for i in range( 1, len( f_list ) ): |
|||
result = lcm( result, carmichael_of_ppower( f_list[i] ) ) |
|||
|
|||
return result |
|||
|
|||
def carmichael_of_ppower( pp ): |
|||
"""Carmichael function of the given power of the given prime. |
|||
""" |
|||
|
|||
p, a = pp |
|||
if p == 2 and a > 2: return 2**(a-2) |
|||
else: return (p-1) * p**(a-1) |
|||
|
|||
|
|||
|
|||
def order_mod( x, m ): |
|||
"""Return the order of x in the multiplicative group mod m. |
|||
""" |
|||
|
|||
# Warning: this implementation is not very clever, and will |
|||
# take a long time if m is very large. |
|||
|
|||
if m <= 1: return 0 |
|||
|
|||
assert gcd( x, m ) == 1 |
|||
|
|||
z = x |
|||
result = 1 |
|||
while z != 1: |
|||
z = ( z * x ) % m |
|||
result = result + 1 |
|||
return result |
|||
|
|||
|
|||
def largest_factor_relatively_prime( a, b ): |
|||
"""Return the largest factor of a relatively prime to b. |
|||
""" |
|||
|
|||
while 1: |
|||
d = gcd( a, b ) |
|||
if d <= 1: break |
|||
b = d |
|||
while 1: |
|||
q, r = divmod( a, d ) |
|||
if r > 0: |
|||
break |
|||
a = q |
|||
return a |
|||
|
|||
|
|||
def kinda_order_mod( x, m ): |
|||
"""Return the order of x in the multiplicative group mod m', |
|||
where m' is the largest factor of m relatively prime to x. |
|||
""" |
|||
|
|||
return order_mod( x, largest_factor_relatively_prime( m, x ) ) |
|||
|
|||
|
|||
def is_prime( n ): |
|||
"""Return True if x is prime, False otherwise. |
|||
|
|||
We use the Miller-Rabin test, as given in Menezes et al. p. 138. |
|||
This test is not exact: there are composite values n for which |
|||
it returns True. |
|||
|
|||
In testing the odd numbers from 10000001 to 19999999, |
|||
about 66 composites got past the first test, |
|||
5 got past the second test, and none got past the third. |
|||
Since factors of 2, 3, 5, 7, and 11 were detected during |
|||
preliminary screening, the number of numbers tested by |
|||
Miller-Rabin was (19999999 - 10000001)*(2/3)*(4/5)*(6/7) |
|||
= 4.57 million. |
|||
""" |
|||
|
|||
# (This is used to study the risk of false positives:) |
|||
global miller_rabin_test_count |
|||
|
|||
miller_rabin_test_count = 0 |
|||
|
|||
if n <= smallprimes[-1]: |
|||
if n in smallprimes: return True |
|||
else: return False |
|||
|
|||
if gcd( n, 2*3*5*7*11 ) != 1: return False |
|||
|
|||
# Choose a number of iterations sufficient to reduce the |
|||
# probability of accepting a composite below 2**-80 |
|||
# (from Menezes et al. Table 4.4): |
|||
|
|||
t = 40 |
|||
n_bits = 1 + int( math.log( n, 2 ) ) |
|||
for k, tt in ( ( 100, 27 ), |
|||
( 150, 18 ), |
|||
( 200, 15 ), |
|||
( 250, 12 ), |
|||
( 300, 9 ), |
|||
( 350, 8 ), |
|||
( 400, 7 ), |
|||
( 450, 6 ), |
|||
( 550, 5 ), |
|||
( 650, 4 ), |
|||
( 850, 3 ), |
|||
( 1300, 2 ), |
|||
): |
|||
if n_bits < k: break |
|||
t = tt |
|||
|
|||
# Run the test t times: |
|||
|
|||
s = 0 |
|||
r = n - 1 |
|||
while ( r % 2 ) == 0: |
|||
s = s + 1 |
|||
r = r // 2 |
|||
for i in xrange( t ): |
|||
a = smallprimes[ i ] |
|||
y = modular_exp( a, r, n ) |
|||
if y != 1 and y != n-1: |
|||
j = 1 |
|||
while j <= s - 1 and y != n - 1: |
|||
y = modular_exp( y, 2, n ) |
|||
if y == 1: |
|||
miller_rabin_test_count = i + 1 |
|||
return False |
|||
j = j + 1 |
|||
if y != n-1: |
|||
miller_rabin_test_count = i + 1 |
|||
return False |
|||
return True |
|||
|
|||
|
|||
def next_prime( starting_value ): |
|||
"Return the smallest prime larger than the starting value." |
|||
|
|||
if starting_value < 2: return 2 |
|||
result = ( starting_value + 1 ) | 1 |
|||
while not is_prime( result ): result = result + 2 |
|||
return result |
|||
|
|||
|
|||
smallprimes = [2, 3, 5, 7, 11, 13, 17, 19, 23, 29, 31, 37, 41, |
|||
43, 47, 53, 59, 61, 67, 71, 73, 79, 83, 89, 97, |
|||
101, 103, 107, 109, 113, 127, 131, 137, 139, 149, |
|||
151, 157, 163, 167, 173, 179, 181, 191, 193, 197, |
|||
199, 211, 223, 227, 229, 233, 239, 241, 251, 257, |
|||
263, 269, 271, 277, 281, 283, 293, 307, 311, 313, |
|||
317, 331, 337, 347, 349, 353, 359, 367, 373, 379, |
|||
383, 389, 397, 401, 409, 419, 421, 431, 433, 439, |
|||
443, 449, 457, 461, 463, 467, 479, 487, 491, 499, |
|||
503, 509, 521, 523, 541, 547, 557, 563, 569, 571, |
|||
577, 587, 593, 599, 601, 607, 613, 617, 619, 631, |
|||
641, 643, 647, 653, 659, 661, 673, 677, 683, 691, |
|||
701, 709, 719, 727, 733, 739, 743, 751, 757, 761, |
|||
769, 773, 787, 797, 809, 811, 821, 823, 827, 829, |
|||
839, 853, 857, 859, 863, 877, 881, 883, 887, 907, |
|||
911, 919, 929, 937, 941, 947, 953, 967, 971, 977, |
|||
983, 991, 997, 1009, 1013, 1019, 1021, 1031, 1033, |
|||
1039, 1049, 1051, 1061, 1063, 1069, 1087, 1091, 1093, |
|||
1097, 1103, 1109, 1117, 1123, 1129, 1151, 1153, 1163, |
|||
1171, 1181, 1187, 1193, 1201, 1213, 1217, 1223, 1229] |
|||
|
|||
miller_rabin_test_count = 0 |
|||
|
|||
def __main__(): |
|||
|
|||
# Making sure locally defined exceptions work: |
|||
# p = modular_exp( 2, -2, 3 ) |
|||
# p = square_root_mod_prime( 2, 3 ) |
|||
|
|||
|
|||
print "Testing gcd..." |
|||
assert gcd( 3*5*7, 3*5*11, 3*5*13 ) == 3*5 |
|||
assert gcd( [ 3*5*7, 3*5*11, 3*5*13 ] ) == 3*5 |
|||
assert gcd( 3 ) == 3 |
|||
|
|||
print "Testing lcm..." |
|||
assert lcm( 3, 5*3, 7*3 ) == 3*5*7 |
|||
assert lcm( [ 3, 5*3, 7*3 ] ) == 3*5*7 |
|||
assert lcm( 3 ) == 3 |
|||
|
|||
print "Testing next_prime..." |
|||
bigprimes = ( 999671, |
|||
999683, |
|||
999721, |
|||
999727, |
|||
999749, |
|||
999763, |
|||
999769, |
|||
999773, |
|||
999809, |
|||
999853, |
|||
999863, |
|||
999883, |
|||
999907, |
|||
999917, |
|||
999931, |
|||
999953, |
|||
999959, |
|||
999961, |
|||
999979, |
|||
999983 ) |
|||
|
|||
for i in xrange( len( bigprimes ) - 1 ): |
|||
assert next_prime( bigprimes[i] ) == bigprimes[ i+1 ] |
|||
|
|||
error_tally = 0 |
|||
|
|||
# Test the square_root_mod_prime function: |
|||
|
|||
for p in smallprimes: |
|||
print "Testing square_root_mod_prime for modulus p = %d." % p |
|||
squares = [] |
|||
|
|||
for root in range( 0, 1+p//2 ): |
|||
sq = ( root * root ) % p |
|||
squares.append( sq ) |
|||
calculated = square_root_mod_prime( sq, p ) |
|||
if ( calculated * calculated ) % p != sq: |
|||
error_tally = error_tally + 1 |
|||
print "Failed to find %d as sqrt( %d ) mod %d. Said %d." % \ |
|||
( root, sq, p, calculated ) |
|||
|
|||
for nonsquare in range( 0, p ): |
|||
if nonsquare not in squares: |
|||
try: |
|||
calculated = square_root_mod_prime( nonsquare, p ) |
|||
except SquareRootError: |
|||
pass |
|||
else: |
|||
error_tally = error_tally + 1 |
|||
print "Failed to report no root for sqrt( %d ) mod %d." % \ |
|||
( nonsquare, p ) |
|||
|
|||
# Test the jacobi function: |
|||
for m in range( 3, 400, 2 ): |
|||
print "Testing jacobi for modulus m = %d." % m |
|||
if is_prime( m ): |
|||
squares = [] |
|||
for root in range( 1, m ): |
|||
if jacobi( root * root, m ) != 1: |
|||
error_tally = error_tally + 1 |
|||
print "jacobi( %d * %d, %d ) != 1" % ( root, root, m ) |
|||
squares.append( root * root % m ) |
|||
for i in range( 1, m ): |
|||
if not i in squares: |
|||
if jacobi( i, m ) != -1: |
|||
error_tally = error_tally + 1 |
|||
print "jacobi( %d, %d ) != -1" % ( i, m ) |
|||
else: # m is not prime. |
|||
f = factorization( m ) |
|||
for a in range( 1, m ): |
|||
c = 1 |
|||
for i in f: |
|||
c = c * jacobi( a, i[0] ) ** i[1] |
|||
if c != jacobi( a, m ): |
|||
error_tally = error_tally + 1 |
|||
print "%d != jacobi( %d, %d )" % ( c, a, m ) |
|||
|
|||
|
|||
# Test the inverse_mod function: |
|||
print "Testing inverse_mod . . ." |
|||
import random |
|||
n_tests = 0 |
|||
for i in range( 100 ): |
|||
m = random.randint( 20, 10000 ) |
|||
for j in range( 100 ): |
|||
a = random.randint( 1, m-1 ) |
|||
if gcd( a, m ) == 1: |
|||
n_tests = n_tests + 1 |
|||
inv = inverse_mod( a, m ) |
|||
if inv <= 0 or inv >= m or ( a * inv ) % m != 1: |
|||
error_tally = error_tally + 1 |
|||
print "%d = inverse_mod( %d, %d ) is wrong." % ( inv, a, m ) |
|||
assert n_tests > 1000 |
|||
print n_tests, " tests of inverse_mod completed." |
|||
|
|||
class FailedTest(Exception): pass |
|||
print error_tally, "errors detected." |
|||
if error_tally != 0: |
|||
raise FailedTest("%d errors detected" % error_tally) |
|||
|
|||
if __name__ == '__main__': |
|||
__main__() |
@ -1,486 +0,0 @@ |
|||
import unittest |
|||
import os |
|||
import time |
|||
import shutil |
|||
import subprocess |
|||
from binascii import hexlify, unhexlify |
|||
from hashlib import sha1, sha256 |
|||
|
|||
from keys import SigningKey, VerifyingKey |
|||
from keys import BadSignatureError |
|||
import util |
|||
from util import sigencode_der, sigencode_strings |
|||
from util import sigdecode_der, sigdecode_strings |
|||
from curves import Curve, UnknownCurveError |
|||
from curves import NIST192p, NIST224p, NIST256p, NIST384p, NIST521p |
|||
import der |
|||
|
|||
class SubprocessError(Exception): |
|||
pass |
|||
|
|||
def run_openssl(cmd): |
|||
OPENSSL = "openssl" |
|||
p = subprocess.Popen([OPENSSL] + cmd.split(), |
|||
stdout=subprocess.PIPE, |
|||
stderr=subprocess.STDOUT) |
|||
stdout, ignored = p.communicate() |
|||
if p.returncode != 0: |
|||
raise SubprocessError("cmd '%s %s' failed: rc=%s, stdout/err was %s" % |
|||
(OPENSSL, cmd, p.returncode, stdout)) |
|||
return stdout |
|||
|
|||
BENCH = False |
|||
|
|||
class ECDSA(unittest.TestCase): |
|||
def test_basic(self): |
|||
priv = SigningKey.generate() |
|||
pub = priv.get_verifying_key() |
|||
|
|||
data = "blahblah" |
|||
sig = priv.sign(data) |
|||
|
|||
self.failUnless(pub.verify(sig, data)) |
|||
self.failUnlessRaises(BadSignatureError, pub.verify, sig, data+"bad") |
|||
|
|||
pub2 = VerifyingKey.from_string(pub.to_string()) |
|||
self.failUnless(pub2.verify(sig, data)) |
|||
|
|||
def test_bad_usage(self): |
|||
# sk=SigningKey() is wrong |
|||
self.failUnlessRaises(TypeError, SigningKey) |
|||
self.failUnlessRaises(TypeError, VerifyingKey) |
|||
|
|||
def test_lengths(self): |
|||
default = NIST192p |
|||
priv = SigningKey.generate() |
|||
pub = priv.get_verifying_key() |
|||
self.failUnlessEqual(len(pub.to_string()), default.verifying_key_length) |
|||
sig = priv.sign("data") |
|||
self.failUnlessEqual(len(sig), default.signature_length) |
|||
if BENCH: |
|||
print |
|||
for curve in (NIST192p, NIST224p, NIST256p, NIST384p, NIST521p): |
|||
start = time.time() |
|||
priv = SigningKey.generate(curve=curve) |
|||
pub1 = priv.get_verifying_key() |
|||
keygen_time = time.time() - start |
|||
pub2 = VerifyingKey.from_string(pub1.to_string(), curve) |
|||
self.failUnlessEqual(pub1.to_string(), pub2.to_string()) |
|||
self.failUnlessEqual(len(pub1.to_string()), |
|||
curve.verifying_key_length) |
|||
start = time.time() |
|||
sig = priv.sign("data") |
|||
sign_time = time.time() - start |
|||
self.failUnlessEqual(len(sig), curve.signature_length) |
|||
if BENCH: |
|||
start = time.time() |
|||
pub1.verify(sig, "data") |
|||
verify_time = time.time() - start |
|||
print "%s: siglen=%d, keygen=%0.3fs, sign=%0.3f, verify=%0.3f" \ |
|||
% (curve.name, curve.signature_length, |
|||
keygen_time, sign_time, verify_time) |
|||
|
|||
def test_serialize(self): |
|||
seed = "secret" |
|||
curve = NIST192p |
|||
secexp1 = util.randrange_from_seed__trytryagain(seed, curve.order) |
|||
secexp2 = util.randrange_from_seed__trytryagain(seed, curve.order) |
|||
self.failUnlessEqual(secexp1, secexp2) |
|||
priv1 = SigningKey.from_secret_exponent(secexp1, curve) |
|||
priv2 = SigningKey.from_secret_exponent(secexp2, curve) |
|||
self.failUnlessEqual(hexlify(priv1.to_string()), |
|||
hexlify(priv2.to_string())) |
|||
self.failUnlessEqual(priv1.to_pem(), priv2.to_pem()) |
|||
pub1 = priv1.get_verifying_key() |
|||
pub2 = priv2.get_verifying_key() |
|||
data = "data" |
|||
sig1 = priv1.sign(data) |
|||
sig2 = priv2.sign(data) |
|||
self.failUnless(pub1.verify(sig1, data)) |
|||
self.failUnless(pub2.verify(sig1, data)) |
|||
self.failUnless(pub1.verify(sig2, data)) |
|||
self.failUnless(pub2.verify(sig2, data)) |
|||
self.failUnlessEqual(hexlify(pub1.to_string()), |
|||
hexlify(pub2.to_string())) |
|||
|
|||
def test_nonrandom(self): |
|||
s = "all the entropy in the entire world, compressed into one line" |
|||
def not_much_entropy(numbytes): |
|||
return s[:numbytes] |
|||
# we control the entropy source, these two keys should be identical: |
|||
priv1 = SigningKey.generate(entropy=not_much_entropy) |
|||
priv2 = SigningKey.generate(entropy=not_much_entropy) |
|||
self.failUnlessEqual(hexlify(priv1.get_verifying_key().to_string()), |
|||
hexlify(priv2.get_verifying_key().to_string())) |
|||
# likewise, signatures should be identical. Obviously you'd never |
|||
# want to do this with keys you care about, because the secrecy of |
|||
# the private key depends upon using different random numbers for |
|||
# each signature |
|||
sig1 = priv1.sign("data", entropy=not_much_entropy) |
|||
sig2 = priv2.sign("data", entropy=not_much_entropy) |
|||
self.failUnlessEqual(hexlify(sig1), hexlify(sig2)) |
|||
|
|||
def failUnlessPrivkeysEqual(self, priv1, priv2): |
|||
self.failUnlessEqual(priv1.privkey.secret_multiplier, |
|||
priv2.privkey.secret_multiplier) |
|||
self.failUnlessEqual(priv1.privkey.public_key.generator, |
|||
priv2.privkey.public_key.generator) |
|||
|
|||
def failIfPrivkeysEqual(self, priv1, priv2): |
|||
self.failIfEqual(priv1.privkey.secret_multiplier, |
|||
priv2.privkey.secret_multiplier) |
|||
|
|||
def test_privkey_creation(self): |
|||
s = "all the entropy in the entire world, compressed into one line" |
|||
def not_much_entropy(numbytes): |
|||
return s[:numbytes] |
|||
priv1 = SigningKey.generate() |
|||
self.failUnlessEqual(priv1.baselen, NIST192p.baselen) |
|||
|
|||
priv1 = SigningKey.generate(curve=NIST224p) |
|||
self.failUnlessEqual(priv1.baselen, NIST224p.baselen) |
|||
|
|||
priv1 = SigningKey.generate(entropy=not_much_entropy) |
|||
self.failUnlessEqual(priv1.baselen, NIST192p.baselen) |
|||
priv2 = SigningKey.generate(entropy=not_much_entropy) |
|||
self.failUnlessEqual(priv2.baselen, NIST192p.baselen) |
|||
self.failUnlessPrivkeysEqual(priv1, priv2) |
|||
|
|||
priv1 = SigningKey.from_secret_exponent(secexp=3) |
|||
self.failUnlessEqual(priv1.baselen, NIST192p.baselen) |
|||
priv2 = SigningKey.from_secret_exponent(secexp=3) |
|||
self.failUnlessPrivkeysEqual(priv1, priv2) |
|||
|
|||
priv1 = SigningKey.from_secret_exponent(secexp=4, curve=NIST224p) |
|||
self.failUnlessEqual(priv1.baselen, NIST224p.baselen) |
|||
|
|||
def test_privkey_strings(self): |
|||
priv1 = SigningKey.generate() |
|||
s1 = priv1.to_string() |
|||
self.failUnlessEqual(type(s1), str) |
|||
self.failUnlessEqual(len(s1), NIST192p.baselen) |
|||
priv2 = SigningKey.from_string(s1) |
|||
self.failUnlessPrivkeysEqual(priv1, priv2) |
|||
|
|||
s1 = priv1.to_pem() |
|||
self.failUnlessEqual(type(s1), str) |
|||
self.failUnless(s1.startswith("-----BEGIN EC PRIVATE KEY-----")) |
|||
self.failUnless(s1.strip().endswith("-----END EC PRIVATE KEY-----")) |
|||
priv2 = SigningKey.from_pem(s1) |
|||
self.failUnlessPrivkeysEqual(priv1, priv2) |
|||
|
|||
s1 = priv1.to_der() |
|||
self.failUnlessEqual(type(s1), str) |
|||
priv2 = SigningKey.from_der(s1) |
|||
self.failUnlessPrivkeysEqual(priv1, priv2) |
|||
|
|||
priv1 = SigningKey.generate(curve=NIST256p) |
|||
s1 = priv1.to_pem() |
|||
self.failUnlessEqual(type(s1), str) |
|||
self.failUnless(s1.startswith("-----BEGIN EC PRIVATE KEY-----")) |
|||
self.failUnless(s1.strip().endswith("-----END EC PRIVATE KEY-----")) |
|||
priv2 = SigningKey.from_pem(s1) |
|||
self.failUnlessPrivkeysEqual(priv1, priv2) |
|||
|
|||
s1 = priv1.to_der() |
|||
self.failUnlessEqual(type(s1), str) |
|||
priv2 = SigningKey.from_der(s1) |
|||
self.failUnlessPrivkeysEqual(priv1, priv2) |
|||
|
|||
def failUnlessPubkeysEqual(self, pub1, pub2): |
|||
self.failUnlessEqual(pub1.pubkey.point, pub2.pubkey.point) |
|||
self.failUnlessEqual(pub1.pubkey.generator, pub2.pubkey.generator) |
|||
self.failUnlessEqual(pub1.curve, pub2.curve) |
|||
|
|||
def test_pubkey_strings(self): |
|||
priv1 = SigningKey.generate() |
|||
pub1 = priv1.get_verifying_key() |
|||
s1 = pub1.to_string() |
|||
self.failUnlessEqual(type(s1), str) |
|||
self.failUnlessEqual(len(s1), NIST192p.verifying_key_length) |
|||
pub2 = VerifyingKey.from_string(s1) |
|||
self.failUnlessPubkeysEqual(pub1, pub2) |
|||
|
|||
priv1 = SigningKey.generate(curve=NIST256p) |
|||
pub1 = priv1.get_verifying_key() |
|||
s1 = pub1.to_string() |
|||
self.failUnlessEqual(type(s1), str) |
|||
self.failUnlessEqual(len(s1), NIST256p.verifying_key_length) |
|||
pub2 = VerifyingKey.from_string(s1, curve=NIST256p) |
|||
self.failUnlessPubkeysEqual(pub1, pub2) |
|||
|
|||
pub1_der = pub1.to_der() |
|||
self.failUnlessEqual(type(pub1_der), str) |
|||
pub2 = VerifyingKey.from_der(pub1_der) |
|||
self.failUnlessPubkeysEqual(pub1, pub2) |
|||
|
|||
self.failUnlessRaises(der.UnexpectedDER, |
|||
VerifyingKey.from_der, pub1_der+"junk") |
|||
badpub = VerifyingKey.from_der(pub1_der) |
|||
class FakeGenerator: |
|||
def order(self): return 123456789 |
|||
badcurve = Curve("unknown", None, FakeGenerator(), (1,2,3,4,5,6)) |
|||
badpub.curve = badcurve |
|||
badder = badpub.to_der() |
|||
self.failUnlessRaises(UnknownCurveError, VerifyingKey.from_der, badder) |
|||
|
|||
pem = pub1.to_pem() |
|||
self.failUnlessEqual(type(pem), str) |
|||
self.failUnless(pem.startswith("-----BEGIN PUBLIC KEY-----"), pem) |
|||
self.failUnless(pem.strip().endswith("-----END PUBLIC KEY-----"), pem) |
|||
pub2 = VerifyingKey.from_pem(pem) |
|||
self.failUnlessPubkeysEqual(pub1, pub2) |
|||
|
|||
def test_signature_strings(self): |
|||
priv1 = SigningKey.generate() |
|||
pub1 = priv1.get_verifying_key() |
|||
data = "data" |
|||
|
|||
sig = priv1.sign(data) |
|||
self.failUnlessEqual(type(sig), str) |
|||
self.failUnlessEqual(len(sig), NIST192p.signature_length) |
|||
self.failUnless(pub1.verify(sig, data)) |
|||
|
|||
sig = priv1.sign(data, sigencode=sigencode_strings) |
|||
self.failUnlessEqual(type(sig), tuple) |
|||
self.failUnlessEqual(len(sig), 2) |
|||
self.failUnlessEqual(type(sig[0]), str) |
|||
self.failUnlessEqual(type(sig[1]), str) |
|||
self.failUnlessEqual(len(sig[0]), NIST192p.baselen) |
|||
self.failUnlessEqual(len(sig[1]), NIST192p.baselen) |
|||
self.failUnless(pub1.verify(sig, data, sigdecode=sigdecode_strings)) |
|||
|
|||
sig_der = priv1.sign(data, sigencode=sigencode_der) |
|||
self.failUnlessEqual(type(sig_der), str) |
|||
self.failUnless(pub1.verify(sig_der, data, sigdecode=sigdecode_der)) |
|||
|
|||
def test_hashfunc(self): |
|||
sk = SigningKey.generate(curve=NIST256p, hashfunc=sha256) |
|||
data = "security level is 128 bits" |
|||
sig = sk.sign(data) |
|||
vk = VerifyingKey.from_string(sk.get_verifying_key().to_string(), |
|||
curve=NIST256p, hashfunc=sha256) |
|||
self.failUnless(vk.verify(sig, data)) |
|||
|
|||
sk2 = SigningKey.generate(curve=NIST256p) |
|||
sig2 = sk2.sign(data, hashfunc=sha256) |
|||
vk2 = VerifyingKey.from_string(sk2.get_verifying_key().to_string(), |
|||
curve=NIST256p, hashfunc=sha256) |
|||
self.failUnless(vk2.verify(sig2, data)) |
|||
|
|||
vk3 = VerifyingKey.from_string(sk.get_verifying_key().to_string(), |
|||
curve=NIST256p) |
|||
self.failUnless(vk3.verify(sig, data, hashfunc=sha256)) |
|||
|
|||
|
|||
class OpenSSL(unittest.TestCase): |
|||
# test interoperability with OpenSSL tools. Note that openssl's ECDSA |
|||
# sign/verify arguments changed between 0.9.8 and 1.0.0: the early |
|||
# versions require "-ecdsa-with-SHA1", the later versions want just |
|||
# "-SHA1" (or to leave out that argument entirely, which means the |
|||
# signature will use some default digest algorithm, probably determined |
|||
# by the key, probably always SHA1). |
|||
# |
|||
# openssl ecparam -name secp224r1 -genkey -out privkey.pem |
|||
# openssl ec -in privkey.pem -text -noout # get the priv/pub keys |
|||
# openssl dgst -ecdsa-with-SHA1 -sign privkey.pem -out data.sig data.txt |
|||
# openssl asn1parse -in data.sig -inform DER |
|||
# data.sig is 64 bytes, probably 56b plus ASN1 overhead |
|||
# openssl dgst -ecdsa-with-SHA1 -prverify privkey.pem -signature data.sig data.txt ; echo $? |
|||
# openssl ec -in privkey.pem -pubout -out pubkey.pem |
|||
# openssl ec -in privkey.pem -pubout -outform DER -out pubkey.der |
|||
|
|||
def get_openssl_messagedigest_arg(self): |
|||
v = run_openssl("version") |
|||
# e.g. "OpenSSL 1.0.0 29 Mar 2010", or "OpenSSL 1.0.0a 1 Jun 2010", |
|||
# or "OpenSSL 0.9.8o 01 Jun 2010" |
|||
vs = v.split()[1].split(".") |
|||
if vs >= ["1","0","0"]: |
|||
return "-SHA1" |
|||
else: |
|||
return "-ecdsa-with-SHA1" |
|||
|
|||
# sk: 1:OpenSSL->python 2:python->OpenSSL |
|||
# vk: 3:OpenSSL->python 4:python->OpenSSL |
|||
# sig: 5:OpenSSL->python 6:python->OpenSSL |
|||
|
|||
def test_from_openssl_nist192p(self): |
|||
return self.do_test_from_openssl(NIST192p, "prime192v1") |
|||
def test_from_openssl_nist224p(self): |
|||
return self.do_test_from_openssl(NIST224p, "secp224r1") |
|||
def test_from_openssl_nist384p(self): |
|||
return self.do_test_from_openssl(NIST384p, "secp384r1") |
|||
def test_from_openssl_nist521p(self): |
|||
return self.do_test_from_openssl(NIST521p, "secp521r1") |
|||
|
|||
def do_test_from_openssl(self, curve, curvename): |
|||
# OpenSSL: create sk, vk, sign. |
|||
# Python: read vk(3), checksig(5), read sk(1), sign, check |
|||
mdarg = self.get_openssl_messagedigest_arg() |
|||
if os.path.isdir("t"): |
|||
shutil.rmtree("t") |
|||
os.mkdir("t") |
|||
run_openssl("ecparam -name %s -genkey -out t/privkey.pem" % curvename) |
|||
run_openssl("ec -in t/privkey.pem -pubout -out t/pubkey.pem") |
|||
data = "data" |
|||
open("t/data.txt","wb").write(data) |
|||
run_openssl("dgst %s -sign t/privkey.pem -out t/data.sig t/data.txt" % mdarg) |
|||
run_openssl("dgst %s -verify t/pubkey.pem -signature t/data.sig t/data.txt" % mdarg) |
|||
pubkey_pem = open("t/pubkey.pem").read() |
|||
vk = VerifyingKey.from_pem(pubkey_pem) # 3 |
|||
sig_der = open("t/data.sig","rb").read() |
|||
self.failUnless(vk.verify(sig_der, data, # 5 |
|||
hashfunc=sha1, sigdecode=sigdecode_der)) |
|||
|
|||
sk = SigningKey.from_pem(open("t/privkey.pem").read()) # 1 |
|||
sig = sk.sign(data) |
|||
self.failUnless(vk.verify(sig, data)) |
|||
|
|||
def test_to_openssl_nist192p(self): |
|||
self.do_test_to_openssl(NIST192p, "prime192v1") |
|||
def test_to_openssl_nist224p(self): |
|||
self.do_test_to_openssl(NIST224p, "secp224r1") |
|||
def test_to_openssl_nist384p(self): |
|||
self.do_test_to_openssl(NIST384p, "secp384r1") |
|||
def test_to_openssl_nist521p(self): |
|||
self.do_test_to_openssl(NIST521p, "secp521r1") |
|||
|
|||
def do_test_to_openssl(self, curve, curvename): |
|||
# Python: create sk, vk, sign. |
|||
# OpenSSL: read vk(4), checksig(6), read sk(2), sign, check |
|||
mdarg = self.get_openssl_messagedigest_arg() |
|||
if os.path.isdir("t"): |
|||
shutil.rmtree("t") |
|||
os.mkdir("t") |
|||
sk = SigningKey.generate(curve=curve) |
|||
vk = sk.get_verifying_key() |
|||
data = "data" |
|||
open("t/pubkey.der","wb").write(vk.to_der()) # 4 |
|||
open("t/pubkey.pem","wb").write(vk.to_pem()) # 4 |
|||
sig_der = sk.sign(data, hashfunc=sha1, sigencode=sigencode_der) |
|||
open("t/data.sig","wb").write(sig_der) # 6 |
|||
open("t/data.txt","wb").write(data) |
|||
open("t/baddata.txt","wb").write(data+"corrupt") |
|||
|
|||
self.failUnlessRaises(SubprocessError, run_openssl, |
|||
"dgst %s -verify t/pubkey.der -keyform DER -signature t/data.sig t/baddata.txt" % mdarg) |
|||
run_openssl("dgst %s -verify t/pubkey.der -keyform DER -signature t/data.sig t/data.txt" % mdarg) |
|||
|
|||
open("t/privkey.pem","wb").write(sk.to_pem()) # 2 |
|||
run_openssl("dgst %s -sign t/privkey.pem -out t/data.sig2 t/data.txt" % mdarg) |
|||
run_openssl("dgst %s -verify t/pubkey.pem -signature t/data.sig2 t/data.txt" % mdarg) |
|||
|
|||
class DER(unittest.TestCase): |
|||
def test_oids(self): |
|||
oid_ecPublicKey = der.encode_oid(1, 2, 840, 10045, 2, 1) |
|||
self.failUnlessEqual(hexlify(oid_ecPublicKey), "06072a8648ce3d0201") |
|||
self.failUnlessEqual(hexlify(NIST224p.encoded_oid), "06052b81040021") |
|||
self.failUnlessEqual(hexlify(NIST256p.encoded_oid), |
|||
"06082a8648ce3d030107") |
|||
x = oid_ecPublicKey + "more" |
|||
x1, rest = der.remove_object(x) |
|||
self.failUnlessEqual(x1, (1, 2, 840, 10045, 2, 1)) |
|||
self.failUnlessEqual(rest, "more") |
|||
|
|||
def test_integer(self): |
|||
self.failUnlessEqual(der.encode_integer(0), "\x02\x01\x00") |
|||
self.failUnlessEqual(der.encode_integer(1), "\x02\x01\x01") |
|||
self.failUnlessEqual(der.encode_integer(127), "\x02\x01\x7f") |
|||
self.failUnlessEqual(der.encode_integer(128), "\x02\x02\x00\x80") |
|||
self.failUnlessEqual(der.encode_integer(256), "\x02\x02\x01\x00") |
|||
#self.failUnlessEqual(der.encode_integer(-1), "\x02\x01\xff") |
|||
|
|||
def s(n): return der.remove_integer(der.encode_integer(n) + "junk") |
|||
self.failUnlessEqual(s(0), (0, "junk")) |
|||
self.failUnlessEqual(s(1), (1, "junk")) |
|||
self.failUnlessEqual(s(127), (127, "junk")) |
|||
self.failUnlessEqual(s(128), (128, "junk")) |
|||
self.failUnlessEqual(s(256), (256, "junk")) |
|||
self.failUnlessEqual(s(1234567890123456789012345678901234567890), |
|||
( 1234567890123456789012345678901234567890,"junk")) |
|||
|
|||
def test_number(self): |
|||
self.failUnlessEqual(der.encode_number(0), "\x00") |
|||
self.failUnlessEqual(der.encode_number(127), "\x7f") |
|||
self.failUnlessEqual(der.encode_number(128), "\x81\x00") |
|||
self.failUnlessEqual(der.encode_number(3*128+7), "\x83\x07") |
|||
#self.failUnlessEqual(der.read_number("\x81\x9b"+"more"), (155, 2)) |
|||
#self.failUnlessEqual(der.encode_number(155), "\x81\x9b") |
|||
for n in (0, 1, 2, 127, 128, 3*128+7, 840, 10045): #, 155): |
|||
x = der.encode_number(n) + "more" |
|||
n1, llen = der.read_number(x) |
|||
self.failUnlessEqual(n1, n) |
|||
self.failUnlessEqual(x[llen:], "more") |
|||
|
|||
def test_length(self): |
|||
self.failUnlessEqual(der.encode_length(0), "\x00") |
|||
self.failUnlessEqual(der.encode_length(127), "\x7f") |
|||
self.failUnlessEqual(der.encode_length(128), "\x81\x80") |
|||
self.failUnlessEqual(der.encode_length(255), "\x81\xff") |
|||
self.failUnlessEqual(der.encode_length(256), "\x82\x01\x00") |
|||
self.failUnlessEqual(der.encode_length(3*256+7), "\x82\x03\x07") |
|||
self.failUnlessEqual(der.read_length("\x81\x9b"+"more"), (155, 2)) |
|||
self.failUnlessEqual(der.encode_length(155), "\x81\x9b") |
|||
for n in (0, 1, 2, 127, 128, 255, 256, 3*256+7, 155): |
|||
x = der.encode_length(n) + "more" |
|||
n1, llen = der.read_length(x) |
|||
self.failUnlessEqual(n1, n) |
|||
self.failUnlessEqual(x[llen:], "more") |
|||
|
|||
def test_sequence(self): |
|||
x = der.encode_sequence("ABC", "DEF") + "GHI" |
|||
self.failUnlessEqual(x, "\x30\x06ABCDEFGHI") |
|||
x1, rest = der.remove_sequence(x) |
|||
self.failUnlessEqual(x1, "ABCDEF") |
|||
self.failUnlessEqual(rest, "GHI") |
|||
|
|||
def test_constructed(self): |
|||
x = der.encode_constructed(0, NIST224p.encoded_oid) |
|||
self.failUnlessEqual(hexlify(x), "a007" + "06052b81040021") |
|||
x = der.encode_constructed(1, unhexlify("0102030a0b0c")) |
|||
self.failUnlessEqual(hexlify(x), "a106" + "0102030a0b0c") |
|||
|
|||
class Util(unittest.TestCase): |
|||
def test_trytryagain(self): |
|||
tta = util.randrange_from_seed__trytryagain |
|||
for i in range(1000): |
|||
seed = "seed-%d" % i |
|||
for order in (2**8-2, 2**8-1, 2**8, 2**8+1, 2**8+2, |
|||
2**16-1, 2**16+1): |
|||
n = tta(seed, order) |
|||
self.failUnless(1 <= n < order, (1, n, order)) |
|||
# this trytryagain *does* provide long-term stability |
|||
self.failUnlessEqual("%x"%(tta("seed", NIST224p.order)), |
|||
"6fa59d73bf0446ae8743cf748fc5ac11d5585a90356417e97155c3bc") |
|||
|
|||
def test_randrange(self): |
|||
# util.randrange does not provide long-term stability: we might |
|||
# change the algorithm in the future. |
|||
for i in range(1000): |
|||
entropy = util.PRNG("seed-%d" % i) |
|||
for order in (2**8-2, 2**8-1, 2**8, |
|||
2**16-1, 2**16+1, |
|||
): |
|||
# that oddball 2**16+1 takes half our runtime |
|||
n = util.randrange(order, entropy=entropy) |
|||
self.failUnless(1 <= n < order, (1, n, order)) |
|||
|
|||
def OFF_test_prove_uniformity(self): |
|||
order = 2**8-2 |
|||
counts = dict([(i, 0) for i in range(1, order)]) |
|||
assert 0 not in counts |
|||
assert order not in counts |
|||
for i in range(1000000): |
|||
seed = "seed-%d" % i |
|||
n = util.randrange_from_seed__trytryagain(seed, order) |
|||
counts[n] += 1 |
|||
# this technique should use the full range |
|||
self.failUnless(counts[order-1]) |
|||
for i in range(1, order): |
|||
print "%3d: %s" % (i, "*"*(counts[i]//100)) |
|||
|
|||
|
|||
def __main__(): |
|||
unittest.main() |
|||
if __name__ == "__main__": |
|||
__main__() |
@ -1,215 +0,0 @@ |
|||
|
|||
import os |
|||
import math |
|||
import binascii |
|||
from hashlib import sha256 |
|||
import der |
|||
from curves import orderlen |
|||
|
|||
# RFC5480: |
|||
# The "unrestricted" algorithm identifier is: |
|||
# id-ecPublicKey OBJECT IDENTIFIER ::= { |
|||
# iso(1) member-body(2) us(840) ansi-X9-62(10045) keyType(2) 1 } |
|||
|
|||
oid_ecPublicKey = (1, 2, 840, 10045, 2, 1) |
|||
encoded_oid_ecPublicKey = der.encode_oid(*oid_ecPublicKey) |
|||
|
|||
def randrange(order, entropy=None): |
|||
"""Return a random integer k such that 1 <= k < order, uniformly |
|||
distributed across that range. For simplicity, this only behaves well if |
|||
'order' is fairly close (but below) a power of 256. The try-try-again |
|||
algorithm we use takes longer and longer time (on average) to complete as |
|||
'order' falls, rising to a maximum of avg=512 loops for the worst-case |
|||
(256**k)+1 . All of the standard curves behave well. There is a cutoff at |
|||
10k loops (which raises RuntimeError) to prevent an infinite loop when |
|||
something is really broken like the entropy function not working. |
|||
|
|||
Note that this function is not declared to be forwards-compatible: we may |
|||
change the behavior in future releases. The entropy= argument (which |
|||
should get a callable that behaves like os.entropy) can be used to |
|||
achieve stability within a given release (for repeatable unit tests), but |
|||
should not be used as a long-term-compatible key generation algorithm. |
|||
""" |
|||
# we could handle arbitrary orders (even 256**k+1) better if we created |
|||
# candidates bit-wise instead of byte-wise, which would reduce the |
|||
# worst-case behavior to avg=2 loops, but that would be more complex. The |
|||
# change would be to round the order up to a power of 256, subtract one |
|||
# (to get 0xffff..), use that to get a byte-long mask for the top byte, |
|||
# generate the len-1 entropy bytes, generate one extra byte and mask off |
|||
# the top bits, then combine it with the rest. Requires jumping back and |
|||
# forth between strings and integers a lot. |
|||
|
|||
if entropy is None: |
|||
entropy = os.urandom |
|||
assert order > 1 |
|||
bytes = orderlen(order) |
|||
dont_try_forever = 10000 # gives about 2**-60 failures for worst case |
|||
while dont_try_forever > 0: |
|||
dont_try_forever -= 1 |
|||
candidate = string_to_number(entropy(bytes)) + 1 |
|||
if 1 <= candidate < order: |
|||
return candidate |
|||
continue |
|||
raise RuntimeError("randrange() tried hard but gave up, either something" |
|||
" is very wrong or you got realllly unlucky. Order was" |
|||
" %x" % order) |
|||
|
|||
class PRNG: |
|||
# this returns a callable which, when invoked with an integer N, will |
|||
# return N pseudorandom bytes. Note: this is a short-term PRNG, meant |
|||
# primarily for the needs of randrange_from_seed__trytryagain(), which |
|||
# only needs to run it a few times per seed. It does not provide |
|||
# protection against state compromise (forward security). |
|||
def __init__(self, seed): |
|||
self.generator = self.block_generator(seed) |
|||
|
|||
def __call__(self, numbytes): |
|||
return "".join([self.generator.next() for i in range(numbytes)]) |
|||
|
|||
def block_generator(self, seed): |
|||
counter = 0 |
|||
while True: |
|||
for byte in sha256("prng-%d-%s" % (counter, seed)).digest(): |
|||
yield byte |
|||
counter += 1 |
|||
|
|||
def randrange_from_seed__overshoot_modulo(seed, order): |
|||
# hash the data, then turn the digest into a number in [1,order). |
|||
# |
|||
# We use David-Sarah Hopwood's suggestion: turn it into a number that's |
|||
# sufficiently larger than the group order, then modulo it down to fit. |
|||
# This should give adequate (but not perfect) uniformity, and simple |
|||
# code. There are other choices: try-try-again is the main one. |
|||
base = PRNG(seed)(2*orderlen(order)) |
|||
number = (int(binascii.hexlify(base), 16) % (order-1)) + 1 |
|||
assert 1 <= number < order, (1, number, order) |
|||
return number |
|||
|
|||
def lsb_of_ones(numbits): |
|||
return (1 << numbits) - 1 |
|||
def bits_and_bytes(order): |
|||
bits = int(math.log(order-1, 2)+1) |
|||
bytes = bits // 8 |
|||
extrabits = bits % 8 |
|||
return bits, bytes, extrabits |
|||
|
|||
# the following randrange_from_seed__METHOD() functions take an |
|||
# arbitrarily-sized secret seed and turn it into a number that obeys the same |
|||
# range limits as randrange() above. They are meant for deriving consistent |
|||
# signing keys from a secret rather than generating them randomly, for |
|||
# example a protocol in which three signing keys are derived from a master |
|||
# secret. You should use a uniformly-distributed unguessable seed with about |
|||
# curve.baselen bytes of entropy. To use one, do this: |
|||
# seed = os.urandom(curve.baselen) # or other starting point |
|||
# secexp = ecdsa.util.randrange_from_seed__trytryagain(sed, curve.order) |
|||
# sk = SigningKey.from_secret_exponent(secexp, curve) |
|||
|
|||
def randrange_from_seed__truncate_bytes(seed, order, hashmod=sha256): |
|||
# hash the seed, then turn the digest into a number in [1,order), but |
|||
# don't worry about trying to uniformly fill the range. This will lose, |
|||
# on average, four bits of entropy. |
|||
bits, bytes, extrabits = bits_and_bytes(order) |
|||
if extrabits: |
|||
bytes += 1 |
|||
base = hashmod(seed).digest()[:bytes] |
|||
base = "\x00"*(bytes-len(base)) + base |
|||
number = 1+int(binascii.hexlify(base), 16) |
|||
assert 1 <= number < order |
|||
return number |
|||
|
|||
def randrange_from_seed__truncate_bits(seed, order, hashmod=sha256): |
|||
# like string_to_randrange_truncate_bytes, but only lose an average of |
|||
# half a bit |
|||
bits = int(math.log(order-1, 2)+1) |
|||
maxbytes = (bits+7) // 8 |
|||
base = hashmod(seed).digest()[:maxbytes] |
|||
base = "\x00"*(maxbytes-len(base)) + base |
|||
topbits = 8*maxbytes - bits |
|||
if topbits: |
|||
base = chr(ord(base[0]) & lsb_of_ones(topbits)) + base[1:] |
|||
number = 1+int(binascii.hexlify(base), 16) |
|||
assert 1 <= number < order |
|||
return number |
|||
|
|||
def randrange_from_seed__trytryagain(seed, order): |
|||
# figure out exactly how many bits we need (rounded up to the nearest |
|||
# bit), so we can reduce the chance of looping to less than 0.5 . This is |
|||
# specified to feed from a byte-oriented PRNG, and discards the |
|||
# high-order bits of the first byte as necessary to get the right number |
|||
# of bits. The average number of loops will range from 1.0 (when |
|||
# order=2**k-1) to 2.0 (when order=2**k+1). |
|||
assert order > 1 |
|||
bits, bytes, extrabits = bits_and_bytes(order) |
|||
generate = PRNG(seed) |
|||
while True: |
|||
extrabyte = "" |
|||
if extrabits: |
|||
extrabyte = chr(ord(generate(1)) & lsb_of_ones(extrabits)) |
|||
guess = string_to_number(extrabyte + generate(bytes)) + 1 |
|||
if 1 <= guess < order: |
|||
return guess |
|||
|
|||
|
|||
def number_to_string(num, order): |
|||
l = orderlen(order) |
|||
fmt_str = "%0" + str(2*l) + "x" |
|||
string = binascii.unhexlify(fmt_str % num) |
|||
assert len(string) == l, (len(string), l) |
|||
return string |
|||
|
|||
def string_to_number(string): |
|||
return int(binascii.hexlify(string), 16) |
|||
|
|||
def string_to_number_fixedlen(string, order): |
|||
l = orderlen(order) |
|||
assert len(string) == l, (len(string), l) |
|||
return int(binascii.hexlify(string), 16) |
|||
|
|||
# these methods are useful for the sigencode= argument to SK.sign() and the |
|||
# sigdecode= argument to VK.verify(), and control how the signature is packed |
|||
# or unpacked. |
|||
|
|||
def sigencode_strings(r, s, order): |
|||
r_str = number_to_string(r, order) |
|||
s_str = number_to_string(s, order) |
|||
return (r_str, s_str) |
|||
|
|||
def sigencode_string(r, s, order): |
|||
# for any given curve, the size of the signature numbers is |
|||
# fixed, so just use simple concatenation |
|||
r_str, s_str = sigencode_strings(r, s, order) |
|||
return r_str + s_str |
|||
|
|||
def sigencode_der(r, s, order): |
|||
return der.encode_sequence(der.encode_integer(r), der.encode_integer(s)) |
|||
|
|||
|
|||
def sigdecode_string(signature, order): |
|||
l = orderlen(order) |
|||
assert len(signature) == 2*l, (len(signature), 2*l) |
|||
r = string_to_number_fixedlen(signature[:l], order) |
|||
s = string_to_number_fixedlen(signature[l:], order) |
|||
return r, s |
|||
|
|||
def sigdecode_strings(rs_strings, order): |
|||
(r_str, s_str) = rs_strings |
|||
l = orderlen(order) |
|||
assert len(r_str) == l, (len(r_str), l) |
|||
assert len(s_str) == l, (len(s_str), l) |
|||
r = string_to_number_fixedlen(r_str, order) |
|||
s = string_to_number_fixedlen(s_str, order) |
|||
return r, s |
|||
|
|||
def sigdecode_der(sig_der, order): |
|||
#return der.encode_sequence(der.encode_integer(r), der.encode_integer(s)) |
|||
rs_strings, empty = der.remove_sequence(sig_der) |
|||
if empty != "": |
|||
raise der.UnexpectedDER("trailing junk after DER sig: %s" % |
|||
binascii.hexlify(empty)) |
|||
r, rest = der.remove_integer(rs_strings) |
|||
s, empty = der.remove_integer(rest) |
|||
if empty != "": |
|||
raise der.UnexpectedDER("trailing junk after DER numbers: %s" % |
|||
binascii.hexlify(empty)) |
|||
return r, s |
|||
|
@ -0,0 +1,29 @@ |
|||
#!/usr/bin/env python |
|||
|
|||
import sys |
|||
from electrum import TcpStratumInterface |
|||
|
|||
try: |
|||
addr = sys.argv[1] |
|||
except: |
|||
print "usage: get_history <bitcoin_address>" |
|||
sys.exit(1) |
|||
|
|||
i = TcpStratumInterface('ecdsa.org', 50001) |
|||
i.init_socket() |
|||
i.start() |
|||
i.send([('blockchain.address.get_history',[addr])]) |
|||
|
|||
while True: |
|||
try: |
|||
r = i.responses.get(True, 100000000000) |
|||
except KeyboardInterrupt: |
|||
break |
|||
method = r.get('method') |
|||
if method == 'blockchain.address.get_history': |
|||
confirmed = unconfirmed = 0 |
|||
h = r.get('result') |
|||
for item in h: |
|||
print item['tx_hash'], item['value'] |
|||
|
|||
break |
@ -1,3 +1,3 @@ |
|||
ELECTRUM_VERSION = "0.58" |
|||
ELECTRUM_VERSION = "0.60" |
|||
SEED_VERSION = 4 # bump this everytime the seed generation is modified |
|||
TRANSLATION_ID = 27601 # version of the wiki page |
|||
TRANSLATION_ID = 28079 # version of the wiki page |
|||
|
Loading…
Reference in new issue