package org.freertr.cry; import org.freertr.enc.encAsn1; import java.math.BigInteger; import org.freertr.pack.packHolder; import org.freertr.pack.packSsh; /** * elliptic curve digital signature algorithm * * @author matecsaba */ public class cryKeyECDSA extends cryKeyGeneric { /** * create instance */ public cryKeyECDSA() { } /** * curve */ public cryECcurve curve; /** * y public key */ public cryECpoint pub; /** * x private key */ protected BigInteger priv; /** * signature r */ public BigInteger sgnR; /** * signature s */ public BigInteger sgnS; /** * get ssh name * * @return name */ public String sshName() { return "ssh-ecdsa-sha2-" + curve; } /** * get name * * @return name */ public String algName() { return "ecdsa"; } /** * read private key * * @param pck packet * @return false on success, true on error */ public boolean privReader(packHolder pck) { encAsn1 a = new encAsn1(); if (a.tagRead(pck)) { return true; } if ((!a.cnst) || (a.tag != encAsn1.tagSequence)) { return true; } pck = a.getPack(); BigInteger ver = encAsn1.readBigInt(pck); if (ver == null) { return true; } if (a.tagRead(pck)) { return true; } if ((a.cnst) || (a.tag != encAsn1.tagOctetString)) { return true; } priv = new BigInteger(a.getPack().getCopy()); if (a.tagRead(pck)) { return true; } if ((!a.cnst) || (a.tag != encAsn1.tagEoc)) { return true; } packHolder p = a.getPack(); if (a.tagRead(p)) { return true; } if ((a.cnst) || (a.tag != encAsn1.tagObjectID)) { return true; } curve = cryECcurve.getByOid(a.buf); if (curve == null) { return true; } if (a.tagRead(pck)) { return true; } if ((!a.cnst) || (a.tag != encAsn1.tagBoolean)) { return true; } p = a.getPack(); if (a.tagRead(p)) { return true; } if ((a.cnst) || (a.tag != encAsn1.tagBitString)) { return true; } pub = cryECpoint.fromBytes2(curve, a.buf, 0); if (pub == null) { return true; } return false; } /** * write private key * * @param pck packet */ public void privWriter(packHolder pck) { packHolder p1 = new packHolder(true, true); encAsn1.writeBigInt(p1, BigInteger.ONE); packHolder p2 = new packHolder(true, true); byte[] buf = priv.toByteArray(); p2.putCopy(buf, 0, 0, buf.length); p2.putSkip(buf.length); p2.merge2beg(); encAsn1.writeOctString(p1, p2); p2.clear(); encAsn1.writeObjectId(p2, curve.oid); encAsn1.writeEoc(p1, p2); p2.clear(); packHolder p3 = new packHolder(true, true); buf = pub.toBytes2(); p3.putCopy(buf, 0, 0, buf.length); p3.putSkip(buf.length); p3.merge2beg(); encAsn1.writeBitString(p2, p3); encAsn1.writeEoc2(p1, p2); encAsn1.writeSequence(pck, p1); } /** * read certificate * * @param pck packet * @return false on success, true on error */ public boolean certReader(packHolder pck) { encAsn1 a = new encAsn1(); if (a.tagRead(pck)) { return true; } if ((!a.cnst) || (a.tag != encAsn1.tagSequence)) { return true; } pck = a.getPack(); if (a.tagRead(pck)) { return true; } if ((!a.cnst) || (a.tag != encAsn1.tagSequence)) { return true; } packHolder p = a.getPack(); if (a.tagRead(p)) { return true; } if (cryCertificate.objid2int(a) != cryCertificate.typEcDssEncrypt) { return true; } if (a.tagRead(p)) { return true; } if ((a.cnst) || (a.tag != encAsn1.tagObjectID)) { return true; } curve = cryECcurve.getByOid(a.buf); if (curve == null) { return true; } if (a.tagRead(pck)) { return true; } if ((a.cnst) || (a.tag != encAsn1.tagBitString)) { return true; } pub = cryECpoint.fromBytes2(curve, a.buf, 1); if (pub == null) { return true; } return false; } /** * write certificate * * @param pck packet */ public void certWriter(packHolder pck) { packHolder p1 = new packHolder(true, true); packHolder p2 = new packHolder(true, true); encAsn1.writeObjectId(p2, cryCertificate.oidEcDssEncrypt); encAsn1.writeObjectId(p2, curve.oid); encAsn1.writeSequence(p1, p2); p2.clear(); byte[] buf = pub.toBytes2(); p2.putByte(0, 0); p2.putCopy(buf, 0, 1, buf.length); p2.putSkip(buf.length + 1); p2.merge2beg(); encAsn1.writeBitString(p1, p2); encAsn1.writeSequence(pck, p1); } public boolean keyMake(String nam) { curve = cryECcurve.getByName(nam); return keyMake(); } private boolean keyMake() { priv = randomBigInt(curve.n.bitLength() - 2); pub = curve.g.mul(priv); return false; } /** * make key * * @param len length */ public boolean keyMake(int len) { curve = cryECcurve.getBySize(len); return keyMake(); } /** * verify key * * @return false on success, true on error */ public boolean keyVerify() { return !curve.g.check(); } /** * get key size * * @return size */ public int keySize() { return curve.p.bitLength(); } /** * read ssh key * * @param key key * @return false on success, true on error */ public boolean sshReader(byte[] key) { packHolder p = new packHolder(true, true); p.putCopy(key, 0, 0, key.length); p.putSkip(key.length); p.merge2beg(); if (!packSsh.stringRead(p).equals(sshName())) { return true; } if (!packSsh.stringRead(p).equals(curve + "")) { return true; } pub = cryECpoint.fromBytes2(curve, packSsh.bytesRead(p), 0); if (pub == null) { return true; } return false; } /** * write ssh key * * @return key */ public byte[] sshWriter() { packHolder p = new packHolder(true, true); packSsh.stringWrite(p, sshName()); packSsh.stringWrite(p, "" + curve); packSsh.bytesWrite(p, pub.toBytes2()); p.merge2beg(); return p.getCopy(); } private BigInteger calcZ(BigInteger n, byte[] msg) { BigInteger z = new BigInteger(msg); int nl = n.bitLength(); int zl = z.bitLength(); if (nl < zl) { z = z.shiftRight(zl - nl); } return z; } /** * do signature * * @param msg cleartext */ public void doSigning(byte[] msg) { BigInteger z = calcZ(curve.n, msg); BigInteger k = randomBigInt(curve.n.bitLength() - 2); cryECpoint Q = curve.g.mul(k); sgnR = Q.x.mod(curve.n); sgnS = k.modInverse(curve.n).multiply(z.add(sgnR.multiply(priv))); } /** * do verification * * @param msg cleartext * @return false on success, true on error */ public boolean doVerify(byte[] msg) { BigInteger z = calcZ(curve.n, msg); BigInteger w = sgnS.modInverse(curve.n); BigInteger u1 = z.multiply(w).mod(curve.n); BigInteger u2 = sgnR.multiply(w).mod(curve.n); cryECpoint Q = curve.g.mul(u1).add(pub.mul(u2)); return sgnR.compareTo(Q.x.mod(curve.n)) != 0; } /** * convert signature to ssh * * @return byte array of r|s */ public byte[] sign2ssh() { int hashBytes = (curve.p.bitLength() + 7) / 8; byte[] b1 = bigInt2buffer(sgnR, hashBytes); byte[] b2 = bigInt2buffer(sgnS, hashBytes); byte[] b3 = new byte[hashBytes + hashBytes]; for (int i = 0; i < hashBytes; i++) { b3[i] = b1[i]; b3[hashBytes + i] = b2[i]; } return b3; } /** * convert ssh to signature * * @param sgn r|s byte array * @return false on success, true on error */ public boolean ssh2sign(byte[] sgn) { int hashBytes = (curve.p.bitLength() + 7) / 8; if (sgn.length != hashBytes + hashBytes) { return true; } sgnR = buffer2bigInt(sgn, 0, hashBytes); sgnS = buffer2bigInt(sgn, hashBytes, hashBytes); return false; } /** * verify ssh signature * * @param hash hash * @param sign signature * @return false on success, true on error */ public boolean sshVerify(cryHashGeneric algo, String algn, byte[] hash, byte[] sign) { hash = cryHashGeneric.compute(algo, hash); packHolder p = new packHolder(true, true); p.putCopy(sign, 0, 0, sign.length); p.putSkip(sign.length); p.merge2beg(); if (!packSsh.stringRead(p).equals(algn)) { return true; } sign = packSsh.bytesRead(p); ssh2sign(sign); return doVerify(hash); } /** * sign for ssh * * @param hash hash * @return signature */ public byte[] sshSigning(cryHashGeneric algo, String algn, byte[] hash) { hash = cryHashGeneric.compute(algo, hash); doSigning(hash); hash = sign2ssh(); packHolder p = new packHolder(true, true); packSsh.stringWrite(p, algn); packSsh.bytesWrite(p, hash); p.merge2beg(); return p.getCopy(); } /** * verify certificate * * @param hash hash * @param sign signature * @return false on success, true on error */ public boolean certVerify(cryHashGeneric pkcs, byte[] hash, byte[] sign) { packHolder p = new packHolder(true, true); p.putCopy(sign, 0, 0, sign.length); p.putSkip(sign.length); p.merge2beg(); p.getSkip(1); encAsn1 a = new encAsn1(); if (a.tagRead(p)) { return true; } if ((!a.cnst) || (a.tag != encAsn1.tagSequence)) { return true; } p = a.getPack(); sgnR = encAsn1.readBigInt(p); sgnS = encAsn1.readBigInt(p); return doVerify(hash); } /** * sign certificate * * @param hash hash * @return signature */ public byte[] certSigning(cryHashGeneric pkcs, byte[] hash) { doSigning(hash); packHolder p1 = new packHolder(true, true); packHolder p2 = new packHolder(true, true); encAsn1.writeBigInt(p1, sgnR); encAsn1.writeBigInt(p1, sgnS); encAsn1.writeSequence(p2, p1); p2.putByte(0, 0); p2.putSkip(1); p2.merge2beg(); return p2.getCopy(); } /** * verify tls * * @param ver version * @param hash hash * @param sign signature * @return false on success, true on error */ public boolean tlsVerify(int ver, cryHashGeneric pkcs, byte[] hash, byte[] sign) { packHolder p = new packHolder(true, true); p.putCopy(sign, 0, 0, sign.length); p.putSkip(sign.length); p.merge2beg(); encAsn1 a = new encAsn1(); if (a.tagRead(p)) { return true; } if ((!a.cnst) || (a.tag != encAsn1.tagSequence)) { return true; } p = a.getPack(); sgnR = encAsn1.readBigInt(p); sgnS = encAsn1.readBigInt(p); return doVerify(hash); } /** * sign for tls * * @param ver version * @param hash hash * @return signature */ public byte[] tlsSigning(int ver, cryHashGeneric pkcs, byte[] hash) { doSigning(hash); packHolder p1 = new packHolder(true, true); packHolder p2 = new packHolder(true, true); encAsn1.writeBigInt(p1, sgnR); encAsn1.writeBigInt(p1, sgnS); encAsn1.writeSequence(p2, p1); p2.merge2beg(); return p2.getCopy(); } }