Branch: Tag:

2013-10-28

2013-10-28 16:57:40 by Martin Nilsson <nilsson@opera.com>

Restructured RSA and DSA so that they resemble each other in structure
and interface.

Deprecated
RSA->cooked_get_n
RSA->cooked_get_e
RSA->cooked_get_d
RSA->cooked_get_p
RSA->cooked_get_q
RSA->cooked_sign
RSA->query_blocksize (now block_size)
RSA->rsa_size (now key_size)
RSA->md5_sign
RSA->md5_verify
RSA->sha_sign
RSA->sha_verify
DSA->sign_rsaref
DSA->verify_rsaref

5:   #pike __REAL_VERSION__   #pragma strict_types    - #if constant(Gmp) && constant(Gmp.mpz) && constant(Crypto.Hash) + #if constant(Gmp.mpz) && constant(Crypto.Hash)    -  + // + // --- Variables and accessors + // +    protected Gmp.mpz n; /* modulo */   protected Gmp.mpz e; /* public exponent */   protected Gmp.mpz d; /* private exponent (if known) */
17: Inside #if constant(Gmp) && constant(Gmp.mpz) && constant(Crypto.Hash)
  protected Gmp.mpz p;   protected Gmp.mpz q;    - //! Can be initialized with a mapping with the elements n, e, d, p and - //! q. - protected void create(mapping(string:Gmp.mpz|int)|void params) - { -  if(!params) return; -  if( params->n && params->e ) -  set_public_key(params->n, params->e); -  if( params->d ) -  set_private_key(params->d, ({ params->p, params->q, params->n })); - } + protected function(int:string) random = .Random.random_string;    - //! Returns the RSA modulo (n). - Gmp.mpz get_n() - { -  return n; - } + Gmp.mpz get_n() { return n; } //! Returns the RSA modulo (n). + Gmp.mpz get_e() { return e; } //! Returns the RSA public exponent (e).    - //! Returns the RSA public exponent (e). - Gmp.mpz get_e() - { -  return e; - } -  +    //! Returns the RSA private exponent (d), if known. - Gmp.mpz get_d() - { -  return d; - } + Gmp.mpz get_d() { return d; }    - //! Returns the first RSA prime (p), if known. - Gmp.mpz get_p() - { -  return p; - } + Gmp.mpz get_p() { return p; } //! Returns the first RSA prime (p), if known. + Gmp.mpz get_q() { return q; } //! Returns the second RSA prime (q), if known.    - //! Returns the second RSA prime (q), if known. - Gmp.mpz get_q() + //! 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:string) r)   { -  return q; +  random = r; +  return this;   }    - //! Returns the RSA modulo (n) as a binary string. - string cooked_get_n() - { -  return n->digits(256); - } + //! Returns the string @expr{"RSA"@}. + string name() { return "RSA"; }    - //! Returns the RSA public exponent (e) as a binary string. - string cooked_get_e() - { -  return e->digits(256); - } + // + // --- Key methods + //    - //! Returns the RSA private exponent (d) as a binary string, if known. - string cooked_get_d() + //! Can be initialized with a mapping with the elements n, e, d, p and + //! q. + protected void create(mapping(string:Gmp.mpz|int)|void params)   { -  return d->digits(256); +  if(!params) return; +  if( params->n && params->e ) +  set_public_key(params->n, params->e); +  if( params->d ) +  set_private_key(params->d, ({ params->p, params->q, params->n }));   }    - //! Returns the first RSA prime (p) as a binary string, if known. - string cooked_get_p() - { -  return p->digits(256); - } -  - //! Returns the second RSA prime (q) as a binary string, if known. - string cooked_get_q() - { -  return q->digits(256); - } -  +    //! Sets the public key.   //! @param modulo   //! The RSA modulo, often called n. This value needs to be >=12.
