summaryrefslogtreecommitdiff
path: root/appl/lib
diff options
context:
space:
mode:
Diffstat (limited to 'appl/lib')
-rw-r--r--appl/lib/spki/spki.b329
-rw-r--r--appl/lib/spki/verifier.b16
2 files changed, 263 insertions, 82 deletions
diff --git a/appl/lib/spki/spki.b b/appl/lib/spki/spki.b
index 5446b0ab..18a2d68a 100644
--- a/appl/lib/spki/spki.b
+++ b/appl/lib/spki/spki.b
@@ -7,7 +7,6 @@ implement SPKI;
# - diagnostics
# - support for dsa
# - finish the TO DO
-# - might like a list of ref Hash for a Key
include "sys.m";
sys: Sys;
@@ -331,7 +330,7 @@ parseprincipal(e: ref Sexp): ref Key
hash := parsehash(e);
if(hash == nil)
return nil;
- return ref Key(nil, nil, 0, hash.alg, hash);
+ return ref Key(nil, nil, 0, nil, nil, hash::nil);
* =>
return nil;
}
@@ -354,9 +353,9 @@ parsekey(e: ref Sexp): ref Key
alg := hd fld;
if(nf > 1)
enc := hd tl fld; # signature hash encoding
+ mha := "sha1";
if(nf > 2)
- mha := hd tl tl fld; # signature hash algorithm
- kha := "md5"; # could be sha1 (TO DO)
+ mha = hd tl tl fld; # signature hash algorithm
kl := (hd l).args();
if(kl == nil)
return nil;
@@ -386,8 +385,8 @@ parsekey(e: ref Sexp): ref Key
return nil;
}
}
-#(ref Key(pk,nil,kha,nil)).hashed(kha); # TEST
- return ref Key(pk, sk, nbits, kha, ref Hash(kha, nil));
+#(ref Key(pk,nil,"md5",nil,nil)).hashed("md5"); # TEST
+ return ref Key(pk, sk, nbits, mha, enc, nil);
}
parsehash(e: ref Sexp): ref Hash
@@ -437,6 +436,49 @@ ckdate(s: string): string
return s;
}
+Toplev.sexp(top: self ref Toplev): ref Sexp
+{
+ pick t := top {
+ C =>
+ return t.v.sexp();
+ Sig =>
+ return t.v.sexp();
+ K =>
+ return t.v.sexp();
+ Seq =>
+ rels := rev(t.v);
+ els: list of ref Sexp;
+ for(; rels != nil; rels = tl rels)
+ els = (hd rels).sexp() :: els;
+ return ref Sexp.List(ref Sexp.String("sequence", nil) :: els);
+ * =>
+ raise "unexpected spki type";
+ }
+}
+
+Toplev.text(top: self ref Toplev): string
+{
+ return top.sexp().text();
+}
+
+Seqel.sexp(se: self ref Seqel): ref Sexp
+{
+ pick r := se {
+ C =>
+ return r.c.sexp();
+ K =>
+ return r.k.sexp();
+ O =>
+ return ref Sexp.List(ref Sexp.String("do",nil) :: ref Sexp.String(r.op,nil) :: r.args);
+ S =>
+ return r.sig.sexp();
+ E =>
+ return r.exp;
+ * =>
+ raise "unsupported value";
+ }
+}
+
Seqel.text(se: self ref Seqel): string
{
pick r := se {
@@ -445,14 +487,13 @@ Seqel.text(se: self ref Seqel): string
K =>
return r.k.text();
O =>
- e := ref Sexp.List(ref Sexp.String("do",nil) :: ref Sexp.String(r.op,nil) :: r.args);
- return e.text();
+ return se.sexp().text();
S =>
return r.sig.text();
E =>
return r.exp.text();
* =>
- return "unsupported";
+ raise "unsupported value";
}
}
@@ -513,22 +554,25 @@ checksig(c: ref Cert, sig: ref Signature): string
return "missing signature value";
pk := sig.key.pk;
if(pk == nil)
- return "missing Keyring->PK for signature"; # TO DO
+ return "missing Keyring->PK for signature"; # TO DO (need a way to tell that key was just a hash)
#rsacomp((hd sig.sig).t1, sig.key);
#sys->print("nbits= %d\n", sig.key.nbits);
(alg, enc, hashalg) := sig.algs();
if(alg == nil)
return "unspecified signature algorithm";
if(hashalg == nil)
- hashalg = "md5"; # TO DO
+ hashalg = "md5"; # TO DO?
+ hash := hashbytes(c.e.pack(), hashalg);
+ if(hash == nil)
+ return "unknown hash algorithm "+hashalg;
if(enc == nil)
- h := hashbytes(c.e.pack(), hashalg);
+ h := hash;
else if(enc == "pkcs" || enc == "pkcs1")
- h = pkcs1_encode(hashalg, c.e.pack(), (sig.key.nbits+7)/8);
+ h = pkcs1_encode(hashalg, hash, (sig.key.nbits+7)/8);
else
return "unknown encoding algorithm "+enc;
- if(h == nil)
- return "unknown hash algorithm "+hashalg;
+#dump("check/hashed", hash);
+#dump("check/h", h);
ip := IPint.bebytestoip(h);
isig := sig2icert(sig, "sdsi", 0);
if(isig == nil)
@@ -538,14 +582,62 @@ checksig(c: ref Cert, sig: ref Signature): string
return nil;
}
+signcert(c: ref Cert, sigalg: string, key: ref Key): (ref Signature, string)
+{
+ if(c.e == nil){
+ c.e = c.sexp();
+ if(c.e == nil)
+ return (nil, "bad input certificate");
+ }
+ return signbytes(c.e.pack(), sigalg, key);
+}
+
+#
+# might be useful to have a separate `signhash' for cases where the data was hashed elsewhere
+#
+signbytes(data: array of byte, sigalg: string, key: ref Key): (ref Signature, string)
+{
+ if(key.sk == nil)
+ return (nil, "missing Keyring->SK for signature");
+ pubkey := ref *key;
+ pubkey.sk = nil;
+ sig := ref Signature(nil, pubkey, sigalg, nil); # ref Hash, key, alg, sig: list of (string, array of byte)
+ (alg, enc, hashalg) := sigalgs(sigalg);
+ if(alg == nil)
+ return (nil, "unspecified signature algorithm");
+ if(hashalg == nil)
+ hashalg = "md5"; # TO DO?
+ hash := hashbytes(data, hashalg);
+ if(hash == nil)
+ return (nil, "unknown hash algorithm "+hashalg);
+ if(enc == nil)
+ h := hash;
+ else if(enc == "pkcs" || enc == "pkcs1")
+ h = pkcs1_encode(hashalg, hash, (sig.key.nbits+7)/8);
+ else
+ return (nil, "unknown encoding algorithm "+enc);
+#dump("sign/hashed", hash);
+#dump("sign/h", h);
+ sig.hash = ref Hash(hashalg, hash);
+ ip := IPint.bebytestoip(h);
+ icert := kr->signm(key.sk, ip, hashalg);
+ if(icert == nil)
+ return (nil, "signature failed"); # can't happen?
+ (nil, nil, nil, vals) := icert2els(icert);
+ if(vals == nil)
+ return (nil, "couldn't extract values from Keyring Certificate");
+ l: list of (string, array of byte);
+ for(; vals != nil; vals = tl vals){
+ (n, v) := hd vals;
+ l = (f2s(n), v) :: l;
+ }
+ sig.sig = revt(l);
+ return (sig, nil);
+}
+
hashexp(e: ref Sexp, alg: string): array of byte
{
- a := e.pack();
-#dump("inp a", a);
- hash := hashbytes(a, alg);
-#dump(alg, hash);
-#sys->print("%s = |%s|\n", alg, base64->enc(hash));
- return hash;
+ return hashbytes(e.pack(), alg);
}
hashbytes(a: array of byte, alg: string): array of byte
@@ -559,11 +651,12 @@ hashbytes(a: array of byte, alg: string): array of byte
hash = array[Keyring->SHA1dlen] of byte;
kr->sha1(a, len a, hash, nil);
* =>
- return nil;
+ raise "Spki->hashbytes: unknown algorithm: "+alg;
}
return hash;
}
+# trim mpint and add leading zero byte if needed to ensure value is unsigned
pre0(a: array of byte): array of byte
{
for(i:=0; i<len a-1; i++)
@@ -589,7 +682,13 @@ dump(s: string, a: array of byte)
Signature.algs(sg: self ref Signature): (string, string, string)
{
- (nf, flds) := sys->tokenize(sg.sa, "-");
+ return sigalgs(sg.sa);
+}
+
+# sig[-[enc-]hash]
+sigalgs(alg: string): (string, string, string)
+{
+ (nf, flds) := sys->tokenize(alg, "-");
if(nf >= 3)
return (hd flds, hd tl flds, hd tl tl flds);
if(nf >= 2)
@@ -613,7 +712,7 @@ Signature.sexp(sg: self ref Signature): ref Sexp
}
sv = ref Sexp.List(rev(l));
}else
- sv = ref Sexp.Binary((hd sg.sig).t1, nil);
+ sv = ref Sexp.Binary((hd sg.sig).t1, nil); # no list if signature has one component
if(sg.sa != nil)
sv = ref Sexp.List(ref Sexp.String(sg.sa,nil) :: sv :: nil);
return ref Sexp.List(ref Sexp.String("signature",nil) :: sg.hash.sexp() :: sg.key.sexp() ::
@@ -706,6 +805,8 @@ Cert.sexp(c: self ref Cert): ref Sexp
{
if(c == nil)
return nil;
+ if(c.e != nil)
+ return c.e;
ds, tag: ref Sexp;
pick d := c {
N =>
@@ -733,8 +834,12 @@ Subject.principal(s: self ref Subject): ref Key
return r.key;
N =>
return r.name.principal;
+ KH =>
+ return r.holder.principal;
+ O =>
+ return nil; # TO DO: need cache of hashed keys
* =>
- return nil; # TO DO
+ return nil; # TO DO? (no particular principal for threshold)
}
}
@@ -897,45 +1002,64 @@ Name.eq(a: self ref Name, b: ref Name): int
return nb == nil;
}
-Key.hashed(key: self ref Key, alg: string): array of byte
+Key.public(key: self ref Key): ref Key
{
- if(key.hash != nil && key.halg == alg && key.hash.hash != nil)
- return key.hash.hash;
- krp := Keyrep.pk(key.pk);
- if(krp == nil)
+ if(key.sk != nil){
+ pk := ref *key;
+ if(pk.pk == nil)
+ pk.pk = kr->sktopk(pk.sk);
+ pk.sk = nil;
+ return pk;
+ }
+ if(key.pk == nil)
return nil;
- n := krp.getb("n");
- e := krp.getb("e");
- if(n == nil || e == nil)
+ return key;
+}
+
+Key.ishash(k: self ref Key): int
+{
+ return k.hash != nil && k.sk == nil && k.pk == nil;
+}
+
+Key.hashed(key: self ref Key, alg: string): array of byte
+{
+ e := key.sexp();
+ if(e == nil)
return nil;
- ex := ref Sexp.List(
- ref Sexp.String("public-key", nil) ::
- ref Sexp.List(
- ref Sexp.String("rsa-pkcs1-"+alg, nil) ::
- ref Sexp.List(ref Sexp.String("e", nil) :: ref Sexp.Binary(e, nil) :: nil) ::
- ref Sexp.List(ref Sexp.String("n", nil) :: ref Sexp.Binary(n, nil) :: nil)
- :: nil)
- :: nil);
-# sys->print("=> %q %s\n", hd tl tl flds, ex.text());
- hash := hashexp(ex, alg);
- if((key.hash == nil || key.hash.hash == nil) && (key.halg == alg || key.halg == nil)){
- key.halg = alg;
- key.hash = ref Hash(alg, hash);
+ return hashexp(key.sexp(), alg);
+}
+
+Key.hashexp(key: self ref Key, alg: string): ref Hash
+{
+ if(key.hash != nil){
+ for(l := key.hash; l != nil; l = tl l){
+ h := hd l;
+ if(h.alg == alg && h.hash != nil)
+ return h;
+ }
}
- return hash;
+ hash := key.hashed(alg);
+ if(hash == nil)
+ return nil;
+ h := ref Hash(alg, hash);
+ key.hash = h :: key.hash;
+ return h;
}
Key.sigalg(k: self ref Key): string
{
- if(k.pk == nil || k.pk.sa == nil)
+ if(k.pk != nil)
+ alg := k.pk.sa.name;
+ else if(k.sk != nil)
+ alg = k.sk.sa.name;
+ else
return nil;
- halg := "";
- if(k.halg != nil)
- halg = "-"+k.halg;
- n := k.pk.sa.name;
- if(n == "rsa" || n == "dsa")
- return n+"-pkcs1"+halg;
- return n+halg;
+ if(k.halg != nil){
+ if(k.henc != nil)
+ alg += "-"+k.henc;
+ alg += "-"+k.halg;
+ }
+ return alg;
}
Key.text(k: self ref Key): string
@@ -948,8 +1072,11 @@ Key.text(k: self ref Key): string
Key.sexp(k: self ref Key): ref Sexp
{
- if(k.hash != nil && k.hash.hash != nil)
- return k.hash.sexp();
+ if(k.sk == nil && k.pk == nil){
+ if(k.hash != nil)
+ return (hd k.hash).sexp();
+ return nil;
+ }
sort := "public-key";
els: list of (string, ref IPint);
if(k.sk != nil){
@@ -980,8 +1107,14 @@ Key.eq(k1: self ref Key, k2: ref Key): int
return 1;
if(k1 == nil || k2 == nil)
return 0;
- if(k1.hash != nil && k2.hash != nil && k1.hash.eq(k2.hash))
- return 1;
+ for(hl1 := k1.hash; hl1 != nil; hl1 = tl hl1){
+ h1 := hd hl1;
+ for(hl2 := k2.hash; hl2 != nil; hl2 = tl hl2){
+ h2 := hd hl2;
+ if(h1.hash != nil && h1.eq(h2))
+ return 1;
+ }
+ }
if(k1.pk != nil && k2.pk != nil)
return kr->pktostr(k1.pk) == kr->pktostr(k2.pk); # TO DO
return 0;
@@ -1000,11 +1133,13 @@ dec(s: string, i: int, l: int): (int, int)
return (n, l);
}
-# TO DO: any valid prefix of a date
-
+# accepts at least any valid prefix of a date
date2epoch(t: string): int
{
- if(len t != 19)
+ # yyyy-mm-dd_hh:mm:ss
+ if(len t >= 4 && len t < 19)
+ t += "-01-01_00:00:00"[len t-4:]; # extend non-standard short forms
+ else if(len t != 19)
return -1;
tm := ref Daytime->Tm;
i: int;
@@ -1043,7 +1178,10 @@ epoch2date(t: int): string
time2secs(s: string): int
{
- if(len s != 8) # HH:MM:SS
+ # HH:MM:SS
+ if(len s >= 2 && len s < 8)
+ s += ":00:00"[len s-2:]; # extend non-standard short forms
+ else if(len s != 8)
return -1;
hh, mm, ss, i: int;
(hh, i) = dec(s, 0, 2);
@@ -1903,7 +2041,8 @@ revt[S,T](l: list of (S,T)): list of (S,T)
}
#
-# the following should probably be in a separate Limbo library module
+# the following should probably be in a separate Limbo library module,
+# or provided in some way directly by Keyring
#
Keyrep: adt {
@@ -1953,7 +2092,7 @@ Keyrep.pk(pk: ref Keyring->PK): ref Keyrep.PK
case hd flds {
"rsa" =>
return ref Keyrep.PK(hd flds, hd tl flds,
- keyextract(tl tl flds, list of {("e",1), ("n",0)}));
+ keyextract(tl tl flds, list of {("ek",1), ("n",0)}));
"elgamal" or "dsa" =>
return ref Keyrep.PK(hd flds, hd tl flds,
keyextract(tl tl flds, list of {("p",0), ("alpha",1), ("key",2)}));
@@ -1968,10 +2107,11 @@ Keyrep.sk(pk: ref Keyring->SK): ref Keyrep.SK
(nf, flds) := sys->tokenize(s, "\n");
if((nf -= 2) < 0)
return nil;
+ # the ordering of components below should match the one defined in the spki spec
case hd flds {
"rsa" =>
return ref Keyrep.SK(hd flds, hd tl flds,
- keyextract(tl tl flds,list of {("e",1), ("n",0), ("!dk",2), ("!p",3), ("!q",4), ("!kp",5), ("!kq",6), ("!c2",7)}));
+ keyextract(tl tl flds,list of {("ek",1), ("n",0), ("!dk",2), ("!q",4), ("!p",3), ("!kq",6), ("!kp",5), ("!c2",7)})); # see comment elsewhere about p, q
"elgamal" or "dsa" =>
return ref Keyrep.SK(hd flds, hd tl flds,
keyextract(tl tl flds, list of {("p",0), ("alpha",1), ("key",2), ("!secret",3)}));
@@ -2001,7 +2141,7 @@ Keyrep.mkpk(k: self ref Keyrep): (ref Keyring->PK, int)
{
case k.alg {
"rsa" =>
- e := k.get("e");
+ e := k.get("ek");
n := k.get("n");
if(e == nil || n == nil)
return (nil, 0);
@@ -2015,7 +2155,7 @@ Keyrep.mksk(k: self ref Keyrep): ref Keyring->SK
{
case k.alg {
"rsa" =>
- e := k.get("e");
+ e := k.get("ek");
n := k.get("n");
dk := k.get("!dk");
p := k.get("!p");
@@ -2040,11 +2180,12 @@ Keyrep.mksk(k: self ref Keyrep): ref Keyring->SK
s2f(s: string): string
{
case s {
+ "e" => return "ek";
"d" => return "!dk";
- "p" => return "!p";
- "q" => return "!q";
- "a" => return "!kp";
- "b" => return "!kq";
+ "p" => return "!q"; # NB: p and q (kp and kq) roles are reversed between libsec and pkcs
+ "q" => return "!p";
+ "a" => return "!kq";
+ "b" => return "!kp";
"c" => return "!c2";
* => return s;
}
@@ -2053,9 +2194,12 @@ s2f(s: string): string
f2s(s: string): string
{
case s {
+ "ek" => return "e";
+ "!p" => return "q"; # see above
+ "!q" => return "p";
"!dk" => return "d";
- "!kp" => return "a";
- "!kq" => return "b";
+ "!kp" => return "b";
+ "!kq" => return "a";
"!c2" => return "c";
* =>
if(s != nil && s[0] == '!')
@@ -2088,6 +2232,36 @@ sig2icert(sig: ref Signature, signer: string, exp: int): ref Keyring->Certificat
return kr->strtocert(s);
}
+icert2els(cert: ref Keyring->Certificate): (string, string, string, list of (string, array of byte))
+{
+ s := kr->certtoattr(cert);
+ if(s == nil)
+ return (nil, nil, nil, nil);
+ (nil, l) := sys->tokenize(s, " "); # really need parseattr, and a better interface
+ vals: list of (string, array of byte);
+ alg, hashalg, signer: string;
+ for(; l != nil; l = tl l){
+ (nf, fld) := sys->tokenize(hd l, "=");
+ if(nf != 2)
+ continue;
+ case hd fld {
+ "sigalg" =>
+ (nf, fld) = sys->tokenize(hd tl fld, "-");
+ if(nf != 2)
+ continue;
+ alg = hd fld;
+ hashalg = hd tl fld;
+ "signer" =>
+ signer = hd tl fld;
+ "expires" =>
+ ; # don't care
+ * =>
+ vals = (hd fld, base16->dec(hd tl fld)) :: vals;
+ }
+ }
+ return (alg, hashalg, signer, revt(vals));
+}
+
#
# pkcs1 asn.1 DER encodings
#
@@ -2121,20 +2295,15 @@ pkcs1_sha1_pfx := array[] of {
#
# mlen should be key length in bytes
#
-pkcs1_encode(ha: string, msg: array of byte, mlen: int): array of byte
+pkcs1_encode(ha: string, hash: array of byte, mlen: int): array of byte
{
# apply hash function to message
- hash: array of byte;
prefix: array of byte;
case ha {
"md5" =>
prefix = pkcs1_md5_pfx;
- hash = array[Keyring->MD5dlen] of byte;
- kr->md5(msg, len msg, hash, nil);
"sha" or "sha1" =>
prefix = pkcs1_sha1_pfx;
- hash = array[Keyring->SHA1dlen] of byte;
- kr->sha1(msg, len msg, hash, nil);
* =>
return nil;
}
diff --git a/appl/lib/spki/verifier.b b/appl/lib/spki/verifier.b
index d712fd2a..ffcdfcad 100644
--- a/appl/lib/spki/verifier.b
+++ b/appl/lib/spki/verifier.b
@@ -50,16 +50,27 @@ putkey(keys: list of ref Key, k: ref Key): list of ref Key
return k :: keys;
}
-keybyhash(h: ref Hash, keys: list of ref Key): ref Key
+keybyhash(hl: list of ref Hash, keys: list of ref Key): ref Key
{
for(kl := keys; kl != nil; kl = tl kl){
k := hd kl;
- if(k.hash != nil && h.eq(k.hash))
+ if(k.hash != nil && anyhashmatch(hl, k.hash))
return k;
}
return nil;
}
+anyhashmatch(hl1, hl2: list of ref Hash): int
+{
+ for(; hl1 != nil; hl1 = tl hl1){
+ h1 := hd hl1;
+ for(; hl2 != nil; hl2 = tl hl2)
+ if(h1.eq(hd hl2))
+ return 1;
+ }
+ return 0;
+}
+
verify(seq: list of ref Seqel): (ref Speaksfor, list of ref Seqel, string)
{
stack: list of ref Seqel;
@@ -184,5 +195,6 @@ verify(seq: list of ref Seqel): (ref Speaksfor, list of ref Seqel, string)
checkcert(c: ref Cert): string
{
+ # TO DO?
return nil;
}