shithub: purgatorio

ref: d4f4b292ca9c8665ef938036340ff32bf5128755
dir: /appl/lib/crypt/pkcs.b/

View raw version
implement PKCS;

include "sys.m";
	sys				: Sys;

include "keyring.m";
	keyring				: Keyring;
	IPint				: import keyring;
	DESstate			: import keyring;

include "security.m";
	random				: Random;

include "asn1.m";
	asn1				: ASN1;
	Elem, Oid			: import asn1;

include "pkcs.m";

# pkcs object identifiers

objIdTab = array [] of {
	id_pkcs =>			Oid(array [] of {1,2,840,113549,1}),
	id_pkcs_1 =>			Oid(array [] of {1,2,840,113549,1,1}),
	id_pkcs_rsaEncryption => 	Oid(array [] of {1,2,840,113549,1,1,1}),
	id_pkcs_md2WithRSAEncryption => Oid(array [] of {1,2,840,113549,1,1,2}),
	id_pkcs_md4WithRSAEncryption => Oid(array [] of {1,2,840,113549,1,1,3}),
	id_pkcs_md5WithRSAEncryption =>	Oid(array [] of {1,2,840,113549,1,1,4}),

	id_pkcs_3 =>			Oid(array [] of {1,2,840,113549,1,3}),
	id_pkcs_dhKeyAgreement =>	Oid(array [] of {1,2,840,113549,1,3,1}),

	id_pkcs_5 =>			Oid(array [] of {1,2,840,113549,1,5}),
	id_pkcs_pbeWithMD2AndDESCBC => 	Oid(array [] of {1,2,840,113549,1,5,1}),
	id_pkcs_pbeWithMD5AndDESCBC =>	Oid(array [] of {1,2,840,113549,1,5,3}),

	id_pkcs_7 =>			Oid(array [] of {1,2,840,113549,1,7}),
	id_pkcs_data =>			Oid(array [] of {1,2,840,113549,1,7,1}),
	id_pkcs_singnedData => 		Oid(array [] of {1,2,840,113549,1,7,2}),
	id_pkcs_envelopedData =>	Oid(array [] of {1,2,840,113549,1,7,3}),
	id_pkcs_signedAndEnvelopedData => 	
					Oid(array [] of {1,2,840,113549,1,7,4}),
	id_pkcs_digestData =>		Oid(array [] of {1,2,840,113549,1,7,5}),
	id_pkcs_encryptedData =>	Oid(array [] of {1,2,840,113549,1,7,6}),

	id_pkcs_9 =>			Oid(array [] of {1,2,840,113549,1,9}),
	id_pkcs_emailAddress => 	Oid(array [] of {1,2,840,113549,1,9,1}),
	id_pkcs_unstructuredName =>	Oid(array [] of {1,2,840,113549,1,9,2}),
	id_pkcs_contentType =>		Oid(array [] of {1,2,840,113549,1,9,3}),
	id_pkcs_messageDigest =>	Oid(array [] of {1,2,840,113549,1,9,4}),
	id_pkcs_signingTime =>		Oid(array [] of {1,2,840,113549,1,9,5}),
	id_pkcs_countersignature =>	Oid(array [] of {1,2,840,113549,1,9,6}),
	id_pkcs_challengePassword =>	Oid(array [] of {1,2,840,113549,1,9,7}),
	id_pkcs_unstructuredAddress =>	Oid(array [] of {1,2,840,113549,1,9,8}),
	id_pkcs_extCertAttrs =>		Oid(array [] of {1,2,840,113549,1,9,9}),
	id_algorithm_shaWithDSS =>	Oid(array [] of {1,3,14,3,2,13})
};

# [public]
# initialize PKCS module

init(): string
{
	sys = load Sys Sys->PATH;
	if(sys == nil)
		return "load sys module failed";

	keyring = load Keyring Keyring->PATH;
	if(keyring == nil)
		return "load keyring module failed";

	random = load Random Random->PATH;
	if(random == nil)
		return "load random module failed";

	asn1 = load ASN1 ASN1->PATH;
	if(asn1 == nil)
		return "load asn1 module failed";
	asn1->init();

	return "";
}

# [public]
# Encrypt data according to PKCS#1, with given blocktype, using given key.