103: Inside #if constant(Gmp) && constant(Gmp.mpz) && constant(Crypto.Hash)
   return this;   }    + //! Compares the public key of this RSA object with another RSA + //! object. + int(0..1) public_key_equal(this_program rsa) + { +  return n == rsa->get_n() && e == rsa->get_e(); + } +    //! Sets the private key.   //! @param priv   //! The private RSA exponent, often called d.
126: Inside #if constant(Gmp) && constant(Gmp.mpz) && constant(Crypto.Hash)
   return this;   }    + // + // --- Key generation + // +  + // Generate a prime with @[bits] number of bits using random function + // @[r]. + protected Gmp.mpz get_prime(int bits, function(int:string) r) + { +  int len = (bits + 7) / 8; +  int bit_to_set = 1 << ( (bits - 1) % 8); +  +  Gmp.mpz p; +  +  do { +  string s = r(len); +  p = Gmp.mpz(sprintf("%c%s", (s[0] & (bit_to_set - 1)) +  | bit_to_set, s[1..]), +  256)->next_prime(); +  } while (p->size() > bits); +  +  return p; + } +  + //! Generate a valid RSA key pair with the size @[bits]. A random + //! function may be provided as arguemnt @[r], otherwise the default + //! random function set in the object will be used. Keys must be at + //! least 128 bits. + this_program generate_key(int(128..) bits, function(int:string)|void r) + { +  if (!r) r = random; +  if (bits < 128) +  error( "Ridiculously small key.\n" ); +  +  /* NB: When multiplying two n-bit integers, +  * you're most likely to get an (2n - 1)-bit result. +  * We therefore add an extra bit to s2. +  * +  * cf [bug 6620]. +  */ +  +  int s1 = bits / 2; /* Size of the first prime */ +  int s2 = 1 + bits - s1; +  +  string msg = "This is a valid RSA key pair\n"; +  +  do +  { +  Gmp.mpz p; +  Gmp.mpz q; +  Gmp.mpz mod; +  do { +  p = get_prime(s1, r); +  q = get_prime(s2, r); +  mod = [object(Gmp.mpz)](p * q); +  } while (mod->size() != bits); +  Gmp.mpz phi = [object(Gmp.mpz)](Gmp.mpz([object(Gmp.mpz)](p-1))* +  Gmp.mpz([object(Gmp.mpz)](q-1))); +  +  array(Gmp.mpz) gs; /* gcd(pub, phi), and pub^-1 mod phi */ +  Gmp.mpz pub = Gmp.mpz( + #ifdef SSL3_32BIT_PUBLIC_EXPONENT +  random(1 << 30) | + #endif /* SSL3_32BIT_PUBLIC_EXPONENT */ +  0x10001); +  +  while ((gs = pub->gcdext2(phi))[0] != 1) +  pub += 1; +  +  if (gs[1] < 0) +  gs[1] += phi; +  +  set_public_key(mod, pub); +  set_private_key(gs[1], ({ p, q })); +  +  } while (!raw_verify(msg, raw_sign(msg))); +  return this; + } +  + // + // --- PKCS methods + // +  + #define Sequence Standards.ASN1.Types.Sequence +  + //! Calls @[Standards.PKCS.RSA.signatue_algorithm_id] with the + //! provided @[hash]. + Sequence pkcs_algorithm_id(.Hash hash) + { +  return [object(Sequence)]Standards.PKCS.RSA->signature_algorithm_id(hash); + } +  + //! Calls @[Standards.PKCS.RSA.build_public_key] with this object as + //! argument. + Sequence pkcs_public_key() + { +  return [object(Sequence)]Standards.PKCS.RSA->build_public_key(this); + } +  + #undef Sequence +  + //! Signs the @[message] with a PKCS-1 signature using hash algorithm + //! @[h]. + Gmp.mpz sign(string message, .Hash h) + { +  return raw_sign(Standards.PKCS.Signature.build_digestinfo(message, h)); + } +  + //! Verify PKCS-1 signature @[sign] of message @[message] using hash + //! algorithm @[h]. + int(0..1) verify(string message, .Hash h, Gmp.mpz sign) + { +  string s = Standards.PKCS.Signature.build_digestinfo(message, h); +  return raw_verify(s, sign); + } +  + // + // --- Encryption/decryption + // +  + //! Pads the message @[s] with @[rsa_pad] type 2, signs it and returns + //! the signature as a byte string. + //! @param r + //! Optional random function to be passed down to @[rsa_pad]. + string encrypt(string s, function(int:string)|void r) + { +  return rsa_pad(s, 2, r)->powm(e, n)->digits(256); + } +  + //! Decrypt a message encrypted with @[encrypt]. + string decrypt(string s) + { +  return rsa_unpad(Gmp.mpz(s, 256)->powm(d, n), 2); + } +  + // + // --- Block cipher compatibility. + // +  + protected int encrypt_mode; // For block cipher compatible functions +  + //! Sets the public key to @[key] and the mode to encryption. + //! @seealso + //! @[set_decrypt_key], @[crypt] + this_program set_encrypt_key(array(Gmp.mpz) key) + { +  set_public_key(key[0], key[1]); +  encrypt_mode = 1; +  return this; + } +  + //! Sets the public key to @[key]and the mod to decryption. + //! @seealso + //! @[set_encrypt_key], @[crypt] + this_program set_decrypt_key(array(Gmp.mpz) key) + { +  set_public_key(key[0], key[1]); +  set_private_key(key[2]); +  encrypt_mode = 0; +  return this; + } +  + //! Encrypt or decrypt depending on set mode. + //! @seealso + //! @[set_encrypt_key], @[set_decrypt_key] + string crypt(string s) + { +  return (encrypt_mode ? encrypt(s) : decrypt(s)); + } +    //! Returns the crypto block size, or zero if not yet set. - int query_blocksize() { + int block_size() + {    if(!size) return 0; -  +  // FIXME: This can be both zero and negative...    return size - 3;   }    -  + //! Returns the size of the key in terms of number of bits. + int(0..) key_size() { return [int(0..)](size*8); } +  +  + // + // --- Below are methods for RSA applied in other standards. + // +  +    //! Pads the @[message] to the current block size with method @[type]   //! and returns the result as an integer. This is equvivalent to   //! OS2IP(EME-PKCS1-V1_5-ENCODE(message)) in PKCS-1.
