|
|
|
|
#pike __REAL_VERSION__ |
#pragma strict_types |
#require constant(Crypto.Hash) |
|
inherit Crypto.Sign; |
|
|
string(8bit) name() { return "DSA"; } |
|
class State { |
inherit ::this_program; |
|
protected string _sprintf(int t) |
{ |
return t=='O' && sprintf("%O(%d,%d)", this_program, p->size(), q->size()); |
} |
|
|
|
|
|
protected Gmp.mpz p; |
protected Gmp.mpz q; |
protected Gmp.mpz g; |
|
protected Gmp.mpz y; |
protected Gmp.mpz x; |
|
protected function(int(0..):string(8bit)) random = random_string; |
|
Gmp.mpz get_p() { return p; } |
Gmp.mpz get_q() { return q; } |
Gmp.mpz get_g() { return g; } |
Gmp.mpz get_y() { return y; } |
Gmp.mpz get_x() { return x; } |
|
|
|
this_program set_random(function(int(0..):string(8bit)) r) |
{ |
random = r; |
return this; |
} |
|
|
string(8bit) name() { return "DSA"; } |
|
|
|
|
|
|
|
|
|
|
|
|
|
|
this_program set_public_key(Gmp.mpz modulo, Gmp.mpz order, |
Gmp.mpz generator, Gmp.mpz key) |
{ |
p = modulo; |
q = order; |
g = generator; |
y = key; |
return this; |
} |
|
|
|
|
|
|
|
variant this_program set_public_key(.DH.Parameters params, |
Gmp.mpz key) |
{ |
p = params->p; |
q = params->q; |
g = params->g; |
y = key; |
return this; |
} |
|
|
|
int(0..1) public_key_equal(this_program dsa) |
{ |
return (p == dsa->get_p()) && (q == dsa->get_q()) && |
(g == dsa->get_g()) && (y == dsa->get_y()); |
} |
|
|
protected int(0..1) _equal(mixed other) |
{ |
if (!objectp(other) || (object_program(other) != object_program(this)) || |
!public_key_equal([object(this_program)]other)) { |
return 0; |
} |
this_program dsa = [object(this_program)]other; |
return x == dsa->get_x(); |
} |
|
|
this_program set_private_key(Gmp.mpz secret) |
{ |
x = secret; |
return this; |
} |
|
|
|
|
|
#if !constant(Nettle.dsa_generate_keypair) |
|
#define SEED_LENGTH 20 |
protected string(8bit) nist_hash(Gmp.mpz x) |
{ |
string(8bit) s = x->digits(256); |
return .SHA1.hash(s[sizeof(s) - SEED_LENGTH..]); |
} |
|
|
|
protected array(Gmp.mpz) nist_primes(int l) |
{ |
if ( (l < 0) || (l > 8) ) |
error( "Unsupported key size.\n" ); |
|
int L = 512 + 64 * l; |
|
int n = (L-1) / 160; |
|
|
for (;;) |
{ |
|
string(8bit) seed = random(SEED_LENGTH); |
Gmp.mpz s = Gmp.mpz(seed, 256); |
|
string(8bit) h = [string(8bit)] |
(nist_hash(s) ^ nist_hash( [object(Gmp.mpz)](s + 1) )); |
|
h = sprintf("%c%s%c", |
[int(8bit)](h[0] | 0x80), |
h[1..<1], |
[int(8bit)](h[-1] | 1)); |
|
Gmp.mpz q = Gmp.mpz(h, 256); |
|
if (!q->probably_prime_p()) |
continue; |
|
|
|
int i, j; |
|
for (i = 0, j = 2; i < 4096; i++, j += n+1) |
{ |
string(8bit) buffer = ""; |
int k; |
|
for (k = 0; k<= n; k++) |
buffer = nist_hash( [object(Gmp.mpz)](s + j + k) ) + buffer; |
|
buffer = buffer[sizeof(buffer) - L/8 ..]; |
buffer[0] = [int(8bit)](buffer[0] | 0x80); |
|
Gmp.mpz p = Gmp.mpz(buffer, 256); |
|
p -= p % (2 * q) - 1; |
|
if (p->probably_prime_p()) |
{ |
|
return ({ p, q }); |
} |
} |
} |
} |
|
protected Gmp.mpz find_generator(Gmp.mpz p, Gmp.mpz q) |
{ |
Gmp.mpz e = [object(Gmp.mpz)]((p - 1) / q); |
Gmp.mpz g; |
|
do { |
|
g = ([object(Gmp.mpz)](random_number( [object(Gmp.mpz)](p-3) ) + 2)) |
|
->powm(e, p); |
} while (g == 1); |
|
return g; |
} |
|
|
|
protected void generate_parameters(int bits) |
{ |
if (!bits || bits % 64) |
error( "Unsupported key size.\n" ); |
|
[p, q] = nist_primes(bits / 64 - 8); |
|
if (p % q != 1) |
error( "Internal error.\n" ); |
|
if (q->size() != 160) |
error( "Internal error.\n" ); |
|
g = find_generator(p, q); |
|
if ( (g == 1) || (g->powm(q, p) != 1)) |
error( "Internal error.\n" ); |
} |
|
variant this_program generate_key(int p_bits, int q_bits) |
{ |
if(q_bits!=160) |
error("Only 1024/160 supported with Nettle version < 2.0\n"); |
generate_parameters(1024); |
return generate_key(); |
} |
|
#else // !constant(Nettle.dsa_generate_keypair) |
|
|
|
|
variant this_program generate_key(int p_bits, int q_bits) |
{ |
[ p, q, g, y, x ] = Nettle.dsa_generate_keypair(p_bits, q_bits, random); |
return this; |
} |
|
#endif |
|
|
|
variant this_program generate_key(.DH.Parameters params) |
{ |
p = params->p; |
g = params->g; |
q = params->q; |
|
[y, x] = params->generate_keypair(random); |
return this; |
} |
|
|
|
|
variant this_program generate_key() |
{ |
|
if(!p || !q || !g) error("Public parameters not set..\n"); |
x = [object(Gmp.mpz)](random_number( [object(Gmp.mpz)](q-2) ) + 2); |
y = g->powm(x, p); |
|
return this; |
} |
|
|
|
|
|
|
#define Sequence Standards.ASN1.Types.Sequence |
#define Integer Standards.ASN1.Types.Integer |
#define BitString Standards.ASN1.Types.BitString |
|
|
|
Sequence pkcs_algorithm_identifier() |
{ |
return |
Sequence( ({ Standards.PKCS.Identifiers.dsa_id, |
Sequence( ({ Integer(get_p()), |
Integer(get_q()), |
Integer(get_g()) |
}) ) |
}) ); |
} |
|
|
|
|
Sequence pkcs_signature_algorithm_id(.Hash hash) |
{ |
switch(hash->name()) |
{ |
case "sha1": |
return Sequence( ({ Standards.PKCS.Identifiers.dsa_sha_id }) ); |
break; |
case "sha224": |
return Sequence( ({ Standards.PKCS.Identifiers.dsa_sha224_id }) ); |
break; |
case "sha256": |
return Sequence( ({ Standards.PKCS.Identifiers.dsa_sha256_id }) ); |
break; |
} |
return 0; |
} |
|
|
|
Sequence pkcs_public_key() |
{ |
return Sequence(({ |
pkcs_algorithm_identifier(), |
BitString(Integer(get_y())->get_der()), |
})); |
} |
|
#undef BitString |
#undef Integer |
#undef Sequence |
|
|
|
string(8bit) pkcs_sign(string(8bit) message, .Hash h) |
{ |
array sign = map(raw_sign(hash(message, h)), Standards.ASN1.Types.Integer); |
return Standards.ASN1.Types.Sequence(sign)->get_der(); |
} |
|
|
|
#define Object Standards.ASN1.Types.Object |
|
|
|
int(0..1) pkcs_verify(string(8bit) message, .Hash h, string(8bit) sign) |
{ |
Object a = Standards.ASN1.Decode.secure_der_decode(sign); |
|
|
|
if (!a |
|| (a->type_name != "SEQUENCE") |
|| (sizeof([array]a->elements) != 2) |
|| (sizeof( ([array(object(Object))]a->elements)->type_name - |
({ "INTEGER" })))) |
return 0; |
|
return raw_verify(hash(message, h), |
[object(Gmp.mpz)]([array(object(Object))]a->elements)[0]-> |
value, |
[object(Gmp.mpz)]([array(object(Object))]a->elements)[1]-> |
value); |
} |
|
#undef Object |
|
|
|
|
|
|
|
Gmp.mpz hash(string(8bit) msg, .Hash h) |
{ |
string(8bit) digest = h->hash(msg)[..q->size()/8-1]; |
return [object(Gmp.mpz)](Gmp.mpz(digest, 256) % q); |
} |
|
protected Gmp.mpz random_number(Gmp.mpz n) |
{ |
return [object(Gmp.mpz)](Gmp.mpz(random( [int(0..)](q->size() + 10 / 8)), |
256) % n); |
} |
|
protected Gmp.mpz random_exponent() |
{ |
return [object(Gmp.mpz)](random_number([object(Gmp.mpz)](q - 1)) + 1); |
} |
|
|
|
array(Gmp.mpz) raw_sign(Gmp.mpz h, void|Gmp.mpz k) |
{ |
if(!k) k = random_exponent(); |
|
Gmp.mpz r = [object(Gmp.mpz)](g->powm(k, p) % q); |
Gmp.mpz s = [object(Gmp.mpz)]((k->invert(q) * (h + x*r)) % q); |
|
return ({ r, s }); |
} |
|
|
int(0..1) raw_verify(Gmp.mpz h, Gmp.mpz r, Gmp.mpz s) |
{ |
Gmp.mpz w; |
if (catch |
{ |
w = s->invert(q); |
}) |
|
return 0; |
|
|
return r == (g->powm( [object(Gmp.mpz)](w * h % q), p) * |
y->powm( [object(Gmp.mpz)](w * r % q), p) % p) % q; |
} |
|
int(0..) key_size() |
{ |
return p->size(); |
} |
} |
|
|
protected State `()() |
{ |
return State(); |
} |
|
|