3e93c42004-02-07Martin Nilsson 
b3e4a02013-10-28Martin Nilsson //! The Digital Signature Algorithm DSA is part of the NIST Digital //! Signature Standard DSS, FIPS-186 (1993).
3e93c42004-02-07Martin Nilsson #pike __REAL_VERSION__ #pragma strict_types
e1fb092014-02-14Martin Nilsson #require constant(Crypto.Hash)
b3e4a02013-10-28Martin Nilsson 
9104d02014-01-13Henrik Grubbström (Grubba) inherit Crypto.Sign;
119b8d2014-08-14Henrik Grubbström (Grubba) //! Returns the string @expr{"DSA"@}. string(8bit) name() { return "DSA"; } class State { inherit ::this_program;
4934022014-08-14Henrik Grubbström (Grubba)  protected string _sprintf(int t) { return t=='O' && sprintf("%O(%d,%d)", this_program, p->size(), q->size()); }
53d1d32014-04-26Martin Nilsson 
4934022014-08-14Henrik Grubbström (Grubba)  // // --- Variables and accessors //
3e93c42004-02-07Martin Nilsson 
010f932021-04-21Henrik Grubbström (Grubba)  protected Gmp.mpz|zero p; // Modulo protected Gmp.mpz|zero q; // Group order protected Gmp.mpz|zero g; // Generator
3e93c42004-02-07Martin Nilsson 
010f932021-04-21Henrik Grubbström (Grubba)  protected Gmp.mpz|zero y; // Public key protected Gmp.mpz|zero x; // Private key
3e93c42004-02-07Martin Nilsson 
e6ceba2016-03-14Martin Nilsson  protected function(int(0..):string(8bit)) random = random_string;
3e93c42004-02-07Martin Nilsson 
010f932021-04-21Henrik Grubbström (Grubba)  Gmp.mpz|zero get_p() { return p; } //! Returns the DSA modulo (p). Gmp.mpz|zero get_q() { return q; } //! Returns the DSA group order (q). Gmp.mpz|zero get_g() { return g; } //! Returns the DSA generator (g). Gmp.mpz|zero get_y() { return y; } //! Returns the DSA public key (y). Gmp.mpz|zero get_x() { return x; } //! Returns the DSA private key (x).
3e93c42004-02-07Martin Nilsson 
4934022014-08-14Henrik Grubbström (Grubba)  //! Sets the random function, used to generate keys and parameters, to
e6ceba2016-03-14Martin Nilsson  //! the function @[r]. Default is @[random_string].
4934022014-08-14Henrik Grubbström (Grubba)  this_program set_random(function(int(0..):string(8bit)) r) { random = r; return this; }
3e93c42004-02-07Martin Nilsson 
4934022014-08-14Henrik Grubbström (Grubba)  //! 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; }
3e93c42004-02-07Martin Nilsson 
a859062015-06-02Henrik Grubbström (Grubba)  //! Sets the public key in this DSA object. //! //! @param params //! The finite-field diffie-hellman group parameters. //! @param key //! The public key y parameter.
bbf70b2016-10-19Henrik Grubbström (Grubba)  variant this_program set_public_key(.DH.Parameters params,
14c29c2015-06-04Henrik Grubbström (Grubba)  Gmp.mpz key)
a859062015-06-02Henrik Grubbström (Grubba)  { p = params->p; q = params->q; g = params->g; y = key; return this; }
4934022014-08-14Henrik Grubbström (Grubba)  //! 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()); }
3e93c42004-02-07Martin Nilsson 
4934022014-08-14Henrik Grubbström (Grubba)  //! 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();
7c383a2014-02-22Henrik Grubbström (Grubba)  }
4934022014-08-14Henrik Grubbström (Grubba)  //! Sets the private key, the x parameter, in this DSA object. this_program set_private_key(Gmp.mpz secret) { x = secret; return this; }
3e93c42004-02-07Martin Nilsson 
4934022014-08-14Henrik Grubbström (Grubba)  // // --- Key generation //
3e93c42004-02-07Martin Nilsson 
3f6dd02013-12-02Martin Nilsson #if !constant(Nettle.dsa_generate_keypair)
3e93c42004-02-07Martin Nilsson #define SEED_LENGTH 20
4934022014-08-14Henrik Grubbström (Grubba)  protected string(8bit) nist_hash(Gmp.mpz x) { string(8bit) s = x->digits(256); return .SHA1.hash(s[sizeof(s) - SEED_LENGTH..]); }
3e93c42004-02-07Martin Nilsson 
4934022014-08-14Henrik Grubbström (Grubba)  // 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" );
3e93c42004-02-07Martin Nilsson 
4934022014-08-14Henrik Grubbström (Grubba)  int L = 512 + 64 * l;
3e93c42004-02-07Martin Nilsson 
4934022014-08-14Henrik Grubbström (Grubba)  int n = (L-1) / 160; // int b = (L-1) % 160;
3e93c42004-02-07Martin Nilsson 
4934022014-08-14Henrik Grubbström (Grubba)  for (;;) { /* Generate q */ string(8bit) seed = random(SEED_LENGTH); Gmp.mpz s = Gmp.mpz(seed, 256);
3e93c42004-02-07Martin Nilsson 
4934022014-08-14Henrik Grubbström (Grubba)  string(8bit) h = [string(8bit)] (nist_hash(s) ^ nist_hash( [object(Gmp.mpz)](s + 1) ));
3e93c42004-02-07Martin Nilsson 
5490bd2015-04-28Henrik Grubbström (Grubba)  h = sprintf("%c%s%c", [int(8bit)](h[0] | 0x80), h[1..<1], [int(8bit)](h[-1] | 1));
3e93c42004-02-07Martin Nilsson 
4934022014-08-14Henrik Grubbström (Grubba)  Gmp.mpz q = Gmp.mpz(h, 256);
3e93c42004-02-07Martin Nilsson 
4934022014-08-14Henrik Grubbström (Grubba)  if (!q->probably_prime_p()) continue;
3e93c42004-02-07Martin Nilsson 
4934022014-08-14Henrik Grubbström (Grubba)  /* q is a prime, with overwelming probability. */
3e93c42004-02-07Martin Nilsson 
4934022014-08-14Henrik Grubbström (Grubba)  int i, j;
3e93c42004-02-07Martin Nilsson 
4934022014-08-14Henrik Grubbström (Grubba)  for (i = 0, j = 2; i < 4096; i++, j += n+1) { string(8bit) buffer = ""; int k;
3e93c42004-02-07Martin Nilsson 
4934022014-08-14Henrik Grubbström (Grubba)  for (k = 0; k<= n; k++) buffer = nist_hash( [object(Gmp.mpz)](s + j + k) ) + buffer;
3e93c42004-02-07Martin Nilsson 
4934022014-08-14Henrik Grubbström (Grubba)  buffer = buffer[sizeof(buffer) - L/8 ..]; buffer[0] = [int(8bit)](buffer[0] | 0x80);
3e93c42004-02-07Martin Nilsson 
4934022014-08-14Henrik Grubbström (Grubba)  Gmp.mpz p = Gmp.mpz(buffer, 256);
3e93c42004-02-07Martin Nilsson 
4934022014-08-14Henrik Grubbström (Grubba)  p -= p % (2 * q) - 1;
3e93c42004-02-07Martin Nilsson 
4934022014-08-14Henrik Grubbström (Grubba)  if (p->probably_prime_p()) { /* Done */ return ({ p, q }); }
3e93c42004-02-07Martin Nilsson  } } }
4934022014-08-14Henrik Grubbström (Grubba)  protected Gmp.mpz find_generator(Gmp.mpz p, Gmp.mpz q)
3e93c42004-02-07Martin Nilsson  {
4934022014-08-14Henrik Grubbström (Grubba)  Gmp.mpz e = [object(Gmp.mpz)]((p - 1) / q); Gmp.mpz g;
3e93c42004-02-07Martin Nilsson 
4934022014-08-14Henrik Grubbström (Grubba)  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);
3e93c42004-02-07Martin Nilsson 
4934022014-08-14Henrik Grubbström (Grubba)  return g; }
3e93c42004-02-07Martin Nilsson 
4934022014-08-14Henrik Grubbström (Grubba)  // 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" );
3e93c42004-02-07Martin Nilsson 
4934022014-08-14Henrik Grubbström (Grubba)  [p, q] = nist_primes(bits / 64 - 8);
3e93c42004-02-07Martin Nilsson 
4934022014-08-14Henrik Grubbström (Grubba)  if (p % q != 1) error( "Internal error.\n" );
3e93c42004-02-07Martin Nilsson 
4934022014-08-14Henrik Grubbström (Grubba)  if (q->size() != 160) error( "Internal error.\n" );
3e93c42004-02-07Martin Nilsson 
4934022014-08-14Henrik Grubbström (Grubba)  g = find_generator(p, q);
3f6dd02013-12-02Martin Nilsson 
4934022014-08-14Henrik Grubbström (Grubba)  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(); }
3e93c42004-02-07Martin Nilsson 
3f6dd02013-12-02Martin Nilsson #else // !constant(Nettle.dsa_generate_keypair)
4934022014-08-14Henrik Grubbström (Grubba)  //! 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; }
3e93c42004-02-07Martin Nilsson 
3f6dd02013-12-02Martin Nilsson #endif
a859062015-06-02Henrik Grubbström (Grubba)  //! Generates a public/private key pair with the specified //! finite field diffie-hellman parameters.
bbf70b2016-10-19Henrik Grubbström (Grubba)  variant this_program generate_key(.DH.Parameters params)
a859062015-06-02Henrik Grubbström (Grubba)  { p = params->p; g = params->g; q = params->q; [y, x] = params->generate_keypair(random); return this; }
4934022014-08-14Henrik Grubbström (Grubba)  //! Generates a public/private key pair. Needs the public parameters
a859062015-06-02Henrik Grubbström (Grubba)  //! p, q and g set, through one of @[set_public_key], //! @[generate_key(int,int)] or @[generate_key(params)].
4934022014-08-14Henrik Grubbström (Grubba)  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);
3e93c42004-02-07Martin Nilsson 
4934022014-08-14Henrik Grubbström (Grubba)  return this; }
3e93c42004-02-07Martin Nilsson 
3f6dd02013-12-02Martin Nilsson 
4934022014-08-14Henrik Grubbström (Grubba)  // // --- PKCS methods //
b3e4a02013-10-28Martin Nilsson  #define Sequence Standards.ASN1.Types.Sequence
a2a39b2014-01-11Henrik Grubbström (Grubba) #define Integer Standards.ASN1.Types.Integer #define BitString Standards.ASN1.Types.BitString
b3e4a02013-10-28Martin Nilsson 
0c4ea52015-08-22Martin Nilsson  //! Returns the AlgorithmIdentifier as defined in //! @rfc{5280:4.1.1.2@} including the DSA parameters.
4934022014-08-14Henrik Grubbström (Grubba)  Sequence pkcs_algorithm_identifier() { return Sequence( ({ Standards.PKCS.Identifiers.dsa_id, Sequence( ({ Integer(get_p()), Integer(get_q()), Integer(get_g()) }) ) }) ); }
a2a39b2014-01-11Henrik Grubbström (Grubba) 
4934022014-08-14Henrik Grubbström (Grubba)  //! Returns the PKCS-1 algorithm identifier for DSA and the provided //! hash algorithm. Only @[SHA1] supported. Sequence pkcs_signature_algorithm_id(.Hash hash)
a2a39b2014-01-11Henrik Grubbström (Grubba)  {
4934022014-08-14Henrik Grubbström (Grubba)  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;
a2a39b2014-01-11Henrik Grubbström (Grubba)  }
3e93c42004-02-07Martin Nilsson 
4934022014-08-14Henrik Grubbström (Grubba)  //! Creates a SubjectPublicKeyInfo ASN.1 sequence for the object.
0c4ea52015-08-22Martin Nilsson  //! See @rfc{5280:4.1.2.7@}.
4934022014-08-14Henrik Grubbström (Grubba)  Sequence pkcs_public_key() { return Sequence(({ pkcs_algorithm_identifier(), BitString(Integer(get_y())->get_der()), })); }
b3e4a02013-10-28Martin Nilsson 
a2a39b2014-01-11Henrik Grubbström (Grubba) #undef BitString #undef Integer
b3e4a02013-10-28Martin Nilsson #undef Sequence
4934022014-08-14Henrik Grubbström (Grubba)  //! 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(); }
ee804c2013-11-21Martin Nilsson 
873c992014-12-23Henrik Grubbström (Grubba)  // FIXME: Consider implementing RFC 6979.
ee804c2013-11-21Martin Nilsson #define Object Standards.ASN1.Types.Object
4934022014-08-14Henrik Grubbström (Grubba)  //! 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) {
1591ba2014-09-30Martin Nilsson  Object a = Standards.ASN1.Decode.secure_der_decode(sign);
4934022014-08-14Henrik Grubbström (Grubba)  // 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]->
ee804c2013-11-21Martin Nilsson  value,
4934022014-08-14Henrik Grubbström (Grubba)  [object(Gmp.mpz)]([array(object(Object))]a->elements)[1]->
ee804c2013-11-21Martin Nilsson  value);
4934022014-08-14Henrik Grubbström (Grubba)  }
ee804c2013-11-21Martin Nilsson  #undef Object
4934022014-08-14Henrik Grubbström (Grubba)  // // --- Below are methods for DSA applied in other standards. //
b3e4a02013-10-28Martin Nilsson 
931d442021-05-27Henrik Grubbström (Grubba)  //! Makes a DSA hash of the message @[msg].
4934022014-08-14Henrik Grubbström (Grubba)  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); }
3524712015-05-26Martin Nilsson 
4934022014-08-14Henrik Grubbström (Grubba)  protected Gmp.mpz random_number(Gmp.mpz n) { return [object(Gmp.mpz)](Gmp.mpz(random( [int(0..)](q->size() + 10 / 8)), 256) % n); }
b3e4a02013-10-28Martin Nilsson 
4934022014-08-14Henrik Grubbström (Grubba)  protected Gmp.mpz random_exponent() { return [object(Gmp.mpz)](random_number([object(Gmp.mpz)](q - 1)) + 1); }
b3e4a02013-10-28Martin Nilsson 
4934022014-08-14Henrik Grubbström (Grubba)  //! 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();
b3e4a02013-10-28Martin Nilsson 
4934022014-08-14Henrik Grubbström (Grubba)  Gmp.mpz r = [object(Gmp.mpz)](g->powm(k, p) % q);
cdec8c2021-11-06Henrik Grubbström (Grubba)  Gmp.mpz s = [object(Gmp.mpz)]((k->invert(q) * (h + [object(Gmp.mpz)](x*r))) % q);
b3e4a02013-10-28Martin Nilsson 
4934022014-08-14Henrik Grubbström (Grubba)  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) {
c43f042022-02-09Henrik Grubbström (Grubba)  object(Gmp.mpz)|zero w;
4934022014-08-14Henrik Grubbström (Grubba)  if (catch
b3e4a02013-10-28Martin Nilsson  { w = s->invert(q); })
4934022014-08-14Henrik Grubbström (Grubba)  /* Non-invertible */ return 0;
b3e4a02013-10-28Martin Nilsson 
4934022014-08-14Henrik Grubbström (Grubba)  /* 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; }
b3e4a02013-10-28Martin Nilsson 
4934022014-08-14Henrik Grubbström (Grubba)  int(0..) key_size() { return p->size(); }
119b8d2014-08-14Henrik Grubbström (Grubba) } //! Calling `() will return a @[State] object. protected State `()() { return State(); }