141: Inside #if constant(Gmp) && constant(Gmp.mpz) && constant(Crypto.Hash)
  //! The message is padded with @expr{0xff@} bytes.   //! @value 2   //! The message is padded with random data, using the @[random] - //! function if provided. Otherwise - //! @[Crypto.Random.random_string] will be used. + //! function if provided. Otherwise the default random function + //! set in the object will be used.   //! @endint   Gmp.mpz rsa_pad(string message, int(1..2) type,    function(int:string)|void random)
160: Inside #if constant(Gmp) && constant(Gmp.mpz) && constant(Crypto.Hash)
   cookie = sprintf("%@c", allocate(len, 0xff));    break;    case 2: -  if (random) -  cookie = replace(random(len), "\0", "\1"); -  else -  cookie = replace(.Random.random_string(len), "\0", "\1"); +  if( !random ) random = this_program::random; +  cookie = replace( random(len), "\0", "\1");    break;    default:    error( "Unknown type.\n" );
188: Inside #if constant(Gmp) && constant(Gmp.mpz) && constant(Crypto.Hash)
   return rsa_pad(digest, 1, 0)->powm(d, n);   }    - //! Signs @[digest] as @[raw_sign] and returns the signature as a byte - //! string. - string cooked_sign(string digest) - { -  return raw_sign(digest)->digits(256); - } -  +    //! Verifies the @[digest] against the signature @[s], assuming pad   //! type 1.   //! @seealso
