Bitcoin Forum
May 10, 2024, 07:16:30 AM *
News: Latest Bitcoin Core release: 27.0 [Torrent]
 
   Home   Help Search Login Register More  
Pages: « 1 [2]  All
  Print  
Author Topic: Pubkey recovery from ECDSA signature (getting owner's public key from its tx)  (Read 458 times)
anubisthecracker (OP)
Newbie
*
Offline Offline

Activity: 10
Merit: 15


View Profile
March 31, 2022, 11:12:13 AM
Last edit: March 31, 2022, 02:25:28 PM by anubisthecracker
 #21

Bump! Sorry if it's too soon to do a bump, also I edited my above reply.

I have another question, it's only to confirm I understood the process.

I read the following stackexchange posts:

- https://bitcoin.stackexchange.com/questions/8250/what-is-relation-between-scriptsig-and-scriptpubkey
- https://bitcoin.stackexchange.com/questions/93966/getting-public-key-from-any-raw-transaction-what-to-take-care-of
- https://bitcoin.stackexchange.com/questions/8235/how-is-the-output-script-formed
- https://bitcoin.stackexchange.com/questions/91088/how-do-i-get-the-scriptpubkey-type-from-the-raw-output-script
- https://bitcoin.stackexchange.com/questions/100907/retrieving-an-addresses-public-key-from-pkscript-sigscript-witness

So, if I understand correctly when you generate a transaction, then you have a sender (input) and receiver (output). When everything is signed and sent into the blockchain, two things happen,

the sender (input) sends this into the blockchain (created by its wallet client):

Code:
scriptPubKey = OP_DUP OP_HASH160 <pubKeyHash> OP_EQUALVERIFY OP_CHECKSIG

and the receiver (output):

Code:
scriptSig = <sig> <pubKey>

Everything right until here? (correct me please)

Then, if I would like to retrieve the public key I must define what it's going on here and which options do I have left.

I'd like to have as many public keys as possible.

To clarify things,

to crawl pubkey from the sender (input) then I'll need to look into the signature on the scriptSig part of the receiver (output), and try to decode it?

to crawl pubkey from the receiver (output) then I'll need to look into the type of script as @pooya87 stated here:

When spending a P2PKH output, the public key is the top stack element when reaching OP_CHECKSIG.
When spending a P2WPKH output, the public key is the second witness item.
When spending a P2PK output, the public key is in the output.
When spending a P2TR output, the public key is a tweaked pubkey in the output (the witness program).
When spending other scripts such as P2SH when you reach any of the above op codes it should be the top stack element or in case of multi signatures there are multiple pubkeys.

So, Idk if I'm missing something more, I think that's the only way to get the public keys on both cases.

There are some made examples that are useful:

- https://github.com/bitcoin-core/btcdeb#script-compiler

Code:
> btcc OP_DUP OP_HASH160 897c81ac37ae36f7bc5b91356cfb0138bfacb3c1 OP_EQUALVERIFY OP_CHECKSIG
> 76a914897c81ac37ae36f7bc5b91356cfb0138bfacb3c188ac

But the hash generated here is not a public key?

And also I saw a php implementation for a rpc client for btc node, and a very interesting issue:

- https://github.com/Bit-Wasp/bitcoin-p2p-php/issues/29

Code:
$db = new PDO('pgsql:dbname=explore;host=localhost;','pgsql','');
$count = $rpc->getblockcount();
for ($i=0;$i<=$count;$i++) {

    $hash = $rpc->getblock($rpc->getblockhash($i));
    foreach ($hash['tx'] as $tx) {
        $raw = $rpc->getrawtransaction($tx);
        if ($raw == -5) {
            continue;
        } else {
            $x = $rpc->decoderawtransaction($raw);
            if(is_array($x)) {
                foreach ($x['vin'] as $vin) {
                    if (isset($vin['coinbase']))
                        continue;
                    $txid = $vin['txid'];
                    $stmt = $db->prepare('delete from tx where txid = :id');
                    $stmt->bindValue('id',$x['txid']);
                    $stmt->execute();
                }
                foreach ($x['vout'] as $index=>$out) {var_dump($out['value']);
                    $stmt = $db->prepare('insert into tx (txid,"value",n,pubkey,address) values (:id,:value,:n,:pubkey,:address)');
                    $stmt->bindValue(':id',$x['txid']);
                    $stmt->bindValue(':value',$out['value']);
                    $stmt->bindValue(':n',$index);
                    $stmt->bindValue(':pubkey',$out['scriptPubKey']['hex']);
                    $stmt->bindValue(':address',$out['scriptPubKey']['addresses'][0]);
                    $stmt->execute();
                }
            }
        }
    }
}

So,

Code:
                    $stmt->bindValue(':pubkey',$out['scriptPubKey']['hex']);
                    $stmt->bindValue(':address',$out['scriptPubKey']['addresses'][0]);

- :pubkey is the hash
- :address is the public key (I need to research this)?


More questions,

- so the easy way is to have the sender (input) pubkey?
- because, the receiver (output) must have to recover the public key from it's signature which is a expensive process for a lot of addreses?
- we have 720M of transactions, txid (right?)? https://www.blockchain.com/charts/n-transactions-total if I list txid I can still use btcc to have pubkey (I think not)?
- having bitcoin core rpc calls: https://developer.bitcoin.org/reference/rpc/index.html which has listunspent that does something similar to btcposbal2csv (as I understood)?
- but isn't there any rpc call to retrieve public keys from an txid? (EDIT: well, I saw this: https://gist.github.com/t4sk/68dbde1ef75762753214c0b3823097e8 and on documentation you can see: https://developer.bitcoin.org/reference/rpc/validateaddress.html but I don't know if it will work)
- and last, even if I have the pubkey from the sender/receiver I must check the balance if it's positive?

