b3e4a02013-10-28Martin Nilsson /* * Follow the PKCS#1 standard for padding and encryption. */
9581ed2013-11-22Martin Nilsson #pike 7.8
b3e4a02013-10-28Martin Nilsson #pragma strict_types
86c6f12013-10-29Martin Nilsson #if constant(Crypto.Hash)
b3e4a02013-10-28Martin Nilsson  protected Gmp.mpz n; /* modulo */ protected Gmp.mpz e; /* public exponent */ protected Gmp.mpz d; /* private exponent (if known) */ protected int size; /* Extra info associated with a private key. Not currently used. */ 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 })); } //! Returns the RSA modulo (n). Gmp.mpz get_n() { return n; } //! 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; } //! Returns the first RSA prime (p), if known. Gmp.mpz get_p() { return p; } //! Returns the second RSA prime (q), if known. Gmp.mpz get_q() { return q; } //! Returns the RSA modulo (n) as a binary string. string cooked_get_n() { return n->digits(256); } //! Returns the RSA public exponent (e) as a binary string. string cooked_get_e() { return e->digits(256); } //! Returns the RSA private exponent (d) as a binary string, if known. string cooked_get_d() { return d->digits(256); } //! 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. //! @param pub //! The public RSA exponent, often called e. 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. //! @param priv //! The private RSA exponent, often called d. //! @param extra //! @array //! @elem Gmp.mpz|int 0 //! The first prime, often called p. //! @elem Gmp.mpz|int 1 //! The second prime, often called q. //! @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]); n = [object(Gmp.mpz)](p*q); size = n->size(256); } return this; } //! Returns the crypto block size, or zero if not yet set. int query_blocksize() { if(!size) return 0; return size - 3; } //! 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. //! @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) { 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(Crypto.Random.random_string(len), "\0", "\1"); break; default: error( "Unknown type.\n" ); } return Gmp.mpz(sprintf("%c", type) + cookie + "\0" + message, 256); } //! Reverse the effect of @[rsa_pad]. string rsa_unpad(Gmp.mpz block, int type) { string s = block->digits(256); int i = search(s, "\0"); if ((i < 9) || (sizeof(s) != (size - 1)) || (s[0] != type)) return 0; return s[i+1..]; } //! Pads the @[digest] with @[rsa_pad] type 1 and signs it. Gmp.mpz raw_sign(string digest) { 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 //! @[rsa_pad], @[raw_sign] int(0..1) raw_verify(string digest, Gmp.mpz s) { 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) { 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); } //! 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) { return n == rsa->get_n() && e == rsa->get_e(); } // end of _rsa //! Signs the @[message] with a PKCS-1 signature using hash algorithm //! @[h]. Gmp.mpz sign(string message, Crypto.Hash h) { return raw_sign(Standards.PKCS.Signature.build_digestinfo(message, h)); } //! Verify PKCS-1 signature @[sign] of message @[msg] using hash //! algorithm @[h]. int(0..1) verify(string msg, Crypto.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) { string s = sprintf("%c%s%1H", 4, "sha1", Crypto.SHA1->hash(message)); return cooked_sign(s);r; } //! @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)); } // 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) { 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; } 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) { 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 //! @[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) { 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; } /* * 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