204: Inside #if constant(Gmp) && constant(Gmp.mpz) && constant(Crypto.Hash)
   return s->powm(e, n) == rsa_pad(digest, 1, 0);   }    - //! Pads the message @[s] with @[rsa_pad] type 2, signs it and returns - //! the signature as a byte string. - //! @param r - //! Optional random function to be passed down to @[rsa_pad]. - string encrypt(string s, function(int:string)|void r) + // + // --- Deprecated stuff + // +  + //! Returns the RSA modulo (n) as a binary string. + __deprecated__ string cooked_get_n()   { -  return rsa_pad(s, 2, r)->powm(e, n)->digits(256); +  return n->digits(256);   }    - //! Decrypt a message encrypted with @[encrypt]. - string decrypt(string s) + //! Returns the RSA public exponent (e) as a binary string. + __deprecated__ string cooked_get_e()   { -  return rsa_unpad(Gmp.mpz(s, 256)->powm(d, n), 2); +  return e->digits(256);   }    - //! Returns the size of the key in terms of number of bits. - int(0..) rsa_size() { return [int(0..)](size*8); } -  - //! Compares the public key of this RSA object with another RSA - //! object. - int(0..1) public_key_equal(this_program rsa) + //! Returns the RSA private exponent (d) as a binary string, if known. + __deprecated__ string cooked_get_d()   { -  return n == rsa->get_n() && e == rsa->get_e(); +  return d->digits(256);   }    - // end of _rsa -  - //! Signs the @[message] with a PKCS-1 signature using hash algorithm - //! @[h]. - Gmp.mpz sign(string message, .Hash h) + //! Returns the first RSA prime (p) as a binary string, if known. + __deprecated__ string cooked_get_p()   { -  return raw_sign(Standards.PKCS.Signature.build_digestinfo(message, h)); +  return p->digits(256);   }    - //! Verify PKCS-1 signature @[sign] of message @[msg] using hash - //! algorithm @[h]. - int(0..1) verify(string msg, .Hash h, Gmp.mpz sign) + //! Returns the second RSA prime (q) as a binary string, if known. + __deprecated__ string cooked_get_q()   { -  string s = Standards.PKCS.Signature.build_digestinfo(msg, h); -  return raw_verify(s, sign); +  return q->digits(256);   }    - //! @fixme - //! Document this function. - string sha_sign(string message, mixed|void r) + //! Signs @[digest] as @[raw_sign] and returns the signature as a byte + //! string. + __deprecated__ string cooked_sign(string digest)   { -  string s = sprintf("%c%s%1H", 4, "sha1", Crypto.SHA1->hash(message)); -  return cooked_sign(s);r; +  return raw_sign(digest)->digits(256);   }    - //! @fixme - //! Document this function. - int sha_verify(string message, string signature) - { -  string s = sprintf("%c%s%1H", 4, "sha1", Crypto.SHA1->hash(message)); -  return raw_verify(s, Gmp.mpz(signature, 256)); + __deprecated__ int query_blocksize() { +  return block_size();   }    -  + __deprecated__ int(0..) rsa_size() { return [int(0..)](size*8); } +  +    // Broken implementation of RSA/MD5 SIG RFC 2537. The 0x00 01 FF* 00   // prefix is missing.      // (RSA/SHA-1 SIG is in RFC 3110)    - string md5_sign(string message, mixed|void r) + __deprecated__ string md5_sign(string message, mixed|void r)   {    string s = Crypto.MD5->hash(message);    s = "0 0\14\6\10*\x86H\x86\xf7\15\2\5\5\0\4\20"+s; -  return cooked_sign(s);r; +  return raw_sign(s)->digits(256);r;   }    - int md5_verify(string message, string signature) + __deprecated__ int md5_verify(string message, string signature)   {    string s = Crypto.MD5->hash(message);    s = "0 0\14\6\10*\x86H\x86\xf7\15\2\5\5\0\4\20"+s;    return raw_verify(s, Gmp.mpz(signature, 256));   }    -  - //! Generate a prime with @[bits] number of bits using random function - //! @[r]. - Gmp.mpz get_prime(int bits, function(int:string) r) + __deprecated__ string sha_sign(string message, mixed|void r)   { -  int len = (bits + 7) / 8; -  int bit_to_set = 1 << ( (bits - 1) % 8); -  -  Gmp.mpz p; -  -  do { -  string s = r(len); -  p = Gmp.mpz(sprintf("%c%s", (s[0] & (bit_to_set - 1)) -  | bit_to_set, s[1..]), -  256)->next_prime(); -  } while (p->size() > bits); -  -  return p; +  string s = sprintf("%c%s%1H", 4, "sha1", Crypto.SHA1->hash(message)); +  return raw_sign(s)->digits(256);r;   }    - //! Generate a valid RSA key pair with the size @[bits]. A random - //! function may be provided as arguemnt @[r], otherwise - //! @[Crypto.Random.random_string] will be used. Keys must be at least - //! 128 bits. - this_program generate_key(int(128..) bits, function(int:string)|void r) + __deprecated__ int sha_verify(string message, string signature)   { -  if (!r) -  r = Crypto.Random.random_string; -  if (bits < 128) -  error( "Ridiculously small key.\n" ); -  -  /* NB: When multiplying two n-bit integers, -  * you're most likely to get an (2n - 1)-bit result. -  * We therefore add an extra bit to s2. -  * -  * cf [bug 6620]. -  */ -  -  int s1 = bits / 2; /* Size of the first prime */ -  int s2 = 1 + bits - s1; -  -  string msg = "This is a valid RSA key pair\n"; -  -  do -  { -  Gmp.mpz p; -  Gmp.mpz q; -  Gmp.mpz mod; -  do { -  p = get_prime(s1, r); -  q = get_prime(s2, r); -  mod = [object(Gmp.mpz)](p * q); -  } while (mod->size() != bits); -  Gmp.mpz phi = [object(Gmp.mpz)](Gmp.mpz([object(Gmp.mpz)](p-1))* -  Gmp.mpz([object(Gmp.mpz)](q-1))); -  -  array(Gmp.mpz) gs; /* gcd(pub, phi), and pub^-1 mod phi */ -  Gmp.mpz pub = Gmp.mpz( - #ifdef SSL3_32BIT_PUBLIC_EXPONENT -  random(1 << 30) | - #endif /* SSL3_32BIT_PUBLIC_EXPONENT */ -  0x10001); -  -  while ((gs = pub->gcdext2(phi))[0] != 1) -  pub += 1; -  -  if (gs[1] < 0) -  gs[1] += phi; -  -  set_public_key(mod, pub); -  set_private_key(gs[1], ({ p, q })); -  -  } while (!sha_verify(msg, sha_sign(msg, r))); -  return this; +  string s = sprintf("%c%s%1H", 4, "sha1", Crypto.SHA1->hash(message)); +  return raw_verify(s, Gmp.mpz(signature, 256));   }    - /* -  * Block cipher compatibility. -  */ -  - protected int encrypt_mode; // For block cipher compatible functions -  - //! Sets the public key to @[key] and the mode to encryption. - //! @seealso - //! @[set_decrypt_key], @[crypt] - this_program set_encrypt_key(array(Gmp.mpz) key) - { -  set_public_key(key[0], key[1]); -  encrypt_mode = 1; -  return this; - } -  - //! Sets the public key to @[key]and the mod to decryption. - //! @seealso - //! @[set_encrypt_key], @[crypt] - this_program set_decrypt_key(array(Gmp.mpz) key) - { -  set_public_key(key[0], key[1]); -  set_private_key(key[2]); -  encrypt_mode = 0; -  return this; - } -  - //! Encrypt or decrypt depending on set mode. - //! @seealso - //! @[set_encrypt_key], @[set_decrypt_key] - string crypt(string s) - { -  return (encrypt_mode ? encrypt(s) : decrypt(s)); - } -  - //! Returns the string @expr{"RSA"@}. - string name() { -  return "RSA"; - } -  +    #else   constant this_program_does_not_exist=1;   #endif