mirror of
https://github.com/TribesNext/t2-scripts.git
synced 2026-01-21 19:04:45 +00:00
492 lines
12 KiB
Ruby
492 lines
12 KiB
Ruby
#
|
|
# Tribes 2 Community System Reengineering Initiative
|
|
# Assymetric Cryptography Identity Provisioning
|
|
# Version 1.0
|
|
#
|
|
# Written by Electricutioner/Thyth
|
|
# http://absolous.no-ip.com/
|
|
# Copyright 2008
|
|
#
|
|
# Released under the terms of the GNU General Public License v3 or later.
|
|
# http://www.gnu.org/licenses/gpl.html
|
|
# Your use of this software is subject to the terms of that license. Use, modification, or distribution
|
|
# constitutes acceptance of these software terms. This license is the only manner by which you are permitted
|
|
# to use this software, thus rejection of the license terms prohibits your use of this software.
|
|
#
|
|
|
|
# fast modular exponentiation -- the key to the RSA algorithm
|
|
# result = (b ^ e) % m
|
|
def rsa_mod_exp(b, e, m)
|
|
result = 1
|
|
while (e > 0)
|
|
if ((e & 1) == 1)
|
|
result = (result * b) % m
|
|
end
|
|
e = e >> 1
|
|
b = (b * b) % m
|
|
end
|
|
return result
|
|
end
|
|
|
|
# RSA key class to keep things nice and organized
|
|
class RSAKey
|
|
# allow reading and writing the key values
|
|
attr_reader :e, :n, :twister, :strength
|
|
attr_writer :e, :d, :n, :twister
|
|
|
|
# allow protecting the d value so it isn't stolen by evil scripts
|
|
# once a key is protected, it cannot be deprotected, but it can be used to decrypt
|
|
def protect
|
|
@protected = 1
|
|
end
|
|
# attribute reader for d that returns nil if key protection is active
|
|
def d
|
|
if (@protected == 1)
|
|
return nil
|
|
else
|
|
return @d
|
|
end
|
|
end
|
|
|
|
# encrypt a message with the public exponent (e)
|
|
# this could be construed as a misnomer, since this is used to verify authentication
|
|
# images from the authentication server, and to verify a client has both parts of the key they
|
|
# claim to have
|
|
def encrypt(message)
|
|
rsa_mod_exp(message, @e, @n)
|
|
end
|
|
|
|
# decrypt a message with the private exponent (d), also usable for signing
|
|
# obviously, this will fail if the instance is only the public part of the key
|
|
def decrypt(message)
|
|
rsa_mod_exp(message, @d, @n)
|
|
end
|
|
|
|
# generate a new random RSA key of the specified bitsize
|
|
# this generates keys that should be resistant to quick factorization techniques
|
|
def generate(bitsize)
|
|
p = 0
|
|
q = 0
|
|
@n = 100
|
|
@strength = bitsize
|
|
|
|
# test for some conditions that could produce insecure RSA keys
|
|
# p, q difference to see if Fermat factorization could be successful
|
|
# p - q must be greater than 2*(n ^ (1/4))
|
|
while ((p - q).abs < (2 * Math.sqrt(Math.sqrt(@n))))
|
|
p = createPrime(bitsize / 2, 150)
|
|
q = createPrime(bitsize / 2, 150)
|
|
@n = p * q
|
|
end
|
|
|
|
totient = (p - 1) * (q - 1)
|
|
|
|
# e must be coprime to the totient. we start at 3 and add 2 whenever coprime test fails
|
|
@e = 3
|
|
coprimee = 0
|
|
while (coprimee)
|
|
if (@e > 7)
|
|
# e over 7 has a large chance of not being coprime to the totient
|
|
generate(bitsize)
|
|
return
|
|
end
|
|
block = extendedEuclid(@e, totient, 0, 1, 1, 0)
|
|
if (block[0] > 1)
|
|
@e = @e + 2
|
|
else
|
|
coprimee = nil
|
|
end
|
|
end
|
|
|
|
# calculate the d value such that d * e = 1 mod totient
|
|
# this calculation is done in the coprime of e verification
|
|
@d = block[1]
|
|
while (@d < 0)
|
|
@d = @d + totient
|
|
end
|
|
|
|
# verify that the generated key is a valid RSA key
|
|
1.upto(10) do |i|
|
|
testVal = @twister.randomnumber(bitsize) % @n
|
|
if (decrypt(encrypt(testVal)) != testVal)
|
|
# key failed... generate a new one
|
|
generate(bitsize)
|
|
return
|
|
end
|
|
end
|
|
end
|
|
|
|
# private methods that people shouldn't be poking without a good reason
|
|
private
|
|
# obtain gcd and return the "d" value that we want
|
|
def extendedEuclid(a, b, c, d, e, f)
|
|
if (b == 0)
|
|
block = Array.new(3, 0)
|
|
block[0] = a; # gcd(a, b)
|
|
block[1] = e; # coefficient of 'a' and the 'd' value we want
|
|
block[2] = f; # coefficient of 'b'
|
|
return block
|
|
else
|
|
return extendedEuclid(b, a % b, e - ((a / b) * c), f - ((a / b) * d), c, d);
|
|
end
|
|
end
|
|
|
|
# create a prime number of the specified bitlength
|
|
# the number of tests specified will control how many miller-rabin primality tests are run
|
|
# this function will return a prime number with a high degree of confidence if sufficient
|
|
# tests are run
|
|
def createPrime(bitlen, tests)
|
|
# generate a random number of the specific bitlen
|
|
p = @twister.randomnumber(bitlen)
|
|
|
|
# run the primality tests
|
|
testrun = 0
|
|
while (testrun < tests)
|
|
if (prime?(p))
|
|
testrun = testrun + 1
|
|
else # not prime -- generate a new one
|
|
return createPrime(bitlen, tests)
|
|
end
|
|
end
|
|
return p
|
|
end
|
|
|
|
# run a miller-rabin primality test on the given number
|
|
# returns true if the number is "probably" prime
|
|
def prime?(potential)
|
|
qandm = getqm(potential)
|
|
if (qandm[0] == -1)
|
|
return nil
|
|
end
|
|
|
|
bval = @twister.randomnumber(@strength / 2)
|
|
mval = qandm[1]
|
|
|
|
if (rsa_mod_exp(bval, mval, potential) == 1)
|
|
return 1
|
|
end
|
|
j = 0
|
|
while (j < qandm[0])
|
|
if ((potential - 1) == rsa_mod_exp(bval, mval, potential))
|
|
return 1
|
|
end
|
|
mval = mval * 2
|
|
j = j + 1
|
|
end
|
|
return nil
|
|
end
|
|
|
|
def getqm(p)
|
|
p = p - 1
|
|
rt = Array.new(2, 0)
|
|
if (p & 1 != 0)
|
|
rt[0] = -1
|
|
rt[1] = -1
|
|
return rt
|
|
end
|
|
div = p / 2
|
|
counter = 1
|
|
while (div & 1 == 0)
|
|
counter = counter + 1
|
|
div = div / 2
|
|
end
|
|
rt[0] = counter
|
|
rt[1] = div
|
|
return rt
|
|
end
|
|
end
|
|
|
|
# Mersenne Twister pseudo random number generator, modified for cryptographic security
|
|
# period length should be 20 * (2 ^ 19937 - 1)
|
|
class MersenneTwister
|
|
@index = 0
|
|
|
|
# build the internal storage array
|
|
def initialize
|
|
@mt = Array.new(624, 0)
|
|
end
|
|
|
|
# initialize the generator from a seed, can be done repeatedly
|
|
def seedgen(seed)
|
|
@mt[0] = seed
|
|
1.upto(623) do |i|
|
|
@mt[i] = 0xffffffff & (1812433243 * (@mt[i - 1] ^ (@mt[i - 1] >> 30)) + i)
|
|
end
|
|
generateNumbers
|
|
end
|
|
|
|
# extract a number that does not give away the state of the generator, takes 37 elements from generator
|
|
# and applies SHA1 on it to get a 20 element number. this is repeated until the required length
|
|
# is reached, and truncated as necessary to bring it down to the requested bitlen
|
|
def randomnumber(bits)
|
|
bytes = bits / 8
|
|
if (bits % 8 != 0)
|
|
bytes = bytes + 1
|
|
end
|
|
|
|
produced = 0
|
|
output = 0
|
|
stages = 0
|
|
mask = 0
|
|
|
|
sha1hash = SHA1Pure.new
|
|
while (produced < bytes)
|
|
sha1hash.prepare
|
|
1.upto(37) do |i|
|
|
sha1hash.append(extractNumber().to_s);
|
|
end
|
|
digest = sha1hash.hexdigest.to_i(16)
|
|
output = output | (digest << (160 * stages))
|
|
produced = produced + 20
|
|
stages = stages + 1
|
|
end
|
|
|
|
0.upto(bits.to_i) do |i|
|
|
mask = (mask.to_i << 1) | 1
|
|
end
|
|
return (output & mask)
|
|
end
|
|
|
|
private
|
|
# extract a tempered pseudorandom number
|
|
def extractNumber()
|
|
if (@index == 0)
|
|
generateNumbers()
|
|
end
|
|
|
|
y = @mt[@index.to_i]
|
|
y = y ^ (y >> 11)
|
|
y = y ^ ((y << 7) & 2636928640)
|
|
y = y ^ ((y << 15) & 4022730752)
|
|
y = y ^ (y >> 18)
|
|
y = y & 0xffffffff
|
|
|
|
@index = (@index.to_i + 1) % 624
|
|
return y
|
|
end
|
|
|
|
# generate 624 untempered numbers for this generator's array
|
|
def generateNumbers()
|
|
0.upto(623) do |i|
|
|
y = (@mt[i] & 0x80000000) + (@mt[(i + 1) % 624] & 0x7FFFFFFF)
|
|
@mt[i] = @mt[(i + 397) % 624] ^ (y >> 1)
|
|
if (y & 1 == 1)
|
|
@mt[i] = @mt[i] ^ 2567483615
|
|
end
|
|
end
|
|
end
|
|
end
|
|
|
|
# SHA1 in Pure Ruby
|
|
class SHA1Pure
|
|
|
|
def initialize
|
|
prepare
|
|
end
|
|
|
|
# prepare the hash digester for a new hash
|
|
def prepare
|
|
@state = Array.new(5, 0)
|
|
@block = Array.new(16, 0)
|
|
@blockIndex = 0
|
|
@count = 0
|
|
|
|
@state[0] = 0x67452301
|
|
@state[1] = 0xefcdab89
|
|
@state[2] = 0x98badcfe
|
|
@state[3] = 0x10325476
|
|
@state[4] = 0xc3d2e1f0
|
|
end
|
|
|
|
# append a string to the string being digested
|
|
def append(str)
|
|
str = str.to_s
|
|
str.each_byte {|c| update(c.to_i & 0xff)}
|
|
end
|
|
|
|
# produce a hexidecimal digest string
|
|
def hexdigest
|
|
bits = Array.new(8, 0)
|
|
0.upto(7) do |i|
|
|
bits[i] = (@count >> (((7 - i) * 8) & 0xff)) & 0xff
|
|
end
|
|
update(128)
|
|
while (@blockIndex != 56)
|
|
update(0)
|
|
end
|
|
0.upto(7) do |i|
|
|
update(bits[i])
|
|
end # this will accomplish a transform
|
|
|
|
# output the digest
|
|
digest = ""
|
|
0.upto(4) do |i|
|
|
chunk = @state[i].to_s(16)
|
|
while(chunk.length < 8)
|
|
chunk = "0" + chunk
|
|
end
|
|
digest = digest + chunk
|
|
end
|
|
prepare
|
|
return digest
|
|
end
|
|
|
|
private
|
|
def rol(val, bits)
|
|
val = val.to_i
|
|
bits = bits.to_i
|
|
return (val << bits) | (val >> (32 - bits))
|
|
end
|
|
|
|
def blk0(i)
|
|
i = i.to_i
|
|
@block[i] = (rol(@block[i], 24) & 0xff00ff00) | (rol(@block[i], 8) & 0xff00ff)
|
|
@block[i] = @block[i] & 0xffffffff
|
|
return @block[i]
|
|
end
|
|
|
|
def blk(i)
|
|
i = i.to_i
|
|
@block[i & 15] = rol(@block[(i + 13) & 15] ^ @block[(i + 8) & 15] ^ @block[(i + 2) & 15] ^ @block[i & 15], 1)
|
|
@block[i & 15] = @block[i & 15] & 0xffffffff
|
|
return @block[i & 15]
|
|
end
|
|
|
|
def r0(data, v, w, x, y, z, i)
|
|
data[z] += ((data[w] & (data[x] ^ data[y])) ^ data[y]) + blk0(i) + 0x5a827999 + rol(data[v], 5)
|
|
data[z] = data[z] & 0xffffffff
|
|
data[w] = rol(data[w], 30) & 0xffffffff
|
|
end
|
|
|
|
def r1(data, v, w, x, y, z, i)
|
|
data[z] += ((data[w] & (data[x] ^ data[y])) ^ data[y]) + blk(i) + 0x5a827999 + rol(data[v], 5)
|
|
data[z] = data[z] & 0xffffffff
|
|
data[w] = rol(data[w], 30) & 0xffffffff
|
|
end
|
|
|
|
def r2(data, v, w, x, y, z, i)
|
|
data[z] += (data[w] ^ data[x] ^ data[y]) + blk(i) + 0x6ed9eba1 + rol(data[v], 5)
|
|
data[z] = data[z] & 0xffffffff
|
|
data[w] = rol(data[w], 30) & 0xffffffff
|
|
end
|
|
|
|
def r3(data, v, w, x, y, z, i)
|
|
data[z] += (((data[w] | data[x]) & data[y]) | (data[w] & data[x])) + blk(i) + 0x8f1bbcdc + rol(data[v], 5)
|
|
data[z] = data[z] & 0xffffffff
|
|
data[w] = rol(data[w], 30) & 0xffffffff
|
|
end
|
|
|
|
def r4(data, v, w, x, y, z, i)
|
|
data[z] += (data[w] ^ data[x] ^ data[y]) + blk(i) + 0xca62c1d6 + rol(data[v], 5)
|
|
data[z] = data[z] & 0xffffffff
|
|
data[w] = rol(data[w], 30) & 0xffffffff
|
|
end
|
|
|
|
def transform
|
|
dd = Array.new(5, 0)
|
|
dd[0] = @state[0]
|
|
dd[1] = @state[1]
|
|
dd[2] = @state[2]
|
|
dd[3] = @state[3]
|
|
dd[4] = @state[4]
|
|
|
|
r0(dd,0,1,2,3,4, 0)
|
|
r0(dd,4,0,1,2,3, 1)
|
|
r0(dd,3,4,0,1,2, 2)
|
|
r0(dd,2,3,4,0,1, 3)
|
|
r0(dd,1,2,3,4,0, 4)
|
|
r0(dd,0,1,2,3,4, 5)
|
|
r0(dd,4,0,1,2,3, 6)
|
|
r0(dd,3,4,0,1,2, 7)
|
|
r0(dd,2,3,4,0,1, 8)
|
|
r0(dd,1,2,3,4,0, 9)
|
|
r0(dd,0,1,2,3,4,10)
|
|
r0(dd,4,0,1,2,3,11)
|
|
r0(dd,3,4,0,1,2,12)
|
|
r0(dd,2,3,4,0,1,13)
|
|
r0(dd,1,2,3,4,0,14)
|
|
r0(dd,0,1,2,3,4,15)
|
|
r1(dd,4,0,1,2,3,16)
|
|
r1(dd,3,4,0,1,2,17)
|
|
r1(dd,2,3,4,0,1,18)
|
|
r1(dd,1,2,3,4,0,19)
|
|
r2(dd,0,1,2,3,4,20)
|
|
r2(dd,4,0,1,2,3,21)
|
|
r2(dd,3,4,0,1,2,22)
|
|
r2(dd,2,3,4,0,1,23)
|
|
r2(dd,1,2,3,4,0,24)
|
|
r2(dd,0,1,2,3,4,25)
|
|
r2(dd,4,0,1,2,3,26)
|
|
r2(dd,3,4,0,1,2,27)
|
|
r2(dd,2,3,4,0,1,28)
|
|
r2(dd,1,2,3,4,0,29)
|
|
r2(dd,0,1,2,3,4,30)
|
|
r2(dd,4,0,1,2,3,31)
|
|
r2(dd,3,4,0,1,2,32)
|
|
r2(dd,2,3,4,0,1,33)
|
|
r2(dd,1,2,3,4,0,34)
|
|
r2(dd,0,1,2,3,4,35)
|
|
r2(dd,4,0,1,2,3,36)
|
|
r2(dd,3,4,0,1,2,37)
|
|
r2(dd,2,3,4,0,1,38)
|
|
r2(dd,1,2,3,4,0,39)
|
|
r3(dd,0,1,2,3,4,40)
|
|
r3(dd,4,0,1,2,3,41)
|
|
r3(dd,3,4,0,1,2,42)
|
|
r3(dd,2,3,4,0,1,43)
|
|
r3(dd,1,2,3,4,0,44)
|
|
r3(dd,0,1,2,3,4,45)
|
|
r3(dd,4,0,1,2,3,46)
|
|
r3(dd,3,4,0,1,2,47)
|
|
r3(dd,2,3,4,0,1,48)
|
|
r3(dd,1,2,3,4,0,49)
|
|
r3(dd,0,1,2,3,4,50)
|
|
r3(dd,4,0,1,2,3,51)
|
|
r3(dd,3,4,0,1,2,52)
|
|
r3(dd,2,3,4,0,1,53)
|
|
r3(dd,1,2,3,4,0,54)
|
|
r3(dd,0,1,2,3,4,55)
|
|
r3(dd,4,0,1,2,3,56)
|
|
r3(dd,3,4,0,1,2,57)
|
|
r3(dd,2,3,4,0,1,58)
|
|
r3(dd,1,2,3,4,0,59)
|
|
r4(dd,0,1,2,3,4,60)
|
|
r4(dd,4,0,1,2,3,61)
|
|
r4(dd,3,4,0,1,2,62)
|
|
r4(dd,2,3,4,0,1,63)
|
|
r4(dd,1,2,3,4,0,64)
|
|
r4(dd,0,1,2,3,4,65)
|
|
r4(dd,4,0,1,2,3,66)
|
|
r4(dd,3,4,0,1,2,67)
|
|
r4(dd,2,3,4,0,1,68)
|
|
r4(dd,1,2,3,4,0,69)
|
|
r4(dd,0,1,2,3,4,70)
|
|
r4(dd,4,0,1,2,3,71)
|
|
r4(dd,3,4,0,1,2,72)
|
|
r4(dd,2,3,4,0,1,73)
|
|
r4(dd,1,2,3,4,0,74)
|
|
r4(dd,0,1,2,3,4,75)
|
|
r4(dd,4,0,1,2,3,76)
|
|
r4(dd,3,4,0,1,2,77)
|
|
r4(dd,2,3,4,0,1,78)
|
|
r4(dd,1,2,3,4,0,79)
|
|
|
|
@state[0] = (@state[0] + dd[0]) & 0xffffffff
|
|
@state[1] = (@state[1] + dd[1]) & 0xffffffff
|
|
@state[2] = (@state[2] + dd[2]) & 0xffffffff
|
|
@state[3] = (@state[3] + dd[3]) & 0xffffffff
|
|
@state[4] = (@state[4] + dd[4]) & 0xffffffff
|
|
end
|
|
|
|
def update(b)
|
|
mask = (8 * (@blockIndex & 3))
|
|
@count = @count + 8
|
|
@block[@blockIndex >> 2] = @block[@blockIndex >> 2] & ~(0xff << mask)
|
|
@block[@blockIndex >> 2] = @block[@blockIndex >> 2] | ((b & 0xff) << mask)
|
|
@blockIndex = @blockIndex + 1
|
|
if (@blockIndex == 64)
|
|
transform
|
|
@blockIndex = 0
|
|
end
|
|
end
|
|
end |