rsa_encrypt(data: array of byte, key: ref RSAKey, blocktype: int)
	: (string, array of byte)
{
	if(key == nil) 
		return ("null pkcs#1 key", nil);
	k := key.modlen;
	dlen := len data;
	if(k < 12 || dlen > k-11)
		return ("bad parameters for pkcs#1", nil);
	padlen := k-3-dlen;
	pad := random->randombuf(Random->NotQuiteRandom, padlen);
	for(i:=0; i < padlen; i++) {
		if(blocktype == 0)
			pad[i] = byte 0;
		else if(blocktype == 1)
			pad[i] = byte 16rff;
		else if(pad[i] == byte 0)
			pad[i] = byte 1;
	}
	eb := array[k] of byte;
	eb[0] = byte 0;
	eb[1] = byte blocktype;
	eb[2:] = pad[0:];
	eb[padlen+2] = byte 0;
	eb[padlen+3:] = data[0:];
	return ("", rsacomp(eb, key));
}

# [public]
# Decrypt data according to PKCS#1, with given key.
# If public is true, expect a block type of 0 or 1, else 2.

rsa_decrypt(data: array of byte, key: ref RSAKey, public: int) 
	: (string, array of byte)
{
	eb := rsacomp(data, key);
	k := key.modlen;
	if(len eb == k) {
		bt := int eb[1];
		if(int eb[0] == 0 && ((public && (bt == 0 || bt == 1)) || (!public && bt == 2))) {
			for(i := 2; i < k; i++)
				if(eb[i] == byte 0)
					break;
			if(i < k-1) {
				ans := array[k-(i+1)] of byte;
				ans[0:] = eb[i+1:];
				return ("", ans);
			}
		}
	}
	return ("pkcs1 decryption error", nil);
}

# [private]
# Do RSA computation on block according to key, and pad
# result on left with zeros to make it key.modlen long.

rsacomp(block: array of byte, key: ref RSAKey): array of byte
{
	x := keyring->IPint.bebytestoip(block);
	y := x.expmod(key.exponent, key.modulus);
	ybytes := y.iptobebytes();
	k := key.modlen;
	ylen := len ybytes;
	if(ylen < k) {
		a := array[k] of { * =>  byte 0};
		a[k-ylen:] = ybytes[0:];
		ybytes = a;
	}
	else if(ylen > k) {
		# assume it has leading zeros (mod should make it so)
		a := array[k] of byte;
		a[0:] = ybytes[ylen-k:];
		ybytes = a;
	}
	return ybytes;
}

# [public]

rsa_sign(data: array of byte, sk: ref RSAKey, algid: int): (string, array of byte)
{
	# digesting and add proper padding to it
	ph := padhash(data, algid);

	return rsa_encrypt(ph, sk, 0); # blocktype <- padding with zero
}

# [public]

rsa_verify(data, signature: array of byte, pk: ref RSAKey, algid: int): int
{
	# digesting and add proper padding to it
	ph := padhash(data, algid);
    
	(err, orig) := rsa_decrypt(signature, pk, 0); # blocktype ?
	if(err != "" || !byte_cmp(orig, ph))
		return 0;

	return 1;
}

# [private]
# padding block A
PA := array [] of {
	byte 16r30, byte 16r20, byte 16r30, byte 16r0c, 
	byte 16r06, byte 16r08, byte 16r2a, byte 16r86, 
	byte 16r48, byte 16r86, byte 16rf7, byte 16r0d, 
	byte 16r02
};

# [private]
# padding block B
PB := array [] of {byte 16r05, byte 16r00, byte 16r04, byte 16r10};

# [private]
# require either md5 or md2 of 16 bytes digest
# length of padded digest = 13 + 1 + 4 + 16

padhash(data: array of byte, algid: int): array of byte
{
	padded := array [34] of byte;
	case algid {
	MD2_WithRSAEncryption =>
		padded[13] = byte 2;
		# TODO: implement md2 in keyring module
		# keyring->md2(data, len data, padded[18:], nil);

	MD5_WithRSAEncryption =>	
		padded[13] = byte 5;
		keyring->md5(data, len data, padded[18:], nil);
	* =>
		return nil;
	}
	padded[0:] = PA;
	padded[14:] = PB;

	return padded;
}

# [private]
# compare byte to byte of two array of byte

byte_cmp(a, b: array of byte): int
{
	if(len a != len b)
		return 0;

	for(i := 0; i < len a; i++) {
		if(a[i] != b[i])
			return 0;
	}

	return 1;
}

# [public]

RSAKey.bits(key: self ref RSAKey): int
{
	return key.modulus.bits();
}

