Assembly Language Programming

October 25, 2012

Digital Signature Algorithm – Generating Large Prime Numbers

Written in x86 assembly language using the Flat Assembler (FASM), this program generates the large prime numbers that are required for the Digital Signature Algorithm (DSA).

The DSA primes p and q are two large prime numbers, which satisfy the relation:

p mod q = 1

In other words, q is a prime factor of p-1.

Digital Signature Algorithm

The Digital Signature Algorithm (DSA) is outlined in chapter 4 of the Digital Signature Standard (DSS), as published by the U.S. National Institute of Standards and Technology:

http://csrc.nist.gov/publications/fips/fips186-3/fips_186-3.pdf

The actual mathematical details for the DSA are specified in the fips 186-3 appendices.

In the DSA, a digital signature is generated by applying a private key (x) to the hash of the message to be signed. A public key (y) is used for verification of the signature. The keys x and y are generated from the domain parameters {p, q, g}. The DSA is based on the ElGamal signature scheme.

In this project, the only parts of the DSA I implement are those related to the generation of the two large prime numbers q and p. I do not compute the generator g, the keys x and y, or implement the actual signature process.

These are the parameters output by my program:

Small Prime c The starter prime of less than 33 bits in length used to generate the prime q.
Small Prime c0 The starter prime of less than 33 bits in length used to generate the prime p0.
First Seed The initial value of the seed for the hash function.
Q Seed The value of the hash function seed at the time the prime q was found.
P0 Seed The value of the hash function seed at the time the prime p0 was found.
P Seed The value of the hash function seed at the time the prime p was found.
Qgen Counter The value of the counter at the time the prime q was found.
P0gen Counter The value of the counter at the time the prime p0 was found.
Pgen Counter The value of the counter at the time the prime p was found.
Q The prime q, with a bit length as given by N.
P0 The prime p0, the starter prime for p.
P The prime p, with a bit length as given by L.

The various seed and counter values allow the primes q and p to be verified, since a particular first seed will always generate the same set of prime numbers and their corresponding seed and counter values.

To generate these numbers, I implement the following sections from FIPS 186-3:

A1.2 Construction and Validation of the Provable Primes p and q.
C.2 Conversion Between Bit Strings and Integers.
C.6 Shawe-Taylor Random_Prime Routine.
C.7 Trial Division.
C.8 Sieve Procedure.

The Keys x and y

I don’t implement the key generation process in my code, but I will briefly discuss it. The private key x is a randomly generated integer value in the range [1,q-1]. The public key y is in the range [1,p-1]. It is calculated from x:

y(x)=gx mod p

The only unknown in the expression above is the private key x, all of the other parameters are publically available. So how does the value of x remain secret? The security of x depends on the well known discrete logarithm problem. Here x is the discrete logarithm (to base g) of y. Currently there is no known method to quickly compute discrete logarithms, and a brute force approach to find x is out of the question because of the size of the DSA primes. Therefore y(x) is basically a one way function.

DSA Security Strength

There are two factors which determine the security strength (or bit strength) of the DSA signature process. The first is the size of the primes p and q, and the second is the choice of hash function used to hash the seed.

The DSA length parameters (L,N) define the size of the two primes, where L is the bit length of p and N is the bit length of q. These are restricted to certain values by the DSS.

The DSS also specifies which hash algorithms can be used in the DSA (the SHA algorithms). Not only is the hash function used to generate the signature for a particular message M, but it is also used to generate the primes p and q.

NIST Special Publication 800-57 (SP 800-57 Part 1) provides some information on the security strengths for the (L,N) pair and hash algorithms. See tables 2 and 3 on pages 63 and 64:

http://csrc.nist.gov/publications/nistpubs/800-57/sp800-57-Part1-revised2_Mar08-2007.pdf

The DSS recommends that the security strength of the hash function either match or exceed that of the selected (L,N) pair. In my program I have chosen the hash functions such that they equal the security strength of each (L,N) pair.

The table shows which combinations of (L,N), SHA functions and seed lengths are supported by my program:

L N Hash() Bits of Security Seed Length
1024 160 SHA-1 80 160 bits
2048 224 SHA-224 112 224 bits
2048 256 SHA-256 128 256 bits
3072 256 SHA-256 128 256 bits

Seeds and Counters

The generation process requires an initial seed value known as the first seed. The hash function is applied to this seed at various points during the prime generation process, to construct starter values for each of the primes. Every time the hash function is used in this way, the seed value is incremented by 1. The value of the first seed can either be set by the user or be randomly generated.

The DSS requires that the bit length of the seed has to be at least equal to N, the bit length of the prime q. In my code I set the length of the seed equal to N bits, as can be seen in the table above.

The generation process also uses a counter, which is initialised to zero. This counter is incremented every time a new candidate prime is generated, and when a candidate is confirmed to be prime the value of the counter is saved.

Using the seed and counter method to generate p and q allows an end user to verify that these values are legitimate. They just take the supplied first seed value and apply the same calculations to it.

Generating q

The method defined in Appendix C.6 (Shawe-Taylor Random_Prime Routine) is used to construct the primes q and p0. There are two parts to this method. Steps 3-13 are used to generate a starter prime of less than 33 bits in length, by repeatedly hashing the seed until a value is obtained that is confirmed to be prime. The sieve procedure outlined in Appendix C.8 is used to test the candidates for this small prime. This method is basically the Sieve of Eratosthenes, an algorithm for testing primes that dates back to Ancient Greece.

Steps 16-34 make up the second part of the method.

Note that the method is recursive. If the input length is greater than 32 bits, the method jumps to step 14 where it calls itself again (but with approx half the length). Once the length is less than 33 bits, steps 3-13 of the algorithm are executed to generate the initial starter prime. Then for each time the method was called at step 14, steps 16-34 are executed.

So for example, if N = 160 the flow of execution looks like this:

ST_Random_Prime(160) - Length greater than 32, jump to step 14.

ST_Random_Prime(81) - Length greater than 32, jump to step 14.

ST_Random_Prime(42) - Length greater than 32, jump to step 14.

ST_Random_Prime(22) - Execute steps 3-13 to get c0 and return.

ST_Random_Prime(42) - Execute steps 16-34 and return the next c0.

ST_Random_Prime(81) - Execute steps 16-34 and return the next c0.

ST_Random_Prime(160) - Execute steps 16-34 and return q.

Steps 16-34 use the input prime c0 to construct a larger prime c, as follows:

The seed is hashed again to produce a string of random bits (x) which is then adjusted so that it has a bit length equal to the input length.

A parameter t is then calculated from x using c0:

t = ceiling(x/2c0)

An initial candidate value for the prime c is then defined as:

c = 2tc0 + 1

This candidate value is then tested for primality. If it is not prime, then the value of t is incremented (t = t + 1), and c is recalculated:

c’ = 2(t + 1)c0 + 1 = c + 2c0

This procedure is repeated until a value of c is found that is prime.

Generating p

The process specified in Appendix A1.2 (Construction and Validation of the Provable Primes p and q) is used to construct the largest prime p. The primes q and p0 are used to construct candidates for p as follows:

p = 2tqp0 + 1

Using q in this way guarantees that q will be a prime factor of p-1, as required.

As for the case of q above, successive candidate values are generated until a prime is obtained:

p’ = 2(t + 1)qp0 + 1 = p + 2qp0

Testing Primality

To test the primality of the candidate primes in the construction process for p and q, the DSA uses the Pocklington-Lehmer Primality Test. For some random integer a, this test guarantees that a number N will be prime if it satisfies the following conditions:

aN-1 mod N = 1

GCD(a(N-1)/p – 1,N) = 1

– where p is a prime factor of N-1.

The DSA generates a random integer a (with a bit length of at least N bits) to test candidates for the prime q, by using the same method that was used to generate x (above). This value is then adjusted so that it is in the range [2,c-2]:

a = 2 + [ a mod (c-3) ]

To test candidates (c = 2tc0 + 1) for the prime q (and p0), the DSA computes a parameter:

z = a2t mod c

It then tests the condition:

GCD(z-1,c) = 1

Since 2t = (c-1)/c0, this is the same as the GCD part of the Pocklington-Lehmer Primality Test.

The DSA then tests the condition:

zc0 mod c = 1

But since:

zc0 = (a2t)c0 = (a(c-1)/c0)c0 = ac-1

– this test is the same as:

ac-1 mod c = 1

For candidates of the prime p, the DSA applies the following tests (for z = a2tq mod p):

GCD(z-1,p) = 1

zp0 mod p = 1

Again it can be shown that this is just the Pocklington-Lehmer Primality Test.

Fast Exclusion Of Non-Primes

To test the candidates for the primes p and q, the DSA uses the Pocklington-Lehmer Primality Test. However, this test requires the use of exponentiation which takes a lot of processing time. Therefore I have introduced an additional step in the testing process, which is not specified in the DSA. The aim of this extra step is to quickly prove that the candidate is not a prime. By doing this the need to carry out the exponentiation can be avoided, thus speeding up the search process for the primes p and q.

To quickly prove that a candidate is non-prime, I just generate some random numbers and use the GCD function to determine if they are coprime (i.e. GCD = 1) with the candidate. If even one of these random numbers is not coprime with the candidate, then the candidate value is not a prime and can be rejected. Quite a few random numbers are needed for this test, because it has been found that any random pair of numbers are likely to be coprime about 60% of the time. Further more, it turns out that some of the candidates generated by the DSA are testing coprime with over 95% of the random values. To make sure that these candidates can be eliminated, I generate 50 random values for testing. This test will not exclude all non-primes because some of the candidates test coprime against 100% of the random values (even if there are a 1000 randoms being generated).

By applying the above test, I can quickly reject about 70-80% of the candidate primes and save a large amount of processing time. For example, the exponentiation computation to test the candidate for p0 (where L = 1024 and L0 = 513) takes about 2 seconds. It would normally take 20 seconds to test 10 candidates in this way, but by applying the GCD filter this time can be reduced to about 4-6 seconds. The time savings are even greater for the larger primes. The exponentiation to test the candidate for p (L = 1024) takes about 6 seconds. Considering that hundreds or even thousands of candidates (up to the limit of 4 x L specified by the DSA) may need to be tested before a value for the prime p is found, the reduction in processing time can be quite significant.

How To Hash The Seed

In the DSA, the SHA hash functions are applied to the seed to generate pseudo-random starter values for the primes p and q. The seed and the starter values are integers, but the inputs and outputs for the SHA functions are bit strings. Therefore conversion between bit strings and integer values is required (as discussed in Appendix C.2).

The byte order in the seed integer value is little endian. To convert this to a message (bit string) that can be hashed, the bytes are reversed such that the byte order becomes big endian. The SHA 1, 224 and 256 algorithms process a message in blocks of 512 bits. Since the maximum length of the seed is 256 bits, it only requires a single 512 bit block as the input to the hash function. This block is in big endian byte order, with the first 160, 224 or 256 bits being the seed. The remaining bits in the block are the padding bits. These are all zero, with the exception of the first bit following the message (the seed) which is set to 1, and the last 16 bits which are set to the length of the message (the seed length).

The SHA functions output the result as an array of 32 bit words:

H[0]H[1]H[2]H[3]H[4] – e.g. for SHA-1.

