-
Notifications
You must be signed in to change notification settings - Fork 306
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
any support for ASN.1 encoding signature? #55
Comments
Currently I write some customized wrapper by myself to solve this issue, but not sure @warner has a plan already. |
This works well for me. Is there any reason why this should not work? How about unsigned integers? from ecdsa import SigningKey, VerifyingKey, NIST256p
import hashlib
import logging
logging.basicConfig(level=logging.DEBUG)
# Generate public & private keys
private_key = SigningKey.generate(curve=NIST256p)
public_key = private_key.get_verifying_key()
# Sign the "ABC" message, hashed with SHA-256
digest = "ABC".encode('utf-8')
signature = private_key.sign(digest, hashfunc=hashlib.sha256)
# Verify the "ABC" message, using the
public_key.verify(signature, digest, hashfunc=hashlib.sha256)
# Encode the signature in ASN.1 format
raw_signature_hex = ''.join(x.encode('hex') for x in signature)
asn1_signature = "3046" + "022100" + raw_signature_hex[:64] + "022100" + raw_signature_hex[64:]
# Write keys to disk
open("private_key.pem","wb").write(private_key.to_pem())
open("public_key.pem","wb").write(public_key.to_pem())
open("data.bin","wb").write(digest)
open("data.sig","wb").write(asn1_signature)
logging.info('Digest : ' + ''.join(x.encode('hex') for x in digest))
logging.info('Signature : ' + asn1_signature)
logging.info('Public key : \n' + public_key.to_pem()) |
@hfossli this is P-256 specific, the ASN.1 encoding will fail with any curve that produces different length output |
Yep, thanks for pointing that out. 👍 |
So, it seems my code has several flaws. I decided to code the asn1 encoder in a language I was more familiar with. The asn1 encoding can look like this. Tested with 10 000 signatures. // https://crypto.stackexchange.com/a/1797
public struct EcdsaAsn1Signature {
static func smallestBigEndian(_ bytes: Data) -> Data {
var smallest = bytes
while smallest.first == 0x00 {
smallest.removeFirst()
}
if let firstByte = smallest.first, firstByte > 0x7f {
return Data(bytes: [0x00]) + smallest
} else {
return smallest
}
}
static func isProperlyAsn1Encoded(_ bytes: Data) -> Bool {
var parser = bytes
guard bytes.count > 2 else { return false }
guard parser.popFirst() == 0x30 else { return false }
guard let sequenceLength = parser.popFirst() else { return false }
guard parser.count == sequenceLength else { return false }
guard parser.popFirst() == 0x02 else { return false }
guard let rLength = parser.popFirst() else { return false }
guard rLength < parser.count else { return false }
parser.removeFirst(Int(rLength))
guard parser.count > 2 else { return false }
guard parser.popFirst() == 0x02 else { return false }
guard let sLength = parser.popFirst() else { return false }
guard sLength == parser.count else { return false }
return true
}
static func encode(_ bytes: Data) -> Data {
guard !isProperlyAsn1Encoded(bytes) else {
return bytes
}
let r = smallestBigEndian(bytes.prefix(bytes.count / 2))
let s = smallestBigEndian(bytes.suffix(bytes.count / 2))
var asn1 = Data()
asn1.append(0x30)
asn1.append(UInt8(2 + r.count + 2 + s.count))
asn1.append(0x02)
asn1.append(UInt8(r.count))
asn1.append(r)
asn1.append(0x02)
asn1.append(UInt8(s.count))
asn1.append(s)
return asn1
}
} It should be fairly easy to port this to python. I don't think this code has a problem with being P-256 specific as it makes no assumptions about length. |
I'm not so sure if it will work for P-521 - for it the the combined length will be 136, which is longer than 127, and if I recall my ASN.1 correctly, that requires different way to encode length... |
Thanks for letting me know. Good point! |
The |
As @wiml pointed out, this feature is already implemented, the example code from #55 (comment) from ecdsa import SigningKey, VerifyingKey, NIST256p
from ecdsa.util import sigencode_der, sigdecode_der
import hashlib
import logging
logging.basicConfig(level=logging.DEBUG)
# Generate public & private keys
private_key = SigningKey.generate(curve=NIST256p)
public_key = private_key.get_verifying_key()
# Sign the "ABC" message, hashed with SHA-256
digest = "ABC".encode('utf-8')
signature = private_key.sign(digest, hashfunc=hashlib.sha256, sigencode=sigencode_der)
# Verify the "ABC" message, using the
public_key.verify(signature, digest, hashfunc=hashlib.sha256, sigdecode=sigdecode_der)
# Write keys to disk
open("private_key.pem","wb").write(private_key.to_pem())
open("public_key.pem","wb").write(public_key.to_pem())
open("data.bin","wb").write(digest)
open("data.sig","wb").write(signature)
logging.info('Digest : ' + digest.hex())
logging.info('Signature : ' + signature.hex())
logging.info('Public key : \n' + str(public_key.to_pem(), 'ascii')) |
And please note that, in general, the signatures should be DER encoded, that means that 0 byte padding needs to be applied only if the first byte of encoded integer has value greater than 0x7f. In other words, the code from #55 (comment) will create malformed signatures (it does create valid BER encoded signatures, so implementations that expect BER will be able to verify them). That being said, |
I wonder to know if the library supports ASN.1 format output of signature?
The text was updated successfully, but these errors were encountered: