0

I am trying to port certain logic from Go to Python 3, and I'm stuck with cryptography-related code.

Original code in Go is here: https://github.com/avast/apkverifier/blob/master/signingblock/frosting.go

In particular, I'm stuck in the verifySignature function:

func (f *frostingInfo) verifySignature(signed, signature []byte, keyBase64 string) error {
    dec := base64.NewDecoder(base64.StdEncoding, strings.NewReader(keyBase64))
    pubKeyAsn, err := ioutil.ReadAll(dec)
    if err != nil {
        return fmt.Errorf("failed to parse key base64: %s", err.Error())
    }

    keyDigest := sha256.Sum256(pubKeyAsn)
    f.usedKeySha256 = hex.EncodeToString(keyDigest[:])

    pkGen, err := x509.ParsePKIXPublicKey(pubKeyAsn)
    if err != nil {
        return fmt.Errorf("failed to unmarshal pk: %s", err.Error())
    }

    pk, ok := pkGen.(*ecdsa.PublicKey)
    if !ok {
        return fmt.Errorf("invalid key type: %T", pkGen)
    }

    digest := sha256.Sum256(signed)

    ecdsaSig := new(ecdsaSignature)
    if rest, err := asn1.Unmarshal(signature, ecdsaSig); err != nil {
        return err
    } else if len(rest) != 0 {
        return errors.New("x509: trailing data after ECDSA signature")
    }

    if ecdsaSig.R.Sign() <= 0 || ecdsaSig.S.Sign() <= 0 {
        return errors.New("x509: ECDSA signature contained zero or negative values")
    }

    if !ecdsa.Verify(pk, digest[:], ecdsaSig.R, ecdsaSig.S) {
        return ErrFrostingInvalidSignature
    }

    return nil
}

How can I port to Python following calls?

  • x509.ParsePKIXPublicKey(pubKeyAsn)
  • pk, ok := pkGen.(*ecdsa.PublicKey)
  • asn1.Unmarshal(signature, ecdsaSig)
  • ecdsa.Verify(pk, digest[:], ecdsaSig.R, ecdsaSig.S)

What I have so far:

import logging

import base64
import hashlib
from .androguard_apk import APK
from .byte_reader import ByteReader
import ecdsa
from Crypto.PublicKey import RSA
from Crypto.Util import asn1


logger = logging.getLogger(__name__)
.....
def verifySignature(signed, signature, keyBase64):
    dec = base64.b64decode(keyBase64)
    pubKeyAsn = dec
    keyDigest = hashlib.sha256(pubKeyAsn).digest()
    logger.info(hashlib.sha256(pubKeyAsn).hexdigest())
    vk = ecdsa.VerifyingKey.from_der(pubKeyAsn)
    logger.info(f"Key:{vk.to_string()} ")
    # vk = ecdsa.VerifyingKey.from_string(hashlib.sha256(pubKeyAsn).hexdigest())
    # vk = ecdsa.VerifyingKey.from_string(keyBase64)
    digest = hashlib.sha256(signed).digest()
    vk.verify_digest(signature, digest)
    #res = vk.verify(signature, signed)
    logger.info(f"Validation result: {res}" )

value of keyBase64 is MFkwEwYHKoZIzj0CAQYIKoZIzj0DAQcDQgAEZH2+1+E07dnErAD3L6BbTnaohU0bbXriNlJI7VxJU+LjdSwPyXR5pomARAMoyPkMksLz/gitUPtFuJoPL2ziEw==

I am keep getting this error and I don't know how to proceed:

  File "apk_frosting_verifier#link-tree/apk_frosting_verifier/apk_frosting_verifier.py", line 173, in verifySignature
    vk.verify_digest(signature, digest)
  File "apk_frosting_verifier#link-tree/ecdsa/keys.py", line 109, in verify_digest
    r, s = sigdecode(signature, self.pubkey.order)
  File "apk_frosting_verifier#link-tree/ecdsa/util.py", line 221, in sigdecode_string
    assert len(signature) == 2*l, (len(signature), 2*l)
AssertionError: (71, 64)
President James K. Polk
  • 40,516
  • 21
  • 95
  • 125
vmg
  • 9,920
  • 13
  • 61
  • 90
  • 1
    And where does ecdsa come from in your python code? And how to prepare the Go signature for transmission to the python code? – President James K. Polk Aug 16 '19 at 18:04
  • There will be no transmission from python to go, my whole solution will be implemented in python. I ported everything that comes before verifySignature function, and stuck there – vmg Aug 16 '19 at 18:10
  • ecdsa comes from here: https://pypi.org/project/ecdsa/ – vmg Aug 16 '19 at 18:11
  • I can't tell how that stacktrace matches up to the code you posted, but it would appear it's expecting a 64 byte signature value while you're passing something that's 71 bytes – Sam Mason Aug 16 '19 at 18:12
  • Yet somehow you have a parameter `signature` to the python `verifySignature` function which you would like to be verified. Where did this signature come from, I don't see it produced anywhere in the Go code. – President James K. Polk Aug 16 '19 at 18:12
  • check line 319, `signedData`: ```signedData := make([]byte, int64(nextKey)-offAfterSignaturesEnd) if _, err := bf.Read(signedData); err != nil { return fmt.Errorf("failed to read signed data: %s", err.Error()) } if err := f.verifySignature(signedData, signature, keys[keyIndex]); err != nil { if err == ErrFrostingInvalidSignature { return err } return fmt.Errorf("failed to verify signature: %s", err.Error()) }``` – vmg Aug 16 '19 at 18:14
  • 2
    Well, go isn't my forte, but I would assume that the signature is a DER-encoded sequence of the integers R and S in common ECDSA notation, I think that would work out to be 71 bytes. Try this keyword argument to verify: `sigdecode=ecdsa.util.sigdecode_der` – President James K. Polk Aug 16 '19 at 18:32
  • 1
    Approximately 71 bytes, 72 bytes max for a 256 bit key size. The two integers in ASN.1 / DER are signed and may have an additional `00` valued byte in front of it. Add 2 + 2 + 2 bytes of Tag / Lenght overhead and you get to 8 bytes of overhead in total. But the R and S values may be smaller, even many bytes smaller as we're performing modular calculations. Note that stupidly enough, DER was not even specified, but most software implementations will only generate DER regardless. – Maarten Bodewes Aug 19 '19 at 18:07

0 Answers0