From the last question, I have some premises that Idk if they are right:

Most of the public keys exposed:

- have a high bit range (above 120 bits)
- the ones left with lower bit range are all spent/cracked (their corresponding btc address has a 0 BTC balance)
1715325390
Hero Member
*
Offline Offline

Posts: 1715325390

View Profile Personal Message (Offline)

Ignore
1715325390
Reply with quote  #2

1715325390
Report to moderator
Each block is stacked on top of the previous one. Adding another block to the top makes all lower blocks more difficult to remove: there is more "weight" above each block. A transaction in a block 6 blocks deep (6 confirmations) will be very difficult to remove.
Advertised sites are not endorsed by the Bitcoin Forum. They may be unsafe, untrustworthy, or illegal in your jurisdiction.
1715325390
Hero Member
*
Offline Offline

Posts: 1715325390

View Profile Personal Message (Offline)

Ignore
1715325390
Reply with quote  #2

1715325390
Report to moderator
1715325390
Hero Member
*
Offline Offline

Posts: 1715325390

View Profile Personal Message (Offline)

Ignore
1715325390
Reply with quote  #2

1715325390
Report to moderator
1715325390
Hero Member
*
Offline Offline

Posts: 1715325390

View Profile Personal Message (Offline)

Ignore
1715325390
Reply with quote  #2

1715325390
Report to moderator
litecoin_messiah
Sr. Member
****
Offline Offline

Activity: 355
Merit: 268



View Profile WWW
April 02, 2022, 10:20:06 PM
 #22

i modified this to work with python3

its not perfect, just past bitcoin address after launching it (name it r.py )

on console do python3 r.py
and enter the bitcoin address, preferably one with nonce reuse known so you see if its vulnerable

Code:
#!/usr/bin/python

#################################################################################
#                                                                               #
#.______               _______.  ______     ___      .__   __.                  #
#|   _  \             /       | /      |   /   \     |  \ |  |                  #
#|  |_)  |    ______ |   (----`|  ,----'  /  ^  \    |   \|  |                  #
#|      /    |______| \   \    |  |      /  /_\  \   |  . `  |                  #
#|  |\  \----.    .----)   |   |  `----./  _____  \  |  |\   |                  #
#| _| `._____|    |_______/     \______/__/     \__\ |__| \__|  v0.2.0          #
#                                                                               #
#GNU PL - 2015 - ca333  (modified by simcity4242)     #
#                                                                               #
#USE AT OWN RISK!                                                               #
#################################################################################

import json
import urllib2
import time
import sys

#for some reason blockchain.info api-chain is 59711 blocks short..
#blockstart = 170399
#blockstart += 59711
#blockcount = urllib2.urlopen("https://blockchain.info/en/q/getblockcount").read()

