arduino / arduino-create-agent

Arduino Cloud Agent
GNU Affero General Public License v3.0
419 stars 149 forks source link

Invalid Signature #213

Open ArkajyotiChatterjee opened 6 years ago

ArkajyotiChatterjee commented 6 years ago

Hello. I am trying to call the arduino create agent plugin from my localhost to upload a hex file to my arduino. I generated a pair of public and private keys and updated the public key in the config.ini file. Then I am generating the signature of my commandline using my private key and then I am updating the same on the js code. But still when I run the code, I am getting "Signature is Invalid". Any ideas as to what I am doing wrong. I am pasting the json payload I am trying to upload .

var abcData = {
           "board":"arduino:avr:uno",
           "port":"COM18",
           "commandline":"\"C:\Program Files(x86)\Arduino\hardware\tools\avr/bin/avrdude -CC:\Program Files (x86)\Arduino\hardware\tools\avr/etc/avrdude.conf -v -patmega328p -carduino -PCOM15 -b115200 -D -Uflash:w:C:\Users\ChatteAr\AppData\Local\Temp\arduino_build_876546/flasher_tryout.ino.hex:i\"",
           "signature":"uJsl8iXd9hNsA6ExtE7msUGaP1/9KU+Yx...." ,
          "hex":"EAAAAAyUXQAMlIUADJSFAAyUhQCEEAAQAAyUhQAMlIUADJSFAAyUhQBMEAAgAAyUhQAMlIUAD.....",
           "filename":"flasher_tryout.ino.hex",
           "extra":{
              "auth":{
                 "username":null,
                 "password":null,
                  "privatekey":null,
                   "port":null
                },
              "wait_for_upload_port":false,
              "use_1200bps_touch":false,
              "network":false,
              "params_verbose":null,
              "params_quiet":null,
              "verbose":false ,
              "ssh":false}

    };
url= "http://localhost:8991/upload";
facchinm commented 6 years ago

Hi @ArkajyotiChatterjee , the procedure looks good. @matteosuppo we don't need to sign anything except the commandline right now, correct?

matteosuppo commented 6 years ago

Yes, only the commandline. The key should be crypto.SHA256.

ArkajyotiChatterjee commented 6 years ago

Hi @facchinm , @matteosuppo , for testing purposes I was using openSSL from my GIT Bash to generate the key pair and sign the commandline. The codes are as follows: `openssl genrsa -out private.pem 2048 // private key

openssl rsa -in private.pem -outform PEM -pubout -out public.pem //public key(used in config.ini)

echo "\"{runtime.tools.avrdude.path}/bin/avrdude\" \"-C{runtime.tools.avrdude.path}/etc/avrdude.conf\" {upload.verbose} -patmega32u4 -cavr109 -P{serial.port} -b57600 -D \"-Uflash:w:{build.path}/{build.project_name}.hex:i\"" | openssl dgst -sha256 -sign private.pem -out /tmp/sign.sha256 //signing the commandline

openssl base64 -in /tmp/sign.sha256 -out sign_commndline // the signature file`

Still I am getting the error"Signature is Invalid". Any ideas as to exactly what I am missing?

ArkajyotiChatterjee commented 6 years ago

Hey guys,any help would be appreciated a lot.

matteosuppo commented 6 years ago

The only thing that comes to mind it that maybe the quotes are being escaped.

You could try with the following go code:

import (
    "crypto/rand"
    "crypto/rsa"
    "crypto/sha256"
    "crypto/x509"
    "encoding/pem"
)
func sign(message []byte, key []byte) ([]byte, error) {
    block, _ := pem.Decode(key)
    if block == nil {
        return nil, errors.New("While decoding key " + string(key))
    }
    rng := rand.Reader
    private, err := x509.ParsePKCS1PrivateKey(block.Bytes)
    if err != nil {
        return nil, errors.New("While parsing key "+ key)
    }

    hashed := sha256.Sum256(message)
    signature, err := rsa.SignPKCS1v15(rng, private, crypto.SHA256, hashed[:])
    if err != nil {
        return nil, errors.New("While signing message "+message+" with key " + private)
    }

    return signature, nil
}

and see if the generated signature is the same

kosciuk commented 5 years ago

I don't know the entire process but if I run --generateCert all keys are ECDSA,I think the signature must be made with key.pem, but the function verifyCommandLine seems to use a RSA in line 216: key.(*rsa.PublicKey)

Log

http2: panic serving 127.0.0.1:43880: interface conversion: interface {} is ecdsa.PublicKey, not rsa.PublicKey goroutine 156 [running]: net/http.(*http2serverConn).runHandler.func1(0xc420206038, 0xc4205dbfaf, 0xc420348000) /usr/lib/go-1.10/src/net/http/h2_bundle.go:5753 +0x190 panic(0xb52080, 0xc4202008c0) /usr/lib/go-1.10/src/runtime/panic.go:502 +0x229 main.verifyCommandLine(0xc42012e540, 0xd4, 0xc4200923f0, 0x61, 0x6, 0x414038) /home/kosciuk/go/src/github.com/arduino/arduino-create-agent/conn.go:215 +0x2c4

matteosuppo commented 5 years ago

The certificates generated with --generateCert are only used for ssl, they are not used to sign the commandline

kosciuk commented 5 years ago

Ups! I used key.pem to sign. I ' ll try generating my own RSA, but with ECDSA I can't verify the signature until I generated it with Python [0], maybe there is a problem with signing with openssl.

[0] https://thanethomson.com/2018/11/30/validating-ecdsa-signatures-golang/

matteosuppo commented 5 years ago

It shouldn't be possible to verify a signature with a ecdsa key, since it expects an rsa public key

kosciuk commented 5 years ago

Not if I add a new function for that :P, I tried again with RSA and the problem was that

openssl base64 -in /tmp/sign.sha256 -out sign_commndline

is wrong since the code expect the signature in hex:

func verifyCommandLine(input string, signature string) error {
    sign, _ := hex.DecodeString(signature)

I couldn't create the signature with openssl or Python, then I used the example in https://stackoverflow.com/questions/20655702/signing-and-decoding-with-rsa-sha-in-go. The digest was the same in Python - Go, but the signature didn't match (using pip install rsa)

Update:

Finally... how to make the signature:

#!/usr/bin/env python

from base64 import (
    b64encode,
    b64decode,
)

from Crypto.Hash import SHA256
from Crypto.Signature import PKCS1_v1_5
from Crypto.PublicKey import RSA

digest = SHA256.new()
digest.update(b'"{runtime ... [complete with your command line] ... }.hex:i"')

with open ("rsakey.pem", "r") as myfile:
    private_key = RSA.importKey(myfile.read())

signer = PKCS1_v1_5.new(private_key)
sig = signer.sign(digest)

print(sig.encode("hex"))

Hope this helps, @ArkajyotiChatterjee