# [public]
# Decode an RSAPublicKey ASN1 type, defined as:
#
#	RSAPublickKey :: SEQUENCE {
#		modulus INTEGER,
#		publicExponent INTEGER
#	}

decode_rsapubkey(a: array of byte): (string, ref RSAKey)
{
parse:
	for(;;) {
		(err, e) := asn1->decode(a);
		if(err != "")
			break parse;
		(ok, el) := e.is_seq();
		if(!ok || len el != 2)
			break parse;
		modbytes, expbytes: array of byte;
		(ok, modbytes) = (hd el).is_bigint();
		if(!ok)
			break parse;
		modulus := IPint.bebytestoip(modbytes);
		# get modlen this way, because sometimes it
		# comes with leading zeros that are to be ignored!
		mbytes := modulus.iptobebytes();
		modlen := len mbytes;
		(ok, expbytes) = (hd tl el).is_bigint();
		if(!ok)
			break parse;
		exponent := keyring->IPint.bebytestoip(expbytes);
		return ("", ref RSAKey(modulus, modlen, exponent));
	}
	return ("rsa public key: syntax error", nil);
}


# [public]
# generate a pair of DSS public and private keys

generateDSSKeyPair(strength: int): (ref DSSPublicKey, ref DSSPrivateKey)
{
	# TODO: need add getRandBetween in IPint
	return (nil, nil);
}

# [public]

dss_sign(a: array of byte, sk: ref DSSPrivateKey): (string, array of byte)
{
	#signature, digest: array of byte;

	#case hash {
	#Keyring->MD4 =>
	#	digest = array [Keyring->MD4dlen] of byte;
	#	keyring->md4(a, len a, digest, nil);
	#Keyring->MD5 =>
	#	digest = array [Keyring->MD5dlen] of byte;
	#	keyring->md5(a, len a, digest, nil);
	#Keyring->SHA =>
	#	digest = array [Keyring->SHA1dlen] of byte;
	#	keyring->sha1(a, len a, digest, nil);
	#* =>
	#	return ("unknown hash algorithm", nil);
	#}

	# TODO: add gcd or getRandBetween in Keyring->IPint
	return ("unsupported error", nil);
}

# [public]

dss_verify(a, signa: array of byte, pk: ref DSSPublicKey): int
{
	unsigned: array of byte;

	#case hash {
	#Keyring->MD4 =>
	#	digest = array [Keyring->MD4dlen] of byte;
	#	keyring->md4(a, len a, digest, nil);
	#Keyring->MD5 =>
	#	digest = array [Keyring->MD5dlen] of byte;
	#	keyring->md5(a, len a, digest, nil);
	#Keyring->SHA =>
	#	digest = array [Keyring->SHA1dlen] of byte;
	#	keyring->sha1(a, len a, digest, nil);
	#* =>
	#	return 0;
	#}

	# get unsigned from signa and compare it with digest

	if(byte_cmp(unsigned, a))
		return 1;

	return 0;
}

# [public]
decode_dsspubkey(a: array of byte): (string, ref DSSPublicKey)
{
	return ("unsupported error", nil);
}


# [public]
# generate DH parameters with prime length at least (default) 512 bits

generateDHParams(primelen: int): ref DHParams
{
	# prime - at least 512 bits
	if(primelen < 512) # DHmodlen
		primelen = 512;

	# generate prime and base (generator) integers
	(p, g) := keyring->dhparams(primelen);
	if(p == nil || g == nil)
		return nil;

	return ref DHParams(p, g, 0);
}

# [public]
# generate public and private key pair
# Note: use iptobytes as integer to octet string conversion
#	and bytestoip as octect string to integer reversion

setupDHAgreement(dh: ref DHParams): (ref DHPrivateKey, ref DHPublicKey)
{
	if(dh == nil || dh.prime == nil || dh.base == nil)
		return (nil, nil);

	# prime length in bits
	bits := dh.prime.bits();

	# generate random private key of length between bits/4 and bits
	x := IPint.random(bits/4, bits);
	if(x == nil)
		return (nil, nil);
	dh.privateValueLength = x.bits();

	# calculate public key
	y := dh.base.expmod(x, dh.prime);
	if(y == nil)
		return (nil, nil);

	return (ref DHPrivateKey(dh, y, x), ref DHPublicKey(dh, x));
}

# [public]
# The second phase of Diffie-Hellman key agreement

