Branch: Tag:

2014-08-14

2014-08-14 14:51:36 by Henrik Grubbström (Grubba) <grubba@grubba.org>

Crypto.Sign: Added sub-class State.

This makes the Crypto.Sign API to behave closer to the other Crypto APIs.

This affects code that uses Crypto.RSA and Crypto.DSA (which now are
modules and not classes).

Crypto.ECC.Curve.ECDSA is currently unmodified.

1:    + //! The Digital Signature Algorithm DSA is part of the NIST Digital + //! Signature Standard DSS, FIPS-186 (1993). +  + #pike __REAL_VERSION__ + #pragma strict_types + #require constant(Crypto.Hash) +  + inherit Crypto.Sign; +  + //! Returns the string @expr{"DSA"@}. + 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()); + } +  + // + // --- Variables and accessors + // +  + protected Gmp.mpz p; // Modulo + protected Gmp.mpz q; // Group order + protected Gmp.mpz g; // Generator +  + protected Gmp.mpz y; // Public key + protected Gmp.mpz x; // Private key +  + protected function(int(0..):string(8bit)) random = .Random.random_string; +  + Gmp.mpz get_p() { return p; } //! Returns the DSA modulo (p). + Gmp.mpz get_q() { return q; } //! Returns the DSA group order (q). + Gmp.mpz get_g() { return g; } //! Returns the DSA generator (g). + Gmp.mpz get_y() { return y; } //! Returns the DSA public key (y). + Gmp.mpz get_x() { return x; } //! Returns the DSA private key (x). +  + //! Sets the random function, used to generate keys and parameters, to + //! the function @[r]. Default is @[Crypto.Random.random_string]. + this_program set_random(function(int(0..):string(8bit)) r) + { +  random = r; +  return this; + } +  + //! Returns the string @expr{"DSA"@}. + string(8bit) name() { return "DSA"; } +  + // + // --- Key methods + // +  + //! Sets the public key in this DSA object. + //! @param modulo + //! This is the p parameter. + //! @param order + //! This is the group order q parameter. + //! @param generator + //! This is the g parameter. + //! @param kye + //! This is the public key y parameter. + 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; + } +  + //! Compares the public key in this object with that in the provided + //! DSA object. + 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()); + } +  + //! Compares the keys of this DSA object with something other. + 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(); + } +  + //! Sets the private key, the x parameter, in this DSA object. + this_program set_private_key(Gmp.mpz secret) + { +  x = secret; +  return this; + } +  + // + // --- Key generation + // +  + #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..]); + } +  + // The (slow) NIST method of generating a DSA prime pair. Algorithm + // 4.56 of Handbook of Applied Cryptography. + 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; +  // int b = (L-1) % 160; +  +  for (;;) +  { +  /* Generate q */ +  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", h[0] | 0x80, h[1..<1], h[-1] | 1); +  +  Gmp.mpz q = Gmp.mpz(h, 256); +  +  if (!q->probably_prime_p()) +  continue; +  +  /* q is a prime, with overwelming probability. */ +  +  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()) +  { +  /* Done */ +  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 +  { +  /* A random number in { 2, 3, ... p - 2 } */ +  g = ([object(Gmp.mpz)](random_number( [object(Gmp.mpz)](p-3) ) + 2)) +  /* Exponentiate to get an element of order 1 or q */ +  ->powm(e, p); +  } +  while (g == 1); +  +  return g; + } +  + // Generate key parameters (p, q and g) using the NIST DSA prime pair + // generation algorithm. @[bits] must be multiple of 64. + 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) +  + //! Generates DSA parameters (p, q, g) and key (x, y). Depending on + //! Nettle version @[q_bits] can be 160, 224 and 256 bits. 160 works + //! for all versions. + 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 +  + //! Generates a public/private key pair. Needs the public parameters + //! p, q and g set, either through @[set_public_key] or + //! @[generate_key(int,int)]. + variant this_program generate_key() + { +  /* x in { 2, 3, ... q - 1 } */ +  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; + } +  +  + // + // --- PKCS methods + // +  + #define Sequence Standards.ASN1.Types.Sequence + #define Integer Standards.ASN1.Types.Integer + #define BitString Standards.ASN1.Types.BitString +  + //! Returns the AlgorithmIdentifier as defined in RFC5280 section + //! 4.1.1.2 including the DSA parameters. + Sequence pkcs_algorithm_identifier() + { +  return +  Sequence( ({ Standards.PKCS.Identifiers.dsa_id, +  Sequence( ({ Integer(get_p()), +  Integer(get_q()), +  Integer(get_g()) +  }) ) +  }) ); + } +  +  + //! Returns the PKCS-1 algorithm identifier for DSA and the provided + //! hash algorithm. Only @[SHA1] supported. + 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; + } +  + //! Creates a SubjectPublicKeyInfo ASN.1 sequence for the object. + //! See RFC 5280 section 4.1.2.7. + Sequence pkcs_public_key() + { +  return Sequence(({ +  pkcs_algorithm_identifier(), +  BitString(Integer(get_y())->get_der()), +  })); + } +  + #undef BitString + #undef Integer + #undef Sequence +  + //! Signs the @[message] with a PKCS-1 signature using hash algorithm + //! @[h]. + 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 +  + //! Verify PKCS-1 signature @[sign] of message @[message] using hash + //! algorithm @[h]. + int(0..1) pkcs_verify(string(8bit) message, .Hash h, string(8bit) sign) + { +  Object a = Standards.ASN1.Decode.simple_der_decode(sign); +  +  // The signature is the DER-encoded ASN.1 sequence Dss-Sig-Value +  // with the two integers r and s. See RFC 3279 section 2.2.2. +  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 +  + // + // --- Below are methods for DSA applied in other standards. + // +  +  + //! Makes a DSA hash of the messge @[msg]. + 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); + } +  + //! Sign the message @[h]. Returns the signature as two @[Gmp.mpz] + //! objects. + 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 }); + } +  + //! Verify the signature @[r],@[s] against the message @[h]. + int(0..1) raw_verify(Gmp.mpz h, Gmp.mpz r, Gmp.mpz s) + { +  Gmp.mpz w; +  if (catch +  { +  w = s->invert(q); +  }) +  /* Non-invertible */ +  return 0; +  +  /* The inner %q's are redundant, as g^q == y^q == 1 (mod p) */ +  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(); + } +  + // + // --- Deprecated stuff + // +  + //! Make a RSA ref signature of message @[msg]. + __deprecated__ string(8bit) sign_rsaref(string(8bit) msg) + { +  [Gmp.mpz r, Gmp.mpz s] = raw_sign(hash(msg, .SHA1)); +  +  return sprintf("%'\0'20s%'\0'20s", r->digits(256), s->digits(256)); + } +  + //! Verify a RSA ref signature @[s] of message @[msg]. + __deprecated__ int(0..1) verify_rsaref(string(8bit) msg, string(8bit) s) + { +  if (sizeof(s) != 40) +  return 0; +  +  return raw_verify(hash(msg, .SHA1), +  Gmp.mpz(s[..19], 256), +  Gmp.mpz(s[20..], 256)); + } +  + //! Make an SSL signature of message @[msg]. + __deprecated__ string(8bit) sign_ssl(string(8bit) msg) + { +  return pkcs_sign(msg, .SHA1); + } +  + //! Verify an SSL signature @[s] of message @[msg]. + __deprecated__ int(0..1) verify_ssl(string(8bit) msg, string(8bit) s) + { +  return pkcs_verify(msg, .SHA1, s); + } +  + } +  + //! Calling `() will return a @[State] object. + protected State `()() + { +  return State(); + }   Newline at end of file added.