c5d9a92011-11-05Martin Nilsson /*
ee90812004-02-03Martin Nilsson  * Follow the PKCS#1 standard for padding and encryption. */ #pike __REAL_VERSION__
b6563a2004-02-04Martin Nilsson #pragma strict_types
ee90812004-02-03Martin Nilsson 
86c6f12013-10-29Martin Nilsson #if constant(Crypto.Hash)
b3e4a02013-10-28Martin Nilsson  // // --- Variables and accessors //
ee90812004-02-03Martin Nilsson 
9eaf1d2008-06-28Martin Nilsson protected Gmp.mpz n; /* modulo */ protected Gmp.mpz e; /* public exponent */ protected Gmp.mpz d; /* private exponent (if known) */ protected int size;
ee90812004-02-03Martin Nilsson  /* Extra info associated with a private key. Not currently used. */
9eaf1d2008-06-28Martin Nilsson protected Gmp.mpz p; protected Gmp.mpz q;
ee90812004-02-03Martin Nilsson 
b3e4a02013-10-28Martin Nilsson protected function(int:string) random = .Random.random_string;
ee90812004-02-03Martin Nilsson 
b3e4a02013-10-28Martin Nilsson Gmp.mpz get_n() { return n; } //! Returns the RSA modulo (n). Gmp.mpz get_e() { return e; } //! Returns the RSA public exponent (e).
ee90812004-02-03Martin Nilsson 
1821012008-05-09Martin Nilsson //! Returns the RSA private exponent (d), if known.
b3e4a02013-10-28Martin Nilsson Gmp.mpz get_d() { return d; }
ee90812004-02-03Martin Nilsson 
b3e4a02013-10-28Martin Nilsson 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.
ee90812004-02-03Martin Nilsson 
b3e4a02013-10-28Martin Nilsson //! 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)
ee90812004-02-03Martin Nilsson {
b3e4a02013-10-28Martin Nilsson  random = r; return this;
ee90812004-02-03Martin Nilsson }
b3e4a02013-10-28Martin Nilsson //! Returns the string @expr{"RSA"@}. string name() { return "RSA"; }
ee90812004-02-03Martin Nilsson 
b3e4a02013-10-28Martin Nilsson // // --- Key methods //
ee90812004-02-03Martin Nilsson 
b3e4a02013-10-28Martin Nilsson //! 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)
ee90812004-02-03Martin Nilsson {
b3e4a02013-10-28Martin Nilsson  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 }));
ee90812004-02-03Martin Nilsson } //! Sets the public key.
1821012008-05-09Martin Nilsson //! @param modulo //! The RSA modulo, often called n. This value needs to be >=12. //! @param pub //! The public RSA exponent, often called e.
ee90812004-02-03Martin Nilsson this_program set_public_key(Gmp.mpz|int modulo, Gmp.mpz|int pub) { n = Gmp.mpz(modulo); e = Gmp.mpz(pub); size = n->size(256); if (size < 12) error( "Too small modulo.\n" ); return this; }
b3e4a02013-10-28Martin Nilsson //! 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(); }
ee90812004-02-03Martin Nilsson //! Sets the private key.
1821012008-05-09Martin Nilsson //! @param priv //! The private RSA exponent, often called d.
ee90812004-02-03Martin Nilsson //! @param extra //! @array //! @elem Gmp.mpz|int 0
1821012008-05-09Martin Nilsson //! The first prime, often called p.
ee90812004-02-03Martin Nilsson //! @elem Gmp.mpz|int 1
1821012008-05-09Martin Nilsson //! The second prime, often called q.
ee90812004-02-03Martin Nilsson //! @endarray this_program set_private_key(Gmp.mpz|int priv, array(Gmp.mpz|int)|void extra) { d = Gmp.mpz(priv); if (extra) { p = Gmp.mpz(extra[0]); q = Gmp.mpz(extra[1]);
b6563a2004-02-04Martin Nilsson  n = [object(Gmp.mpz)](p*q);
ee90812004-02-03Martin Nilsson  size = n->size(256); } return this; }
b3e4a02013-10-28Martin Nilsson // // --- Key generation //
11283d2013-11-24Martin Nilsson #if constant(Nettle.rsa_generate_keypair)
d5f6892013-11-24Martin Nilsson  this_program generate_key(int bits, void|int e) { // While a smaller e is possible, and more efficient, using 0x10001 // has become standard and is the only value supported by several // TLS implementations. if(!e) e = 0x10001; else { if(!(e&1)) error("e needs to be odd.\n"); if(e<3) error("e is too small.\n"); if(e->size()>bits) error("e has to be smaller in size than the key.\n"); } if(bits<89) error("Too small key length.\n"); array(Gmp.mpz) key = Nettle.rsa_generate_keypair(bits, e, random); if(!key) error("Error generating key.\n"); [ n, d, p, q ] = key; this_program::e = Gmp.mpz(e); size = n->size(256); return this; } #else
b3e4a02013-10-28Martin Nilsson // 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; }
d5f6892013-11-24Martin Nilsson //! Generate a valid RSA key pair with the size @[bits] using the //! random function set with @[set_random()]. The public exponent @[e] //! will be used, which defaults to 65537. Keys must be at least 89 //! bits. this_program generate_key(int(128..) bits, void|int e)
b3e4a02013-10-28Martin Nilsson { if (bits < 128) error( "Ridiculously small key.\n" );
c49a322013-11-29Martin Nilsson  if( e ) { if(!(e&1)) error("e needs to be odd.\n"); if(e<3) error("e is too small.\n"); if(e->size()>bits) error("e has to be smaller in size than the key.\n"); }
b3e4a02013-10-28Martin Nilsson  /* 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;
c49a322013-11-29Martin Nilsson  string msg = "A" * (bits/8-3-8);
b3e4a02013-10-28Martin Nilsson  do { Gmp.mpz p; Gmp.mpz q; Gmp.mpz mod; do {
d5f6892013-11-24Martin Nilsson  p = get_prime(s1, random); q = get_prime(s2, random);
b3e4a02013-10-28Martin Nilsson  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 */
acb1742013-10-29Martin Nilsson  // For a while it was thought that small exponents were a security // problem, but turned out was a padding problem. The exponent // 0x10001 has however become common practice, although a smaller // value would be more efficient.
d5f6892013-11-24Martin Nilsson  Gmp.mpz pub = Gmp.mpz(e || 0x10001);
acb1742013-10-29Martin Nilsson  // For security reason we need to ensure no common denominator // between n and phi. We could create a different exponent, but // some Crypto packages are hard coded for 0x10001, so instead // we'll just start over. if ((gs = pub->gcdext2(phi))[0] != 1) continue;
b3e4a02013-10-28Martin Nilsson  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; }
d5f6892013-11-24Martin Nilsson #endif
e8e8cc2013-12-04Henrik Grubbström (Grubba) //! Compatibility with Pike 7.8. variant __deprecated__ this_program generate_key(int(128..) bits,
de3fa82013-12-04Martin Nilsson  function(int:string) rnd)
e8e8cc2013-12-04Henrik Grubbström (Grubba) {
de3fa82013-12-04Martin Nilsson  function(int:string) old_rnd = random;
e8e8cc2013-12-04Henrik Grubbström (Grubba)  random = rnd; this_program res = generate_key(bits); random = old_rnd; return res; }
d5f6892013-11-24Martin Nilsson 
b3e4a02013-10-28Martin Nilsson // // --- 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].
00e03d2013-11-21Martin Nilsson string pkcs_sign(string message, .Hash h)
b3e4a02013-10-28Martin Nilsson {
00e03d2013-11-21Martin Nilsson  string di = Standards.PKCS.Signature.build_digestinfo(message, h); return raw_sign(di)->digits(256);
b3e4a02013-10-28Martin Nilsson } //! Verify PKCS-1 signature @[sign] of message @[message] using hash //! algorithm @[h].
00e03d2013-11-21Martin Nilsson int(0..1) pkcs_verify(string message, .Hash h, string sign)
b3e4a02013-10-28Martin Nilsson { string s = Standards.PKCS.Signature.build_digestinfo(message, h);
00e03d2013-11-21Martin Nilsson  return raw_verify(s, Gmp.mpz(sign, 256));
b3e4a02013-10-28Martin Nilsson } // // --- 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)); }
b6563a2004-02-04Martin Nilsson //! Returns the crypto block size, or zero if not yet set.
b3e4a02013-10-28Martin Nilsson int block_size() {
b6563a2004-02-04Martin Nilsson  if(!size) return 0;
b3e4a02013-10-28Martin Nilsson  // FIXME: This can be both zero and negative...
b6563a2004-02-04Martin Nilsson  return size - 3; }
ee90812004-02-03Martin Nilsson 
b3e4a02013-10-28Martin Nilsson //! 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. //
ee90812004-02-03Martin Nilsson //! Pads the @[message] to the current block size with method @[type]
1821012008-05-09Martin Nilsson //! and returns the result as an integer. This is equvivalent to //! OS2IP(EME-PKCS1-V1_5-ENCODE(message)) in PKCS-1.
b6563a2004-02-04Martin Nilsson //! @param type //! @int //! @value 1 //! The message is padded with @expr{0xff@} bytes. //! @value 2 //! The message is padded with random data, using the @[random]
b3e4a02013-10-28Martin Nilsson //! function if provided. Otherwise the default random function //! set in the object will be used.
b6563a2004-02-04Martin Nilsson //! @endint Gmp.mpz rsa_pad(string message, int(1..2) type, function(int:string)|void random)
ee90812004-02-03Martin Nilsson {
0faa802013-10-28Martin Nilsson  string cookie = "";
ee90812004-02-03Martin Nilsson  int len; len = size - 3 - sizeof(message); if (len < 8) error( "Block too large. (%d,%d)\n", sizeof(message), size-3 ); switch(type) { case 1: cookie = sprintf("%@c", allocate(len, 0xff)); break; case 2:
b3e4a02013-10-28Martin Nilsson  if( !random ) random = this_program::random;
0faa802013-10-28Martin Nilsson  do { cookie += random(len-sizeof(cookie)) - "\0"; } while( sizeof(cookie)<len );
ee90812004-02-03Martin Nilsson  break; default: error( "Unknown type.\n" ); } return Gmp.mpz(sprintf("%c", type) + cookie + "\0" + message, 256); }
b6563a2004-02-04Martin Nilsson //! Reverse the effect of @[rsa_pad].
ee90812004-02-03Martin Nilsson string rsa_unpad(Gmp.mpz block, int type) { string s = block->digits(256);
b6563a2004-02-04Martin Nilsson  int i = search(s, "\0");
ee90812004-02-03Martin Nilsson  if ((i < 9) || (sizeof(s) != (size - 1)) || (s[0] != type)) return 0; return s[i+1..]; }
b6563a2004-02-04Martin Nilsson //! Pads the @[digest] with @[rsa_pad] type 1 and signs it.
ee90812004-02-03Martin Nilsson Gmp.mpz raw_sign(string digest) { return rsa_pad(digest, 1, 0)->powm(d, n); }
b6563a2004-02-04Martin Nilsson //! Verifies the @[digest] against the signature @[s], assuming pad //! type 1. //! @seealso //! @[rsa_pad], @[raw_sign]
ee90812004-02-03Martin Nilsson int(0..1) raw_verify(string digest, Gmp.mpz s) { return s->powm(e, n) == rsa_pad(digest, 1, 0); }
b3e4a02013-10-28Martin Nilsson // // --- Deprecated stuff // //! Returns the RSA modulo (n) as a binary string. __deprecated__ string cooked_get_n()
ee90812004-02-03Martin Nilsson {
b3e4a02013-10-28Martin Nilsson  return n->digits(256);
ee90812004-02-03Martin Nilsson }
b3e4a02013-10-28Martin Nilsson //! Returns the RSA public exponent (e) as a binary string. __deprecated__ string cooked_get_e()
ee90812004-02-03Martin Nilsson {
b3e4a02013-10-28Martin Nilsson  return e->digits(256);
ee90812004-02-03Martin Nilsson }
b3e4a02013-10-28Martin Nilsson //! Returns the RSA private exponent (d) as a binary string, if known. __deprecated__ string cooked_get_d()
ee90812004-02-03Martin Nilsson {
b3e4a02013-10-28Martin Nilsson  return d->digits(256);
ee90812004-02-03Martin Nilsson }
b3e4a02013-10-28Martin Nilsson //! Returns the first RSA prime (p) as a binary string, if known. __deprecated__ string cooked_get_p()
ee90812004-02-03Martin Nilsson {
b3e4a02013-10-28Martin Nilsson  return p->digits(256);
ee90812004-02-03Martin Nilsson }
b3e4a02013-10-28Martin Nilsson //! Returns the second RSA prime (q) as a binary string, if known. __deprecated__ string cooked_get_q()
ee90812004-02-03Martin Nilsson {
b3e4a02013-10-28Martin Nilsson  return q->digits(256);
ee90812004-02-03Martin Nilsson }
b3e4a02013-10-28Martin Nilsson //! Signs @[digest] as @[raw_sign] and returns the signature as a byte //! string. __deprecated__ string cooked_sign(string digest)
ee90812004-02-03Martin Nilsson {
b3e4a02013-10-28Martin Nilsson  return raw_sign(digest)->digits(256);
ee90812004-02-03Martin Nilsson }
b3e4a02013-10-28Martin Nilsson  __deprecated__ int query_blocksize() { return block_size();
ee90812004-02-03Martin Nilsson }
b3e4a02013-10-28Martin Nilsson __deprecated__ int(0..) rsa_size() { return [int(0..)](size*8); }
1821012008-05-09Martin Nilsson // Broken implementation of RSA/MD5 SIG RFC 2537. The 0x00 01 FF* 00 // prefix is missing. // (RSA/SHA-1 SIG is in RFC 3110)
c49a322013-11-29Martin Nilsson #if constant(Crypto.MD5)
b3e4a02013-10-28Martin Nilsson __deprecated__ string md5_sign(string message, mixed|void r)
ee90812004-02-03Martin Nilsson {
78a3032004-02-19Martin Nilsson  string s = Crypto.MD5->hash(message);
ee90812004-02-03Martin Nilsson  s = "0 0\14\6\10*\x86H\x86\xf7\15\2\5\5\0\4\20"+s;
b3e4a02013-10-28Martin Nilsson  return raw_sign(s)->digits(256);r;
ee90812004-02-03Martin Nilsson }
b3e4a02013-10-28Martin Nilsson __deprecated__ int md5_verify(string message, string signature)
ee90812004-02-03Martin Nilsson {
78a3032004-02-19Martin Nilsson  string s = Crypto.MD5->hash(message);
ee90812004-02-03Martin Nilsson  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)); }
c49a322013-11-29Martin Nilsson #endif
b3e4a02013-10-28Martin Nilsson __deprecated__ string sha_sign(string message, mixed|void r)
ee90812004-02-03Martin Nilsson {
b3e4a02013-10-28Martin Nilsson  string s = sprintf("%c%s%1H", 4, "sha1", Crypto.SHA1->hash(message)); return raw_sign(s)->digits(256);r;
ee90812004-02-03Martin Nilsson }
b3e4a02013-10-28Martin Nilsson __deprecated__ int sha_verify(string message, string signature)
ee90812004-02-03Martin Nilsson {
b3e4a02013-10-28Martin Nilsson  string s = sprintf("%c%s%1H", 4, "sha1", Crypto.SHA1->hash(message)); return raw_verify(s, Gmp.mpz(signature, 256));
ee90812004-02-03Martin Nilsson }
9581ed2013-11-22Martin Nilsson __deprecated__ Gmp.mpz sign(string message, .Hash h) { return raw_sign(Standards.PKCS.Signature.build_digestinfo(message, h)); } __deprecated__ int(0..1) verify(string message, .Hash h, Gmp.mpz sign) { return raw_verify(Standards.PKCS.Signature.build_digestinfo(message, h), sign); }
627c732004-04-14Martin Nilsson #else constant this_program_does_not_exist=1;
ee90812004-02-03Martin Nilsson #endif