Search code examples
javacryptographydigital-signaturebouncycastleecdsa

Put and extract text from ECDSA Signature


I am using secp256k1 to sign hash of a text in server and verify the signature on client side . Now, I need to send actual text as well to the client but my main constraint is bandwidth and I can't add text separately. Therefore, I need to be able to put the actual text inside signature and extract it during verification?

Here is my code

Signing

 static final X9ECParameters curve = SECNamedCurves.getByName ("secp256k1");
static final ECDomainParameters domain = new ECDomainParameters(curve.getCurve (), curve.getG (), curve.getN (), curve.getH ());

public byte[] sign (byte[] hash) throws CryptoException
{
    if ( priv == null )
    {
        throw new CryptoException (ErrorCode.KEY_NOT_FOUND, "Need private key to sign");
    }
    ECDSASigner signer = new ECDSASigner(new HMacDSAKCalculator(new SHA256Digest()));
    signer.init (true, new ECPrivateKeyParameters(priv, domain));
    BigInteger[] signature = signer.generateSignature (hash);
    ByteArrayOutputStream s = new ByteArrayOutputStream();
    try
    {
        DERSequenceGenerator seq = new DERSequenceGenerator(s);
        seq.addObject (new ASN1Integer(signature[0]));
        seq.addObject (new ASN1Integer(signature[1]));
        seq.close ();
        return s.toByteArray ();
    }
    catch ( IOException e )
    {
    }
    return null;
}

Verification

public static boolean verify (byte[] hash, byte[] signature, byte[] pub)
{
    ASN1InputStream asn1 = new ASN1InputStream(signature);
    try
    {
        ECDSASigner signer = new ECDSASigner();
        signer.init (false, new ECPublicKeyParameters(curve.getCurve ().decodePoint (pub), domain));

        DLSequence seq = (DLSequence) asn1.readObject ();
        BigInteger r = ((ASN1Integer) seq.getObjectAt (0)).getPositiveValue ();
        BigInteger s = ((ASN1Integer) seq.getObjectAt (1)).getPositiveValue ();
        return signer.verifySignature (hash, r, s);
    }
    catch ( Exception e )
    {
        // threat format errors as invalid signatures
        return false;
    }
    finally
    {
        try
        {
            asn1.close ();
        }
        catch ( IOException e )
        {
        }
    }
}

Solution

  • ECDSA requires a hash over the message to be secure. So all information is lost. Some signature schemes use a hash + part of the message. This is for instance the case for RSA in the ISO/IEC 9796 signature schemes giving (partial) message recovery - which is the technical term of what you (and Dave Thompson) are talking about.

    However ECDSA signatures are just big enough to contain a single hash (and usually not even that); there is not much you can do to add data to the hash value. Trying to use EC signatures for partial message recovery is an exercise in futility (also because how verification is performed).


    However if you want to use fewer bits you can do still things:

    • use the most dense representation of message and signature (e.g. just R and S concatenated for ECDSA signatures);
    • use the smallest key size possible (check https://keylength.com/ for info);
    • switch to a signature scheme that uses smaller signatures, for instance the BLS signature scheme over elliptic curves - it will halve the signature size compared to ECDSA (using the most efficient encoding).

    I've put the BLS scheme last because it is usually not in generic libraries; I consider it the expert way out. You may hit a learning curve.


    Signatures themselves don't compress well, by the way, unless you encode them badly. The other disadvantage of compression is that it is usually tricky to calculate a maximum size for all possible messages.