M cmd/okp/main.ha => cmd/okp/main.ha +2 -2
@@ 177,7 177,7 @@ fn loadkey(path: str) openpgp::key = {
//defer close_file(&f);
// TODO ascii armor
- match (openpgp::parse_pubkey(&f)) {
+ match (openpgp::parse_key(&f)) {
case let p: openpgp::key =>
return p;
case let e: openpgp::error =>
@@ 247,7 247,7 @@ fn load_privkey(path: str) openpgp::key = {
//defer close_file(&f);
// TODO ascii armor
- match (openpgp::parse_privkey(&f)) {
+ match (openpgp::parse_key(&f)) {
case let p: openpgp::key =>
return p;
case let e: openpgp::error =>
M format/openpgp/errors.ha => format/openpgp/errors.ha +15 -3
@@ 1,11 1,13 @@
use io;
-export type unsupported = !void;
+export type unsupported = !str;
export type badsig = !void;
export type invalid = !void;
+export type unexpected = !void;
+export type weakhash = !void;
-export type error = !(io::error | unsupported | badsig | invalid);
+export type error = !(io::error | unsupported | badsig | invalid | unexpected | weakhash);
export fn strerror(e: error) str = {
match (e) {
@@ 14,8 16,18 @@ export fn strerror(e: error) str = {
case badsig =>
return "Signature verification failed";
case invalid =>
- return "Invalid";
+ return "Invalid data format";
+ case unexpected =>
+ return "Unexpected data";
+ case weakhash =>
+ return "Signature on key or data is done using a weak hash function and can not be trusted.";
case let e: io::error =>
return io::strerror(e);
};
};
+
+let unsupported_version: unsupported = "Unsupported file version";
+let unsupported_encalgo: unsupported = "Unsupported encryption algorithm";
+let unsupported_hashalgo: unsupported = "Unsupported hash algorithm";
+let unsupported_fp: unsupported = "Unsupported fingerprint method";
+let unsupported_critsub: unsupported = "Unsupported critical signature sub-packet";
M format/openpgp/key.ha => format/openpgp/key.ha +3 -195
@@ 47,7 47,7 @@ export type key = struct {
export type subkey = struct {
key: *keypair,
- // TODO signature
+ // TODO signatures
// TODO revocation
};
@@ 101,7 101,7 @@ fn parse_s2k(src: io::handle) (s2k | error) = {
};
if (s.s2ktype != s2ktype::SIMPLE && s.s2ktype != s2ktype::SALTED
&& s.s2ktype != s2ktype::ITERATED_AND_SALTED) {
- return unsupported;
+ abort("todo");
};
if (s.s2ktype == s2ktype::SALTED
@@ 127,11 127,10 @@ fn ivsz(algo: u8) (size | error) = {
case sencalgo::AES256 =>
return 32z;
case =>
- return unsupported;
+ return unsupported_encalgo;
};
};
-
fn keypair_verify(
k: *keypair,
sig: io::handle,
@@ 159,194 158,3 @@ fn keypair_finish(k: *keypair) void = {
k.vtable.finish(k);
};
-export type rsakey = struct {
- key: keypair,
- pub: []u8,
- priv: []u8,
-};
-
-export fn newrsakey(created: date::date) rsakey = rsakey {
- key = keypair {
- vtable = &rsakey_vtable,
- algo = 1,
- priv = false,
- created = created,
- fp = [0...],
- s2k = null,
- },
- ...
-};
-
-const rsakey_vtable = keypair_vtable {
- read_pub = &rsa_read_pub,
- read_priv = &rsa_read_priv,
- sign = &rsa_sign,
- verify = &rsa_verify,
- finish = &rsa_finish,
- keygrip = &rsa_keygrip,
-};
-
-fn rsa_read_pub(k: *keypair, src: io::handle) (void | error) = {
- let k = k: *rsakey;
- let pub = rsa::pubparams { ... };
-
- pub.n = readmpi(src)?;
- defer free(pub.n);
- pub.e = readmpi(src)?;
- defer free(pub.e);
-
- assert(len(k.pub) == 0, "can not reassign keys");
- k.pub = alloc([0...], rsa::PUBKEYSZ);
- match (rsa::pubkey_init(k.pub, pub)) {
- case size =>
- yield;
- case rsa::error =>
- free(k.pub);
- return errors::invalid;
- };
-};
-
-fn rsa_read_priv(k: *keypair, src: io::handle) (void | error) = {
- let k = k: *rsakey;
-
- let priv = rsa::privparams { ... };
- let d = readmpi(src)?;
- defer {
- bytes::zero(d);
- free(d);
- };
- priv.q = readmpi(src)?;
- defer {
- bytes::zero(priv.q);
- free(priv.q);
- };
- priv.p = readmpi(src)?;
- defer {
- bytes::zero(priv.p);
- free(priv.p);
- };
- priv.iq = readmpi(src)?;
- defer {
- bytes::zero(priv.iq);
- free(priv.iq);
- };
-
- let check: [2]u8 = [0...];
- readall(src, check)?;
- bytes::zero(check);
-
- let pub = rsa::pubkey_params(k.pub);
-
- k.priv = alloc([0...], rsa::PRIVKEYSZ);
- match (rsa::privkey_initd(k.priv, priv, d, pub.n)) {
- case size =>
- yield;
- case rsa::error =>
- bytes::zero(k.priv);
- free(k.priv);
- return errors::invalid;
- };
-};
-
-fn rsa_verify(
- k: *keypair,
- sig: io::handle,
- hashsum: []u8,
- hashalgo: u8,
-) (void | error) = {
- let k = k: *rsakey;
- let buf: [rsa::PKCS1_VERIFYBUFSZ]u8 = [0...];
- let sig = io::drain(sig)?;
- defer free(sig);
-
- // first two bytes contain the length of the signature in bits
- const nsize = endian::begetu16(sig[..2]);
- if (nsize > rsa::pubkey_nbitlen(k.pub)) {
- return badsig;
- };
-
- let sig = sig[2..];
- const algo = rsa_hashalgo(hashalgo)?;
-
- let r = rsa::pkcs1_verify(k.pub, hashsum, sig, algo, buf);
- if (r is rsa::error) {
- return badsig;
- };
-};
-
-fn rsa_hashalgo(ha: u8) (rsa::pkcs1_hashalgo | error) = {
- switch (ha) {
- case hashalgo::SHA256 =>
- return rsa::pkcs1_hashalgo::SHA256;
- case hashalgo::SHA512 =>
- return rsa::pkcs1_hashalgo::SHA512;
- case =>
- return unsupported;
- };
-};
-
-fn rsa_sign(
- k: *keypair,
- hashsum: []u8,
- hashalgo: u8,
- sig: io::handle,
-) (size | error) = {
- let k = k: *rsakey;
- let buf: [rsa::PKCS1_SIGNBUFSZ]u8 = [0...];
-
- const nsize = rsa::privkey_nsize(k.priv);
- let sigbuf: []u8 = alloc([0...], nsize);
- defer free(sigbuf);
-
- const algo = rsa_hashalgo(hashalgo)?;
-
- match (rsa::pkcs1_sign(k.priv, hashsum, sigbuf, algo, buf)) {
- case rsa::error =>
- return badsig;
- case void =>
- yield;
- };
-
- // Natural occuring computational errors during the signature generation
- // can lead to a complete compromise. Therefore to work around this
- // issue the signature will be verified before writing it to 'sink'.
- //
- // See "Passive SSH Key Compromise via Lattices"
- // https://eprint.iacr.org/2023/1711
- match (rsa::pkcs1_verify(k.pub, hashsum, sigbuf, algo, buf)) {
- case rsa::error =>
- return badsig;
- case void =>
- yield;
- };
-
- const nbitlen = rsa::privkey_nbitlen(k.priv);
- let n = io::writeall(sig, [(nbitlen >> 8): u8, nbitlen: u8])?;
- n += io::writeall(sig, sigbuf)?;
- return n;
-};
-
-fn rsa_finish(k: *keypair) void = {
- let k = k: *rsakey;
- bytes::zero(k.priv);
- free(k.priv);
- free(k.pub);
-};
-
-// TODO pass kg by reference to write to?
-fn rsa_keygrip(k: *keypair) [KEYGRIPSZ]u8 = {
- let kg: [KEYGRIPSZ]u8 = [0...];
-
- let params = rsa::pubkey_params((k: *rsakey).pub);
- let n = params.n;
- let i = 0z;
- for (i < len(n) && n[i] == 0; i += 1) continue;
-
- let h = sha1::sha1();
- if (n[i] & 0x80 == 0x80) {
- io::write(&h, [0u8])!;
- };
- io::write(&h, n[i..])!;
- hash::sum(&h, kg);
- return kg;
-};
M format/openpgp/multihash.ha => format/openpgp/multihash.ha +51 -2
@@ 1,17 1,23 @@
+use crypto::sha1;
use crypto::sha256;
use crypto::sha512;
use hash;
use io;
+def MAX_HASHSZ = sha512::SZ;
+
const multihash_vt = io::vtable {
writer = &multihash_writer,
+ closer = &multihash_closer,
...
};
type multihash = struct {
hash::hash,
+ sha1: sha1::state,
sha256: sha256::state,
sha512: sha512::digest,
+ selected: (void | hashalgo),
};
fn newmultihash() multihash = multihash {
@@ 20,22 26,65 @@ fn newmultihash() multihash = multihash {
reset = null,
sz = 0,
bsz = 0,
+ sha1 = sha1::sha1(),
sha256 = sha256::sha256(),
sha512 = sha512::sha512(),
+ selected = void,
};
fn multihash_writer(s: *io::stream, buf: const []u8) (size | io::error) = {
let mh = s: *multihash;
+ io::write(&mh.sha1, buf)!;
io::write(&mh.sha256, buf)!;
io::write(&mh.sha512, buf)!;
return len(buf);
};
+fn multihash_select(h: *multihash, algo: u8) (void | unsupported) = {
+ switch (algo) {
+ case hashalgo::SHA1, hashalgo::SHA256, hashalgo::SHA512 =>
+ yield;
+ case =>
+ return unsupported_hashalgo;
+ };
+
+ h.selected = algo: hashalgo;
+ let sh = multihash_selected(h);
+ h.sz = hash::sz(sh);
+ h.bsz = hash::bsz(sh);
+};
+
+fn multihash_selected(h: *multihash) *hash::hash = {
+ match (h.selected) {
+ case void =>
+ abort("hash not selected");
+ case let a: hashalgo =>
+ switch (a) {
+ case hashalgo::SHA1 =>
+ return &h.sha1;
+ case hashalgo::SHA256 =>
+ return &h.sha256;
+ case hashalgo::SHA512 =>
+ return &h.sha512;
+ case =>
+ abort("unsupported in select");
+ };
+ };
+};
+
fn multihash_sum(h: *hash::hash, buf: []u8) void = {
- let mh = h: *multihash;
- hash::sum(&mh.sha256, buf);
+ let h = h: *multihash;
+ let hs = multihash_selected(h);
+ hash::sum(hs, buf);
};
fn multihash_clone(mh: *multihash) multihash = {
return *mh;
};
+
+fn multihash_closer(h: *io::stream) (void | io::error) = {
+ let h = h: *multihash;
+ hash::close(&h.sha1);
+ hash::close(&h.sha256);
+ hash::close(&h.sha512);
+};
M format/openpgp/reader.ha => format/openpgp/reader.ha +68 -31
@@ 15,9 15,9 @@ use time::chrono;
use time::date;
use time;
-// use debug::*;
+use debug::*;
-type reader = struct {
+export type reader = struct {
h: io::handle,
next: (void | packet),
};
@@ 80,7 80,9 @@ fn next(d: *reader) (packet | error) = {
};
fn hasnext(d: *reader) (bool | error) = {
+ print("hasnext");
if (d.next is packet) {
+ print("cached");
return true;
};
@@ 305,19 307,38 @@ fn pkt_parseuserid(p: *packet, mh: *multihash) (str | error) = {
};
};
-export fn parse_pubkey(h: io::handle) (key | error) = {
- return parse_key(h, false);
+export type keyiter = struct {
+ r: reader,
};
-export fn parse_privkey(h: io::handle) (key | error) = {
- return parse_key(h, true);
+export fn load_keys(h: io::handle) keyiter = keyiter {
+ r = newreader(h),
};
-fn parse_key(h: io::handle, priv: bool) (key | error) = {
+export fn parse_key(h: io::handle) (key | void | error) = {
+ let iter = load_keys(h);
+ return keys_next(&iter);
+};
+
+export fn keys_next(iter: *keyiter) (key | void | error) = {
+ print("Next");
let mh = newmultihash();
- let d = newreader(h);
+ let g = &iter.r;
- let p = next(&d)?;
+ if (!hasnext(g)?) {
+ return void;
+ };
+ let p = next(g)?;
+ print(p.tag: u8);
+
+ const priv = switch (p.tag) {
+ case tag::SECRET_KEY =>
+ yield true;
+ case tag::PUBLIC_KEY =>
+ yield false;
+ case =>
+ return unexpected;
+ };
let k = key {
primary = pkt_parse_key(&p, &mh, priv)?,
@@ 328,29 349,41 @@ fn parse_key(h: io::handle, priv: bool) (key | error) = {
let valid = false;
defer if (!valid) key_finish(&k);
- for (hasnext(&d)? && peek(&d) == tag::SIGNATURE) {
- p = next(&d)?;
+ for (hasnext(g)? && peek(g) == tag::SIGNATURE) {
+ p = next(g)?;
// TODO Zero or more revocation signatures
//println("TODO: REVOCATION SIGNATURE")!;
pkt_dump(&p);
};
- if (!hasnext(&d)? || peek(&d) != tag::USER_ID) {
+ if (!hasnext(g)? || peek(g) != tag::USER_ID) {
return invalid;
};
- for (hasnext(&d)? && peek(&d) == tag::USER_ID) {
+ print("parse user ids");
+ for (hasnext(g)? && peek(g) == tag::USER_ID) {
+ print(" user id");
let mh = multihash_clone(&mh);
- p = next(&d)?;
+ p = next(g)?;
let id = pkt_parseuserid(&p, &mh)?;
append(k.userids, userid { id = id });
- if (hasnext(&d)? && peek(&d) == tag::SIGNATURE) {
- p = next(&d)?;
+ for (hasnext(g)? && peek(g) == tag::SIGNATURE) {
+ let mh = multihash_clone(&mh);
+ print(" sig");
+ p = next(g)?;
let sig = pkg_parsesig(&p)?;
defer sig_free(&sig);
- verify(&sig, &mh, &k)?;
+ multihash_select(&mh, sig.hashalgo)?;
+ // TODO figure out self signed signatures, verify
+ // and mark them
+ //verify(&sig, &mh, &k)?;
+ //verified
+ // TODO parse unsupported signatures and mark them
+ // as not verified, or implement DSA?
};
+ // TODO Sig type - Positive certification of a User ID and Public Key packet(0x13).
+ // TODO Sig type - Generic certification of a User ID and Public Key packet(0x10).
};
// TODO primary key attributes:
@@ 382,12 415,13 @@ fn parse_key(h: io::handle, priv: bool) (key | error) = {
// contain an issuer subpacket for each key, as a way of explicitly
// tying those keys to the signature.
+ print("parse user attributes");
+ for (hasnext(g)? && peek(g) == tag::USER_ATTRIBUTE) {
+ print(" has attr");
+ p = next(g)?;
- for (hasnext(&d)? && peek(&d) == tag::USER_ATTRIBUTE) {
- p = next(&d)?;
-
- if (hasnext(&d)? && peek(&d) == tag::SIGNATURE) {
- p = next(&d)?;
+ if (hasnext(g)? && peek(g) == tag::SIGNATURE) {
+ p = next(g)?;
};
};
@@ 404,30 438,33 @@ fn parse_key(h: io::handle, priv: bool) (key | error) = {
// used only for certifying subkeys that are used for encryption and
// signatures.
+ print("parse sub keys");
const subkeytag = if (priv) tag::SECRET_SUBKEY else tag::PUBLIC_SUBKEY;
- for (hasnext(&d)? && peek(&d) == subkeytag) {
+ for (hasnext(g)? && peek(g) == subkeytag) {
+ print(" - subkey");
let mh = multihash_clone(&mh);
- p = next(&d)?;
+ p = next(g)?;
let sk = pkt_parse_key(&p, &mh, priv)?;
append(k.sub, subkey { key = sk });
- p = next(&d)?;
+ p = next(g)?;
let sig = pkg_parsesig(&p)?;
+ multihash_select(&mh, sig.hashalgo)?;
defer sig_free(&sig);
+ if (sig.sigtype != sigtype::SUBKEY_BINDING_SIG) {
+ abort("unexpected subkey sig");
+ };
verify(&sig, &mh, &k)?;
- if (hasnext(&d)? && peek(&d) == tag::SIGNATURE) {
- p = next(&d)?;
+ if (hasnext(g)? && peek(g) == tag::SIGNATURE) {
+ p = next(g)?;
pkt_dump(&p);
};
};
- if (hasnext(&d)?) {
- return invalid;
- };
-
+ print("done, next is", (g.next: packet).tag: u8);
valid = true;
return k;
};
A format/openpgp/rsa.ha => format/openpgp/rsa.ha +205 -0
@@ 0,0 1,205 @@
+use bytes;
+use crypto::rsa;
+use crypto::sha1;
+use crypto::sha256;
+use crypto::sha512;
+use time::date;
+use endian;
+use errors;
+use hash;
+use io;
+
+export type rsakey = struct {
+ key: keypair,
+ pub: []u8,
+ priv: []u8,
+};
+
+fn newrsakey(created: date::date) rsakey = rsakey {
+ key = keypair {
+ vtable = &rsakey_vtable,
+ algo = 1,
+ priv = false,
+ created = created,
+ fp = [0...],
+ s2k = null,
+ },
+ ...
+};
+
+const rsakey_vtable = keypair_vtable {
+ read_pub = &rsa_read_pub,
+ read_priv = &rsa_read_priv,
+ sign = &rsa_sign,
+ verify = &rsa_verify,
+ finish = &rsa_finish,
+ keygrip = &rsa_keygrip,
+};
+
+fn rsa_read_pub(k: *keypair, src: io::handle) (void | error) = {
+ let k = k: *rsakey;
+ let pub = rsa::pubparams { ... };
+
+ pub.n = readmpi(src)?;
+ defer free(pub.n);
+ pub.e = readmpi(src)?;
+ defer free(pub.e);
+
+ assert(len(k.pub) == 0, "can not reassign keys");
+ k.pub = alloc([0...], rsa::PUBKEYSZ);
+ match (rsa::pubkey_init(k.pub, pub)) {
+ case size =>
+ yield;
+ case rsa::error =>
+ free(k.pub);
+ return errors::invalid;
+ };
+};
+
+fn rsa_read_priv(k: *keypair, src: io::handle) (void | error) = {
+ let k = k: *rsakey;
+
+ let priv = rsa::privparams { ... };
+ let d = readmpi(src)?;
+ defer {
+ bytes::zero(d);
+ free(d);
+ };
+ priv.q = readmpi(src)?;
+ defer {
+ bytes::zero(priv.q);
+ free(priv.q);
+ };
+ priv.p = readmpi(src)?;
+ defer {
+ bytes::zero(priv.p);
+ free(priv.p);
+ };
+ priv.iq = readmpi(src)?;
+ defer {
+ bytes::zero(priv.iq);
+ free(priv.iq);
+ };
+
+ let check: [2]u8 = [0...];
+ readall(src, check)?;
+ bytes::zero(check);
+
+ let pub = rsa::pubkey_params(k.pub);
+
+ k.priv = alloc([0...], rsa::PRIVKEYSZ);
+ match (rsa::privkey_initd(k.priv, priv, d, pub.n)) {
+ case size =>
+ yield;
+ case rsa::error =>
+ bytes::zero(k.priv);
+ free(k.priv);
+ return errors::invalid;
+ };
+};
+
+fn rsa_verify(
+ k: *keypair,
+ sig: io::handle,
+ hashsum: []u8,
+ hashalgo: u8,
+) (void | error) = {
+ let k = k: *rsakey;
+ let buf: [rsa::PKCS1_VERIFYBUFSZ]u8 = [0...];
+ let sig = io::drain(sig)?;
+ defer free(sig);
+
+ // first two bytes contain the length of the signature in bits
+ const nsize = endian::begetu16(sig[..2]);
+ if (nsize > rsa::pubkey_nbitlen(k.pub)) {
+ return badsig;
+ };
+
+ let sig = sig[2..];
+ const algo = rsa_hashalgo(hashalgo)?;
+
+ let r = rsa::pkcs1_verify(k.pub, hashsum, sig, algo, buf);
+ if (r is rsa::error) {
+ return badsig;
+ };
+};
+
+fn rsa_hashalgo(ha: u8) (rsa::pkcs1_hashalgo | error) = {
+ switch (ha) {
+ case hashalgo::SHA1 =>
+ return rsa::pkcs1_hashalgo::SHA1;
+ case hashalgo::SHA256 =>
+ return rsa::pkcs1_hashalgo::SHA256;
+ case hashalgo::SHA512 =>
+ return rsa::pkcs1_hashalgo::SHA512;
+ case =>
+ return unsupported_hashalgo;
+ };
+};
+
+fn rsa_sign(
+ k: *keypair,
+ hashsum: []u8,
+ hashalgo: u8,
+ sig: io::handle,
+) (size | error) = {
+ let k = k: *rsakey;
+ let buf: [rsa::PKCS1_SIGNBUFSZ]u8 = [0...];
+
+ const nsize = rsa::privkey_nsize(k.priv);
+ let sigbuf: []u8 = alloc([0...], nsize);
+ defer free(sigbuf);
+
+ const algo = rsa_hashalgo(hashalgo)?;
+
+ match (rsa::pkcs1_sign(k.priv, hashsum, sigbuf, algo, buf)) {
+ case rsa::error =>
+ return badsig;
+ case void =>
+ yield;
+ };
+
+ // Natural occuring computational errors during the signature generation
+ // can lead to a complete compromise. Therefore to work around this
+ // issue the signature will be verified before writing it to 'sink'.
+ //
+ // See "Passive SSH Key Compromise via Lattices"
+ // https://eprint.iacr.org/2023/1711
+ match (rsa::pkcs1_verify(k.pub, hashsum, sigbuf, algo, buf)) {
+ case rsa::error =>
+ return badsig;
+ case void =>
+ yield;
+ };
+
+ const nbitlen = rsa::privkey_nbitlen(k.priv);
+ let n = io::writeall(sig, [(nbitlen >> 8): u8, nbitlen: u8])?;
+ n += io::writeall(sig, sigbuf)?;
+ return n;
+};
+
+fn rsa_finish(k: *keypair) void = {
+ let k = k: *rsakey;
+ bytes::zero(k.priv);
+ free(k.priv);
+ free(k.pub);
+};
+
+// TODO pass kg by reference to write to?
+fn rsa_keygrip(k: *keypair) [KEYGRIPSZ]u8 = {
+ let kg: [KEYGRIPSZ]u8 = [0...];
+
+ let params = rsa::pubkey_params((k: *rsakey).pub);
+ let n = params.n;
+ let i = 0z;
+ for (i < len(n) && n[i] == 0; i += 1) continue;
+
+ let h = sha1::sha1();
+ if (n[i] & 0x80 == 0x80) {
+ io::write(&h, [0u8])!;
+ };
+ io::write(&h, n[i..])!;
+ hash::sum(&h, kg);
+ return kg;
+};
+
M format/openpgp/sig.ha => format/openpgp/sig.ha +166 -11
@@ 18,27 18,177 @@ use time;
use types;
use debug::*;
-use crypto::rsa;
export type sigtype = enum u8 {
+ // Signature of a binary document
BINARY = 0x00,
+
+ // Signature of a canonical text document, where line endings have been
+ // converted to <CR><LF>
CANONICAL_TEXT = 0x01,
+
+ // A signature of its own subpacket contents.
STANDALONE = 0x02,
+
+ // The issuer of this certification does not make any particular
+ // assertion as to how well the certifier has checked that the owner
+ // of the key is in fact the person described by the User ID.
+ GENERIC_CERT_OF_USERID_AND_PUBKEY = 0x10,
+
+ // The issuer of this certification has not done any verification of
+ // the claim that the owner of this key is the User ID specified.
+ PERSONA_CERT_OF_USERID_AND_PUBKEY = 0x11,
+
+ // The issuer of this certification has done some casual
+ // verification of the claim of identity.
+ CASUAL_CERT_OF_USERID_AND_PUBKEY = 0x12,
+
+ // The issuer of this certification has done substantial
+ // verification of the claim of identity.
+ POSITIVE_CERT_OF_USERID_AND_PUBKEY = 0x13,
+
+ // This signature is a statement by the top-level signing key that
+ // indicates that it owns the subkey.
+ SUBKEY_BINDING_SIG = 0x18,
+
+ // This signature is a statement by a signing subkey, indicating
+ // that it is owned by the primary key and subkey.
+ PRIMARY_BINDING_SIG = 0x19,
+
+ // This signature is calculated directly on a key.
+ DIRECT_KEY_SIG = 0x1f,
+
+ // The signature is calculated directly on the key being revoked.
+ REVOCATION_SIG = 0x20,
+
+ // The signature is calculated directly on the subkey being revoked.
+ SUBKEY_REVOCATION_SIG = 0x28,
+
+ // This signature revokes an earlier User ID certification signature
+ // (signature class 0x10 through 0x13) or direct-key signature (0x1F).
+ CERT_REVOCATION_SIG = 0x30,
+
+ TIMESTAMP_SIG = 0x40,
+
+ THIRD_PARTY_CONFIRM_SIG = 0x50,
+};
+
+export fn strsigtype(st: u8) str = {
+ switch (st) {
+ case sigtype::BINARY =>
+ return "BINARY";
+ case sigtype::CANONICAL_TEXT =>
+ return "CANONICAL_TEXT";
+ case sigtype::STANDALONE =>
+ return "STANDALONE";
+ case sigtype::GENERIC_CERT_OF_USERID_AND_PUBKEY =>
+ return "GENERIC_CERT_OF_USERID_AND_PUBKEY";
+ case sigtype::PERSONA_CERT_OF_USERID_AND_PUBKEY =>
+ return "PERSONA_CERT_OF_USERID_AND_PUBKEY";
+ case sigtype::CASUAL_CERT_OF_USERID_AND_PUBKEY =>
+ return "CASUAL_CERT_OF_USERID_AND_PUBKEY";
+ case sigtype::POSITIVE_CERT_OF_USERID_AND_PUBKEY =>
+ return "POSITIVE_CERT_OF_USERID_AND_PUBKEY";
+ case sigtype::SUBKEY_BINDING_SIG =>
+ return "SUBKEY_BINDING_SIG";
+ case sigtype::PRIMARY_BINDING_SIG =>
+ return "PRIMARY_BINDING_SIG";
+ case sigtype::DIRECT_KEY_SIG =>
+ return "DIRECT_KEY_SIG";
+ case sigtype::REVOCATION_SIG =>
+ return "REVOCATION_SIG";
+ case sigtype::SUBKEY_REVOCATION_SIG =>
+ return "SUBKEY_REVOCATION_SIG";
+ case sigtype::CERT_REVOCATION_SIG =>
+ return "CERT_REVOCATION_SIG";
+ case sigtype::TIMESTAMP_SIG =>
+ return "TIMESTAMP_SIG";
+ case sigtype::THIRD_PARTY_CONFIRM_SIG =>
+ return "THIRD_PARTY_CONFIRM_SIG";
+ case =>
+ return "UNKNOWN";
+ };
};
export type subtype = enum u8 {
SIG_CREATION_TIME = 2,
SIG_EXPIRATION_TIME = 3,
-
+ EXPORTABLE_CERT = 4,
+ TRUST_SIG = 5,
+ REG_EXP = 6,
+ REVOCABLE = 7,
+ KEY_EXPIRATION_TIME = 8,
PREFERRED_SYM_ALGOS = 11,
+ REVOCATION_KEY = 12,
+ ISSUER = 16,
+ NOTATION_DATA = 20,
PREFERRED_HASH_ALGOS = 21,
PREFERRED_COMPRESSION_ALGOS = 22,
-
+ KEY_SERVER_PREFERENCES = 24,
+ PRIMARY_USERID = 25,
+ POLICY_URI = 26,
KEYFLAGS = 27,
-
+ SIGNER_USERID = 28,
+ REVOCATION_REASON = 29,
+ FEATURES = 30,
+ SIG_TARGET = 31,
+ EMBEDDED_SIG = 32,
ISSUER_FINGERPRINT = 33,
};
+export fn strsubtype(st: u8) str = {
+ switch (st) {
+ case subtype::SIG_CREATION_TIME =>
+ return "SIG_CREATION_TIME";
+ case subtype::SIG_EXPIRATION_TIME =>
+ return "SIG_EXPIRATION_TIME";
+ case subtype::EXPORTABLE_CERT =>
+ return "EXPORTABLE_CERT";
+ case subtype::TRUST_SIG =>
+ return "TRUST_SIG";
+ case subtype::REG_EXP =>
+ return "REG_EXP";
+ case subtype::REVOCABLE =>
+ return "REVOCABLE";
+ case subtype::KEY_EXPIRATION_TIME =>
+ return "KEY_EXPIRATION_TIME";
+ case subtype::PREFERRED_SYM_ALGOS =>
+ return "PREFERRED_SYM_ALGOS";
+ case subtype::REVOCATION_KEY =>
+ return "REVOCATION_KEY";
+ case subtype::ISSUER =>
+ return "ISSUER";
+ case subtype::NOTATION_DATA =>
+ return "NOTATION_DATA";
+ case subtype::PREFERRED_HASH_ALGOS =>
+ return "PREFERRED_HASH_ALGOS";
+ case subtype::PREFERRED_COMPRESSION_ALGOS =>
+ return "PREFERRED_COMPRESSION_ALGOS";
+ case subtype::KEY_SERVER_PREFERENCES =>
+ return "KEY_SERVER_PREFERENCES";
+ case subtype::PRIMARY_USERID =>
+ return "PRIMARY_USERID";
+ case subtype::POLICY_URI =>
+ return "POLICY_URI";
+ case subtype::KEYFLAGS =>
+ return "KEYFLAGS";
+ case subtype::SIGNER_USERID =>
+ return "SIGNER_USERID";
+ case subtype::REVOCATION_REASON =>
+ return "REVOCATION_REASON";
+ case subtype::FEATURES =>
+ return "FEATURES";
+ case subtype::SIG_TARGET =>
+ return "SIG_TARGET";
+ case subtype::EMBEDDED_SIG =>
+ return "EMBEDDED_SIG";
+ case subtype::ISSUER_FINGERPRINT =>
+ return "ISSUER_FINGERPRINT";
+ case =>
+ return "UNKNOWN";
+ };
+};
+
export type keyflag = enum u8 {
// The key may be used to certify other keys.
CERTIFY = 0x01,
@@ 99,10 249,11 @@ fn pkg_parsesig(p: *packet) (sig | error) = {
};
s.v = readu8(p)?;
if (s.v != 4) {
- return errors::unsupported;
+ return unsupported_version;
};
s.sigtype = readu8(p)?;
+ print("SIGTYPE: ", strsigtype(s.sigtype));
s.pubkeyalgo = readu8(p)?;
s.hashalgo = readu8(p)?;
@@ 139,11 290,11 @@ fn pkg_parsesig(p: *packet) (sig | error) = {
n += 1;
case subtype::ISSUER_FINGERPRINT =>
const fpv = readu8(&sub)?;
- if (fpv != 4 || subsz != 1 + FPSZ) return unsupported;
+ if (fpv != 4 || subsz != 1 + FPSZ) return unsupported_fp;
s.opts.keyfp = io::drain(&sub)?;
n += 1 + len(s.opts.keyfp);
case =>
- if (crit) return unsupported;
+ if (crit) return unsupported_critsub;
n += io::copy(io::empty, &sub)?;
};
@@ 191,17 342,20 @@ fn parsesubsz(src: io::handle) (size | error | io::EOF) = {
// Hash must be freed and finished after use.
export fn sig_hashfn(s: *sig) (*hash::hash | unsupported) = {
switch (s.hashalgo) {
+ case hashalgo::SHA1 =>
+ return alloc(sha1::sha1());
case hashalgo::SHA256 =>
return alloc(sha256::sha256());
case hashalgo::SHA512 =>
return alloc(sha512::sha512());
case =>
- return unsupported;
+ return unsupported_hashalgo;
};
};
// TODO maybe verify_sig or just verify?
export fn verify(s: *sig, h: *hash::hash, k: *key) (void | error) = {
+ print("verify");
io::writeall(h, [s.v, s.sigtype, s.pubkeyalgo, s.hashalgo])!;
if (len(s.hsub) > types::U16_MAX) {
@@ 221,10 375,12 @@ export fn verify(s: *sig, h: *hash::hash, k: *key) (void | error) = {
endian::beputu32(szbuf, sz: u32);
io::writeall(h, szbuf)!;
- let sum: [sha256::SZ]u8 = [0...];
+ let sum: [MAX_HASHSZ]u8 = [0...];
+ let sum = sum[..hash::sz(h)];
hash::sum(h, sum);
if (!bytes::equal(s.hprefix, sum[..2])) {
+ print("invalid PREFIX", s.hashalgo: u8);
return invalid;
};
@@ 283,8 439,7 @@ export fn sign(dest: io::handle, key: *key, opts: signopts) (size | error) = {
io::writeall(&w, s[..2])?;
// TODO get enc key
- let k = key.primary: *rsakey;
-
+ let k = key.primary;
keypair_sign(k, s, hashalgo::SHA256, &w)?;
return pkt_write(dest, tag::SIGNATURE, memio::buffer(&w))?;
M format/openpgp/testcases+test.ha => format/openpgp/testcases+test.ha +5 -5
@@ 12,7 12,7 @@ use debug::*;
@test fn parse_pub() void = {
let bin = memio::fixed(samp_rsakey.pub);
- let pub = parse_pubkey(&bin)!;
+ let pub = parse_key(&bin)! as key;
defer key_finish(&pub);
assert(bytes::equal(pub.primary.fp, [
@@ 26,7 26,7 @@ use debug::*;
@test fn parse_sig() void = {
let bin = memio::fixed(samp_rsakey.pub);
- let pub = parse_pubkey(&bin)!;
+ let pub = parse_key(&bin)! as key;
defer key_finish(&pub);
let binsig = memio::fixed(samp_extsig);
@@ 45,7 45,7 @@ use debug::*;
@test fn parse_priv() void = {
let bin = memio::fixed(samp_unprotrsakey.priv);
- let priv = parse_privkey(&bin)!;
+ let priv = parse_key(&bin)! as key;
defer key_finish(&priv);
assert(bytes::equal(priv.primary.fp, [
@@ 61,7 61,7 @@ use debug::*;
let dest = memio::dynamic();
let bin = memio::fixed(samp_unprotrsakey.priv);
- let priv = parse_privkey(&bin)!;
+ let priv = parse_key(&bin)! as key;
defer key_finish(&priv);
let h = sha256::sha256();
@@ 90,7 90,7 @@ use debug::*;
};
let bin = memio::fixed(samp_rsakey.pub);
- let pub = parse_pubkey(&bin)!;
+ let pub = parse_key(&bin)! as key;
defer key_finish(&pub);
verify(&sig, hf, &pub)!;
};
M format/openpgp/types.ha => format/openpgp/types.ha +4 -4
@@ 5,7 5,7 @@ use hash;
use io;
use time::date;
-type sizetype = enum {
+export type sizetype = enum {
FIXED,
INDETERMINATE,
PARTIAL,
@@ 24,7 24,7 @@ fn strsizetype(s: sizetype) str = {
};
};
-type packet = struct {
+export type packet = struct {
stream: io::stream,
h: io::handle,
pos: size,
@@ 34,7 34,7 @@ type packet = struct {
sz: size,
};
-type tag = enum u8 {
+export type tag = enum u8 {
RESERVED = 0,
PUBLIC_KEY_ENCRYPTED_SESSION_KEY = 1,
SIGNATURE = 2,
@@ 144,7 144,7 @@ fn newhash(algo: u8) (hash | unsupported) = {
case hashalgo::SHA512 =>
h.sha512 = sha512::sha512();
case =>
- return unsupported;
+ return unsupported_hashalgo;
};
return h;
};