This output is converted to a number, just by reversing the array indexing (e.g. H[4] becomes H'[0] etc):

H'[4]H'[3]H'[2]H'[1]H'[0]

It is this form of the hash output that is used to generate the pseudo-random starter values for the candidate primes.

Program Speed

The program uses a lot of processing power, so it can take a while to generate the primes. For L = 1024, the program should be able to generate some primes in less than 30 minutes (on a decent machine). For L = 2048 and L = 3072, it can take up to a couple of hours (or longer) to generate the primes.

Test Vectors

A zip file containing test vectors can be found on the NIST website. It is called: 186-3dsatestvectors.zip

Usage

The program allows the primes to be generated either all at once, or one at a time.

To generate all of the primes at once, press the [Generate All] button. Note that it can take up to one or two hours for a result to be returned.

To generate the primes one at a time (in sequence), first press the [Generate q] button to compute q. Once q has been found, press [Generate p0] to compute the prime p0. Finally, press [Generate p] to find the prime p. The program only takes a couple of minutes to find q, up to 30 minutes to find p0, and one or two hours to find p.

Of course, before generating any primes the L,N pair should be selected by pressing the appropiate button.

After the L,N pair has been set, the user may optionally enter a value for the first seed and press the [Capture] button to get this value. If a first seed value is not supplied in this way, a random value for the first seed will be internally generated by the program.

The FASM x86 Code

To grab this code, select it with the mouse and copy it. It can then be pasted directly into the FASM IDE.

; -------------------------------------------------------------------------------------

 format PE GUI 4.0

 entry start

 include 'win32a.inc'

; -------------------------------------------------------------------------------------

 IDD_THE_DIALOG = 102

 IDC_CAPTURE = 1000
 IDC_GEN_Q = 1001
 IDC_GEN_P0 = 1002
 IDC_GEN_P = 1003
 IDC_GEN_ALL = 1004
 IDC_START_OVER = 1005
 IDC_TOGGLE = 1006

 IDC_OUTPUT = 1007
 IDC_FIRST_SEED = 1008

 IDC_BTN_1024_160 = 1009
 IDC_BTN_2048_224 = 1010
 IDC_BTN_2048_256 = 1011
 IDC_BTN_3072_256 = 1012

; -------------------------------------------------------------------------------------

 HEX_LEN = 400

 DBL_HEX_LEN = 2*HEX_LEN

 BF_DISP_LEN = 6000

; -------------------------------------------------------------------------------------

 section '.code' code readable executable

  start:

    invoke GetModuleHandle,0
    invoke DialogBoxParam,eax,IDD_THE_DIALOG,0,DialogProc,0

  exit:

    invoke  ExitProcess,0 

; -------------------------------------------------------------------------------------

proc DialogProc uses esi edi ebx,hwnddlg,msg,wparam,lparam

	cmp [msg],WM_INITDIALOG
	je .wminitdialog

	cmp [msg],WM_COMMAND
	je .wmcommand

	cmp [msg],WM_CLOSE
	je .wmclose

	xor eax,eax
	jmp .quit

  .wminitdialog:

	invoke SetDlgItemText,[hwnddlg],IDC_FIRST_SEED,""

	stdcall CalcSieve

	stdcall PrintDomainParameters

	invoke SetDlgItemText,[hwnddlg],IDC_OUTPUT,bfDisplay

	jmp .done

  .wmcommand:

	cmp [wparam], BN_CLICKED shl 16 + IDC_BTN_1024_160
	je .1024_160

	cmp [wparam], BN_CLICKED shl 16 + IDC_BTN_2048_224
	je .2048_224

	cmp [wparam], BN_CLICKED shl 16 + IDC_BTN_2048_256
	je .2048_256

	cmp [wparam], BN_CLICKED shl 16 + IDC_BTN_3072_256
	je .3072_256

	cmp [wparam], BN_CLICKED shl 16 + IDC_CAPTURE
	je .CAPTURE

	cmp [wparam], BN_CLICKED shl 16 + IDC_GEN_Q
	je .GENERATE_Q

	cmp [wparam], BN_CLICKED shl 16 + IDC_GEN_P0
	je .GENERATE_P0

	cmp [wparam], BN_CLICKED shl 16 + IDC_GEN_P
	je .GENERATE_P

	cmp [wparam], BN_CLICKED shl 16 + IDC_GEN_ALL
	je .GENERATE_ALL

	cmp [wparam], BN_CLICKED shl 16 + IDC_START_OVER
	je .START_OVER

	cmp [wparam], BN_CLICKED shl 16 + IDC_TOGGLE
	je .TOGGLE_VIEW

	jmp .done

  .1024_160:

	mov [DSA_N],word 160

	mov [DSA_L],word 1024

	stdcall PrintDomainParameters

	mov [bToggle],byte 0

	invoke SetDlgItemText,[hwnddlg],IDC_OUTPUT,bfDisplay

	jmp .done

  .2048_224:

	mov [DSA_N],word 224

	mov [DSA_L],word 2048

	stdcall PrintDomainParameters

	mov [bToggle],byte 0

	invoke SetDlgItemText,[hwnddlg],IDC_OUTPUT,bfDisplay

	jmp .done

  .2048_256:

	mov [DSA_N],word 256

	mov [DSA_L],word 2048

	stdcall PrintDomainParameters

	mov [bToggle],byte 0

	invoke SetDlgItemText,[hwnddlg],IDC_OUTPUT,bfDisplay

	jmp .done


  .3072_256:

	mov [DSA_N],word 256

	mov [DSA_L],word 3072

	stdcall PrintDomainParameters

	mov [bToggle],byte 0

	invoke SetDlgItemText,[hwnddlg],IDC_OUTPUT,bfDisplay

	jmp .done

  .CAPTURE:

	invoke GetDlgItemText,[hwnddlg],IDC_FIRST_SEED,bfDisplay,BF_DISP_LEN

	stdcall ReadFirstSeed

	stdcall PrintFirstSeed

	invoke SetDlgItemText,[hwnddlg],IDC_FIRST_SEED,bfDisplay

	jmp .done

  .GENERATE_Q:

	stdcall Generate_q

	cmp al,0

	je .ERROR

	stdcall Print_q_Found

	mov [bToggle],byte 1

	invoke SetDlgItemText,[hwnddlg],IDC_OUTPUT,bfDisplay

	jmp .done

  .GENERATE_P0:

	stdcall Generate_p0

	cmp al,0

	je .ERROR

	stdcall Print_p0_Found

	mov [bToggle],byte 1

	invoke SetDlgItemText,[hwnddlg],IDC_OUTPUT,bfDisplay

	jmp .done

  .GENERATE_P:

	stdcall Generate_p

	cmp al,0

	je .ERROR

	stdcall Print_p_Found

	mov [bToggle],byte 1

	invoke SetDlgItemText,[hwnddlg],IDC_OUTPUT,bfDisplay

	jmp .done

  .GENERATE_ALL:

	stdcall Generate_q

	cmp al,0

	je .ERROR

	stdcall Generate_p0

	cmp al,0

	je .ERROR

	stdcall Generate_p

	cmp al,0

	je .ERROR

	stdcall Print_pq_Found

	mov [bToggle],byte 1

	invoke SetDlgItemText,[hwnddlg],IDC_OUTPUT,bfDisplay

	jmp .done

  .START_OVER:

	stdcall StartOver

	invoke SetDlgItemText,[hwnddlg],IDC_FIRST_SEED,""

	stdcall PrintDomainParameters

	mov [bToggle],byte 0

	invoke SetDlgItemText,[hwnddlg],IDC_OUTPUT,bfDisplay

	jmp .done

  .TOGGLE_VIEW:

	cmp [bToggle],byte 0

	je .TOGGLE_1

	stdcall PrintDomainParameters

	mov [bToggle],byte 0

	invoke SetDlgItemText,[hwnddlg],IDC_OUTPUT,bfDisplay

	jmp .done

  .TOGGLE_1:

	stdcall PrintLastCandidate

	mov [bToggle],byte 1

	invoke SetDlgItemText,[hwnddlg],IDC_OUTPUT,bfDisplay

	jmp .done

  .ERROR:

	stdcall PrintError

	invoke SetDlgItemText,[hwnddlg],IDC_OUTPUT,bfDisplay

	jmp .done

  .wmclose:

	invoke EndDialog,[hwnddlg],0

  .done:

	mov eax,1

  .quit:

	ret

endp

; -------------------------------------------------------------------------------------

macro PRINT_CRLF
{
  mov [edi],byte 13
  inc edi

  mov [edi],byte 10
  inc edi
}

; -------------------------------------------------------------------------------------

proc PrintDomainParameters

  ; print the domain parameters

	lea edi,[bfDisplay]

	; N (length of p)

	mov [edi],dword "N = "
	add edi,4

	mov ax,[DSA_N]

	stdcall HexToDec

	mov [edi],dword "    "
	add edi,4

	; L (length of q)

	mov [edi],dword "L = "
	add edi,4

	mov ax,[DSA_L]

	stdcall HexToDec

	PRINT_CRLF
	PRINT_CRLF

	; small prime c

	mov [edi],dword "Smal"
	add edi,4
	mov [edi],dword "l Pr"
	add edi,4
	mov [edi],dword "ime_"
	add edi,4
	mov [edi],dword "c:  "
	add edi,4

	mov eax,[Prime_c]

	stdcall DWordToStr

	PRINT_CRLF
	PRINT_CRLF

	; small prime c0

	mov [edi],dword "Smal"
	add edi,4
	mov [edi],dword "l Pr"
	add edi,4
	mov [edi],dword "ime_"
	add edi,4
	mov [edi],dword "c0: "
	add edi,4

	mov eax,[Prime_c0]

	stdcall DWordToStr

	PRINT_CRLF
	PRINT_CRLF

	; q gen counter

	mov [edi],dword "q_ge"
	add edi,4
	mov [edi],dword "n_co"
	add edi,4
	mov [edi],dword "unt:"
	add edi,4
	mov [edi],dword "    "
	add edi,4

	mov ax,[Qgen_counter]

	stdcall HexToDec

	PRINT_CRLF
	PRINT_CRLF

	; p0 gen counter

	mov [edi],dword "p0_g"
	add edi,4
	mov [edi],dword "en_c"
	add edi,4
	mov [edi],dword "ount"
	add edi,4
	mov [edi],dword ":   "
	add edi,4

	mov ax,[P0gen_counter]

	stdcall HexToDec

	PRINT_CRLF
	PRINT_CRLF

	; p gen counter

	mov [edi],dword "p_ge"
	add edi,4
	mov [edi],dword "n_co"
	add edi,4
	mov [edi],dword "unt:"
	add edi,4
	mov [edi],dword "    "
	add edi,4

	mov ax,[Pgen_counter]

	stdcall HexToDec

	PRINT_CRLF
	PRINT_CRLF

	; first seed

	mov [edi],dword "Firs"
	add edi,4
	mov [edi],dword "t Se"
	add edi,4
	mov [edi],dword "ed: "
	add edi,4

	PRINT_CRLF
	PRINT_CRLF

	lea esi,[First_Seed]

	mov edx,32

	stdcall PrintHexValue

	; q seed

	mov [edi],dword "q Se"
	add edi,4
	mov [edi],dword "ed: "
	add edi,4

	PRINT_CRLF
	PRINT_CRLF

	lea esi,[Q_Seed]

	mov edx,32

	stdcall PrintHexValue

	; p0 seed

	mov [edi],dword "p0 S"
	add edi,4
	mov [edi],dword "eed:"
	add edi,4

	PRINT_CRLF
	PRINT_CRLF

	lea esi,[P0_Seed]

	mov edx,32

	stdcall PrintHexValue

	; p seed

	mov [edi],dword "p Se"
	add edi,4
	mov [edi],dword "ed: "
	add edi,4

	PRINT_CRLF
	PRINT_CRLF

	lea esi,[P_Seed]

	mov edx,32

	stdcall PrintHexValue

	; prime q

	mov [edi],dword "Prim"
	add edi,4
	mov [edi],dword "e q:"
	add edi,4

	PRINT_CRLF
	PRINT_CRLF

	lea esi,[Prime_Q]

	mov edx,32

	stdcall PrintHexValue

	; prime p0

	mov [edi],dword "Prim"
	add edi,4
	mov [edi],dword "e p0"
	add edi,4
	mov [edi],byte ':'
	inc edi

	PRINT_CRLF
	PRINT_CRLF

	lea esi,[Prime_P0]

	mov edx,200

	stdcall PrintHexValue

	; prime p

	mov [edi],dword "Prim"
	add edi,4
	mov [edi],dword "e p:"
	add edi,4

	PRINT_CRLF
	PRINT_CRLF

	lea esi,[Prime_P]

	mov edx,400

	stdcall PrintHexValue

	mov [edi],byte 0

	ret

endp

; -------------------------------------------------------------------------------------

proc PrintLastCandidate

  ; print current seed, last value of candidate prime, current t and last x

	lea edi,[bfDisplay]

	; current seed

	mov [edi],dword "Curr"
	add edi,4
	mov [edi],dword "ent "
	add edi,4
	mov [edi],dword "Seed"
	add edi,4
	mov [edi],byte ':'
	inc edi

	PRINT_CRLF
	PRINT_CRLF

	lea esi,[Prime_Seed]

	mov edx,32

	stdcall PrintHexValue

	; candidate prime

	mov [edi],dword "Cand"
	add edi,4
	mov [edi],dword "idat"
	add edi,4
	mov [edi],dword "e Pr"
	add edi,4
	mov [edi],dword "ime:"
	add edi,4

	PRINT_CRLF
	PRINT_CRLF

	lea esi,[Candidate]

	mov edx,400

	stdcall PrintHexValue

	; first candidate prime

	mov [edi],dword "Firs"
	add edi,4
	mov [edi],dword "t Ca"
	add edi,4
	mov [edi],dword "ndid"
	add edi,4
	mov [edi],dword "ate:"
	add edi,4

	PRINT_CRLF
	PRINT_CRLF

	lea esi,[First_Candidate]

	mov edx,400

	stdcall PrintHexValue

	; last c0

	mov [edi],dword "Last"
	add edi,4
	mov [edi],dword " c0:"
	add edi,4

	PRINT_CRLF
	PRINT_CRLF

	lea esi,[Last_c0]

	mov edx,400

	stdcall PrintHexValue

	; last x

	mov [edi],dword "Last"
	add edi,4
	mov [edi],dword " x: "
	add edi,4

	PRINT_CRLF
	PRINT_CRLF

	lea esi,[Last_x]

	mov edx,400

	stdcall PrintHexValue

	; current t

	mov [edi],dword "Curr"
	add edi,4
	mov [edi],dword "ent "
	add edi,4
	mov [edi],dword "t:  "
	add edi,4

	PRINT_CRLF
	PRINT_CRLF

	lea esi,[Current_t]

	mov edx,400

	stdcall PrintHexValue

	; current z

	mov [edi],dword "Curr"
	add edi,4
	mov [edi],dword "ent "
	add edi,4
	mov [edi],dword "z:  "
	add edi,4

	PRINT_CRLF
	PRINT_CRLF

	lea esi,[Current_z]

	mov edx,400

	stdcall PrintHexValue

	mov [edi],byte 0

	ret

endp

; -------------------------------------------------------------------------------------

proc Print_q_Found

  ; the prime q has been found

	lea edi,[bfDisplay]

	lea esi,[szQFoundMsg]

	stdcall AddStringToEDI

	PRINT_CRLF
	PRINT_CRLF

	lea esi,[Prime_Q]

	mov edx,32

	stdcall PrintHexValue

	mov [edi],byte 0

	ret

endp

; -------------------------------------------------------------------------------------

proc Print_p0_Found

  ; the prime p0 has been found

	lea edi,[bfDisplay]

	lea esi,[szP0FoundMsg]

	stdcall AddStringToEDI

	PRINT_CRLF
	PRINT_CRLF

	lea esi,[Prime_P0]

	mov edx,200

	stdcall PrintHexValue

	mov [edi],byte 0

	ret

endp

; -------------------------------------------------------------------------------------

proc Print_p_Found

  ; the prime p has been found

	lea edi,[bfDisplay]

	lea esi,[szPFoundMsg]

	stdcall AddStringToEDI

	PRINT_CRLF
	PRINT_CRLF

	lea esi,[Prime_P]

	mov edx,400

	stdcall PrintHexValue

	mov [edi],byte 0

	ret

endp

; -------------------------------------------------------------------------------------

proc Print_pq_Found

  ; the primes p and q have been found

	lea edi,[bfDisplay]

	lea esi,[szPQFoundMsg]

	stdcall AddStringToEDI

	PRINT_CRLF
	PRINT_CRLF

	lea esi,[Prime_P]

	mov edx,400

	stdcall PrintHexValue

	lea esi,[Prime_Q]

	mov edx,32

	stdcall PrintHexValue

	mov [edi],byte 0

	ret

endp

; -------------------------------------------------------------------------------------

proc PrintError uses esi edi

  ; print the error message to bfDisplay

	lea edi,[bfDisplay]

	; check the error code

	cmp [nErrorCode],word 0

	je .ERR_0

	cmp [nErrorCode],word 1

	je .ERR_1

	cmp [nErrorCode],word 2

	je .ERR_2

	cmp [nErrorCode],word 3

	je .ERR_3

	cmp [nErrorCode],word 4

	je .ERR_4

	cmp [nErrorCode],word 5

	je .ERR_5

	cmp [nErrorCode],word 6

	je .ERR_6

  .ERR_0:

	lea esi,[szErrMessage_0]

	jmp .PRINT

  .ERR_1:

	lea esi,[szErrMessage_1]

	jmp .PRINT

  .ERR_2:

	lea esi,[szErrMessage_2]

	jmp .PRINT

  .ERR_3:

	lea esi,[szErrMessage_3]

	jmp .PRINT

  .ERR_4:

	lea esi,[szErrMessage_4]

	jmp .PRINT

  .ERR_5:

	lea esi,[szErrMessage_5]

	jmp .PRINT

  .ERR_6:

	lea esi,[szErrMessage_6]

  .PRINT:

	mov dl,[esi]

	cmp dl,0

	je .DONE

	mov [edi],dl

	inc esi
	inc edi

	jmp .PRINT

  .DONE:

	mov [edi],byte 0

	ret

endp

; -------------------------------------------------------------------------------------

  ; generate the prime q

; -------------------------------------------------------------------------------------

proc Generate_q

  ; generate the prime q

	mov [nErrorCode],word 0

	; get the first seed

	stdcall GetFirstSeed

	; initialise the counter

	mov [Counter],word 0

	cmp [DSA_N],word 160

	je .160

	cmp [DSA_N],word 224

	je .224

	cmp [DSA_N],word 256

	je .256

	jmp .ERR_1

  .160:

	; first generate the small prime c0

	; pass length in AX

	mov ax,22

	stdcall ST_Random_Small_Prime

	cmp al,0

	je .ERR_1

	; first random prime q

	; pass length in AX

	mov ax,42

	stdcall ST_Random_Prime_q

	cmp al,0

	je .ERR_2

	stdcall Update_Last_c0

	; next random prime q

	; pass length in AX

	mov ax,81

	stdcall ST_Random_Prime_q

	cmp al,0

	je .ERR_2

	stdcall Update_Last_c0

	; last random prime q

	; pass length in AX

	mov ax,160

	stdcall ST_Random_Prime_q

	cmp al,0

	je .ERR_2

	jmp .SUCCESS

  .224:

	; first generate the small prime c0

	mov ax,30

	stdcall ST_Random_Small_Prime

	cmp al,0

	je .ERR_1

	; first random prime q

	; pass length in AX

	mov ax,58

	stdcall ST_Random_Prime_q

	cmp al,0

	je .ERR_2

	stdcall Update_Last_c0

	; next random prime q

	; pass length in AX

	mov ax,113

	stdcall ST_Random_Prime_q

	cmp al,0

	je .ERR_2

	stdcall Update_Last_c0

	; last random prime q

	; pass length in AX

	mov ax,224

	stdcall ST_Random_Prime_q

	cmp al,0

	je .ERR_2

	jmp .SUCCESS

  .256:

	; first generate the small prime c0

	mov ax,18

	stdcall ST_Random_Small_Prime

	cmp al,0

	je .ERR_1

	; first random prime q

	; pass length in AX

	mov ax,34

	stdcall ST_Random_Prime_q

	cmp al,0

	je .ERR_2

	stdcall Update_Last_c0

	; next random prime q

	; pass length in AX

	mov ax,66

	stdcall ST_Random_Prime_q

	cmp al,0

	je .ERR_2

	stdcall Update_Last_c0

	; next random prime q

	; pass length in AX

	mov ax,129

	stdcall ST_Random_Prime_q

	cmp al,0

	je .ERR_2

	stdcall Update_Last_c0

	; last random prime q

	; pass length in AX

	mov ax,256

	stdcall ST_Random_Prime_q

	cmp al,0

	je .ERR_2

	jmp .SUCCESS

  .ERR_1:

	; failed to generate small prime

	xor eax,eax

	mov [nErrorCode],word 1

	jmp .DONE

  .ERR_2:

	; failed to generate the prime q

	xor eax,eax

	mov [nErrorCode],word 2

	jmp .DONE

  .SUCCESS:

	stdcall onPrimeFound_q

	; AL = 1 for success

	mov al,1

  .DONE:

	ret

endp

; -------------------------------------------------------------------------------------

proc ST_Random_Prime_q uses esi edi

  ; generate the random prime q

  ; required length passed in AX

	locals

	  length dw 0

	endl

	mov [length],ax

	; clear the candidate prime

	stdcall ClearCandidate

	mov [Qgen_counter],word 0

	; calculate x

	; put nIterations into DL

	; For the prime q, nIterations is always zero

	; N = 160 / SHA-1: iterations = 0

	; N = 224 / SHA-224: iterations = 0

	; N = 256 / SHA-256: iterations = 0

	xor edx,edx

	mov dl,0

	stdcall Calc_Random_x

	xor edx,edx

	mov dx,[length]

	stdcall Fix_Length_x

	stdcall Init_t

	mov dx,[length]

	stdcall FirstCandidate

  .NEXT:

	inc word [Counter]

	inc word [Qgen_counter]

	xor edx,edx

	mov dl,0

	stdcall Calc_a

	stdcall Test_GCD

	cmp al,0

	je .NEXT_T

	stdcall Test_Prime

	cmp al,0

	je .NEXT_T

	stdcall Calc_z

	; test q for primality

	stdcall ST_Test_Primality

	cmp al,1

	je .SUCCESS

  .NEXT_T:

	; test: Qgen_counter greater than or equal to (4 x length)

	mov dx,[length]

	shl dx,2

	cmp [Qgen_counter],dx

	jge .FAILURE

	; increment t

	lea edi,[Current_t]

	stdcall Increment

	mov dx,[length]

	; increment the candidate

	stdcall NextCandidate

	jmp .NEXT

  .SUCCESS:

	; AL = 1 for success

	mov al,1

	jmp .DONE

  .FAILURE:

	xor eax,eax

  .DONE:

	ret

endp

; -------------------------------------------------------------------------------------

proc onPrimeFound_q uses esi edi ecx

  ; set q, qseed and qgen_counter

	lea esi,[Candidate]

	lea edi,[Prime_Q]

	xor ecx,ecx

  .COPY:

	; set the prime q

	mov dl,[esi+ecx]

	mov [edi+ecx],dl

	inc cx

	cmp cx,32

	jl .COPY

	; set qseed

	lea esi,[Prime_Seed]

	lea edi,[Q_Seed]

	xor ecx,ecx

  .SEED:

	mov dl,[esi+ecx]

	mov [edi+ecx],dl

	inc cx

	cmp cx,32

	jl .SEED

	; qgen_counter

	mov dx,[Counter]

	mov [Qgen_counter],dx

	; Prime_c

	mov edx,[Prime_c0]

	mov [Prime_c],edx

	mov [Prime_c0],dword 0

	ret

endp

; -------------------------------------------------------------------------------------

  ; generate the prime p0

; -------------------------------------------------------------------------------------

proc Generate_p0

  ; generate the prime p0

	mov [nErrorCode],word 0

	; initialise the counter

	mov [Counter],word 0

	; has the prime q been generated

	stdcall IsPrimeQZero

	cmp al,1

	je .ERR_1

	cmp [DSA_L],word 1024

	je .1024

	cmp [DSA_L],word 2048

	je .1024

	cmp [DSA_L],word 3072

	je .3072

	jmp .ERR_2

  .1024:

	; first generate the small prime c0

	; pass length in AX

	mov ax,18

	stdcall ST_Random_Small_Prime

	cmp al,0

	je .ERR_2

	; first random prime p0

	; pass length in AX

	mov ax,34

	stdcall ST_Random_Prime_p0

	cmp al,0

	je .ERR_3

	stdcall Update_Last_c0

	; next random prime p0

	; pass length in AX

	mov ax,66

	stdcall ST_Random_Prime_p0

	cmp al,0

	je .ERR_3

	stdcall Update_Last_c0

	; next random prime p0

	; pass length in AX

	mov ax,130

	stdcall ST_Random_Prime_p0

	cmp al,0

	je .ERR_3

	stdcall Update_Last_c0

	; next random prime p0

	; pass length in AX

	mov ax,258

	stdcall ST_Random_Prime_p0

	cmp al,0

	je .ERR_3

	stdcall Update_Last_c0

	cmp [DSA_L],word 2048

	je .2048

	; last random prime p0

	; pass length in AX

	mov ax,513

	stdcall ST_Random_Prime_p0

	cmp al,0

	je .ERR_3

	jmp .SUCCESS

  .2048:

	; next random prime p0

	; pass length in AX

	mov ax,514

	stdcall ST_Random_Prime_p0

	cmp al,0

	je .ERR_3

	stdcall Update_Last_c0

	; last random prime p0

	; pass length in AX

	mov ax,1025

	stdcall ST_Random_Prime_p0

	cmp al,0

	je .ERR_3

	jmp .SUCCESS

  .3072:

	; first generate the small prime c0

	; pass length in AX

	mov ax,26

	stdcall ST_Random_Small_Prime

	cmp al,0

	je .ERR_2

	; first random prime p0

	; pass length in AX

	mov ax,50

	stdcall ST_Random_Prime_p0

	cmp al,0

	je .ERR_3

	stdcall Update_Last_c0

	; next random prime p0

	; pass length in AX

	mov ax,98

	stdcall ST_Random_Prime_p0

	cmp al,0

	je .ERR_3

	stdcall Update_Last_c0

	; next random prime p0

	; pass length in AX

	mov ax,194

	stdcall ST_Random_Prime_p0

	cmp al,0

	je .ERR_3

	stdcall Update_Last_c0

	; next random prime p0

	; pass length in AX

	mov ax,386

	stdcall ST_Random_Prime_p0

	cmp al,0

	je .ERR_3

	stdcall Update_Last_c0

	; next random prime p0

	; pass length in AX

	mov ax,770

	stdcall ST_Random_Prime_p0

	cmp al,0

	je .ERR_3

	stdcall Update_Last_c0

	; last random prime p0

	; pass length in AX

	mov ax,1537

	stdcall ST_Random_Prime_p0

	cmp al,0

	je .ERR_3

	jmp .SUCCESS

  .ERR_1:

	; prime q has not been generated

	xor eax,eax

	mov [nErrorCode],word 3

	jmp .DONE

  .ERR_2:

	; failed to generate small prime

	xor eax,eax

	mov [nErrorCode],word 1

	jmp .DONE

  .ERR_3:

	; failed to generate the prime p0

	xor eax,eax

	mov [nErrorCode],word 4

	jmp .DONE

  .SUCCESS:

	stdcall onPrimeFound_p0

	; AL = 1 for success

	mov al,1

  .DONE:

	ret

endp

; -------------------------------------------------------------------------------------

proc ST_Random_Prime_p0 uses esi edi

  ; generate the random prime p0

  ; required length passed in AX

	locals

	  length dw 0

	  nIterations db 0

	endl

	; store the length

	mov [length],ax

	; get nIterations

	stdcall Get_nIterations

	; nIterations returned in AL

	mov [nIterations],al

	; clear the candidate prime

	stdcall ClearCandidate

	mov [P0gen_counter],word 0

	; calculate x

	; put nIterations into DL

	xor edx,edx

	mov dl,[nIterations]

	stdcall Calc_Random_x

	xor edx,edx

	mov dx,[length]

	stdcall Fix_Length_x

	stdcall Init_t

	mov dx,[length]

	stdcall FirstCandidate

  .NEXT:

	inc word [Counter]

	inc word [P0gen_counter]

	mov dl,[nIterations]

	stdcall Calc_a

	stdcall Test_GCD

	cmp al,0

	je .NEXT_T

	stdcall Test_Prime

	cmp al,0

	je .NEXT_T

	stdcall Calc_z

	; test p0 for primality

	stdcall ST_Test_Primality

	cmp al,1

	je .SUCCESS

  .NEXT_T:

	; test: Qgen_counter greater than or equal to (4 x length)

	mov dx,[length]

	shl dx,2

	cmp [P0gen_counter],dx

	jge .FAILURE

	; increment t

	lea edi,[Current_t]

	stdcall Increment

	mov dx,[length]

	; increment the candidate

	stdcall NextCandidate

	jmp .NEXT

  .SUCCESS:

	; AL = 1 for success

	mov al,1

	jmp .DONE

  .FAILURE:

	xor eax,eax

  .DONE:

	ret

endp

; -------------------------------------------------------------------------------------

proc Get_nIterations uses ecx

  ; nIterations for the prime p0:

  ; N = 160 / SHA-1

  ; N = 224 / SHA-224

  ; N = 256 / SHA-256

  ; length passed in AX

	locals

	  outlen dw 0

	endl

	xor ecx,ecx

	cmp [DSA_N],word 160

	je .160

	cmp [DSA_N],word 224

	je .224

	cmp [DSA_N],word 256

	je .256

  .160:

	inc cx

	add [outlen],word 160

	cmp ax,[outlen]

	jle .DONE

	jmp .160

  .224:

	inc cx

	add [outlen],word 224

	cmp ax,[outlen]

	jle .DONE

	jmp .224

  .256:

	inc cx

	add [outlen],word 256

	cmp ax,[outlen]

	jle .DONE

	jmp .256

  .DONE:

	; return nIterations in AL

	xor eax,eax

	dec cl

	mov al,cl

	ret

endp

; -------------------------------------------------------------------------------------

proc onPrimeFound_p0 uses esi edi ecx

  ; set p0, p0_seed and p0gen_counter

	lea esi,[Candidate]

	lea edi,[Prime_P0]

	xor ecx,ecx

  .COPY:

	; set the prime p0

	mov dl,[esi+ecx]

	mov [edi+ecx],dl

	inc cx

	cmp cx,200

	jl .COPY

	; set p0_seed

	lea esi,[Prime_Seed]

	lea edi,[P0_Seed]

	xor ecx,ecx

  .SEED:

	mov dl,[esi+ecx]

	mov [edi+ecx],dl

	inc cx

	cmp cx,32

	jl .SEED

	; p0gen_counter

	mov dx,[Counter]

	mov [P0gen_counter],dx

	ret

endp

; -------------------------------------------------------------------------------------

  ; computations for the primes q and p0

; -------------------------------------------------------------------------------------

proc Init_t uses esi edi

  ; calc: t = ceiling(x/2c0)

	; divide Last_x by 2c0

	lea esi,[Last_x]

	; copy Last_x to the product buffer

	stdcall CopyToProductBf

	; set ShiftBf = 2c0

	lea esi,[Last_c0]

	stdcall CopyToShiftBf

	; 2 x c0

	lea edi,[ShiftBf]

	stdcall ShiftLeft

	; do the division: ProductBf by ShiftBf

	stdcall Divide

	; copy the quotient to the destination

	lea edi,[Current_t]

	stdcall QuotientToDest

	; add 1 if there was a remainder

	stdcall IsProductBfZero

	cmp al,1

	je .DONE

	stdcall Increment

  .DONE:

	ret

endp

; -------------------------------------------------------------------------------------

proc Calc_From_t uses esi edi

  ; calc: c = 2tc0 + 1

	; set ShiftBf = 2c0

	lea esi,[Last_c0]

	stdcall CopyToShiftBf

	; 2 x c0

	lea edi,[ShiftBf]

	stdcall ShiftLeft

	lea esi,[Current_t]

	; multiply ShiftBf by ESI

	stdcall Multiply

	; copy the result from ProductBf back to EDI

	lea edi,[Candidate]

	stdcall CopyFromProductBf

	; add 1 to EDI

	stdcall Increment

	ret

endp

; -------------------------------------------------------------------------------------

proc FirstCandidate uses esi edi

  ; length passed in DX

	locals

	  length dw 0

	endl

	mov [length],dx

	; calc: c = 2tc0 + 1

	stdcall Calc_From_t

	; is (2tc0 + 1) greater than 2^length ?

	; test if the bit at 2^length is set

	mov dx,[length]

	stdcall TestCandidateLength

	cmp al,1

	jne .DONE

	; recalculate t

	mov dx,[length]

	stdcall Recalc_t

	; recalculate the candidate

	stdcall Calc_From_t

  .DONE:

	stdcall SaveFirstCandidate

	ret

endp

; -------------------------------------------------------------------------------------

proc NextCandidate uses esi edi

  ; length passed in DX

	locals

	  length dw 0

	endl

	mov [length],dx

	; get the next Candidate

	stdcall Calc_From_t

  .TEST:

	; is the Candidate greater than 2^length ?

	; test if the bit at 2^length is set

	mov dx,[length]

	stdcall TestCandidateLength

	cmp al,1

	jne .DONE

	; recalculate t

	mov dx,[length]

	stdcall Recalc_t

	; recalculate the candidate

	stdcall Calc_From_t

  .DONE:

	ret

endp

; -------------------------------------------------------------------------------------

proc Recalc_t uses esi edi ecx

  ; reset t

  ; t = ceiling(2^(length-1)/2c0)

  ; required length passed in DX

	locals

	  length dw 0

	endl

	mov [length],dx

	lea edi,[Current_t]

	xor ecx,ecx

	; zero t

	stdcall ZeroHexBuffer

	; set the bit at 2^(length-1)

	; get the index of the byte containing the bit

	; index of bit is length-1

	dec dx

	xor ecx,ecx

	mov cx,dx

	shr cx,3

	; add to EDI

	add edi,ecx

	; get the index of the bit

	and dx,7

	mov cl,dl

	; set the bit

	mov al,1

	shl al,cl

	; or into EDI

	or [edi],al

	; copy the new t to ProductBf

	lea esi,[Current_t]

	stdcall CopyToProductBf

	; divide 2c0 into ProductBf

	; set ShiftBf = 2c0

	lea esi,[Last_c0]

	stdcall CopyToShiftBf

	; 2 x c0

	lea edi,[ShiftBf]

	stdcall ShiftLeft

	; do the division: ProductBf by ShiftBf

	stdcall Divide

	; copy the quotient to the destination

	lea edi,[Current_t]

	stdcall QuotientToDest

	; add 1 if there was a remainder

	stdcall IsProductBfZero

	cmp al,1

	je .DONE

	stdcall Increment

  .DONE:

	ret

endp

; -------------------------------------------------------------------------------------

proc Calc_a uses esi edi

  ; compute a (stored in Current_z)

	; use the hash function to generate a random starter value for a

	stdcall Calc_Random_a

	; calc: a = 2 + (a mod (c-3))

	lea esi,[Candidate]

	stdcall CopyToShiftBf

	; ShiftBf = (c-3)

	lea edi,[ShiftBf]

	stdcall Decrement
	stdcall Decrement
	stdcall Decrement

	lea esi,[Current_z]

	stdcall CopyToProductBf

	; a mod (c-3)

	stdcall Modulus

	lea edi,[Current_z]

	stdcall CopyFromProductBf

	; add 2 to a

	stdcall Increment
	stdcall Increment

	ret

endp

; -------------------------------------------------------------------------------------

proc Calc_z uses esi edi

  ; calc: z = (a ^ 2t) mod c

	; candidate to ModBf

	lea esi,[Candidate]

	stdcall CopyToModBf

	; t to ExpBf

	lea esi,[Current_t]

	stdcall CopyToExpBf

	; multiply Exp by 2

	lea edi,[ExpBf]

	stdcall HexShiftLeft

	; z to BaseBf

	lea esi,[Current_z]

	stdcall CopyToBaseBf

	; mod_exp

	stdcall Mod_Exp

	; copy result back to Current_z

	lea edi,[Current_z]

	stdcall CopyFromProductBf

	ret

endp

; -------------------------------------------------------------------------------------

proc ST_Test_Primality uses esi edi

  ; Test if the candidate q or p0 is prime

  ; result returned in AL

	; Test 1: GCD(z-1,Candidate) == 1

	stdcall Calc_GCD

	stdcall Check_GCD_Passed

	cmp al,1

	jne .DONE

	; Test 2: (z ^ c0) mod c = 1

	lea esi,[Current_z]

	stdcall CopyToBaseBf

	lea esi,[Last_c0]

	stdcall CopyToExpBf

	lea esi,[Candidate]

	stdcall CopyToModBf

	stdcall Mod_Exp

	stdcall Check_MEXP_Passed

  .DONE:

	; if prime, AL = 1

	ret

endp

; -------------------------------------------------------------------------------------

proc Calc_GCD uses esi edi

  ; calc GCD of Candidate with (z-1)

	lea esi,[Candidate]

	stdcall CopyToProductBf

	lea esi,[Current_z]

	stdcall CopyToShiftBf

	lea edi,[ShiftBf]

	stdcall Decrement

  .GCD:

	stdcall Modulus

	stdcall XchgProductBfShiftBf

	stdcall IsShiftBfZero

	cmp al,1

	je .DONE

	jmp .GCD

  .DONE:

	ret

endp

; -------------------------------------------------------------------------------------

proc Check_GCD_Passed uses esi ecx

  ; did the test prime pass the GCD test ?

	lea esi,[ProductBf]

	xor ecx,ecx

	mov cx,DBL_HEX_LEN-1

  .TEST:

	cmp [esi+ecx],byte 0

	jne .FAILED

	dec cx

	cmp cx,0

	jg .TEST

	; test [esi]

	cmp [esi],byte 1

	jne .FAILED

  .PASSED:

	mov al,1

	jmp .DONE

  .FAILED:

	xor eax,eax

  .DONE:

	ret

endp

; -------------------------------------------------------------------------------------

proc Check_MEXP_Passed uses esi ecx

  ; did the test prime pass the mod exp test ?

	lea esi,[ProductBf]

	xor ecx,ecx

	mov cx,DBL_HEX_LEN-1

  .TEST:

	cmp [esi+ecx],byte 0

	jne .FAILED

	dec cx

	cmp cx,0

	jg .TEST

	; test [esi]

	cmp [esi],byte 1

	jne .FAILED

  .PASSED:

	mov al,1

	jmp .DONE

  .FAILED:

	xor eax,eax

  .DONE:

	ret

endp

; -------------------------------------------------------------------------------------

proc Test_Prime uses esi edi

  ; does: a^(p-1) mod p = 1

	lea esi,[Current_z]

	stdcall CopyToBaseBf

	lea esi,[Candidate]

	stdcall CopyToExpBf

	lea edi,[ExpBf]

	stdcall Decrement

	lea esi,[Candidate]

	stdcall CopyToModBf

	stdcall Mod_Exp

	stdcall Check_MEXP_Passed

  .DONE:

	; if prime, AL = 1

	ret

endp

; -------------------------------------------------------------------------------------

proc TestCandidateLength uses edi ecx

  ; test the bit length of the candidate

  ; required length passed in EDX

	locals

	  length dw 0

	  count db 8

	endl

	mov [length],dx

	lea edi,[Candidate]

	xor ecx,ecx

	mov cx,399

  .FIND:

	; find the first non zero byte

	mov dl,[edi+ecx]

	cmp dl,0

	jne .COUNT

	dec cx

	cmp cx,0

	jge .FIND

	; the candidate is zero, if the loop exits here:

	jmp .GT

  .COUNT:

	shl dl,1

	jc .TEST

	dec byte [count]

	cmp [count],byte 0

	jg .COUNT

  .TEST:

	; length of candidate = (8 x ECX) + count

	shl ecx,3

	xor edx,edx

	mov dl,[count]

	add ecx,edx

	cmp cx,[length]

	jg .GT

	xor eax,eax

	jmp .DONE

  .GT:

	; the length of the candidate is greater than the required length

	mov al,1

  .DONE:

	ret

endp

; -------------------------------------------------------------------------------------

  ; generate candidates for the prime p

; -------------------------------------------------------------------------------------

proc Random_Prime_P uses esi edi

  ; generate the random prime p

	locals

	  nIterations db 0

	endl

	; clear the candidate prime

	stdcall ClearCandidate

	mov [Pgen_counter],word 0

	; For the prime p:

	; (1024,160): outlen = 160, iterations = 6

	; (2048,224): outlen = 224, iterations = 9

	; (2048,256): outlen = 256, iterations = 7

	; (3072,256): outlen = 256, iterations = 11

	; set nIterations:

	mov [nIterations],byte 6

	cmp [DSA_N],word 160

	je .CALC_X

	mov [nIterations],byte 9

	cmp [DSA_N],word 224

	je .CALC_X

	mov [nIterations],byte 7

	cmp [DSA_L],word 2048

	je .CALC_X

	mov [nIterations],byte 11

  .CALC_X:

	; calculate x

	; put nIterations into DL

	xor edx,edx

	mov dl,[nIterations]

	stdcall Calc_Random_x

	xor edx,edx

	mov dx,[DSA_L]

	stdcall Fix_Length_x

	stdcall Init_t_P

	mov dx,[DSA_L]

	stdcall FirstCandidate_P

  .NEXT:

	inc word [Counter]

	inc word [Pgen_counter]

	mov dl,[nIterations]

	stdcall Calc_a

	stdcall Test_GCD

	cmp al,0

	je .NEXT_T

	stdcall Test_Prime

	cmp al,0

	je .NEXT_T

	stdcall Calc_z_P

	; test p for primality

	stdcall Test_Primality_P

	cmp al,1

	je .SUCCESS

  .NEXT_T:

	; test: Qgen_counter greater than or equal to (4 x length)

	mov dx,[DSA_L]

	shl dx,2

	cmp [Pgen_counter],dx

	jge .FAILURE

	; increment t

	lea edi,[Current_t]

	stdcall Increment

	mov dx,[DSA_L]

	; increment the candidate

	stdcall NextCandidate_P

	jmp .NEXT

  .SUCCESS:

	stdcall onPrimeFound_p

	; AL = 1 for success

	mov al,1

	jmp .DONE

  .FAILURE:

	xor eax,eax

  .DONE:

	ret

endp

; -------------------------------------------------------------------------------------

proc Calc_2qp0 uses esi edi

  ; compute 2qp0

	lea edi,[qp0x2]

	stdcall ZeroHexBuffer

	; put p0 into ShiftBf

	stdcall CopyP0ToShiftBf

	; multiply ShiftBf by 2

	lea edi,[ShiftBf]

	stdcall ShiftLeft

	; multiply ESI by the ShiftBf

	stdcall CopyQToTempBf

	lea esi,[TempBf]

	stdcall Multiply

	; copy result from ProductBf to EDI

	lea edi,[qp0x2]

	stdcall CopyFromProductBf

	ret

endp

; -------------------------------------------------------------------------------------

proc Init_t_P uses esi edi

  ; calc: t = ceiling(x/2qp0)

	; calc 2qp0

	stdcall Calc_2qp0

	; divide Last_x by 2qp0

	lea esi,[Last_x]

	; copy Last_x to the product buffer

	stdcall CopyToProductBf

	; set divisor = 2qp0

	lea esi,[qp0x2]

	stdcall CopyToShiftBf

	; do the division: ProductBf by ShiftBf

	stdcall Divide

	; copy the quotient to the destination

	lea edi,[Current_t]

	stdcall QuotientToDest

	; add 1 if there was a remainder

	stdcall IsProductBfZero

	cmp al,1

	je .DONE

	stdcall Increment

  .DONE:

	ret

endp

; -------------------------------------------------------------------------------------

proc FirstCandidate_P uses esi edi

  ; length passed in DX

	locals

	  length dw 0

	endl

	mov [length],dx

	; calc: c = 2tqp0 + 1

	stdcall Calc_From_t_P

	; is (2tqp0 + 1) greater than 2^length ?

	; test if the bit at 2^length is set

	mov dx,[length]

	stdcall TestCandidateLength

	cmp al,1

	jne .DONE

	; recalculate t

	mov dx,[length]

	stdcall Recalc_t_P

	; recalculate the candidate

	stdcall Calc_From_t_P

  .DONE:

	stdcall SaveFirstCandidate

	ret

endp

; -------------------------------------------------------------------------------------

proc NextCandidate_P uses esi edi

  ; length passed in DX

	locals

	  length dw 0

	endl

	mov [length],dx

	; get the next Candidate

	stdcall Calc_From_t_P

  .TEST:

	; is the Candidate greater than 2^length ?

	; test if the bit at 2^length is set

	mov dx,[length]

	stdcall TestCandidateLength

	cmp al,1

	jne .DONE

	; recalculate t

	mov dx,[length]

	stdcall Recalc_t_P

	; recalculate the candidate

	stdcall Calc_From_t_P

  .DONE:

	ret

endp

; -------------------------------------------------------------------------------------

proc Calc_From_t_P uses esi edi

  ; calc: c = 2tqp0 + 1

	lea esi,[qp0x2]

	stdcall CopyToShiftBf

	lea esi,[Current_t]

	; multiply ShiftBf by ESI

	stdcall Multiply

	; copy the result from ProductBf back to EDI

	lea edi,[Candidate]

	stdcall CopyFromProductBf

	; add 1 to EDI

	stdcall Increment

	ret

endp

; -------------------------------------------------------------------------------------

proc Recalc_t_P uses esi edi ecx

  ; reset t

  ; t = ceiling(2^(length-1)/2qp0)

  ; required length passed in DX

	locals

	  length dw 0

	endl

	mov [length],dx

	lea edi,[Current_t]

	xor ecx,ecx

	; zero t

	stdcall ZeroHexBuffer

	; set the bit at 2^(length-1)

	; get the index of the byte containing the bit

	; index of bit is length-1

	dec dx

	xor ecx,ecx

	mov cx,dx

	shr cx,3

	; add to EDI

	add edi,ecx

	; get the index of the bit

	and dx,7

	mov cl,dl

	; set the bit

	mov al,1

	shl al,cl

	; or into EDI

	or [edi],al

	; copy the new t to ProductBf

	lea esi,[Current_t]

	stdcall CopyToProductBf

	; divide 2c0 into ProductBf

	lea esi,[qp0x2]

	stdcall CopyToShiftBf

	; do the division: ProductBf by ShiftBf

	stdcall Divide

	; copy the quotient to the destination

	lea edi,[Current_t]

	stdcall QuotientToDest

	; add 1 if there was a remainder

	stdcall IsProductBfZero

	cmp al,1

	je .DONE

	stdcall Increment

  .DONE:

	ret

endp

; -------------------------------------------------------------------------------------

proc Calc_z_P uses esi edi

  ; calc: z = (a ^ 2tq) mod p

	; candidate to ModBf

	lea esi,[Candidate]

	stdcall CopyToModBf

	; 2tq to ExpBf

	stdcall Calc_2tq

	; z to BaseBf

	lea esi,[Current_z]

	stdcall CopyToBaseBf

	; mod_exp

	stdcall Mod_Exp

	; copy result back to Current_z

	lea edi,[Current_z]

	stdcall CopyFromProductBf

	ret

endp

; -------------------------------------------------------------------------------------

proc Calc_2tq uses esi edi

  ; put 2tq into ExpBf

	stdcall CopyQToTempBf

	lea esi,[TempBf]

	stdcall CopyToShiftBf

	; init ExpBf to 2t

	lea esi,[Current_t]

	stdcall CopyToExpBf

	; multiply Exp by 2

	lea edi,[ExpBf]

	stdcall HexShiftLeft

	; multiply ShiftBf by ExpBf

	lea esi,[ExpBf]

	stdcall Multiply

	; copy result back to ExpBf

	lea edi,[ExpBf]

	stdcall CopyFromProductBf

	ret

endp

; -------------------------------------------------------------------------------------

proc Test_Primality_P uses esi edi

  ; Test if the candidate p is prime

  ; result returned in AL

	; Test 1: GCD(z-1,Candidate) == 1

	stdcall Calc_GCD

	stdcall Check_GCD_Passed

	cmp al,1

	jne .DONE

	; Test 2: (z ^ p0) mod p = 1

	lea esi,[Current_z]

	stdcall CopyToBaseBf

	; p0 to ExpBf

	stdcall CopyP0ToExpBf

	; candidate p to ModBf

	lea esi,[Candidate]

	stdcall CopyToModBf

	stdcall Mod_Exp

	stdcall Check_MEXP_Passed

  .DONE:

	; if prime, AL = 1

	ret

endp

; -------------------------------------------------------------------------------------

proc Generate_p

  ; generate the prime p

	mov [nErrorCode],word 0

	; has the prime q been generated

	stdcall IsPrimeQZero

	cmp al,1

	je .ERR_1

	; has the prime q been generated

	stdcall IsPrimeP0Zero

	cmp al,1

	je .ERR_2

	stdcall Random_Prime_P

	cmp al,0

	je .ERR_3

	; AL = 1 for success

	mov al,1

	jmp .DONE

  .ERR_1:

	; prime q has not been generated

	xor eax,eax

	mov [nErrorCode],word 3

	jmp .DONE

  .ERR_2:

	; prime p0 has not been generated

	xor eax,eax

	mov [nErrorCode],word 5

	jmp .DONE

  .ERR_3:

	; failed to generate the prime p

	xor eax,eax

	mov [nErrorCode],word 6

  .DONE:

	ret

endp

; -------------------------------------------------------------------------------------

proc onPrimeFound_p uses esi edi ecx

  ; set p, p_seed and pgen_counter

	lea esi,[Candidate]

	lea edi,[Prime_P]

	xor ecx,ecx

  .COPY:

	; set the prime p

	mov dl,[esi+ecx]

	mov [edi+ecx],dl

	inc cx

	cmp cx,400

	jl .COPY

	; set p_seed

	lea esi,[Prime_Seed]

	lea edi,[P_Seed]

	xor ecx,ecx

  .SEED:

	mov dl,[esi+ecx]

	mov [edi+ecx],dl

	inc cx

	cmp cx,32

	jl .SEED

	; pgen_counter

	mov dx,[Counter]

	mov [Pgen_counter],dx

	ret

endp

; -------------------------------------------------------------------------------------

  ; Math functions

; -------------------------------------------------------------------------------------

proc Test_GCD uses esi edi ecx

  ; generate 50 random numbers and calc the GCD with the candidate prime

  ; candidate is eliminated as a prime if a GCD not equal to 1 is found

  ; result is returned in AL

  ; AL = 1 if all GCD's = 1

	locals

	  count db 50

	endl

	; read the time stamp counter (into EDX:EAX)

	rdtsc

	; use RNG Linear Congruential Generator

	; X(n+1) = (aX(n) + c) mod m

	; a = 0x19660D, c = 0x3C6EF35F, m = 0x100000000

	; X(0) = EAX - from rdtsc

  .RAND:

	mov edx,dword 0x19660D

	; multiply EAX by EDX

	mul edx

	; result in EDX:EAX

	add eax,0x3C6EF35F

  .TEST:

	; copy the test prime to the product buffer

	lea esi,[Candidate]

	stdcall CopyToProductBf

	stdcall ZeroShiftBf

	lea edi,[ShiftBf]

	mov [edi],eax

	stdcall Modulus

	; put the 32 bit modulus into EDX

	lea esi,[ProductBf]

	mov edx,[esi]

	; compute GCD of EAX and EDX

	stdcall GCD_32

	cmp edx,1

	jne .NOT_COPRIME

	dec byte [count]

	cmp [count],byte 0

	jg .RAND

	; if the loop exits here, candidate passed all tests

	mov al,1

	jmp .DONE

  .NOT_COPRIME:

	xor eax,eax

  .DONE:

	ret

endp

; -------------------------------------------------------------------------------------

proc GCD_32 uses ebx ecx

  ; calc GCD(EAX,EDX)

  ; result returned in EDX

	; store EAX in ECX

	mov ecx,eax

  .MOD:

	mov ebx,edx

	xor edx,edx

	; divide EDX:EAX by EBX

	div ebx

	; remainder is in EDX

	; if EDX is zero, EBX is the GCD

	cmp edx,0

	je .DONE

	; the divisor EBX becomes the dividend EAX

	mov eax,ebx

	jmp .MOD

  .DONE:

	; put the GCD into EDX

	mov edx,ebx

	; restore EAX from ECX

	mov eax,ecx

	ret

endp

; -------------------------------------------------------------------------------------

proc CalcSum uses ecx

  ; Add two double sized hex numbers

  ; ESI and EDI are set outside of this procedure

	mov cx,0

	; save CF from iteration to iteration

	; init CF = 0

	clc

	; push flags register to the stack

	pushf

  .SUM:

	mov eax,dword [esi]

	; get the flags register from the stack

	popf

	; [EDI] = [EDI] + [ESI] + CF

	adc dword [edi],eax

	; save the flags register to the stack, for the next iteration

	pushf

	; these affect the CF, which is why it has to be saved to the stack

	add esi,4
	add edi,4

	add cx,4

	; cmp also affects the CF

	cmp cx,DBL_HEX_LEN

	jl .SUM

	; clean up the stack

	popf

	ret

endp

; -------------------------------------------------------------------------------------

proc CalcDiff uses ecx

  ; Calc difference between two double sized hex numbers

  ; diff = dest - src

  ; ESI and EDI are set outside of this procedure

	mov cx,0

	; compute diff as: diff = dest + ~src + 1

	; the 1 is introduced by initialising CF to 1

	; save CF from iteration to iteration

	; init CF = 1

	stc

	; push flags register to the stack

	pushf

  .SUB:

	mov eax,dword [esi]

	; ~src here:

	not eax

	; get the flags register from the stack

	popf

	; [EDI] = [EDI] + ~[ESI] + CF

	adc dword [edi],eax

	; save the flags register to the stack, for the next iteration

	pushf

	; these affect the CF, which is why it has to be saved to the stack

	add esi,4
	add edi,4

	add cx,4

	; cmp also affects the CF

	cmp cx,DBL_HEX_LEN

	jl .SUB

	; clean up the stack

	popf

	ret

endp

; -------------------------------------------------------------------------------------

proc HexShiftLeft uses ecx

  ; Shift dest number left by 1 bit

  ; EDI is set outside of this procedure

	; start from the high order double word in the dest

	add edi,HEX_LEN-4

	mov cx,HEX_LEN-4

  .SHIFT:

	mov eax,dword [edi-4]

	shld dword [edi],eax,1

	sub edi,4

	sub cx,4

	cmp cx,0

	jg .SHIFT

	shl dword [edi],1

	ret

endp

; -------------------------------------------------------------------------------------

proc ShiftLeft uses ecx

  ; Shift dest number left by 1 bit

  ; EDI is set outside of this procedure

	; start from the high order double word in the dest

	add edi,DBL_HEX_LEN-4

	mov cx,DBL_HEX_LEN-4

  .SHIFT:

	mov eax,dword [edi-4]

	shld dword [edi],eax,1

	sub edi,4

	sub cx,4

	cmp cx,0

	jg .SHIFT

	shl dword [edi],1

	ret

endp

; -------------------------------------------------------------------------------------

proc ShiftRight uses ecx

  ; Shift dest number right by 1 bit

  ; EDI is set outside of this procedure

	mov cx,0

  .SHIFT:

	mov eax,dword [edi+4]

	shrd dword [edi],eax,1

	add edi,4

	add cx,4

	cmp cx,DBL_HEX_LEN-4

	jl .SHIFT

	shr dword [edi],1

	ret

endp

; -------------------------------------------------------------------------------------

proc Multiply uses edi ecx

  ; calculate ProductBf = ESI * ShiftBf

  ; ESI is set outside of this procedure

	locals

	  nBit db 0

	endl

	; zero ProductBf

	stdcall ZeroProductBf

	; ESI is a hex value of length HEX_LEN

	; Algorithm:

	; first find the byte in ESI containing the high order bit

	; then starting from this byte, iterate through the bits in ESI
	; start from the high order bit down to the low order bit

	; for each iteration:

	; (1) left shift ProductBf by 1 bit
	; (2) test the bit
	; (3) if the bit is set, add ShiftBf to ProductBf

	mov cx,HEX_LEN

	; start from end of ESI (the highest byte)

	add esi,HEX_LEN-1

  .FIND_HOB:

	cmp byte [esi],0

	jne .FOUND

	dec esi

	dec cx

	cmp cx,0

	je .QUIT

	jmp .FIND_HOB

  .FOUND:

	; ESI now points to the byte that has the high order bit

	; cx contains the byte number (index into A)

	mov [nBit],byte 7

	; load the byte at address ESI into BL for testing

	mov bl,byte [esi]

  .FIND_BIT:

	test bl,0x80

	jnz .HO_BIT

	shl bl,1

	dec [nBit]

	cmp [nBit],byte 0

	jl .QUIT

	jmp .FIND_BIT

  .HO_BIT:

	; the high order bit has been found

	; initialise ProductBf

	stdcall AddShiftBfToProductBf

  .NEXT_BIT:

	; get the next bit

	shl bl,1

	dec [nBit]

	cmp [nBit],byte 0

	jl .NEXT_BYTE

	jmp .SHIFT

  .NEXT_BYTE:

	; get the next byte

	dec esi

	dec cx

	; no more bytes when cx = 0

	cmp cx,0

	jle .QUIT

	mov [nBit],byte 7

	mov bl,byte [esi]

  .SHIFT:

	; shift ProductBf 1 bit to the left

	lea edi,[ProductBf]

	stdcall ShiftLeft

	; test the current bit to see if an addition is required

	test bl,0x80

	jz .NEXT_BIT

  .ADD:

	; add to ProductBf

	stdcall AddShiftBfToProductBf

	jmp .NEXT_BIT

  .QUIT:

	ret

endp

; -------------------------------------------------------------------------------------

proc AddShiftBfToProductBf uses esi edi

  ; add ShiftBf to ProductBf

	lea esi,[ShiftBf]

	lea edi,[ProductBf]

	stdcall CalcSum

	ret

endp

; -------------------------------------------------------------------------------------

proc Divide uses edi esi eax ecx

  ; divide ProductBf by ShiftBf

  ; ProductBf is of length DBL_HEX_LEN

  ; ShiftBf is of length DBL_HEX_LEN

  ; length of value in ShiftBf is up to HEX_LEN

  ; the remainder is left in ProductBf

  ; length of remainder is less than or equal to HEX_LEN

  ; result is in QuotientBf

	stdcall ZeroQuotientBf

	; if ShiftBf is zero, do nothing

	stdcall IsShiftBfZero

	cmp al,1

	je .QUIT

	; if ProductBf is less than ShiftBf, do nothing

	stdcall IsProductBfLessThanShiftBf

	cmp al,1

	je .QUIT

	; left shift ShiftBf to align the high order bits with ProductBf

	locals

	  hob_PBf dw 0
	  hob_SBf dw 0

	  shift dw 0

	endl

	; find the high order bit in ProductBf

	lea eax,[ProductBf]

	stdcall FindHighOrderBit

	; check if the high order bit was found

	cmp eax,0xffffffff

	je .QUIT

	mov word [hob_PBf],ax

	; find the high order bit in ShiftBf

	lea eax,[ShiftBf]

	stdcall FindHighOrderBit

	; check if the high order bit was found

	cmp eax,0xffffffff

	je .QUIT

	mov word [hob_SBf],ax

	; compute how much to shift ShiftBf by

	mov ax,[hob_PBf]
	sub ax,[hob_SBf]
	mov word [shift],ax

	; do the shift

	lea eax,[ShiftBf]
	xor edx,edx
	mov dx,word [shift]

	stdcall BigLeftShift

	; the high order bits of ProductBf and ShiftBf are now aligned

	; Algorithm:

	; for (shift + 1) bits in ProductBf starting from the high order bit:

	; (1) determine if ProductBf is less than ShiftBf
	; (2) if false, subtract ShiftBf from ProductBf
	; (3) right shift ShiftBf by 1 bit
	; (4) repeat from (1)

	mov cx,word [shift]

  .DIVIDE:

	lea eax,[ProductBf]
	lea edx,[ShiftBf]

	stdcall IsLessThan

	test al,1

	jnz .SHIFT

	lea edi,[ProductBf]
	lea esi,[ShiftBf]

	stdcall CalcDiff

	mov dx,cx

	stdcall AddBitToQuotient

  .SHIFT:

	; want to retain the original value of ShiftBf
	; so when cx reaches 0, quit before the final shift

	cmp cx,0

	jle .QUIT

	; right shift ShiftBf

	lea edi,[ShiftBf]

	stdcall ShiftRight

	dec cx

	jmp .DIVIDE

	; the result is now in A

  .QUIT:

	ret

endp

; -------------------------------------------------------------------------------------

proc AddBitToQuotient uses edi eax ecx

  ; set the bit in QuotientBf

  ; bit index is passed in DX

	lea edi,[QuotientBf]

	xor ecx,ecx

	mov cx,dx

	; get the byte index

	shr cx,3

	; add to EDI

	add edi,ecx

	; get the bit index within the byte

	and dx,7

	mov cl,dl

	mov al,1

	shl al,cl

	; set the bit in EDI

	or [edi],al

	ret

endp

; -------------------------------------------------------------------------------------

proc QuotientToDest uses esi ecx

  ; copy the quotient to EDI

  ; EDI is set outside of this procedure

	lea esi,[QuotientBf]

	xor ecx,ecx

  .COPY:

	mov dl,[esi+ecx]

	mov [edi+ecx],dl

	inc cx

	cmp cx,HEX_LEN

	jl .COPY

	ret

endp

; -------------------------------------------------------------------------------------

proc Modulus uses edi esi eax ecx

  ; calculate ProductBf mod ShiftBf

  ; ProductBf is of length DBL_HEX_LEN

  ; ShiftBf is of length DBL_HEX_LEN

  ; length of value in ShiftBf is up to HEX_LEN

  ; the remainder is left in ProductBf

  ; length of remainder is less than or equal to HEX_LEN

	; if ShiftBf is zero, do nothing

	stdcall IsShiftBfZero

	cmp al,1

	je .QUIT

	; if ProductBf is less than ShiftBf, do nothing

	stdcall IsProductBfLessThanShiftBf

	cmp al,1

	je .QUIT

	; left shift ShiftBf to align the high order bits with ProductBf

	locals

	  hob_PBf dw 0
	  hob_SBf dw 0

	  shift dw 0

	endl

	; find the high order bit in ProductBf

	lea eax,[ProductBf]

	stdcall FindHighOrderBit

	; check if the high order bit was found

	cmp eax,0xffffffff

	je .QUIT

	mov word [hob_PBf],ax

	; find the high order bit in ShiftBf

	lea eax,[ShiftBf]

	stdcall FindHighOrderBit

	; check if the high order bit was found

	cmp eax,0xffffffff

	je .QUIT

	mov word [hob_SBf],ax

	; compute how much to shift ShiftBf by

	mov ax,[hob_PBf]
	sub ax,[hob_SBf]
	mov word [shift],ax

	; do the shift

	lea eax,[ShiftBf]
	xor edx,edx
	mov dx,word [shift]

	stdcall BigLeftShift

	; the high order bits of ProductBf and ShiftBf are now aligned

	; Algorithm:

	; for (shift + 1) bits in ProductBf starting from the high order bit:

	; (1) determine if ProductBf is less than ShiftBf
	; (2) if false, subtract ShiftBf from ProductBf
	; (3) right shift ShiftBf by 1 bit
	; (4) repeat from (1)

	mov cx,word [shift]

  .CALC_MOD:

	lea eax,[ProductBf]
	lea edx,[ShiftBf]

	stdcall IsLessThan

	test al,1

	jnz .SHIFT

	lea edi,[ProductBf]
	lea esi,[ShiftBf]

	stdcall CalcDiff

  .SHIFT:

	; want to retain the original value of ShiftBf
	; so when cx reaches 0, quit before the final shift

	cmp cx,0

	jle .QUIT

	; right shift ShiftBf

	lea edi,[ShiftBf]

	stdcall ShiftRight

	dec cx

	jmp .CALC_MOD

	; the result is now in A

  .QUIT:

	ret

endp

; -------------------------------------------------------------------------------------

proc BigLeftShift uses esi edi ecx

  ; left shift a double hex value by more than 1 bit

  ; address is passed in EAX
  ; size of shift passed in EDX

	mov edi,eax
	mov esi,eax

	locals

	  nBytes dw 0
	  nBits db 0

	  addr dd ?

	endl

	; save the address for later

	mov dword [addr],eax

	; size of shift = 8 * nBytes + nBits

	; get nBits

	mov al,dl

	and al,7

	mov byte [nBits],al

	; get nBytes

	shr dx,3

	mov word [nBytes],dx

	cmp word [nBytes],0

	je .BITS

	cmp word [nBytes],DBL_HEX_LEN

	jge .ZERO_HEX

	; copy up the bytes in the hex value by nBytes

	add edi,DBL_HEX_LEN-1
	add esi,DBL_HEX_LEN-1

	sub si,word [nBytes]

	mov cx,DBL_HEX_LEN
	sub cx,word [nBytes]

	; copy up (DBL_HEX_LEN - nBytes) bytes

  .COPY_UP:

	mov al,[esi]
	mov [edi],al

	dec esi
	dec edi

	dec cx

	cmp cx,0

	jg .COPY_UP

	mov cx,word [nBytes]

	; fill the rest of the hex array with zero bytes

  .FILL_0:

	mov [edi],byte 0

	dec edi

	dec cx

	cmp cx,0

	jg .FILL_0

	; all done if nBits is zero

	cmp byte [nBits],0

	je .DONE

  .BITS:

	; set edi back to the high order dword in the hex value

	mov edi,dword [addr]

	add edi,DBL_HEX_LEN-4

	mov cx,DBL_HEX_LEN-4

  .SHIFT_BITS:

	mov eax,[edi-4]

	push cx

	mov cl,byte [nBits]

	shld dword [edi],eax,cl

	pop cx

	sub edi,4

	sub cx,4

	cmp cx,0

	jg .SHIFT_BITS

	mov cl,byte [nBits]

	shl dword [edi],cl

	jmp .DONE

  .ZERO_HEX:

	; shift is greater than the size of the hex value, just set to zero

	; zero EDI

	stdcall ZeroDoubleHexBuffer

  .DONE:

	ret

endp

; -------------------------------------------------------------------------------------

proc FindHighOrderBit uses esi ecx

  ; scan a double hex length value for the high order bit

  ; return an index number giving the position of the high order bit

  ; address passed in EAX

  ; index returned in EAX

	mov esi,eax

	; start from the high order byte

	add esi,DBL_HEX_LEN-1

	mov cx,DBL_HEX_LEN

	mov eax,DBL_HEX_LEN-1

	; scan byte by byte for the first non-zero byte

  .SCAN:

	cmp byte [esi],0

	jne .FOUND

	dec cx

	cmp cx,0

	; not found if cx = 0, so quit

	jle .NOT_FOUND

	dec esi
	dec eax

	jmp .SCAN

  .FOUND:

	; multiply EAX by 8

	shl eax,3

	mov ebx,7

	mov dl,byte [esi]

  .FIND_BIT:

	test dl,0x80

	jnz .FINISH

	shl dl,1

	dec bl

	cmp bl,0

	jl .NOT_FOUND

	jmp .FIND_BIT

  .FINISH:

	; add the bit position to get the final index value

	add eax,ebx

	jmp .DONE

  .NOT_FOUND:

	mov eax,0xffffffff

  .DONE:

	ret

endp

; -------------------------------------------------------------------------------------

proc IsLessThan uses esi edi ecx

  ; is A less than B ?

  ; A and B are treated as unsigned values

  ; address of A passed in EAX
  ; address of B passed in EDX

	mov edi,eax
	mov esi,edx

	; start from the high order bytes

	add esi,DBL_HEX_LEN-4
	add edi,DBL_HEX_LEN-4

	mov cx,0

	; result is returned in AL

	xor al,al

  .CHECK_LT:

	mov edx,dword [edi]
	cmp edx,dword [esi]

	jb .LESS_THAN

	jnbe .QUIT

	add cx,4

	cmp cx,DBL_HEX_LEN

	jge .QUIT

	sub esi,4
	sub edi,4

	jmp .CHECK_LT

	; AL = true if less than

  .LESS_THAN:

	mov al,1

  .QUIT:

	ret

endp

; -------------------------------------------------------------------------------------

proc Mod_Exp uses esi edi

  ; compute Mod_Exp = pow(A,B) mod C

  ; A is in BaseBf
  ; B is in ExpBf
  ; C is in ModBf

  ; result in ProductBf

	locals

	  nBit db 0

	  index dw 0

	  bits db 0

	endl

	stdcall ZeroProductBf

	; if C is zero, do nothing

	stdcall IsModBfZero

	cmp al,1

	je .QUIT

	; init ProductBf to 1

	lea edi,[ProductBf]

	mov [edi],byte 1

	; if B is zero, do nothing

	stdcall IsExpBfZero

	cmp al,1

	je .QUIT

	; ProductBf is now 1

  .START:

	; find the first non zero byte in ExpBf

	lea esi,[ExpBf]

	xor ecx,ecx

	mov cx,HEX_LEN-1

  .FIND:

	cmp [esi+ecx],byte 0

	jne .FOUND

	dec cx

	cmp cx,0

	jge .FIND

	; if the loop exits here, ExpBf is zero

	jmp .QUIT

  .FOUND:

	; save the index

	mov [index],cx

	; save the byte

	mov al,byte [esi+ecx]

	mov [bits],al

	; set the bit index

	mov [nBit],byte 8

	; start processing

  .SQUARE:

	; always square, whether the bit is 0 or 1

	; square the value in ProductBf

	stdcall CopyProductBfToShiftBf

	stdcall CopyProductBfToTempBf

	stdcall ZeroProductBf

	lea esi,[TempBf]

	stdcall Multiply

	stdcall CopyModBfToShiftBf

	stdcall Modulus

  .TEST:

	; only multiply if the bit is 1

	; decrement the bit index

	dec [nBit]

	; test the bit in [bits]

	shl byte [bits],1

	jnc .NEXT_BIT

  .MULTIPLY:

	; multiply ProductBf by BaseBf

	stdcall CopyProductBfToShiftBf

	stdcall ZeroProductBf

	lea esi,[BaseBf]

	stdcall Multiply

	stdcall CopyModBfToShiftBf

	stdcall Modulus

  .NEXT_BIT:

	; if nBit = 0, will need to get the next byte

	cmp [nBit],byte 0

	jg .SQUARE

  .NEXT_BYTE:

	; get the next byte

	; if index = 0, there is no next byte, so quit

	cmp [index],word 0

	je .QUIT

	dec word [index]

	mov [nBit],byte 8

	; make sure ESI points to ExpBf

	lea esi,[ExpBf]

	xor ecx,ecx

	mov cx,[index]

	mov al,byte [esi+ecx]

	mov [bits],al

	jmp .SQUARE

  .QUIT:

	ret

endp

; -------------------------------------------------------------------------------------

proc IsHexZero uses ecx

  ; is a hex number of length HEX_LEN equal to zero ?

  ; EDI is set outside of this procedure

  ; EDI is not modifed by this procedure

	xor ecx,ecx

  .TEST:

	cmp [edi+ecx],byte 0

	jne .NON_ZERO

	inc cx

	cmp cx,HEX_LEN

	jl .TEST

	; if the loop exits here, the value is zero

	mov al,1

	jmp .DONE

  .NON_ZERO:

	xor eax,eax

  .DONE:

	ret

endp

; -------------------------------------------------------------------------------------

proc IsDblHexZero uses ecx

  ; is a double hex number of length DBL_HEX_LEN equal to zero ?

  ; EDI is set outside of this procedure

  ; EDI is not modifed by this procedure

	xor ecx,ecx

  .TEST:

	cmp [edi+ecx],byte 0

	jne .NON_ZERO

	inc cx

	cmp cx,DBL_HEX_LEN

	jl .TEST

	; if the loop exits here, the value is zero

	mov al,1

	jmp .DONE

  .NON_ZERO:

	xor eax,eax

  .DONE:

	ret

endp

; -------------------------------------------------------------------------------------

proc Increment uses ecx

  ; increment the value in EDI

  ; EDI is set outside of this procedure

	add [edi],byte 1

	jnc .DONE

	xor ecx,ecx

	mov cx,1

  .ADD_C:

	add [edi+ecx],byte 1

	jnc .DONE

	inc cx

	cmp cx,HEX_LEN

	jl .ADD_C

  .DONE:

	ret

endp

; -------------------------------------------------------------------------------------

proc Decrement uses ecx

  ; decrement the value in EDI

  ; EDI is set outside of this procedure

	sub [edi],byte 1

	jnc .DONE

	xor ecx,ecx

	mov cx,1

  .SUB_B:

	sub [edi+ecx],byte 1

	jnc .DONE

	inc cx

	cmp cx,HEX_LEN

	jl .SUB_B

  .DONE:

	ret

endp

; -------------------------------------------------------------------------------------

  ; use the Hash functions to create a random integer of the required length

; -------------------------------------------------------------------------------------

proc Calc_Random_x uses edi

  ; use the hash function to generate the random number x

  ; number of iterations passed in DL

	locals

	  nIterations db 0

	endl

	mov [nIterations],dl

	inc byte [nIterations]

	; clear the temp buffer

	stdcall ClearTempBuffer

	xor eax,eax

	cmp [DSA_N],160

	je .SHA_1

	cmp [DSA_N],224

	je .SHA_224

	cmp [DSA_N],256

	je .SHA_256

	jmp .DONE

  .SHA_1:

	; x = x + SHL(Hash(Prime_Seed),outlen x iteration)

	stdcall HashSeed_SHA_1

	stdcall Add_Hash_1

	stdcall IncrementSeed

	add eax,20

	dec byte [nIterations]

	cmp [nIterations],byte 0

	jg .SHA_1

	jmp .DONE

  .SHA_224:

	; x = x + SHL(Hash(Prime_Seed),outlen x iteration)

	stdcall HashSeed_SHA_224

	stdcall Add_Hash_224

	stdcall IncrementSeed

	add eax,28

	dec byte [nIterations]

	cmp [nIterations],byte 0

	jg .SHA_224

	jmp .DONE

  .SHA_256:

	; x = x + SHL(Hash(Prime_Seed),outlen x iteration)

	stdcall HashSeed_SHA_256

	stdcall Add_Hash_256

	stdcall IncrementSeed

	add eax,32

	dec byte [nIterations]

	cmp [nIterations],byte 0

	jg .SHA_256

  .DONE:

	; copy from the result from TempBf to Last_x

	lea edi,[Last_x]

	stdcall CopyFromTempBuffer

	ret

endp

; -------------------------------------------------------------------------------------

proc Fix_Length_x uses edi ecx

  ; make sure Last_x has the length as given in EDX (in bits)

  ; i.e. x = 2^(length-1) + (x mod 2^(length-1))

  ; truncate and then set the bit at 2^(length-1)

	lea edi,[Last_x]

	; calc the byte index of the high order bit

	mov ecx,edx

	dec ecx

	shr ecx,3

	; zero all bytes above the byte containing the HOB

	inc ecx

  .ZERO:

	mov [edi+ecx],byte 0

	inc ecx

	cmp ecx,400

	jl .ZERO

  .SET:

	; get the bit index

	mov ecx,edx

	dec ecx

	and ecx,7

	; generate a bit mask

	inc ecx

	mov ax,1

	shl ax,cl

	dec ax

	; and the bit mask into the byte in EDI

	mov ecx,edx

	dec ecx

	shr ecx,3

	and [edi+ecx],al

	; make sure the HOB is set

	inc ax

	shr ax,1

	or [edi+ecx],al

	ret

endp

; -------------------------------------------------------------------------------------

proc Calc_Random_a uses esi edi ecx

  ; use the hash function to generate the random number a

  ; number of iterations passed in DL

	locals

	  nIterations db 0

	endl

	mov [nIterations],dl

	inc byte [nIterations]

	; clear the temp buffer

	stdcall ClearTempBuffer

	xor eax,eax

	cmp [DSA_N],160

	je .SHA_1

	cmp [DSA_N],224

	je .SHA_224

	cmp [DSA_N],256

	je .SHA_256

	jmp .NEXT

  .SHA_1:

	; a = a + SHL(Hash(Prime_Seed),outlen x iteration)

	stdcall HashSeed_SHA_1

	stdcall Add_Hash_1

	stdcall IncrementSeed

	add eax,20

	dec byte [nIterations]

	cmp [nIterations],byte 0

	jg .SHA_1

	jmp .NEXT

  .SHA_224:

	; a = a + SHL(Hash(Prime_Seed),outlen x iteration)

	stdcall HashSeed_SHA_224

	stdcall Add_Hash_224

	stdcall IncrementSeed

	add eax,28

	dec byte [nIterations]

	cmp [nIterations],byte 0

	jg .SHA_224

	jmp .NEXT

  .SHA_256:

	; a = a + SHL(Hash(Prime_Seed),outlen x iteration)

	stdcall HashSeed_SHA_256

	stdcall Add_Hash_256

	stdcall IncrementSeed

	add eax,32

	dec byte [nIterations]

	cmp [nIterations],byte 0

	jg .SHA_256

  .NEXT:

	; copy from the result from TempBf to Current_z

	lea edi,[Current_z]

	stdcall CopyFromTempBuffer

	ret

endp

; -------------------------------------------------------------------------------------

proc Add_Hash_1 uses esi edi ecx edx

  ; offset in EAX

	lea esi,[HashInteger]

	lea edi,[TempBf]

	xor ecx,ecx

	add edi,eax

  .COPY:

	mov dl,[esi+ecx]

	mov [edi+ecx],dl

	inc cx

	cmp cx,20

	jl .COPY

	ret

endp

; -------------------------------------------------------------------------------------

proc Add_Hash_224 uses esi edi ecx edx

  ; offset in EAX

	lea esi,[HashInteger]

	lea edi,[TempBf]

	xor ecx,ecx

	add edi,eax

  .COPY:

	mov dl,[esi+ecx]

	mov [edi+ecx],dl

	inc cx

	cmp cx,28

	jl .COPY

	ret

endp

; -------------------------------------------------------------------------------------

proc Add_Hash_256 uses esi edi ecx edx

  ; offset in EAX

	lea esi,[HashInteger]

	lea edi,[TempBf]

	xor ecx,ecx

	add edi,eax

  .COPY:

	mov dl,[esi+ecx]

	mov [edi+ecx],dl

	inc cx

	cmp cx,32

	jl .COPY

	ret

endp

; -------------------------------------------------------------------------------------

  ; Input/Output functions

; -------------------------------------------------------------------------------------

proc CountDigits uses esi ecx

  ; count the number of hex digits in the bfDisplay string

	mov ax,0

	xor ecx,ecx

	lea esi,[bfDisplay]

  .COUNT:

	mov dl,[esi]

	cmp dl,0

	je .DONE

	inc cx

	inc esi

	jmp .COUNT

  .DONE:

	; return the digit count in AX

	mov ax,cx

	ret

endp

; -------------------------------------------------------------------------------------

proc StrToHexStr uses esi edi

  ; filter out any non hex digit characters from the bfDisplay string

	lea esi,[bfDisplay]
	lea edi,[bfDisplay]

  .FILTER:

	mov al,[esi]

	cmp al,0

	je .DONE

	cmp al,48

	jl .NOT_HEX

	cmp al,57

	jle .COPY

	cmp al,65

	jl .NOT_HEX

	cmp al,70

	jle .COPY

	cmp al,97

	jl .NOT_HEX

	cmp al,102

	jg .NOT_HEX

  .COPY:

	mov [edi],al

	inc edi

  .NOT_HEX:

	inc esi

	jmp .FILTER

  .DONE:

	; null terminate the new string

	mov [edi],byte 0

	ret

endp

; -------------------------------------------------------------------------------------

proc HexDigitToValue

  ; convert a hex digit to a 4 bit value

  ; input in AL - output in AL

	; is AL in the range: 48 - 57 (0 - 9) ?

	cmp al,48

	jl .NOT_HEX

	cmp al,57

	jg .A_Z

	sub al,48

	jmp .DONE

  .A_Z:

	; is AL in the range: 65 - 70 (A - B) ?

	cmp al,65

	jl .NOT_HEX

	cmp al,70

	jg .a_z

	sub al,55

	jmp .DONE

  .a_z:

	; is AL in the range: 97 - 102 (a - b) ?

	cmp al,97

	jl .NOT_HEX

	cmp al,102

	jg .NOT_HEX

	sub al,87

	jmp .DONE

  .NOT_HEX:

	; set EAX to 0xffff if input is not a valid hex digit

	mov eax,0xffff

  .DONE:

	ret

endp

; -------------------------------------------------------------------------------------

proc ByteToDigits

  ; convert 1 byte to 2 hex digits

  ; input in AL - output in AX

	; copy AL to AH

	mov ah,al

	; AH: get the upper 4 bits of the byte

	shr ah,4

	; nibble to hex digit

	add ah,48

	cmp ah,57

	jle .NEXT

	add ah,7

  .NEXT:

	; AL: get the lower 4 bits of the byte

	and al,0xf

	; nibble to hex digit

	add al,48

	cmp al,57

	jle .DONE

	add al,7

  .DONE:

	; output is in AX

	ret

endp

; -------------------------------------------------------------------------------------

proc PrintHexValue uses ecx

  ; ESI and EDI set outside of this procedure

  ; pass number of bytes to print in EDX

	locals

	  count db 0

	endl

	mov cx,dx

	sub cx,4

  .FIND:

	; find first non zero dword

	mov eax,[esi+ecx]

	cmp eax,dword 0

	jne .PRINT

	sub cx,4

	cmp cx,0

	jg .FIND

  .PRINT:

	mov eax,[esi+ecx]

	; dword to 8 digits

	stdcall DWordToStr

	jcxz .DONE

	sub cx,4

	inc [count]

	; add a CRLF to the output string after every 8 dwords

	cmp [count],byte 8

	je .ADD_CRLF

	jmp .PRINT

  .ADD_CRLF:

	mov [count],byte 0

	PRINT_CRLF

	jmp .PRINT

  .DONE:

	PRINT_CRLF
	PRINT_CRLF

	ret

endp

; -------------------------------------------------------------------------------------

proc WordToStr uses edx

  ; print a word (2 bytes) to a string

  ; word passed in AX
  ; EDI set outside of this function

	mov dx,ax
	mov al,ah

	stdcall ByteToDigits

	mov [edi],ah
	inc edi

	mov [edi],al
	inc edi

	mov al,dl

	stdcall ByteToDigits

	mov [edi],ah
	inc edi

	mov [edi],al
	inc edi

	ret

endp

; -------------------------------------------------------------------------------------

proc DWordToStr

  ; print a dword (4 bytes) to a string

  ; word passed in EAX
  ; EDI set outside of this function

	locals

	  tmp dw 0

	endl

	mov word [tmp],ax

	shr eax,16

	stdcall WordToStr

	mov ax,word [tmp]

	stdcall WordToStr

	; add a space

	mov [edi],byte 32
	inc edi

	ret

endp

; -------------------------------------------------------------------------------------

  ; seed functions

; -------------------------------------------------------------------------------------

proc ReadFirstSeed uses esi edi ecx

  ; read the 256 bit (32 byte) first seed from the bfDisplay string

	; filter out any non hex digit chars from bfDisplay

	stdcall StrToHexStr

	; reset the first seed

	stdcall ClearFirstSeed

	lea esi,[bfDisplay]

	; truncate bfDisplay so that it has no more than 64 hex digits

	mov [esi+64],byte 0

	lea edi,[First_Seed]

	; count the digits in bfDisplay and save to AX

	stdcall CountDigits

	; check if the input buffer is empty

	cmp ax,0

	je .DONE

	; calculate the offset in bytes from the start of EDI

	; based on the number of digits in the input buffer (given by AX)

	xor ecx,ecx

	mov cx,ax

	shr cx,1

	test ax,1

	jnz .ODD

  .EVEN:

	; the input buffer contains an even number of hex digits

	; offset = (nDigits/2) - 1

	dec cx

	add edi,ecx

	jmp .GET_DIGITS

  .ODD:

	; the input buffer contains an odd number of hex digits

	; offset = nDigits/2

	; convert the first digit

	add edi,ecx

	mov al,[esi]

	stdcall HexDigitToValue

	mov [edi],al

	inc esi

	dec edi

	dec cx

	cmp cx,0

	jl .DONE

  .GET_DIGITS:

	; convert 2 hex digits to 1 byte

	mov al,[esi]

	stdcall HexDigitToValue

	shl al,4

	mov [edi],al

	inc esi

	mov al,[esi]

	stdcall HexDigitToValue

	or [edi],al

	inc esi

	dec cx

	cmp cx,0

	jl .DONE

	dec edi

	jmp .GET_DIGITS

  .DONE:

	; if N = 160 or N = 224, truncate the first seed

	cmp [DSA_N],word 160

	je .160

	cmp [DSA_N],word 224

	je .224

	jmp .QUIT

  .160:

	lea edi,[First_Seed]

	; truncate the first seed to 160 bits

	mov [edi+20],dword 0
	mov [edi+24],dword 0
	mov [edi+28],dword 0

	jmp .QUIT

  .224:

	lea edi,[First_Seed]

	; truncate the first seed to 224 bits

	mov [edi+28],dword 0

  .QUIT:

	ret

endp

; -------------------------------------------------------------------------------------

proc PrintFirstSeed uses esi edi ecx

  ; print the first seed to bfDisplay

	lea esi,[First_Seed]

	lea edi,[bfDisplay]

	xor ecx,ecx

	mov cx,16

	cmp [DSA_N],word 160

	je .PRINT

	mov cx,24

	cmp [DSA_N],word 224

	je .PRINT

	mov cx,28

  .PRINT:

	mov eax,[esi+ecx]

	; dword to 8 digits

	stdcall DWordToStr

	jcxz .DONE

	sub cx,4

	jmp .PRINT

  .DONE:

	mov [edi],byte 0

	ret

endp

; -------------------------------------------------------------------------------------

proc ClearFirstSeed uses edi ecx

  ; reset the first seed

	lea edi,[First_Seed]

	xor ecx,ecx

  .CLEAR:

	mov [edi+ecx],byte 0

	inc cx

	cmp cx,32

	jl .CLEAR

	ret

endp

; -------------------------------------------------------------------------------------

proc GenerateRandomFirstSeed uses edi ecx

  ; generate a random 256 bit (32 byte) first seed

	stdcall ClearFirstSeed

	; read the time stamp counter (into EDX:EAX)

	rdtsc

	; use RNG Linear Congruential Generator

	; X(n+1) = (aX(n) + c) mod m

	; a = 0x19660D, c = 0x3C6EF35F, m = 0x100000000

	; X(0) = EAX - from rdtsc

	lea edi,[First_Seed]

	xor ecx,ecx

  .RAND:

	mov edx,dword 0x19660D

	; multiply EAX by EDX

	mul edx

	; result in EDX:EAX

	add eax,0x3C6EF35F

	mov [edi+ecx],al

	inc cx

	cmp cx,32

	jl .RAND

	; if N = 160 or N = 224, truncate the first seed

	cmp [DSA_N],word 160

	je .160

	cmp [DSA_N],word 224

	je .224

	jmp .DONE

  .160:

	lea edi,[First_Seed]

	; truncate the first seed to 160 bits

	mov [edi+20],dword 0
	mov [edi+24],dword 0
	mov [edi+28],dword 0

	jmp .DONE

  .224:

	lea edi,[First_Seed]

	; truncate the first seed to 224 bits

	mov [edi+28],dword 0

  .DONE:

	ret

endp

; -------------------------------------------------------------------------------------

proc GetFirstSeed uses esi edi ecx

  ; use the first seed to init the seed value

	lea esi,[First_Seed]

	lea edi,[Prime_Seed]

	xor ecx,ecx

  .TEST:

	; is the first seed zero ?

	cmp [esi+ecx],dword 0

	jne .NEXT

	add cx,4

	cmp cx,32

	jl .TEST

	; the first seed is zero, generate a random value

	stdcall GenerateRandomFirstSeed

  .NEXT:

	xor ecx,ecx

	; init the seed value from the first seed

  .COPY:

	mov edx,[esi+ecx]

	mov [edi+ecx],edx

	add cx,4

	cmp cx,32

	jl .COPY

	ret

endp

; -------------------------------------------------------------------------------------

proc IncrementSeed uses edi ecx

  ; increment the seed

	lea edi,[Prime_Seed]

	xor ecx,ecx

	add [edi],byte 1

	jnc .DONE

  .ADD_C:

	inc cx

	cmp cx,32

	jge .DONE

	add [edi+ecx],byte 1

	jc .ADD_C

  .DONE:

	; if N = 160 or N = 224, truncate the first seed

	cmp [DSA_N],word 160

	je .160

	cmp [DSA_N],word 224

	je .224

	jmp .QUIT

  .160:

	lea edi,[First_Seed]

	; truncate the first seed to 160 bits

	mov [edi+20],dword 0
	mov [edi+24],dword 0
	mov [edi+28],dword 0

	jmp .QUIT

  .224:

	lea edi,[First_Seed]

	; truncate the first seed to 224 bits

	mov [edi+28],dword 0

  .QUIT:

	ret

endp

; -------------------------------------------------------------------------------------

proc SeedToHashMessage uses esi edi eax ecx

  ; copy the 256 bit (32 byte) seed to the hash message block

  ; only one 512 bit message block is needed

  ; the SHA functions expects the message block to have the big endian byte order

	; convert the seed to a bit string

	stdcall SeedToBitString

	; then copy to the hash message block

	lea esi,[BitString]

	lea edi,[MESSAGE]

	xor ecx,ecx

  .COPY:

	; the first 32 bytes contain the seed (in big endian byte order)

	mov dl,[esi+ecx]

	mov [edi+ecx],dl

	inc cx

	cmp cx,32

	jl .COPY

	; zero the padding bits

  .ZERO:

	mov [edi+ecx],byte 0

	inc cx

	cmp cx,64

	jl .ZERO

	; set the number of 512 bit blocks

	mov [nBlocks512],dword 1

	cmp [DSA_N],word 160

	je .160

	cmp [DSA_N],word 224

	je .224

	cmp [DSA_N],word 256

	je .256

  .160:

	; set the bit length = 160

	mov [BitLength],dword 160

	; the first bit after the 20 seed bytes is set to 1

	mov [edi+20],byte 0x80

	jmp .BIT_LEN

  .224:

	; set the bit length = 224

	mov [BitLength],dword 224

	; the first bit after the 28 seed bytes is set to 1

	mov [edi+28],byte 0x80

	jmp .BIT_LEN

  .256:

	; set the bit length = 256

	mov [BitLength],dword 256

	; the first bit after the 32 seed bytes is set to 1

	mov [edi+32],byte 0x80

  .BIT_LEN:

	; BitLength is a dword (32 bits)

	; copy to the last 32 bits of the block

	mov eax,[BitLength]

	; big endian byte order

	bswap eax

	mov [edi+60],eax

	ret

endp

; -------------------------------------------------------------------------------------

proc SeedToBitString uses esi edi ecx

  ; convert the seed number to a bit string

  ; the conversion is done by reversing the byte order

  ; the seed number is in little endian byte order

  ; the seed bit string is in big endian byte order

  ; the dwords within each are indexed from 0 to 7

  ; first the order of the dwords is reversed

  ; then the byte order within each dword is reversed, using the BSWAP instruction

	lea esi,[Prime_Seed]

	lea edi,[BitString]

	add edi,28

	mov cx,0

  .CONVERT:

	mov edx,[esi]

	bswap edx

	mov [edi],edx

	add esi,4
	sub edi,4

	inc cx

	cmp cx,8

	jl .CONVERT

	; if N = 160 or N = 224 the bits need to be shifted down by 12 bytes or 4 bytes

	cmp [DSA_N],word 160

	je .160

	cmp [DSA_N],word 224

	je .224

	jmp .DONE

  .160:

	lea esi,[BitString]
	lea edi,[BitString]

	xor ecx,ecx

  .COPY_3:

	; copy down the dwords

	mov edx,[esi+12]

	mov [edi],edx

	mov [esi+12],dword 0

	add esi,4
	add edi,4

	inc cx

	cmp cx,5

	jl .COPY_3

	jmp .DONE

  .224:

	lea esi,[BitString]
	lea edi,[BitString]

	xor ecx,ecx

  .COPY_1:

	; copy down the dwords

	mov edx,[esi+4]

	mov [edi],edx

	mov [esi+4],dword 0

	add esi,4
	add edi,4

	inc cx

	cmp cx,7

	jl .COPY_1

  .DONE:

	ret

endp

; -------------------------------------------------------------------------------------

proc SHA_1_ToInteger uses esi edi ecx

  ; convert the hash result H_0_1 to an integer value

  ; by reversing the order of the dwords within the hash result

  ; within each dword the byte order is already little endian, no need to call BSWAP

	; H_0_1 = 5 x 4 byte words

	lea esi,[H_0_1]

	add esi,16

	lea edi,[HashInteger]

	mov cx,0

  .CONVERT:

	mov edx,[esi]

	mov [edi],edx

	sub esi,4
	add edi,4

	inc cx

	cmp cx,5

	jl .CONVERT

	; zero the rest of the HashInteger value

  .ZERO:

	mov [edi],dword 0

	add edi,4

	inc cx

	cmp cx,8

	jl .ZERO

	ret

endp

; -------------------------------------------------------------------------------------

proc SHA_224_ToInteger uses esi edi ecx

  ; convert the hash result H_0_256 to an integer value

  ; by reversing the order of the dwords within the hash result

  ; within each dword the byte order is already little endian, no need to call BSWAP

	; for SHA-224, H_0_256 = 7 x 4 byte words

	lea esi,[H_0_256]

	add esi,24

	lea edi,[HashInteger]

	mov cx,0

  .CONVERT:

	mov edx,[esi]

	mov [edi],edx

	sub esi,4
	add edi,4

	inc cx

	cmp cx,7

	jl .CONVERT

	; zero the last dword in the HashInteger value

	mov [edi],dword 0

	ret

endp

; -------------------------------------------------------------------------------------

proc SHA_256_ToInteger uses esi edi ecx

  ; convert the hash result H_0_256 to an integer value

  ; by reversing the order of the dwords within the hash result

  ; within each dword the byte order is already little endian, no need to call BSWAP

	; H_0_256 = 8 x 4 byte words

	lea esi,[H_0_256]

	add esi,28

	lea edi,[HashInteger]

	mov cx,0

  .CONVERT:

	mov edx,[esi]

	mov [edi],edx

	sub esi,4
	add edi,4

	inc cx

	cmp cx,8

	jl .CONVERT

	ret

endp

; -------------------------------------------------------------------------------------

proc HashSeed_SHA_1

	stdcall SeedToHashMessage

	stdcall Compute_SHA_1

	stdcall SHA_1_ToInteger

	ret

endp

; -------------------------------------------------------------------------------------

proc HashSeed_SHA_224

	stdcall SeedToHashMessage

	stdcall Compute_SHA_224

	stdcall SHA_224_ToInteger

	ret

endp

; -------------------------------------------------------------------------------------

proc HashSeed_SHA_256

	stdcall SeedToHashMessage

	stdcall Compute_SHA_256

	stdcall SHA_256_ToInteger

	ret

endp

; -------------------------------------------------------------------------------------

  ; SHA-1, SHA-224, SHA-256 functions

; -------------------------------------------------------------------------------------

proc Init_H_0_SHA_1 uses edi

	lea edi,[H_0_1]

	mov [edi],dword 0x67452301
	mov [edi+4],dword 0xefcdab89
	mov [edi+8],dword 0x98badcfe
	mov [edi+12],dword 0x10325476
	mov [edi+16],dword 0xc3d2e1f0

	ret

endp

; -------------------------------------------------------------------------------------

proc Init_H_0_SHA_224 uses edi

	lea edi,[H_0_256]

	mov [edi],dword 0xc1059ed8
	mov [edi+4],dword 0x367cd507
	mov [edi+8],dword 0x3070dd17
	mov [edi+12],dword 0xf70e5939
	mov [edi+16],dword 0xffc00b31
	mov [edi+20],dword 0x68581511
	mov [edi+24],dword 0x64f98fa7
	mov [edi+28],dword 0xbefa4fa4

	ret

endp

; -------------------------------------------------------------------------------------

proc Init_H_0_SHA_256 uses edi

	lea edi,[H_0_256]

	mov [edi],dword 0x6a09e667
	mov [edi+4],dword 0xbb67ae85
	mov [edi+8],dword 0x3c6ef372
	mov [edi+12],dword 0xa54ff53a
	mov [edi+16],dword 0x510e527f
	mov [edi+20],dword 0x9b05688c
	mov [edi+24],dword 0x1f83d9ab
	mov [edi+28],dword 0x5be0cd19

	ret

endp

; -------------------------------------------------------------------------------------

proc Compute_SHA_1 uses esi edi eax ecx

  ; process the single 512 bit (64 byte) message block

  ; this function expects the message block to have the big endian byte ordering

	stdcall Init_H_0_SHA_1

	mov ecx,[nBlocks512]

	lea esi,[MESSAGE]

  .HASH_BLOCK:

	lea edi,[Message_Schedule]

	mov edx,64

  .INIT_SCHED:

	; copy the 512 bit block to the first 512 bits of the message schedule

	mov eax,[esi]

	; big endian to little endian

	bswap eax

	mov [edi],eax

	add esi,4
	add edi,4

	sub dx,4

	cmp dx,0

	jg .INIT_SCHED

	; iterate the hash value

	stdcall SHA_1_Iterate

	dec cx

	jcxz .DONE

	jmp .HASH_BLOCK

  .DONE:

	ret

endp

; -------------------------------------------------------------------------------------

proc Compute_SHA_224 uses esi edi eax ecx

  ; process the single 512 bit (64 byte) message block

  ; this function expects the message block to have the big endian byte ordering

	stdcall Init_H_0_SHA_224

	mov ecx,[nBlocks512]

	lea esi,[MESSAGE]

  .HASH_BLOCK:

	lea edi,[Message_Schedule]

	mov dx,64

  .INIT_SCHED:

	; copy the 512 bit block to the first 512 bits of the message schedule

	mov eax,[esi]

	; big endian to little endian

	bswap eax

	mov [edi],eax

	add esi,4
	add edi,4

	sub dx,4

	cmp dx,0

	jg .INIT_SCHED

	; iterate the hash value

	stdcall SHA_224_256_Iterate

	dec cx

	jcxz .DONE

	jmp .HASH_BLOCK

  .DONE:

	ret

endp

; -------------------------------------------------------------------------------------

proc Compute_SHA_256 uses esi edi eax ecx

  ; process the single 512 bit (64 byte) message block

  ; this function expects the message block to have the big endian byte ordering

	stdcall Init_H_0_SHA_256

	mov ecx,[nBlocks512]

	lea esi,[MESSAGE]

  .HASH_BLOCK:

	lea edi,[Message_Schedule]

	mov dx,64

  .INIT_SCHED:

	; copy the 512 bit block to the first 512 bits of the message schedule

	mov eax,[esi]

	; big endian to little endian

	bswap eax

	mov [edi],eax

	add esi,4
	add edi,4

	sub dx,4

	cmp dx,0

	jg .INIT_SCHED

	; iterate the hash value

	stdcall SHA_224_256_Iterate

	dec cx

	jcxz .DONE

	jmp .HASH_BLOCK

  .DONE:

	ret

endp

; -------------------------------------------------------------------------------------

proc SHA_1_Iterate uses esi edi ebx ecx

	; prepare the message schedule

	; the first 16 words of the message schedule
	; already contain the 512 bit message block

	lea esi,[Message_Schedule]
	lea edi,[Message_Schedule]

	add esi,64
	add edi,64

	mov cx,16

  .PREPARE:

	; W[t] = ROTL(W[t-3] xor W[t-8] xor W[t-14] xor W[t-16])

	mov eax,[esi-12]
	xor eax,[esi-32]
	xor eax,[esi-56]
	xor eax,[esi-64]
	rol eax,1

	mov [edi],eax

	add esi,4
	add edi,4

	inc cx

	cmp cx,80

	jl .PREPARE

	; initialise a,b,c,d,e

	lea esi,[H_0_1]
	lea edi,[Temp_H]

	mov eax,[esi]
	mov [edi],eax

	mov eax,[esi+4]
	mov [edi+4],eax

	mov eax,[esi+8]
	mov [edi+8],eax

	mov eax,[esi+12]
	mov [edi+12],eax

	mov eax,[esi+16]
	mov [edi+16],eax

	; compute the hash

	xor ecx,ecx

	mov cx,0

	lea esi,[Temp_H]
	lea edi,[Message_Schedule]

  .HASH:

	; T = ROTL(a,5)

	mov eax,[esi]
	rol eax,5

	; T += e

	add eax,[esi+16]

	mov edx,ecx
	shl edx,2

	; T += W[t]

	add eax,[edi+edx]

  .0_19:

	cmp cx,19
	jg .20_39

	; add Kt

	add eax,0x5a827999

	; f(t) = Ch(b,c,d) = (b and c) xor (not(b) and d)

	; calc f(t)(b,c,d)

	mov ebx,[esi+4]
	and ebx,[esi+8]

	mov edx,[esi+4]
	not edx
	and edx,[esi+12]

	xor edx,ebx

	; add f(t) to T

	add eax,edx

	jmp .NEXT

  .20_39:

	cmp cx,39
	jg .40_59

	; add Kt

	add eax,0x6ed9eba1

	; f(t) = Parity(b,c,d) = b xor c xor d

	; calc f(t)(b,c,d)

	mov edx,[esi+4]
	xor edx,[esi+8]
	xor edx,[esi+12]

	; add f(t) to T

	add eax,edx

	jmp .NEXT

  .40_59:

	cmp cx,59
	jg .60_79

	; add Kt

	add eax,0x8f1bbcdc

	; f(t) = Maj(b,c,d) = (b and c) xor (b and d) xor (c and d)

	; calc f(t)(b,c,d)

	mov ebx,[esi+4]
	and ebx,[esi+8]

	mov edx,[esi+4]
	and edx,[esi+12]

	xor edx,ebx

	mov ebx,[esi+8]
	and ebx,[esi+12]

	xor edx,ebx

	; add f(t) to T

	add eax,edx

	jmp .NEXT

  .60_79:

	; add Kt

	add eax,0xca62c1d6

	; f(t) = Parity(b,c,d) = b xor c xor d

	; calc f(t)(b,c,d)

	mov edx,[esi+4]
	xor edx,[esi+8]
	xor edx,[esi+12]

	; add f(t) to T

	add eax,edx

  .NEXT:

	; e = d

	mov edx,[esi+12]
	mov [esi+16],edx

	; d = c

	mov edx,[esi+8]
	mov [esi+12],edx

	; c = ROTL(30,b)

	mov edx,[esi+4]
	rol edx,30
	mov [esi+8],edx

	; b = a

	mov edx,[esi]
	mov [esi+4],edx

	; a = T

	mov [esi],eax

	inc cx
	cmp cx,80

	jl .HASH

  .UPDATE:

	; add a,b,c,d,e to the hash

	lea esi,[Temp_H]
	lea edi,[H_0_1]

	mov eax,[edi]
	add eax,[esi]
	mov [edi],eax

	mov eax,[edi+4]
	add eax,[esi+4]
	mov [edi+4],eax

	mov eax,[edi+8]
	add eax,[esi+8]
	mov [edi+8],eax

	mov eax,[edi+12]
	add eax,[esi+12]
	mov [edi+12],eax

	mov eax,[edi+16]
	add eax,[esi+16]
	mov [edi+16],eax

	ret

endp

; -------------------------------------------------------------------------------------

proc SHA_224_256_Iterate uses esi edi ebx ecx

	locals

	  T1 dd 0
	  T2 dd 0

	endl

	; prepare the message schedule

	; the first 16 words of the message schedule
	; already contain the 512 bit message block

	lea esi,[Message_Schedule]
	lea edi,[Message_Schedule]

	add esi,64
	add edi,64

	mov cx,16

  .PREPARE:

	; W[t] = W[t-7] + W[t-16] + sigma_256_1(W[t-2]) + sigma_256_0(W[t-15])

	; W[t-7]

	mov eax,[esi-28]

	; W[t-16]

	add eax,[esi-64]

	mov [edi],eax

	; SHR(W(t-2),10)

	mov edx,[esi-8]
	shr edx,10

	mov eax,edx

	; ROTR(W(t-2),17)

	mov edx,[esi-8]
	ror edx,17

	xor eax,edx

	; ROTR(W(t-2),17)

	mov edx,[esi-8]
	ror edx,19

	xor eax,edx

	add [edi],eax

	; SHR(W(t-15),3)

	mov edx,[esi-60]
	shr edx,3

	mov eax,edx

	; ROTR(W(t-15),7)

	mov edx,[esi-60]
	ror edx,7

	xor eax,edx

	; ROTR(W(t-15),18)

	mov edx,[esi-60]
	ror edx,18

	xor eax,edx

	add [edi],eax

	add esi,4
	add edi,4

	inc cx

	cmp cx,64

	jl .PREPARE

	; initialise a,b,c,d,e,f,g,h

	lea esi,[H_0_256]
	lea edi,[Temp_H]

	mov eax,[esi]
	mov [edi],eax

	mov eax,[esi+4]
	mov [edi+4],eax

	mov eax,[esi+8]
	mov [edi+8],eax

	mov eax,[esi+12]
	mov [edi+12],eax

	mov eax,[esi+16]
	mov [edi+16],eax

	mov eax,[esi+20]
	mov [edi+20],eax

	mov eax,[esi+24]
	mov [edi+24],eax

	mov eax,[esi+28]
	mov [edi+28],eax

	; compute the hash

	xor ecx,ecx

	mov cx,0

	lea esi,[Temp_H]
	lea edi,[Message_Schedule]
	lea ebx,[SHA_224_256_Kt]

  .HASH:

	; T1 = h

	mov eax,[esi+28]

	; T1 += W[t]

	mov edx,ecx
	shl edx,2

	add eax,[edi+edx]

	; T1 += Kt

	add eax,[ebx+edx]

	mov [T1],eax

	; T1 += Ch(e,f,g) = (e and f) xor (not(e) and g)

	mov eax,[esi+16]
	and eax,[esi+20]

	mov edx,[esi+16]
	not edx
	and edx,[esi+24]

	xor eax,edx

	add [T1],eax

	; T1 += SIGMA_256_1(e) = ROTR(e,6) xor ROTR(e,11) xor ROTR(e,25)

	mov eax,[esi+16]
	ror eax,6

	mov edx,[esi+16]
	ror edx,11

	xor eax,edx

	mov edx,[esi+16]
	ror edx,25

	xor eax,edx

	add [T1],eax

	; T2 =  SIGMA_256_0(a) = ROTR(a,2) xor ROTR(a,13) xor ROTR(a,22)

	mov eax,[esi]
	ror eax,2

	mov edx,[esi]
	ror edx,13

	xor eax,edx

	mov edx,[esi]
	ror edx,22

	xor eax,edx

	mov [T2],eax

	; T2 += Maj(a,b,c) = (a and b) xor (a and c) xor (b and c)

	mov eax,[esi]
	and eax,[esi+4]

	mov edx,[esi]
	and edx,[esi+8]

	xor eax,edx

	mov edx,[esi+4]
	and edx,[esi+8]

	xor eax,edx

	add [T2],eax

	; h = g

	mov edx,[esi+24]
	mov [esi+28],edx

	; g = f

	mov edx,[esi+20]
	mov [esi+24],edx

	; f = e

	mov edx,[esi+16]
	mov [esi+20],edx

	; e = d + T1

	mov edx,[T1]
	add edx,[esi+12]
	mov [esi+16],edx

	; d = c

	mov edx,[esi+8]
	mov [esi+12],edx

	; c = b

	mov edx,[esi+4]
	mov [esi+8],edx

	; b = a

	mov edx,[esi]
	mov [esi+4],edx

	; a = T1 + T2

	mov edx,[T1]
	add edx,[T2]
	mov [esi],edx

	inc cx
	cmp cx,64

	jl .HASH

  .UPDATE:

	; add a,b,c,d,e,f,g,h to the hash

	lea esi,[Temp_H]
	lea edi,[H_0_256]

	mov eax,[edi]
	add eax,[esi]
	mov [edi],eax

	mov eax,[edi+4]
	add eax,[esi+4]
	mov [edi+4],eax

	mov eax,[edi+8]
	add eax,[esi+8]
	mov [edi+8],eax

	mov eax,[edi+12]
	add eax,[esi+12]
	mov [edi+12],eax

	mov eax,[edi+16]
	add eax,[esi+16]
	mov [edi+16],eax

	mov eax,[edi+20]
	add eax,[esi+20]
	mov [edi+20],eax

	mov eax,[edi+24]
	add eax,[esi+24]
	mov [edi+24],eax

	mov eax,[edi+28]
	add eax,[esi+28]
	mov [edi+28],eax

	ret

endp

; -------------------------------------------------------------------------------------

  ; generate the small prime

; -------------------------------------------------------------------------------------

proc ST_Random_Small_Prime

  ; generate the small random prime c0 (of length less than 33 bits)

	locals

	  length dw 0

	  l_count dw 0

	endl

	; length is passed in AX (less than 33 bits)

	cmp ax,33

	jge .FAILURE

	cmp ax,1

	jle .FAILURE

	; store the length

	mov [length],ax

  .HASH:

	lea esi,[HashInteger]

	lea edi,[Prime_c0]

	cmp [DSA_N],160

	je .SHA_1

	cmp [DSA_N],224

	je .SHA_224

	cmp [DSA_N],256

	je .SHA_256

	jmp .FAILURE

  .SHA_1:

	; c = Hash(Prime_Seed) xor Hash(Prime_Seed + 1)

	stdcall HashSeed_SHA_1

	mov eax,[esi]

	mov [edi],eax

	stdcall IncrementSeed

	stdcall HashSeed_SHA_1

	mov eax,[esi]

	xor [edi],eax

	jmp .ADJUST_C

  .SHA_224:

	; c = Hash(Prime_Seed) xor Hash(Prime_Seed + 1)

	stdcall HashSeed_SHA_224

	mov eax,[esi]

	mov [edi],eax

	stdcall IncrementSeed

	stdcall HashSeed_SHA_224

	mov eax,[esi]

	xor [edi],eax

	jmp .ADJUST_C

  .SHA_256:

	; c = Hash(Prime_Seed) xor Hash(Prime_Seed + 1)

	stdcall HashSeed_SHA_256

	mov eax,[esi]

	mov [edi],eax

	stdcall IncrementSeed

	stdcall HashSeed_SHA_256

	mov eax,[esi]

	xor [edi],eax

  .ADJUST_C:

	; c = 2^(length-1) + c mod 2^(length-1)

	; 2^(length-1) = shl(1,length-1)

	; c mod 2^(length-1) = c and (shl(1,length-1) - 1)

	mov cx,[length]

	; CL = length - 1

	dec cx

	; EAX = 2^(length-1)

	mov eax,1

	shl eax,cl

	dec eax

	and [edi],eax

	inc eax

	or [edi],eax

	; set c to the least odd integer gte c

	or [edi],dword 1

	; increment counters and prime seed

	inc word [l_count]

	inc word [Counter]

	stdcall IncrementSeed

	stdcall TestSmallPrime

	cmp al,1

	je .SUCCESS

	; check: local counter greater than (4 x length)

	mov ax,[length]

	shl ax,2

	cmp ax,[l_count]

	jle .FAILURE

	jmp .HASH

  .SUCCESS:

	; copy to Last_c0

	lea edi,[Last_c0]

	stdcall ZeroHexBuffer

	mov eax,[Prime_c0]

	mov [edi],eax

	mov eax,1

	jmp .DONE

  .FAILURE:

	xor eax,eax

  .DONE:

	ret

endp

; -------------------------------------------------------------------------------------

  ; sieve to test candidate small primes

; -------------------------------------------------------------------------------------

proc CalcSieve uses esi edi ebx ecx

	lea esi,[FactorBase]

	lea edi,[FactorBase]

	xor ecx,ecx

  .CLEAR:

	mov [edi+ecx],byte 0

	inc cx

	cmp cx,4096

	jl .CLEAR

	; Factor Base array: 4096 bytes = 32768 bits

	; all the odd numbers from 1 to 65535

	; bit_index = 0 to 32767

	; 1 at bit_index 0 divides everything:

	mov [edi],byte 1

	; EBX = bit_index

	; number = 1 + (2 x bit_index)

	xor ebx,ebx

	; start at bit_index = 1 (3)

	inc ebx

  .SIEVE:

	mov edx,ebx

	; EAX = 1 + (bit_index x 2)

	mov eax,ebx

	shl eax,1

	inc eax

  .SET_1:

	; mark as composite by setting the bit to 1

	add edx,eax

	cmp edx,32767

	jg .NEXT

	; EDX gives the bit index, set the bit at this index to 1

	; get the byte index

	mov ecx,edx

	shr ecx,3

	mov edi,esi

	add edi,ecx

	; get the bit index

	mov ecx,edx

	and ecx,7

	mov ch,1

	shl ch,cl

	; set the bit

	or [edi],ch

	jmp .SET_1

  .NEXT:

	inc ebx

	cmp ebx,32767

	jl .SIEVE

	ret

endp

; -------------------------------------------------------------------------------------

proc TestSmallPrime uses esi edi ebx ecx

  ; test the candidate value c0 for primality

  ; result returned in AL

	locals

	  bit db 0

	  bit_index dw 0

	endl

	; divide by each prime number in the FactorBase array

	lea esi,[FactorBase]

	xor ecx,ecx

  .NEXT:

	; get the byte

	mov dl,[esi+ecx]

  .FIND:

	; find the next prime number in FactorBase

	; test each bit in the byte

	shr byte dl,1

	jc .NEXT_BIT

	; the bit is 0, so get the number

	; number = 1 + (2 x bit_index)

	; put the number into BX

	mov bx,[bit_index]

	shl bx,1

	inc bx

  .TEST:

	mov eax,[Prime_c0]

	; calculate EAX mod BX

	stdcall Mod32

	; remainder is in EAX, candidate not a prime if 0

	cmp eax,0

	je .NOT_PRIME

  .NEXT_BIT:

	; get the next bit

	inc word [bit_index]

	inc byte [bit]

	cmp [bit],8

	jl .FIND

  .NEXT_BYTE:

	mov [bit],byte 0

	inc cx

	cmp cx,4096

	jl .NEXT

	; candidate is a prime

	mov eax,1

	jmp .DONE

  .NOT_PRIME:

	xor eax,eax

  .DONE:

	ret

endp

; -------------------------------------------------------------------------------------

proc Mod32 uses edx

  ; compute EAX mod BX

  ; result returned in EAX

	mov dx,bx

	; make sure upper bytes of EBX are zero

	xor ebx,ebx

	mov bx,dx

	xor edx,edx

	; divide EDX:EAX by EBX

	div ebx

	; remainder in EDX

	mov eax,edx

	ret

endp

; -------------------------------------------------------------------------------------

proc HexToDec uses ecx edx

  ; convert the hex number in AX to a decimal string

  ; EDI is the destination string

  ; EDI is set outside of this procedure

	locals

	  power db 4

	  digit db 0

	endl

	; the largest number that AX can hold is 65536

	; so start with 10 to the power of 4

  .CONVERT:

	mov cl,[power]

	stdcall Power_of_Ten

	mov [digit],byte 0

  .SUB:

	; subtract powers of ten to get the left most decimal digit

	cmp ax,dx

	jl .NEXT

	sub ax,dx

	inc [digit]

	jmp .SUB

  .NEXT:

	; add the digit to the output string

	mov bl,[digit]

	mov [edi],bl

	add [edi],byte 48

	inc edi

	dec [power]

	cmp [power],0

	jge .CONVERT

	mov [edi],byte 32

	inc edi

	ret

endp

; -------------------------------------------------------------------------------------

proc Power_of_Ten

  ; the exponent is passed in CL

  ; the result is returned in EDX

  ; EDX = 10 ^ CL

	mov edx,1

  .x10:

	cmp cl,0

	je .DONE

	; multiply EDX by 10

	mov ebx,edx

	shl edx,3

	shl ebx,1

	add edx,ebx

	dec cl

	jmp .x10

  .DONE:

	ret

endp

; -------------------------------------------------------------------------------------

proc StartOver

  ; reset everything

	mov [Qgen_counter],word 0
	mov [P0gen_counter],word 0
	mov [Pgen_counter],word 0

	mov [Prime_c],dword 0
	mov [Prime_c0],dword 0

	mov dx,32

	lea edi,[First_Seed]
	stdcall ZeroBuffer

	lea edi,[Prime_Seed]
	stdcall ZeroBuffer

	lea edi,[Q_Seed]
	stdcall ZeroBuffer

	lea edi,[P0_Seed]
	stdcall ZeroBuffer

	lea edi,[P_Seed]
	stdcall ZeroBuffer

	lea edi,[Prime_Q]
	stdcall ZeroBuffer

	mov dx,200

	lea edi,[Prime_P0]
	stdcall ZeroBuffer

	lea edi,[Candidate]
	stdcall ZeroHexBuffer

	lea edi,[First_Candidate]
	stdcall ZeroHexBuffer

	lea edi,[Last_x]
	stdcall ZeroHexBuffer

	lea edi,[Last_c0]
	stdcall ZeroHexBuffer

	lea edi,[Current_t]
	stdcall ZeroHexBuffer

	lea edi,[Current_z]
	stdcall ZeroHexBuffer

	lea edi,[Prime_P]
	stdcall ZeroHexBuffer

	ret

endp

; -------------------------------------------------------------------------------------

  ; miscellaneous functions

; -------------------------------------------------------------------------------------


proc IsShiftBfZero uses edi

  ; is ShiftBf zero ?

	lea edi,[ShiftBf]

	stdcall IsDblHexZero

	ret

endp

; -------------------------------------------------------------------------------------

proc Update_Last_c0 uses esi edi ecx

  ; update Last_c0 from the candidate prime

	lea esi,[Candidate]

	lea edi,[Last_c0]

	xor ecx,ecx

  .COPY:

	mov dl,[esi+ecx]

	mov [edi+ecx],dl

	inc cx

	cmp cx,HEX_LEN

	jl .COPY

	ret

endp

; -------------------------------------------------------------------------------------

proc SaveFirstCandidate uses esi edi ecx

  ; save the first candidate

	lea esi,[Candidate]

	lea edi,[First_Candidate]

	xor ecx,ecx

  .COPY:

	mov dl,[esi+ecx]

	mov [edi+ecx],dl

	inc cx

	cmp cx,HEX_LEN

	jl .COPY

	ret

endp

; -------------------------------------------------------------------------------------

proc IsPrimeQZero uses edi ecx

  ; is the prime q zero ?

	lea edi,[Prime_Q]

	xor ecx,ecx

  .TEST:

	cmp [edi+ecx],byte 0

	jne .NON_ZERO

	inc cx

	cmp cx,32

	jl .TEST

	; q is zero if the loop exits here

	mov al,1

	jmp .DONE

  .NON_ZERO:

	xor eax,eax

  .DONE:

	ret

endp

; -------------------------------------------------------------------------------------

proc IsPrimeP0Zero uses edi ecx

  ; is the prime p0 zero ?

	lea edi,[Prime_P0]

	xor ecx,ecx

  .TEST:

	cmp [edi+ecx],byte 0

	jne .NON_ZERO

	inc cx

	cmp cx,200

	jl .TEST

	; p0 is zero if the loop exits here

	mov al,1

	jmp .DONE

  .NON_ZERO:

	xor eax,eax

  .DONE:

	ret

endp

; -------------------------------------------------------------------------------------

proc ClearTempBuffer uses edi

  ; clear TempBf

	lea edi,[TempBf]

	stdcall ZeroHexBuffer

	ret

endp

; -------------------------------------------------------------------------------------

proc CopyToTempBuffer uses edi ecx

  ; copy from ESI to the temp buffer

  ; ESI is not modified

	lea edi,[TempBf]

	xor ecx,ecx

  .COPY:

	mov al,[esi+ecx]

	mov [edi+ecx],al

	inc cx

	cmp cx,HEX_LEN

	jl .COPY

	ret

endp

; -------------------------------------------------------------------------------------

proc CopyFromTempBuffer uses esi ecx

  ; copy to EDI from the temp buffer

  ; EDI is not modified

	lea esi,[TempBf]

	xor ecx,ecx

  .COPY:

	mov al,[esi+ecx]

	mov [edi+ecx],al

	inc cx

	cmp cx,HEX_LEN

	jl .COPY

	ret

endp

; -------------------------------------------------------------------------------------

proc ClearCandidate uses edi

  ; clear the candidate prime

	lea edi,[Candidate]

	stdcall ZeroHexBuffer

	ret

endp

; -------------------------------------------------------------------------------------

proc CopyQToTempBf uses esi edi ecx

  ; copy q to TempBf

	lea esi,[Prime_Q]

	lea edi,[TempBf]

	stdcall ZeroHexBuffer

	xor ecx,ecx

  .COPY:

	mov dl,[esi+ecx]

	mov [edi+ecx],dl

	inc cx

	cmp cx,32

	jl .COPY

	ret

endp

; -------------------------------------------------------------------------------------

proc CopyP0ToExpBf uses esi edi ecx

  ; copy p0 to ExpBf

	lea esi,[Prime_P0]

	lea edi,[ExpBf]

	stdcall ZeroHexBuffer

	xor ecx,ecx

  .COPY:

	mov dl,[esi+ecx]

	mov [edi+ecx],dl

	inc cx

	cmp cx,200

	jl .COPY

	ret

endp

; -------------------------------------------------------------------------------------

proc CopyP0ToShiftBf uses esi edi ecx

  ; copy p0 to ShiftBf

	lea esi,[Prime_P0]

	lea edi,[ShiftBf]

	stdcall ZeroDoubleHexBuffer

	xor ecx,ecx

  .COPY:

	mov dl,[esi+ecx]

	mov [edi+ecx],dl

	inc cx

	cmp cx,200

	jl .COPY

	ret

endp

; -------------------------------------------------------------------------------------

proc CopyToBaseBf uses edi ecx

  ; copy ESI to BaseBf

  ; ESI is set outside of this procedure

	lea edi,[BaseBf]

	xor ecx,ecx

  .COPY:

	mov dl,[esi+ecx]

	mov [edi+ecx],dl

	inc cx

	cmp cx,HEX_LEN

	jl .COPY

	ret

endp

; -------------------------------------------------------------------------------------

proc CopyToExpBf uses edi ecx

  ; copy ESI to ExpBf

  ; ESI is set outside of this procedure

	lea edi,[ExpBf]

	xor ecx,ecx

  .COPY:

	mov dl,[esi+ecx]

	mov [edi+ecx],dl

	inc cx

	cmp cx,HEX_LEN

	jl .COPY

	ret

endp

; -------------------------------------------------------------------------------------

proc CopyToModBf uses edi ecx

  ; copy ESI to ModBf

  ; ESI is set outside of this procedure

	lea edi,[ModBf]

	xor ecx,ecx

  .COPY:

	mov dl,[esi+ecx]

	mov [edi+ecx],dl

	inc cx

	cmp cx,HEX_LEN

	jl .COPY

	ret

endp

; -------------------------------------------------------------------------------------

proc XchgProductBfShiftBf uses esi edi ecx

  ; swap the values in ProductBf and ShiftBf

	lea esi,[ProductBf]

	lea edi,[ShiftBf]

	xor ecx,ecx

  .SWAP:

	mov dl,[esi+ecx]

	mov bl,[edi+ecx]

	mov [edi+ecx],dl

	mov [esi+ecx],bl

	inc cx

	cmp cx,DBL_HEX_LEN

	jl .SWAP

	ret

endp

; -------------------------------------------------------------------------------------

proc IsExpBfZero uses edi

  ; is ExpBf equal to zero ?

	lea edi,[ExpBf]

	stdcall IsHexZero

	ret

endp

; -------------------------------------------------------------------------------------

proc IsModBfZero uses edi

  ; is ModBf equal to zero ?

	lea edi,[ModBf]

	stdcall IsHexZero

	ret

endp

; -------------------------------------------------------------------------------------

proc IsProductBfZero uses edi

  ; is the hex value in ProductBf zero ?

	lea edi,[ProductBf]

	stdcall IsDblHexZero

	ret

endp

; -------------------------------------------------------------------------------------

proc CopyProductBfToShiftBf uses esi edi ecx

  ; copy ProductBf to ShiftBf

	lea esi,[ProductBf]

	lea edi,[ShiftBf]

	xor ecx,ecx

  .COPY:

	mov dl,[esi+ecx]

	mov [edi+ecx],dl

	inc cx

	cmp cx,DBL_HEX_LEN

	jl .COPY

	ret

endp

; -------------------------------------------------------------------------------------

proc CopyProductBfToTempBf uses esi edi ecx

  ; copy ProductBf to TempBf

	lea esi,[ProductBf]

	lea edi,[TempBf]

	xor ecx,ecx

  .COPY:

	mov dl,[esi+ecx]

	mov [edi+ecx],dl

	inc cx

	cmp cx,HEX_LEN

	jl .COPY

	ret

endp

; -------------------------------------------------------------------------------------

proc ZeroQuotientBf uses edi

  ; zero the quotient buffer

	lea edi,[QuotientBf]

	stdcall ZeroHexBuffer

	ret

endp

; -------------------------------------------------------------------------------------

proc ZeroShiftBf uses edi

  ; zero the shift buffer

	lea edi,[ShiftBf]

	stdcall ZeroDoubleHexBuffer

	ret

endp

; -------------------------------------------------------------------------------------

proc CopyModBfToShiftBf uses esi

  ; copy ModBf to ShiftBf

	lea esi,[ModBf]

	stdcall CopyToShiftBf

	ret

endp

; -------------------------------------------------------------------------------------

proc CopyBaseBfToProductBf uses esi

  ; copy BaseBf to ProductBf

	lea esi,[BaseBf]

	stdcall CopyToProductBf

	ret

endp

; -------------------------------------------------------------------------------------

proc CopyToShiftBf uses edi ecx

  ; copy ESI to ShiftBf

  ; ESI is of length HEX_LEN

  ; ESI is set outside of this procedure

	lea edi,[ShiftBf]

	xor ecx,ecx

  .COPY:

	mov dl,[esi+ecx]

	mov [edi+ecx],dl

	inc cx

	cmp cx,HEX_LEN

	jl .COPY

  .ZERO:

	; zero the upper bytes of ShiftBf

	mov [edi+ecx],byte 0

	inc cx

	cmp cx,DBL_HEX_LEN

	jl .ZERO

	ret

endp

; -------------------------------------------------------------------------------------

proc ZeroProductBf uses edi

  ; zero the product buffer

	lea edi,[ProductBf]

	stdcall ZeroDoubleHexBuffer

	ret

endp

; -------------------------------------------------------------------------------------

proc CopyToProductBf uses edi ecx

  ; copy ESI to ProductBf

  ; ESI is of length HEX_LEN

  ; ESI is set outside of this procedure

	lea edi,[ProductBf]

	xor ecx,ecx

  .COPY:

	mov dl,[esi+ecx]

	mov [edi+ecx],dl

	inc cx

	cmp cx,HEX_LEN

	jl .COPY

  .ZERO:

	; zero the upper bytes of ProductBf

	mov [edi+ecx],byte 0

	inc cx

	cmp cx,DBL_HEX_LEN

	jl .ZERO

	ret

endp

; -------------------------------------------------------------------------------------

proc CopyFromProductBf uses esi ecx

  ; copy from ProductBf to EDI

  ; EDI is of length HEX_LEN

  ; EDI is set outside of this procedure

	lea esi,[ProductBf]

	xor ecx,ecx

  .COPY:

	mov dl,[esi+ecx]

	mov [edi+ecx],dl

	inc cx

	cmp cx,HEX_LEN

	jl .COPY

	ret

endp

; -------------------------------------------------------------------------------------

proc IsProductBfLessThanShiftBf uses esi edi ecx

  ; is ProductBf less than ShiftBf

	lea esi,[ShiftBf]

	lea edi,[ProductBf]

	xor ecx,ecx

	mov cx,DBL_HEX_LEN-1

  .COMPARE:

	mov dl,[esi+ecx]

	cmp [edi+ecx],dl

	jb .LT

	ja .GTE

	dec cx

	cmp cx,0

	jge .COMPARE

	; if the loop exits here, the values are equal

  .GTE:

	xor eax,eax

	jmp .DONE

  .LT:

	mov al,1

  .DONE:

	ret

endp

; -------------------------------------------------------------------------------------

proc ZeroBuffer uses ecx

  ; clear the buffer (EDI)

  ; size passed in DX

	xor ecx,ecx

  .CLEAR:

	mov [edi+ecx],byte 0

	inc cx

	cmp cx,dx

	jl .CLEAR

	ret

endp

; -------------------------------------------------------------------------------------

proc ZeroHexBuffer uses ecx

  ; zero the hex value in memory

  ; EDI is set outside of this procedure

	xor ecx,ecx

  .ZERO:

	mov [edi+ecx],byte 0

	inc cx

	cmp cx,HEX_LEN

	jl .ZERO

	ret

endp

; -------------------------------------------------------------------------------------

proc ZeroDoubleHexBuffer uses ecx

  ; zero the double hex value in memory

  ; EDI is set outside of this procedure

	xor ecx,ecx

  .ZERO:

	mov [edi+ecx],byte 0

	inc cx

	cmp cx,DBL_HEX_LEN

	jl .ZERO

	ret

endp

; -------------------------------------------------------------------------------------

proc AddStringToEDI

  ; print a string to the EDI buffer

  ; ESI and EDI are set outside of this procedure

  .PRINT:

	mov dl,[esi]

	cmp dl,0

	je .DONE

	mov [edi],dl

	inc esi
	inc edi

	jmp .PRINT

  .DONE:

	ret

endp

; -------------------------------------------------------------------------------------

section '.idata' import data readable writeable

  library kernel,'KERNEL32.DLL',\
	  user,'USER32.DLL'

  import kernel,\
	 GetModuleHandle,'GetModuleHandleA',\
	 ExitProcess,'ExitProcess'

  import user,\
	 DialogBoxParam,'DialogBoxParamA',\ 
	 SetDlgItemText,'SetDlgItemTextA',\
	 GetDlgItemText,'GetDlgItemTextA',\
	 EndDialog,'EndDialog'

; -------------------------------------------------------------------------------------

section '.data' readable writeable

  ShiftBf rb DBL_HEX_LEN
  ProductBf rb DBL_HEX_LEN
  QuotientBf rb HEX_LEN

  TempBf rb HEX_LEN

  BaseBf rb HEX_LEN
  ExpBf rb HEX_LEN
  ModBf rb HEX_LEN

  DSA_N dw 160
  DSA_L dw 1024

  First_Seed rb 32
  Prime_Seed rb 32

  Q_Seed rb 32
  P0_Seed rb 32
  P_Seed rb 32

  BitString rb 32
  HashInteger rb 32

  Prime_c dd 0
  Prime_c0 dd 0

  Candidate rb 400
  First_Candidate rb 400
  Last_c0 rb 400
  Last_x rb 400
  Current_t rb 400
  Current_z rb 400

  qp0x2 rb 400

  Prime_Q rb 32
  Prime_P0 rb 200
  Prime_P rb 400

  bfDisplay rb BF_DISP_LEN

  Qgen_counter dw 0
  P0gen_counter dw 0
  Pgen_counter dw 0

  Counter dw 0

  nErrorCode dw 0

  bToggle db 0

; -------------------------------------------------------------------------------------

section '.hash' readable writeable

  MESSAGE rb 64

  BitLength dd 0

  nBlocks512 dd 0

  H_0_1 rd 5

  H_0_256 rd 8

  Message_Schedule rd 160

  Temp_H rd 8

  SHA_224_256_Kt dd 0x428a2f98,0x71374491,0xb5c0fbcf,0xe9b5dba5,\
		    0x3956c25b,0x59f111f1,0x923f82a4,0xab1c5ed5,\
		    0xd807aa98,0x12835b01,0x243185be,0x550c7dc3,\
		    0x72be5d74,0x80deb1fe,0x9bdc06a7,0xc19bf174,\
		    0xe49b69c1,0xefbe4786,0x0fc19dc6,0x240ca1cc,\
		    0x2de92c6f,0x4a7484aa,0x5cb0a9dc,0x76f988da,\
		    0x983e5152,0xa831c66d,0xb00327c8,0xbf597fc7,\
		    0xc6e00bf3,0xd5a79147,0x06ca6351,0x14292967,\
		    0x27b70a85,0x2e1b2138,0x4d2c6dfc,0x53380d13,\
		    0x650a7354,0x766a0abb,0x81c2c92e,0x92722c85,\
		    0xa2bfe8a1,0xa81a664b,0xc24b8b70,0xc76c51a3,\
		    0xd192e819,0xd6990624,0xf40e3585,0x106aa070,\
		    0x19a4c116,0x1e376c08,0x2748774c,0x34b0bcb5,\
		    0x391c0cb3,0x4ed8aa4a,0x5b9cca4f,0x682e6ff3,\
		    0x748f82ee,0x78a5636f,0x84c87814,0x8cc70208,\
		    0x90befffa,0xa4506ceb,0xbef9a3f7,0xc67178f2

  FactorBase rb 4096

  szErrMessage_0 db "Unknown Error.",0
  szErrMessage_1 db "Failed to generate the prime c0.",0
  szErrMessage_2 db "Failed to generate the prime q.",0
  szErrMessage_3 db "The prime q has not been generated.",0
  szErrMessage_4 db "Failed to generate the prime p0.",0
  szErrMessage_5 db "The prime p0 has not been generated.",0
  szErrMessage_6 db "Failed to generate the prime p.",0

  szQFoundMsg db "THE PRIME q HAS BEEN FOUND:",0
  szP0FoundMsg db "THE PRIME p0 HAS BEEN FOUND:",0
  szPFoundMsg db "THE PRIME p HAS BEEN FOUND:",0
  szPQFoundMsg db "THE PRIMES p AND q HAVE BEEN FOUND:",0

; -------------------------------------------------------------------------------------

section '.rc' resource data readable

  directory RT_DIALOG,dialogs

  resource dialogs,IDD_THE_DIALOG,LANG_ENGLISH+SUBLANG_DEFAULT,the_dialog

  dialog the_dialog,\
  'DSA Primes',20,50,406,328,\
  DS_MODALFRAME+WS_MINIMIZEBOX+WS_POPUP+WS_VISIBLE+WS_CAPTION+WS_SYSMENU,\
  0,0,"Lucida Console",11

  dialogitem 'EDIT',0,IDC_OUTPUT,11,14,309,255,ES_MULTILINE+ES_AUTOVSCROLL+ES_WANTRETURN+WS_VSCROLL+WS_BORDER+WS_VISIBLE,0
  dialogitem 'EDIT',0,IDC_FIRST_SEED,10,298,320,14,ES_AUTOHSCROLL+WS_BORDER+WS_VISIBLE,0

  dialogitem 'BUTTON',"Output",-1,6,3,320,276,BS_GROUPBOX+WS_VISIBLE,0
  dialogitem 'BUTTON',"Action",-1,332,3,68,124,BS_GROUPBOX+WS_VISIBLE,0
  dialogitem 'BUTTON',"First Seed",-1,6,284,394,37,BS_GROUPBOX+WS_VISIBLE,0
  dialogitem 'BUTTON',"Select L, N",-1,332,132,68,147,BS_GROUPBOX+WS_VISIBLE,0

  dialogitem 'BUTTON',"Generate q",IDC_GEN_Q,336,14,60,16,BS_PUSHBUTTON+WS_VISIBLE,0
  dialogitem 'BUTTON',"Generate p0",IDC_GEN_P0,336,32,60,16,BS_PUSHBUTTON+WS_VISIBLE,0
  dialogitem 'BUTTON',"Generate p",IDC_GEN_P,336,50,60,16,BS_PUSHBUTTON+WS_VISIBLE,0
  dialogitem 'BUTTON',"Generate All",IDC_GEN_ALL,336,68,60,16,BS_PUSHBUTTON+WS_VISIBLE,0
  dialogitem 'BUTTON',"Start Over",IDC_START_OVER,336,86,60,16,BS_PUSHBUTTON+WS_VISIBLE,0
  dialogitem 'BUTTON',"Toggle View",IDC_TOGGLE,336,104,60,16,BS_PUSHBUTTON+WS_VISIBLE,0

  dialogitem 'BUTTON',"Capture",IDC_CAPTURE,336,297,60,16,BS_PUSHBUTTON+WS_VISIBLE,0

  dialogitem 'BUTTON',"1024,160",IDC_BTN_1024_160,336,144,60,16,BS_PUSHBUTTON+WS_VISIBLE,0
  dialogitem 'BUTTON',"2048,224",IDC_BTN_2048_224,336,162,60,16,BS_PUSHBUTTON+WS_VISIBLE,0
  dialogitem 'BUTTON',"2048,256",IDC_BTN_2048_256,336,180,60,16,BS_PUSHBUTTON+WS_VISIBLE,0
  dialogitem 'BUTTON',"3072,256",IDC_BTN_3072_256,336,198,60,16,BS_PUSHBUTTON+WS_VISIBLE,0

  enddialog

; -------------------------------------------------------------------------------------
Advertisements

Create a free website or blog at WordPress.com.

%d bloggers like this: