summaryrefslogtreecommitdiff
path: root/appl/lib/crypt/pkcs.b
diff options
context:
space:
mode:
authorCharles.Forsyth <devnull@localhost>2006-12-22 17:07:39 +0000
committerCharles.Forsyth <devnull@localhost>2006-12-22 17:07:39 +0000
commit37da2899f40661e3e9631e497da8dc59b971cbd0 (patch)
treecbc6d4680e347d906f5fa7fca73214418741df72 /appl/lib/crypt/pkcs.b
parent54bc8ff236ac10b3eaa928fd6bcfc0cdb2ba46ae (diff)
20060303a
Diffstat (limited to 'appl/lib/crypt/pkcs.b')
-rw-r--r--appl/lib/crypt/pkcs.b572
1 files changed, 572 insertions, 0 deletions
diff --git a/appl/lib/crypt/pkcs.b b/appl/lib/crypt/pkcs.b
new file mode 100644
index 00000000..e74391ca
--- /dev/null
+++ b/appl/lib/crypt/pkcs.b
@@ -0,0 +1,572 @@
+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)
+{
+
+}
+