def rscan(addr):
"""Check address for duplicated r values."""
# TODO: add BCI API check address

print "WELCOME TO R-scan v0.1.2!"
print "ADDRESS-R-SCAN: "

urladdr = 'https://blockchain.info/address/%s?format=json&offset=%s'

###control api-url
#print str(urladdr[:-22] % addr)

addrdata = json.load(urllib2.urlopen(urladdr % (addr, '0')))
ntx = addrdata['n_tx']
print "Data for pubkey: " + str(addr) + " has " + str(addrdata['n_tx']).center(6) + "Tx%s" % 's'[ntx==1:]
#print "number of txs: " + str(addrdata['n_tx'])

#tx-details:

txs = []
for i in range(0, ntx//50 + 1):
sys.stderr.write("Fetching Txs from offset\t%s\n" % str(i*50))
jdata = json.load(urllib2.urlopen(urladdr % (addr, str(i*50))))
txs.extend(jdata['txs'])

#assert len(txs) == ntx
addrdata['txs'] = txs


y = 0
inputs = []
while y < ntx:
#print "#################################################################################"
#print "TX nr :" + str(y+1)
#print "hash: " + str(addrdata['txs'][y]['hash'])
#print "number of inputs: " + str(addrdata['txs'][y]['vin_sz'])
#only if
#if addrdata['txs'][y]['vin_sz'] > 1:
zy = 0
while zy < addrdata['txs'][y]['vin_sz']:
print "Input-ScriptNR " + str(zy+1) + " :" + str(addrdata['txs'][y]['inputs'][zy]['script'])
inputs.append(addrdata['txs'][y]['inputs'][zy]['script'])
zy += 1
y += 1

xi = 0
zi = 1
lenx = len(inputs)
alert = 0

bad = []
#compare the sig values in each input script
while xi < lenx-1:
x = 0
while x < lenx-zi:
if inputs[xi][10:74] == inputs[x+zi][10:74]:
print "In Input NR: " + str(xi) + "[global increment] " + str(inputs[xi])
print('\a')
print "Resued R-Value: "
print inputs[x+zi]
bad.append((int(x), str(inputs[x+zi][10:74])))
alert += 1
x += 1
zi += 1
xi += 1

#check duplicates
#alert when everything ok

if alert < 1:
print "Good pubKey. No problems."
else:
print "Address %s has %d reused R value%s!" % (addr, len(bad), "s"[len(bad)==1:])
return bad

if __name__ == '__main__':
from sys import argv
print """python rscan.py 1BFhrfTTZP3Nw4BNy4eX4KFLsn9ZeijcMm"""
if len(argv) == 1:
addr = raw_input("Enter Bitcoin address eg 1BFhrfTTZP3Nw4BNy4eX4KFLsn9ZeijcMm")
elif len(argv) == 2 and isinstance(argv[1], basestring):
addr = str(argv[1])
rscan(addr)

# 9ec4bc49e828d924af1d1029cacf709431abbde46d59554b62bc270e3b29c4b1
litecoin_messiah
Sr. Member
****
Offline Offline

Activity: 355
Merit: 268



View Profile WWW
April 10, 2022, 07:41:23 AM
 #23

i modified this to work with python3

its not perfect, just past bitcoin address after launching it (name it r.py )

on console do python3 r.py
and enter the bitcoin address, preferably one with nonce reuse known so you see if its vulnerable

--snip--

I skimmed the code and there are few things i'd like to mention,
1. blockchain.info now is blockchain.com
2. blockchain.com ask you to limit queries to 1 query/10 seconds. They might block if the script is too fast.

Please limit your queries to a maximum of 1 every 10 seconds.

I hate blockchain.com/info but do you think i would post something without checking? well i probably do as i forgot to mention it's for python27,

i edited a line so it worked without breaking i forgot which one it was

Code:
#!/usr/bin/python

#################################################################################
#                                                                               #
#.______               _______.  ______     ___      .__   __.                  #
#|   _  \             /       | /      |   /   \     |  \ |  |                  #
#|  |_)  |    ______ |   (----`|  ,----'  /  ^  \    |   \|  |                  #
#|      /    |______| \   \    |  |      /  /_\  \   |  . `  |                  #
#|  |\  \----.    .----)   |   |  `----./  _____  \  |  |\   |                  #
#| _| `._____|    |_______/     \______/__/     \__\ |__| \__|  v0.2.0          #
#                                                                               #
#GNU PL - 2015 - ca333  (modified by simcity4242)                                                           #
#                                                                               #
#USE AT OWN RISK!                                                               #
#################################################################################

import json
import urllib2
import time
import sys

#for some reason blockchain.info api-chain is 59711 blocks short..
#blockstart = 170399
#blockstart += 59711
#blockcount = urllib2.urlopen("https://blockchain.info/en/q/getblockcount").read()

def rscan(addr):
        """Check address for duplicated r values."""
        # TODO: add BCI API check address

        print "WELCOME TO R-scan v0.1.2!"
        print "ADDRESS-R-SCAN: "

        urladdr = 'https://blockchain.info/address/%s?format=json&offset=%s'

        ###control api-url
        #print str(urladdr[:-22] % addr)

        addrdata = json.load(urllib2.urlopen(urladdr % (addr, '0')))
        ntx = addrdata['n_tx']
        print "Data for pubkey: " + str(addr) + " has " + str(addrdata['n_tx']).center(6) + "Tx%s" % 's'[ntx==1:]
        #print "number of txs: " + str(addrdata['n_tx'])

        #tx-details:

        txs = []
        for i in range(0, ntx//50 + 1):
                sys.stderr.write("Fetching Txs from offset\t%s\n" % str(i*50))
                jdata = json.load(urllib2.urlopen(urladdr % (addr, str(i*50))))
                txs.extend(jdata['txs'])

        #assert len(txs) == ntx
        addrdata['txs'] = txs


        y = 0
        inputs = []
        while y < ntx:
                #print "#################################################################################"
                #print "TX nr :" + str(y+1)
                #print "hash: " + str(addrdata['txs'][y]['hash'])
                #print "number of inputs: " + str(addrdata['txs'][y]['vin_sz'])
                #only if
                        #if addrdata['txs'][y]['vin_sz'] > 1:
                zy = 0
                while zy < addrdata['txs'][y]['vin_sz']:
                        print "Input-ScriptNR " + str(zy+1) + " :" + str(addrdata['txs'][y]['inputs'][zy]['script'])
                        inputs.append(addrdata['txs'][y]['inputs'][zy]['script'])
                        zy += 1
                y += 1

        xi = 0
        zi = 1
        lenx = len(inputs)
        alert = 0

        bad = []
        #compare the sig values in each input script
        while xi < lenx-1:
                x = 0
                while x < lenx-zi:
                        if inputs[xi][10:74] == inputs[x+zi][10:74]:
                                print "In Input NR: " + str(xi) + "[global increment] " + str(inputs[xi])
                                print('\a')
                                print "Resued R-Value: "
                                print inputs[x+zi]
                                bad.append((int(x), str(inputs[x+zi][10:74])))
                                alert += 1
                        x += 1
                        zi += 1
                xi += 1

        #check duplicates
        #alert when everything ok

        if alert < 1:
                print "Good pubKey. No problems."
        else:
                print "Address %s has %d reused R value%s!" % (addr, len(bad), "s"[len(bad)==1:])
                return bad

if __name__ == '__main__':
        from sys import argv
        print """python rscan.py 1BFhrfTTZP3Nw4BNy4eX4KFLsn9ZeijcMm"""
        if len(argv) == 1:
                addr = raw_input("Enter Bitcoin address eg 1BFhrfTTZP3Nw4BNy4eX4KFLsn9ZeijcMm")
        elif len(argv) == 2 and isinstance(argv[1], basestring):
                addr = str(argv[1])
        rscan(addr)

# 9ec4bc49e828d924af1d1029cacf709431abbde46d59554b62bc270e3b29c4b1


Quote
Data for pubkey: 1C8x2hqqgE2b3TZPQcFgas73xYWNh6TK9W has   8   Txs
Fetching Txs from offset        0
Input-ScriptNR 1 :48304502200f18c2d1fe6513b90f44513e975e05cc498e7f5a565b46c65b1d448734392c6f02210 0f80dee18431a82bfbbede36fe455cbd63158bd2c118ed36772a18da213db51ec014104d0fe07ff 74c9ef5b00fed1104fad43ecf72dbab9e60733e4f56eacf24b20cf3b8cd945bcabcc73ba0158bf9 ce769d43e94bd58c5c7e331a188922b3fe9ca1f5a
Input-ScriptNR 1 :48304502202b83d59c1d23c08efd82ee0662fec23309c3adbcbd1f0b8695378db4b14e736602210 0933bd8673a9ef8289a66305a5d86b11e93eace454220cdddd45ddfab4d6b27d4014104d0fe07ff 74c9ef5b00fed1104fad43ecf72dbab9e60733e4f56eacf24b20cf3b8cd945bcabcc73ba0158bf9 ce769d43e94bd58c5c7e331a188922b3fe9ca1f5a
Input-ScriptNR 1 :4b3048022200002b83d59c1d23c08efd82ee0662fec23309c3adbcbd1f0b8695378db4b14e73660 2220000334a96676e58b1bb01784cb7c556dd8ce1c220171904da22e18fe1e7d1510db5014104d0 fe07ff74c9ef5b00fed1104fad43ecf72dbab9e60733e4f56eacf24b20cf3b8cd945bcabcc73ba0 158bf9ce769d43e94bd58c5c7e331a188922b3fe9ca1f5a
Input-ScriptNR 1 :483045022100e9507a191ea86c8e689377130a1c775637d54184168a5b74cdb3c63018006be3022 062dba5c682e2413f2ae5461348dd7421b46f74e76d33ef0390fa7ec069dd8c07014104d0fe07ff 74c9ef5b00fed1104fad43ecf72dbab9e60733e4f56eacf24b20cf3b8cd945bcabcc73ba0158bf9 ce769d43e94bd58c5c7e331a188922b3fe9ca1f5a
Input-ScriptNR 1 :483045022100e2f79235bd4ef09ee56737422ad6b2f224d9bbfd5654738946fa7721860a56b0022 00936e3dbe07340f74ebafd39e1166c1c4b777b5e48a234939050bc9bb9f10a0e014104d0fe07ff 74c9ef5b00fed1104fad43ecf72dbab9e60733e4f56eacf24b20cf3b8cd945bcabcc73ba0158bf9 ce769d43e94bd58c5c7e331a188922b3fe9ca1f5a
Input-ScriptNR 1 :483045022100c81a4ec35697dfdf3fd21e6ed2a654d6d9a62ddc96494838f10f66c8c25f679e022 00e76db47b8463877971d1324f644c65c7ad5a44993db7b9e75a43d2978b6d045014104d0fe07ff 74c9ef5b00fed1104fad43ecf72dbab9e60733e4f56eacf24b20cf3b8cd945bcabcc73ba0158bf9 ce769d43e94bd58c5c7e331a188922b3fe9ca1f5a
Input-ScriptNR 1 :48304502200f18c2d1fe6513b90f44513e975e05cc498e7f5a565b46c65b1d448734392c6f02210 0b29a06f2e30064542b298d711c315dca9ca99756180219699f776dd3d6a6bbc90141049ba39856 eec011b79f1acb997760ed9d3f90d477077d17df2571d94b2fa2137bf0976d786b6aabc903746e2 69628b2c28e4b5db753845e5713a48ee7d6b97aaf
Input-ScriptNR 2 :48304502202b83d59c1d23c08efd82ee0662fec23309c3adbcbd1f0b8695378db4b14e736602210 0d66567fbfc4bb78520883d599bc51585f75393d2e6b227997410331e09fce5ae0141049ba39856 eec011b79f1acb997760ed9d3f90d477077d17df2571d94b2fa2137bf0976d786b6aabc903746e2 69628b2c28e4b5db753845e5713a48ee7d6b97aaf
Input-ScriptNR 1 :48304502200f18c2d1fe6513b90f44513e975e05cc498e7f5a565b46c65b1d448734392c6f02210 0917766d14f2e9933eb269c83b3ad440ed8432da8beb5733f34046509e48b1d850141049ba39856 eec011b79f1acb997760ed9d3f90d477077d17df2571d94b2fa2137bf0976d786b6aabc903746e2 69628b2c28e4b5db753845e5713a48ee7d6b97aaf
In Input NR: 1[global increment] 48304502202b83d59c1d23c08efd82ee0662fec23309c3adbcbd1f0b8695378db4b14e736602210 0933bd8673a9ef8289a66305a5d86b11e93eace454220cdddd45ddfab4d6b27d4014104d0fe07ff 74c9ef5b00fed1104fad43ecf72dbab9e60733e4f56eacf24b20cf3b8cd945bcabcc73ba0158bf9 ce769d43e94bd58c5c7e331a188922b3fe9ca1f5a

Resued R-Value:
48304502202b83d59c1d23c08efd82ee0662fec23309c3adbcbd1f0b8695378db4b14e736602210 0d66567fbfc4bb78520883d599bc51585f75393d2e6b227997410331e09fce5ae0141049ba39856 eec011b79f1acb997760ed9d3f90d477077d17df2571d94b2fa2137bf0976d786b6aabc903746e2 69628b2c28e4b5db753845e5713a48ee7d6b97aaf
Address 1C8x2hqqgE2b3TZPQcFgas73xYWNh6TK9W has 1 reused R value!
root@localhost:~/rsz#

anyway the real script i should have shared is https://github.com/tintinweb/ecdsa-private-key-recovery

edit: the script could run for half hour strait but maybe crash, bcinfo don't limit as they say nxt time plz don't skim but test
bittraffic
Hero Member
*****
Offline Offline

Activity: 2940
Merit: 612


#SWGT PRE-SALE IS LIVE


View Profile WWW
April 10, 2022, 08:03:07 AM
 #24


anyway the real script i should have shared is https://github.com/tintinweb/ecdsa-private-key-recovery

edit: the script could run for half hour strait but maybe crash, bcinfo don't limit as they say nxt time plz don't skim but test

So much information here I didn't even thought that every digits in public key has significant information corresponding to some data. I just want to ask if the script to recover privatekey from the wallet that exposes its public key works which are we all in danger to lose our BTC because of doing a transaction?
I understand why there is the need for using fresh BTC address every transaction to avoid publishing publickey. But seems a very weak security if this is the case.


.SWG.io.













..Pre-Sale is LIVE at $0.15..







..Buy Now..







``█████████████████▄▄
``````▄▄▄▄▄▄▄▄▄▄▄▄████▄
````````````````````▀██▄
```▀▀▀▀``▀▀▀▀▀▀▀▀▀▀▀▄███
``````▄▄▄▄▄▄▄▄▄▄▄▄``▄███
``▄▄▄▄▄▄▄```▄▄▄▄▄``▄███
``````````````````▄██▀
```````````████████████▄
````````````````````▀▀███
`````````▀▀▀▀▀▀▀▀▀▀▀▀▄████
```▄▄▄``▄▄▄▄▄▄▄▄▄▄`````███
`▄▄▄▄▄▄▄▄▄``▄▄▄▄▄▄`````███
▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▀████
```````````````````▄▄████
``▀▀▀▀▀``▀▀▀▀▀▀▀▀▀█████
██``███████████████▀▀

FIRST LISTING
..CONFIRMED..






pooya87
Legendary
*
Offline Offline

Activity: 3444
Merit: 10558



View Profile
April 10, 2022, 08:09:50 AM
 #25

I just want to ask if the script to recover privatekey from the wallet that exposes its public key works which are we all in danger to lose our BTC because of doing a transaction?
It is impossible to recover a private key from public key and the operation is so expensive that it probably won't be solved in our lifetime.

Quote
I understand why there is the need for using fresh BTC address every transaction to avoid publishing publickey. But seems a very weak security if this is the case.
It is better to not-reuse addresses for many reasons involving privacy but you should know that if it were possible to compute private key from public key, bitcoin would become worthless and it won't matter if you didn't reuse addresses.!

.
.BLACKJACK ♠ FUN.
█████████
██████████████
████████████
█████████████████
████████████████▄▄
░█████████████▀░▀▀
██████████████████
░██████████████
████████████████
░██████████████
████████████
███████████████░██
██████████
CRYPTO CASINO &
SPORTS BETTING
▄▄███████▄▄
▄███████████████▄
███████████████████
█████████████████████
███████████████████████
█████████████████████████
█████████████████████████
█████████████████████████
███████████████████████
█████████████████████
███████████████████
▀███████████████▀
█████████
.
Pages: « 1 [2]  All
  Print  
 
Jump to:  

Powered by MySQL Powered by PHP Powered by SMF 1.1.19 | SMF © 2006-2009, Simple Machines Valid XHTML 1.0! Valid CSS!