|
|
|
|
#pike __REAL_VERSION__ |
#pragma strict_types |
#require constant(Crypto.Hash) |
|
inherit Crypto.Sign; |
|
|
string(8bit) name() { return "RSA"; } |
|
protected class LowState { |
inherit Sign::State; |
|
protected string _sprintf(int t) |
{ |
return t=='O' && sprintf("%O(%d)", this_program, n->size()); |
} |
|
|
|
|
|
protected Gmp.mpz n; |
protected Gmp.mpz e; |
protected Gmp.mpz d; |
|
|
|
protected Gmp.mpz p; |
protected Gmp.mpz q; |
|
protected function(int(0..):string(8bit)) random = random_string; |
|
Gmp.mpz get_n() { return n; } |
Gmp.mpz get_e() { return e; } |
|
|
Gmp.mpz get_d() { return d; } |
|
Gmp.mpz get_p() { return p; } |
Gmp.mpz get_q() { return q; } |
|
|
|
this_program set_random(function(int(0..):string(8bit)) r) |
{ |
random = r; |
return this; |
} |
|
|
string(8bit) name() { return "RSA"; } |
|
|
|
|
|
|
|
|
string(7bit) jwa(.Hash hash); |
|
|
|
|
|
|
|
protected void create(mapping(string(8bit):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 })); |
} |
|
|
|
|
|
|
this_program set_public_key(Gmp.mpz|int modulo, Gmp.mpz|int pub) |
{ |
n = Gmp.mpz(modulo); |
e = Gmp.mpz(pub); |
if (n->size(256) < 12) |
error( "Too small modulo.\n" ); |
return this; |
} |
|
|
|
int(0..1) public_key_equal(this_program rsa) |
{ |
return n == rsa->get_n() && e == rsa->get_e(); |
} |
|
|
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 rsa = [object(this_program)]other; |
return d == rsa->get_d(); |
} |
|
|
|
|
|
|
|
|
|
|
|
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); |
} |
return this; |
} |
|
|
|
|
|
#if constant(Nettle.rsa_generate_keypair) |
|
this_program generate_key(int bits, void|int e) |
{ |
|
|
|
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::e = Gmp.mpz(e); |
return this; |
} |
|
#else |
|
|
|
protected Gmp.mpz get_prime(int bits, function(int(0..):string(8bit)) r) |
{ |
int len = (bits + 7) / 8; |
int bit_to_set = 1 << ( (bits - 1) % 8); |
|
Gmp.mpz p; |
|
do { |
string(8bit) s = r([int(0..)]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; |
} |
|
|
|
|
|
this_program generate_key(int(128..) bits, void|int|Gmp.mpz e) |
{ |
if (bits < 128) |
error( "Ridiculously small key.\n" ); |
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"); |
if (e->size() >= 64) { |
|
error("e: %O is too large.\n", e); |
} |
} |
|
|
|
|
|
|
|
|
int s1 = bits / 2; |
int s2 = 1 + bits - s1; |
|
string(8bit) msg = "A" * (bits/8-3-8); |
|
do { |
Gmp.mpz p; |
Gmp.mpz q; |
Gmp.mpz mod; |
do { |
p = get_prime(s1, random); |
q = get_prime(s2, random); |
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; |
|
|
|
|
|
Gmp.mpz pub = Gmp.mpz(e || 0x10001); |
|
|
|
|
|
if ((gs = pub->gcdext2(phi))[0] != 1) |
continue; |
|
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; |
} |
|
#endif |
|
|
|
|
|
|
protected int encrypt_mode; |
|
|
|
|
this_program set_encrypt_key(array(Gmp.mpz) key) |
{ |
set_public_key(key[0], key[1]); |
encrypt_mode = 1; |
return this; |
} |
|
|
|
|
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; |
} |
|
|
int(0..) key_size() { return n->size(); } |
|
|
|
|
|
|
|
string(8bit) encrypt(string(8bit) s, function(int:string(8bit))|void r); |
string(8bit) decrypt(string(8bit) s); |
string(8bit) crypt(string(8bit) s); |
int block_size(); |
Gmp.mpz rsa_pad(string(8bit) message, int(1..2) type, |
function(int(0..):string(8bit))|void random); |
string(8bit) rsa_unpad(Gmp.mpz block, int type); |
Gmp.mpz raw_sign(string(8bit) digest); |
int(0..1) raw_verify(string(8bit) digest, Gmp.mpz s); |
|
|
|
|
this_program `PSS(); |
this_program `OAEP(); |
this_program `PKCS1_5(); |
} |
|
#define Sequence Standards.ASN1.Types.Sequence |
|
private class PKCS_RSA_class { |
Sequence signature_algorithm_id(.Hash); |
Sequence pss_signature_algorithm_id(.Hash, int(0..)|void saltlen); |
Sequence build_public_key(global::State); |
} |
private object(PKCS_RSA_class) PKCS_RSA = |
[object(PKCS_RSA_class)]Standards.PKCS["RSA"]; |
|
|
|
|
|
class OAEPState { |
inherit LowState; |
|
local { |
|
this_program `OAEP() { return this_program::this; } |
|
string(7bit) name() { return "RSAES-OEAP"; } |
|
protected .Hash hash_alg = .SHA1; |
|
optional .Hash get_hash_algorithm() |
{ |
return hash_alg; |
} |
|
optional void set_hash_algorithm(.Hash h) |
{ |
if ((h->digest_size() * 2 + 2) >= n->size(256)) { |
error("Too large hash digest (%d, max: %d) for modulo.\n", |
h->digest_size(), n->size(256)/2 - 3); |
} |
hash_alg = h; |
} |
|
|
|
|
string(8bit) crypt(string(8bit) s, string(8bit)|void label) |
{ |
return (encrypt_mode ? encrypt(s, UNDEFINED, label) : decrypt(s, label)); |
} |
|
|
int block_size() |
{ |
|
return n->size(256) - 2*(hash_alg->digest_size() + 1); |
} |
|
string(8bit) encrypt(string(8bit) s, |
function(int(0..):string(8bit))|void r, |
string(8bit)|void label) |
{ |
if (!r) r = random; |
string(8bit) em = |
hash_alg->eme_oaep_encode(s, n->size(256), |
r(hash_alg->digest_size()), |
label); |
if (!em) error("Message too long.\n"); |
return [string(8bit)]sprintf("%*c", |
n->size(256), Gmp.mpz(em, 256)->powm(e, n)); |
} |
|
string(8bit) decrypt(string(8bit) s, string(8bit)|void label) |
{ |
if (sizeof(s) != n->size(256)) { |
error("Decryption error.\n"); |
} |
Gmp.mpz c = Gmp.mpz(s, 256); |
if (c >= n) { |
error("Decryption error.\n"); |
} |
string(8bit) m = |
hash_alg->eme_oaep_decode([string(8bit)] |
sprintf("%*c", n->size(256), c->powm(d, n)), |
label); |
if (!m) { |
error("Decryption error.\n"); |
} |
return m; |
} |
} |
} |
|
|
|
|
|
class PSSState { |
inherit OAEPState; |
|
local { |
|
this_program `PSS() { return this_program::this; } |
|
protected int(0..) default_salt_size = 20; |
|
|
|
|
|
|
|
|
|
|
Sequence pkcs_signature_algorithm_id(.Hash hash, int(0..)|void saltlen) |
{ |
if (undefinedp(saltlen)) saltlen = default_salt_size; |
return PKCS_RSA->pss_signature_algorithm_id(hash, saltlen); |
} |
|
string(7bit) name() { return "RSASSA-PSS"; } |
|
|
|
|
|
|
|
|
string(7bit) jwa(.Hash hash) |
{ |
switch(hash->name()) { |
case "sha256": |
return "PS256"; |
case "sha384": |
return "PS384"; |
case "sha512": |
return "PS512"; |
} |
return 0; |
} |
|
optional int(0..) salt_size() { return default_salt_size; } |
|
optional void set_salt_size(int(0..) salt_size) |
{ |
default_salt_size = salt_size; |
} |
|
protected int(0..1) _equal(mixed x) |
{ |
return objectp(x) && (object_program(x) == this_program) && |
(salt_size == ([object(this_program)]x)->salt_size) && |
::_equal(x); |
} |
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
string(8bit) pkcs_sign(string(8bit) message, .Hash h, |
string(8bit)|int(0..)|void salt) |
{ |
if (undefinedp(salt)) salt = default_salt_size; |
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
if (intp(salt)) { |
salt = random([int(0..)]salt); |
} |
string(8bit) em = |
h->emsa_pss_encode(message, [int(1..)](n->size()-1), |
[string(8bit)]salt); |
|
|
|
|
|
|
Gmp.mpz m = Gmp.smpz(em, 256); |
|
|
|
|
|
|
Gmp.mpz s = m->powm(d, n); |
|
|
|
|
|
|
|
return [string(8bit)]sprintf("%*c", n->size(256), s); |
} |
|
|
|
|
|
|
int(0..1) pkcs_verify(string(8bit) message, .Hash h, string(8bit) sign, |
int(0..)|void saltlen) |
{ |
if (undefinedp(saltlen)) saltlen = default_salt_size; |
|
|
|
if (sizeof(sign) != n->size(256)) { |
werror("Bad size\n"); |
return 0; |
} |
|
|
|
|
|
|
Gmp.mpz s = Gmp.smpz(sign, 256); |
|
|
|
|
|
|
Gmp.mpz m = s->powm(e, n); |
|
|
|
if (m >= n) { |
werror("Out of range\n"); |
return 0; |
} |
|
|
|
|
|
|
string(8bit) em = |
[string(8bit)]sprintf("%*c", [int(0..)]((n->size()+6)/8), m); |
|
|
|
|
|
|
|
|
|
|
|
|
|
|
return h->emsa_pss_verify(message, em, |
[int(1..)](n->size() - 1), saltlen); |
} |
} |
} |
|
|
|
|
|
class PKCS1_5State |
{ |
inherit PSSState; |
|
|
this_program `PKCS1_5() { return this_program::this; } |
|
|
|
Sequence pkcs_signature_algorithm_id(.Hash hash) |
{ |
return PKCS_RSA->signature_algorithm_id(hash); |
} |
|
|
|
Sequence pkcs_public_key() |
{ |
return PKCS_RSA->build_public_key(this); |
} |
|
#undef Sequence |
|
|
string(8bit) name() { return "RSA"; } |
|
|
|
|
|
|
|
|
string(7bit) jwa(.Hash hash) |
{ |
switch(hash->name()) { |
case "sha256": |
return "RS256"; |
case "sha384": |
return "RS384"; |
case "sha512": |
return "RS512"; |
} |
return 0; |
} |
|
|
|
|
|
string(8bit) pkcs_sign(string(8bit) message, .Hash h) |
{ |
string(8bit) di = Standards.PKCS.Signature.build_digestinfo(message, h); |
return [string(8bit)]sprintf("%*c", n->size(256), raw_sign(di)); |
} |
|
|
|
int(0..1) pkcs_verify(string(8bit) message, .Hash h, string(8bit) sign) |
{ |
if( sizeof(sign)!=n->size(256) ) return 0; |
string(8bit) s = Standards.PKCS.Signature.build_digestinfo(message, h); |
return raw_verify(s, Gmp.mpz(sign, 256)); |
} |
|
|
|
|
|
|
|
|
|
string(8bit) encrypt(string(8bit) s, function(int:string(8bit))|void r) |
{ |
return rsa_pad(s, 2, r)->powm(e, n)->digits(256); |
} |
|
|
string(8bit) decrypt(string(8bit) s) |
{ |
return rsa_unpad(Gmp.smpz(s, 256)->powm(d, n), 2); |
} |
|
|
|
|
|
|
|
|
string(8bit) crypt(string(8bit) s) |
{ |
return (encrypt_mode ? encrypt(s) : decrypt(s)); |
} |
|
|
int block_size() |
{ |
|
return n->size(256) - 3; |
} |
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
Gmp.mpz rsa_pad(string(8bit) message, int(1..2) type, |
function(int(0..):string(8bit))|void random) |
{ |
string(8bit) padding = ""; |
|
|
|
|
|
int len = n->size(256) - 3 - sizeof(message); |
if (len < 8) |
error( "Block too large. (%d>%d)\n", sizeof(message), n->size(256)-11 ); |
|
switch(type) |
{ |
case 1: |
padding = sprintf("%@c", allocate(len, 0xff)); |
break; |
case 2: |
if( !random ) random = this::random; |
do { |
padding += random([int(0..)](len-sizeof(padding))) - "\0"; |
} while( sizeof(padding)<len ); |
break; |
default: |
error( "Unknown type.\n" ); |
} |
return Gmp.smpz(sprintf("%c", type) + padding + "\0" + message, 256); |
} |
|
|
string(8bit) rsa_unpad(Gmp.mpz block, int type) |
{ |
string(8bit) s = block->digits(256); |
|
|
if( sizeof(s)!=(n->size(256)-1) ) return 0; |
|
int i = Nettle.rsa_unpad(s, type); |
if( !i ) return 0; |
|
return s[i..]; |
} |
|
|
|
|
Gmp.mpz raw_sign(string(8bit) digest) |
{ |
return rsa_pad(digest, 1, 0)->powm(d, n); |
} |
|
|
|
|
|
int(0..1) raw_verify(string(8bit) digest, Gmp.mpz s) |
{ |
return Gmp.smpz(s)->powm(e, n) == rsa_pad(digest, 1, 0); |
} |
} |
|
|
class State |
{ |
inherit PKCS1_5State; |
} |
|
|
protected State `()(mapping(string(8bit):Gmp.mpz|int)|void params) |
{ |
return State(params); |
} |
|
|