DSAConvertSignatureFormat

From Crypto++ Wiki
Jump to navigation Jump to search

DSAConvertSignatureFormat allows you to convert between different DSA and ECDSA signature formats. Signature formats and how to use them with Crypto++ comes up on occasion, like Convert DSA_P1363 to DSA_DER, ECDSA sign with OpenSSL, verify with Crypto++ and ECDSA sign with BouncyCastle and verify with Crypto++ on Stack Overflow.

Crypto++ supports three types of DSA and ECDSA signature formats. The first is the library's native format and it is IEEE's P1363. P1363 is a simple concatenation of r and s. The second format is ASN.1/DER format, and it is used by OpenSSL, Java and .Net. ASN.1/DER is a SEQUENCE of two INTEGER, r and s. The final format is OpenPGP format.

A full sample that uses DSAConvertSignatureFormat is available at OpenSSL and Java on the wiki. DSAConvertSignatureFormat is only a small part of the code.

Function Signature

DSAConvertSignatureFormat is declared in dsa.h and defined in dsa.cpp. The signature of the function is shown below.

size_t DSAConvertSignatureFormat(
    byte *buffer, size_t bufferSize, DSASignatureFormat toFormat,
    const byte *signature, size_t signatureLen, DSASignatureFormat fromFormat);

buffer is the destination buffer.

bufferSize is the destination buffer size.

toFormat is the new encoding format.

signature is the source buffer.

signatureLen is the source buffer size.

fromFormat is the existing encoding format.

DSASignatureFormat is an enumeration and is one of DSA_P1363, DSA_DER or DSA_OPENPGP.

Crypto++ and P1363

Crypto++ uses P1363 format by default for signatures. The signature is a concatenation of r and s, where both r and s are based on the size of a field element and the subgroup order. Sometimes the signature is denoted r || s because it is a simple concatenation.

The table below shows different element and signature sizes for different fields. DSA uses the field of integers, and ECDSA uses a field over elliptic curves. NIST random curves are shown, and Koblitz curves are similar.

Algorithm Element Size Signature Size
DSA-1024 16 40
DSA-2048 32 56
DSA-3072 48 64
secp112r1 14 28
secp128r1 16 32
secp224r1 28 56
secp256r1 32 64
secp192r1 40 80
secp384r1 48 96
secp521r1 66 132

P1363 to ASN.1/DER

Crypto++ uses P1363 formatted signatures, and OpenSSL, Java and .Net use ASN.1/DER formatted signatures. The code below converts from P1363 to ASN.1/DER. The tricky part in the code is determining the ASN.1/DER buffer size.

The code below assumes you have a Crypto++ signature in p1363Signature produced by a function like SignMessage. The signature is r || s.

std::string p1363Signature, derSignature;

// ...Calculate signature
p1363Signature = SignMessage(signer, message);

// Make room for the ASN.1/DER encoding
derSignature.resize(3+3+3+2+p1363Signature.size())

size_t encodedSize = DSAConvertSignatureFormat(
    (byte*) (&derSignature[0]), derSignature.size(), DSA_DER,
    (const byte*) (p1363Signature.data()), p1363Signature.size(), DSA_P1363);

ASSERT(encodedSize <= derSignature.size());
derSignature.resize(encodedSize );

The derSignature buffer size is calculated as follows. The maximum size of the ASN.1/DER objects are shown below. The final ASN.1/DER object may be smaller.

Each ASN.1 object needs a tag that takes one octet, and a length that takes up to 2 octets. An ASN.1 INTEGER may need a leading 0 prepended to r or s. COUNTOF is the count of the bytes in r or s. The number of bytes in r or s is determined by the size of an element in the underlying field and subgroup order.

SEQUENCE := {   // 1+2
    INTEGER r;  // 1+2+COUNTOF(r)+1
    INTEGER s;  // 1+2+COUNTOF(s)+1
}

ASN.1/DER to P1363

Crypto++ uses P1363 formatted signatures, and OpenSSL, Java and .Net use ASN.1/DER formatted signatures. The code below converts from ASN.1/DER to P1363. The code is easier because the P1363 buffer is smaller than the ASN.1/DER buffer.

The one caveat during conversion is, the P1363 buffer must be correctly sized. The buffer cannot be oversized and then trimmed later. "Correctly sized" means the buffer is exactly SignatureLength() in size. The requirement is because the integers r and s must be padded to a particular size so the concatenation is r || s is the correct size.

The code below assumes you have a OpenSSL, Java or .Net signature in derSignature, and loaded by a function like LoadSignatureOnMessage. The inbound signature format is SEQUENCE := { INTEGER r; INTEGER s; }, and it needs to be converted to P1363 so Crypto++ can use it.

std::string derSignature, p1363Signature;

// ...From OpenSSL, Java or .Net
derSignature = LoadSignatureOnMessage(...);

// Need the verifier object to size the P1363 buffer
ECDSA<ECP, SHA256>::Verifier verifier(publicKey);

// Make room for the P1363 encoding
p1363Signature.resize(verifier.SignatureLength());

size_t encodedSize = DSAConvertSignatureFormat(
    (byte*) (&p1363Signature[0]), p1363Signature.size(), DSA_P1363,
    (const byte*) (derSignature.data()), derSignature.size(), DSA_DER);

ASSERT(encodedSize <= p1363Signature.size());
p1363Signature.resize(encodedSize);

P1363 to OpenPGP

Crypto++ uses P1363 formatted signatures, and OpenPGP uses a custom formatted signatures. The code below converts from P1363 to OpenPGP. The tricky part in the code is determining the OpenPGP buffer size.

The code below assumes you have a Crypto++ signature in p1363Signature produced by a function like SignMessage. The signature is r || s.

std::string p1363Signature, openpgpSignature;

// ...Calculate signature
p1363Signature = SignMessage(signer, message);

// Make room for the ASN.1/DER encoding
openpgpSignature.resize(2+2+p1363Signature.size())

size_t encodedSize = DSAConvertSignatureFormat(
    (byte*) (&openpgpSignature[0]), openpgpSignature.size(), DSA_OPENPGP,
    (const byte*) (p1363Signature.data()), p1363Signature.size(), DSA_P1363);

ASSERT(encodedSize <= openpgpSignature.size());
openpgpSignature.resize(encodedSize );

The openpgpSignature buffer size is calculated as follows. OpenPGP uses a length prefixed value, which is a 16-bit length prefix. The maximum size of the OpenPGP objects are 2 plus the byte size of r and 2 plus the byte size of s.