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 
627c732004-04-14Martin Nilsson #if constant(Gmp) && constant(Gmp.mpz) && constant(Crypto.Hash)
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 
1821012008-05-09Martin Nilsson //! Returns the RSA modulo (n).
ee90812004-02-03Martin Nilsson Gmp.mpz get_n() { return n; }
1821012008-05-09Martin Nilsson //! Returns the RSA public exponent (e).
ee90812004-02-03Martin Nilsson Gmp.mpz get_e() { return e; }
1821012008-05-09Martin Nilsson //! Returns the RSA private exponent (d), if known.
ee90812004-02-03Martin Nilsson Gmp.mpz get_d() { return d; }
1821012008-05-09Martin Nilsson //! Returns the first RSA prime (p), if known.
ee90812004-02-03Martin Nilsson Gmp.mpz get_p() { return p; }
1821012008-05-09Martin Nilsson //! Returns the second RSA prime (q), if known.
ee90812004-02-03Martin Nilsson Gmp.mpz get_q() { return q; }
1821012008-05-09Martin Nilsson //! Returns the RSA modulo (n) as a binary string.
ee90812004-02-03Martin Nilsson string cooked_get_n() { return n->digits(256); }
1821012008-05-09Martin Nilsson //! Returns the RSA public exponent (e) as a binary string.
ee90812004-02-03Martin Nilsson string cooked_get_e() { return e->digits(256); }
1821012008-05-09Martin Nilsson //! Returns the RSA private exponent (d) as a binary string, if known.
ee90812004-02-03Martin Nilsson string cooked_get_d() { return d->digits(256); }
1821012008-05-09Martin Nilsson //! Returns the first RSA prime (p) as a binary string, if known.
ee90812004-02-03Martin Nilsson string cooked_get_p() { return p->digits(256); }
1821012008-05-09Martin Nilsson //! Returns the second RSA prime (q) as a binary string, if known.
ee90812004-02-03Martin Nilsson string cooked_get_q() { return q->digits(256); } //! 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; } //! 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; }
b6563a2004-02-04Martin Nilsson //! Returns the crypto block size, or zero if not yet set. int query_blocksize() { if(!size) return 0; return size - 3; }
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] //! function if provided. Otherwise //! @[Crypto.Random.random_string] will be used. //! @endint Gmp.mpz rsa_pad(string message, int(1..2) type, function(int:string)|void random)
ee90812004-02-03Martin Nilsson { string cookie; 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: if (random) cookie = replace(random(len), "\0", "\1"); else cookie = replace(.Random.random_string(len), "\0", "\1"); 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 //! Signs @[digest] as @[raw_sign] and returns the signature as a byte //! string.
ee90812004-02-03Martin Nilsson string cooked_sign(string digest) { return raw_sign(digest)->digits(256); }
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); }
b6563a2004-02-04Martin Nilsson //! 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)
ee90812004-02-03Martin Nilsson { return rsa_pad(s, 2, r)->powm(e, n)->digits(256); }
b6563a2004-02-04Martin Nilsson //! Decrypt a message encrypted with @[encrypt].
ee90812004-02-03Martin Nilsson string decrypt(string s) { return rsa_unpad(Gmp.mpz(s, 256)->powm(d, n), 2); }
b6563a2004-02-04Martin Nilsson //! Returns the size of the key in terms of number of bits. int(0..) rsa_size() { return [int(0..)](size*8); }
ee90812004-02-03Martin Nilsson 
b6563a2004-02-04Martin Nilsson //! Compares the public key of this RSA object with another RSA //! object.
ee90812004-02-03Martin Nilsson int(0..1) public_key_equal(this_program rsa) { return n == rsa->get_n() && e == rsa->get_e(); } // end of _rsa
4eaec42004-02-08Martin Nilsson //! Signs the @[message] with a PKCS-1 signature using hash algorithm //! @[h].
ee90812004-02-03Martin Nilsson Gmp.mpz sign(string message, .Hash h) { return raw_sign(Standards.PKCS.Signature.build_digestinfo(message, h)); }
4eaec42004-02-08Martin Nilsson //! Verify PKCS-1 signature @[sign] of message @[msg] using hash //! algorithm @[h].
ee90812004-02-03Martin Nilsson int(0..1) verify(string msg, .Hash h, Gmp.mpz sign) { string s = Standards.PKCS.Signature.build_digestinfo(msg, h); return raw_verify(s, sign); } //! @fixme //! Document this function. string sha_sign(string message, mixed|void r) {
78a3032004-02-19Martin Nilsson  string s = Crypto.SHA1->hash(message);
ee90812004-02-03Martin Nilsson  s = sprintf("%c%s%c%s", 4, "sha1", sizeof(s), s);
9fcdca2008-01-05Henrik Grubbström (Grubba)  return cooked_sign(s);r;
ee90812004-02-03Martin Nilsson } //! @fixme //! Document this function. int sha_verify(string message, string signature) {
78a3032004-02-19Martin Nilsson  string s = Crypto.SHA1->hash(message);
ee90812004-02-03Martin Nilsson  s = sprintf("%c%s%c%s", 4, "sha1", sizeof(s), s); return raw_verify(s, Gmp.mpz(signature, 256)); }
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)
ee90812004-02-03Martin Nilsson string md5_sign(string message, mixed|void r) {
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;
9fcdca2008-01-05Henrik Grubbström (Grubba)  return cooked_sign(s);r;
ee90812004-02-03Martin Nilsson } int md5_verify(string message, string signature) {
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)); }
1821012008-05-09Martin Nilsson 
4eaec42004-02-08Martin Nilsson //! Generate a prime with @[bits] number of bits using random function //! @[r].
b6563a2004-02-04Martin Nilsson Gmp.mpz get_prime(int bits, function(int:string) r)
ee90812004-02-03Martin Nilsson { 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; }
b6563a2004-02-04Martin Nilsson //! Generate a valid RSA key pair with the size @[bits]. A random //! function may be provided as arguemnt @[r], otherwise
8d1fb42004-02-04Martin Nilsson //! @[Crypto.Random.random_string] will be used. Keys must be at least //! 128 bits.
b6563a2004-02-04Martin Nilsson this_program generate_key(int(128..) bits, function(int:string)|void r)
ee90812004-02-03Martin Nilsson { if (!r) r = Crypto.Random.random_string; if (bits < 128) error( "Ridiculously small key.\n" );
15c1482012-12-07Henrik Grubbström (Grubba)  /* 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]. */
ee90812004-02-03Martin Nilsson  int s1 = bits / 2; /* Size of the first prime */
15c1482012-12-07Henrik Grubbström (Grubba)  int s2 = 1 + bits - s1;
ee90812004-02-03Martin Nilsson  string msg = "This is a valid RSA key pair\n"; do {
15c1482012-12-07Henrik Grubbström (Grubba)  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);
b6563a2004-02-04Martin Nilsson  Gmp.mpz phi = [object(Gmp.mpz)](Gmp.mpz([object(Gmp.mpz)](p-1))* Gmp.mpz([object(Gmp.mpz)](q-1)));
ee90812004-02-03Martin Nilsson 
b6563a2004-02-04Martin Nilsson  array(Gmp.mpz) gs; /* gcd(pub, phi), and pub^-1 mod phi */
ee90812004-02-03Martin Nilsson  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;
15c1482012-12-07Henrik Grubbström (Grubba)  set_public_key(mod, pub);
ee90812004-02-03Martin Nilsson  set_private_key(gs[1], ({ p, q })); } while (!sha_verify(msg, sha_sign(msg, r))); return this; } /* * Block cipher compatibility. */
9eaf1d2008-06-28Martin Nilsson protected int encrypt_mode; // For block cipher compatible functions
ee90812004-02-03Martin Nilsson 
4eaec42004-02-08Martin Nilsson //! Sets the public key to @[key] and the mode to encryption.
b6563a2004-02-04Martin Nilsson //! @seealso //! @[set_decrypt_key], @[crypt]
ee90812004-02-03Martin Nilsson this_program set_encrypt_key(array(Gmp.mpz) key) { set_public_key(key[0], key[1]); encrypt_mode = 1; return this; }
4eaec42004-02-08Martin Nilsson //! Sets the public key to @[key]and the mod to decryption.
b6563a2004-02-04Martin Nilsson //! @seealso //! @[set_encrypt_key], @[crypt]
ee90812004-02-03Martin Nilsson 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; }
4eaec42004-02-08Martin Nilsson //! Encrypt or decrypt depending on set mode.
b6563a2004-02-04Martin Nilsson //! @seealso //! @[set_encrypt_key], @[set_decrypt_key]
ee90812004-02-03Martin Nilsson string crypt(string s) { return (encrypt_mode ? encrypt(s) : decrypt(s)); } //! Returns the string @expr{"RSA"@}. string name() { return "RSA"; }
627c732004-04-14Martin Nilsson #else constant this_program_does_not_exist=1;
ee90812004-02-03Martin Nilsson #endif