computeDHAgreedKey(dh: ref DHParams, mysk, upk: ref IPint)
	: array of byte
{
	if(mysk == nil || upk == nil)
		return nil;

	# exponential - calculate agreed key (shared secret)
	z := upk.expmod(mysk, dh.prime);

	# integer to octet conversion
	return z.iptobebytes();
}

# [public]
# ASN1 encoding

decode_dhpubkey(a: array of byte): (string, ref DHPublicKey)
{
	return ("unsupported error", nil);
}


# [public]
# Digest the concatenation of password and salt with count iterations of
# selected message-digest algorithm (either md2 or md5).
# The first 8 bytes of the message digest become the DES key.
# The last 8 bytes of the message digest become the initializing vector IV.

generateDESKey(pw: array of byte, param: ref PBEParams, alg: int)
	: (ref DESstate, array of byte, array of byte)
{
	if(param.iterationCount < 1)
		return (nil, nil, nil);

	# concanate password and salt
	pwlen := len pw;
	pslen := pwlen + len param.salt;
	ps := array [pslen] of byte;
	ps[0:] = pw;
	ps[pwlen:] = param.salt;
	key, iv: array of byte;

	# digest iterations
	case alg {
	PBE_MD2_DESCBC =>
		ds : ref Keyring->DigestState = nil;
		# TODO: implement md2 in keyring module
		#result := array [Keyring->MD2dlen] of byte;
		#for(i := 0; i < param.iterationCount; i++)
		#	ds = keyring->md2(ps, pslen, nil, ds);	
		#keyring->md2(ps, pslen, result, ds);	
		#key = result[0:8];
		#iv = result[8:];

	PBE_MD5_DESCBC =>
		ds: ref Keyring->DigestState = nil;
		result := array [Keyring->MD5dlen] of byte;
		for(i := 0; i < param.iterationCount; i++) 
			ds = keyring->md5(ps, pslen, nil, ds);
		keyring->md5(ps, pslen, result, ds);
		key = result[0:8];
		iv = result[8:];

	* =>
		return (nil, nil, nil);
	}

	state := keyring->dessetup(key, iv);

	return (state, key, iv);
}

# [public]
# The message M and a padding string PS shall be formatted into
# an octet string EB
# 	EB = M + PS
# where
#	PS = 1 if M mod 8 = 7;
#	PS = 2 + 2 if M mod 8 = 6;
#	...
#	PS = 8 + 8 + 8 + 8 + 8 + 8 + 8 + 8 if M mod 8 = 0;

pbe_encrypt(state: ref DESstate, m: array of byte): array of byte
{
	mlen := len m;
	padvalue :=  mlen % 8;
	pdlen := 8 - padvalue;

	eb := array [mlen + pdlen] of byte;
	eb[0:] = m;
	for(i := mlen; i < pdlen; i++)
		eb[i] = byte padvalue;

	keyring->descbc(state, eb, len eb, Keyring->Encrypt);

	return eb;
}

# [public]

pbe_decrypt(state: ref DESstate, eb: array of byte): array of byte
{
	eblen := len eb;
	if(eblen%8 != 0) # must a multiple of 8 bytes
		return nil;

	keyring->descbc(state, eb, eblen, Keyring->Decrypt);	

	# remove padding
	for(i := eblen -8; i < 8; i++) {
		if(int eb[i] == i) {
			for(j := i; j < 8; j++)
				if(int eb[j] != i)
					break;
			if(j == 8)
				break;
		}
	}

	return eb[0:i];
}

# [public]

PrivateKeyInfo.encode(p: self ref PrivateKeyInfo): (string, array of byte)
{

	return ("unsupported error", nil);
}

# [public]

PrivateKeyInfo.decode(a: array of byte): (string, ref PrivateKeyInfo)
{
	return ("unsupported error", nil);
}

# [public]

EncryptedPrivateKeyInfo.encode(p: self ref EncryptedPrivateKeyInfo)
	: (string, array of byte)
{

	return ("unsupported error", nil);
}

# [public]

EncryptedPrivateKeyInfo.decode(a: array of byte)
	: (string, ref EncryptedPrivateKeyInfo)
{
	return ("unsupported error", nil);
}

# [public]

decode_extcertorcert(a: array of byte): (int, int, array of byte)
{
	(err, all) := asn1->decode(a);
	if(err == "") {

	}
}

# [public]

encode_extcertorcert(a: array of byte, which: int): (int, array of byte)
{

}