petzval/btferret

Support for BLE connection with 6-digit passkey authentication

Closed this issue · 15 comments

Does your library provide support for BLE connection with 6-digit passkey authentication?

If yes, could you please provide any examples on how to implement a BLE Client and connect it to a server using this functionality?

I think the answer is probably no. A Classic connection does implement passkey authentication, so the code is there if the server requests a key, but I have not tested it with a BLE device that requires a secure encrypted connection. It can be quickly checked by running btferret which can be a BLE client. Run an LE scan (b command) to find the BLE device, then try and connect with the c command.

As I suspected, the BLE device wants a secure connection. Authentication for LE devices has not been implemented and would not be simple because there are multiple key exchanges. But there is one un-documented and un-tested thing you can try. Other than this, the only option would be to use bluez which has all the security features.

In btlib.c (version 13 - recently uploaded) at line 4345 in leconnect function
Insert lepair call as follows:

  ....
  popins();
  flushprint();
// pair here
  lepair(ndevice);
  return(1);
  } 

There may be a simple fix, or the BLE device may be requesting a higher security level than programmed in lepair. If you want one more try, it is necessary to see the Bluetooth traffic that is causing the pair failure. It can be sent to a file "btout.txt" with the following procedure.

In btferret

k command
  input option 2 - verbose

connect to LE device - Bluetooth traffic is displayed

o command
   saves screen output to file btout.txt

I think I have a solution if the passkey procedure is either a fixed passkey, or if the BLE device displays a passkey that must be entered on the connecting client device. I have this working with a few modified code sections, and can read attributes that need authentication. Let me know if you want me to post the changes here.

Yes, I have interest in that solution! In our case, we need to access the characteristics of a BLE server with a fixed passkey.

Thank you for the support and fast replies!

Four modifications as follows
1.

AT LINE 4368 - replace lepair function
             - call lepair() as before at LINE 4345

int lepair(int ndevice)
  {  
  int n;
  preq[PAKHEADSIZE+10] = 0x04;  // kbd + dis
  preq[PAKHEADSIZE+12] = 0x04;  // auth mitm = passkey required
  sendhci(preq,ndevice); 
  readhci(ndevice,IN_AUTOEND,0,gpar.leclientwait,0);   // now user-set LE wait
  n = findhci(IN_AUTOEND,ndevice,INS_POP);
  if(n >= 0 && insdat[n] == AUTO_PAIROK)
    NPRINT "PAIR OK\n");
  else
    NPRINT "PAIR FAIL\n");
  popins();
  flushprint();
  return(1);
  }
AT LINE 6715
REPLACE
  unsigned char key[16],out[16],ia[6],ra[6];
WITH
  unsigned char out[16],ia[6],ra[6];
  static unsigned char key[16];
     AT LINE 7305 replace code block and code the passkey at j = ....
     
     if(insdat[n] == 2)
       {
       VPRINT "GOT pair response - SEND I confirm\n");
       // save for confirm calc
       for(j = 0 ; j < 7 ; ++j)
         sp->pair[j] = insdat[n+j];  // response 
                           
       for(j = 0 ; j < 16 ; ++j)
         sp->irand[j] = rand() & 0xFF;

       for(j = 0 ; j < 16 ; ++j)
         key[j] = 0;

       // IF passkey is fixed e.g. 123456
       j = 123456;
        
       // *** OR if the passkey is displayed on the remote device
       // printf("Input passkey\n? ");
       // j = setkeymode(0);
       // fgets(buf,16,stdin);
       // setkeymode(j); 
       // j = atoi(buf);
       // *** end input passkey

       key[0] = j & 0xFF;
       key[1] = (j >> 8) & 0xFF;
       key[2] = (j >> 16) & 0xFF;
       key[3] = (j >> 24) & 0xFF;

       calcc1(key,sp->irand,preq+PAKHEADSIZE+9,sp->pair,0,rp->leaddtype & 1,ia,ra,confirm+PAKHEADSIZE+10);
       sendhci(confirm,devicen);
       }
     AT LINE 7332 - remove two instructions
   
     else if(insdat[n] == 4)
       {
       VPRINT "GOT R random\n");
       for(j = 0 ; j < 16 ; ++j)
         {
         sp->rrand[j] = insdat[n+j+1];
         // key[j] = 0;  ############### REMOVE THIS #########
         }
       calcc1(key,sp->rrand,preq+PAKHEADSIZE+9,sp->pair,0,rp->leaddtype & 1,ia,ra,out);
       getout = 0;
       for(j = 0 ; j < 16 && getout == 0 ; ++j)
         {
         if(sp->confirm[j] != out[j])
           getout = 1;
         }
       if(getout == 0)
         {
         VPRINT "R confirm OK\n");
         sp->cryptoflag = 1;
         }
       else
         VPRINT "I confirm fail\n");
         
       VPRINT "SEND LTK encrypt\n");
       calcs1(key,sp->rrand,sp->irand,sp->linkey);
       for(j = 0 ; j < 16 ; ++j)
         {
         // key[j] = 0;  ############ REMOVE THIS ############
         ltkcrypt[j+PAKHEADSIZE+16] = sp->linkey[j];
         }
       sendhci(ltkcrypt,devicen);
       }

Hi, I've only been able to test it today.

Using your modifications, it still returned me with PAIR FAIL. With a deeper analysis, I could find out that my n and insdat[n], right before if(n >= 0 && insdat[n] == AUTO_PAIROK) in the lepair() function, are :

n:0, insdat[n]:5

I could not figure out what those variables mean just by looking through the code, but I've noticed that that insdat[n] should be equals to 4 to get a PAIR OK.

Would you have any guess on how we can fix that?

One quick thing to try, just in case the stored key is at fault. Delete the link.key file. It will be in the directory from which the code was run.

I have no link.key files here.

And, just like your implementation, my device has a fixed passkey=123456.

It's OK that there is no link.key file. Try this modification. It will print progress information and any error codes.

   AT line 7295 replace this else block
   

    else if(gotflag == IN_CRYPTO && (dev[devicen]->conflag & CON_LX) == 0)
      {  // client
      ip = dev[0];
      rp = dev[devicen];
      sp = rp;
      for(j = 0 ; j < 6 ; ++j)
        {
        ia[j] = ip->baddr[5-j];
        ra[j] = rp->baddr[5-j];
        }       
     if(insdat[n] == 2)
       {
       NPRINT "GOT pair response - SEND I confirm\n");
       // save for confirm calc
       for(j = 0 ; j < 7 ; ++j)
         sp->pair[j] = insdat[n+j];  // response 
                           
       for(j = 0 ; j < 16 ; ++j)
         sp->irand[j] = rand() & 0xFF;

       for(j = 0 ; j < 16 ; ++j)
         key[j] = 0;

       // IF passkey is fixed e.g. 123456
       j = 123456;
        
       key[0] = j & 0xFF;
       key[1] = (j >> 8) & 0xFF;
       key[2] = (j >> 16) & 0xFF;
       key[3] = (j >> 24) & 0xFF;

       calcc1(key,sp->irand,preq+PAKHEADSIZE+9,sp->pair,0,rp->leaddtype & 1,ia,ra,confirm+PAKHEADSIZE+10);
       sendhci(confirm,devicen);
       }
     else if(insdat[n] == 3)
       {
       NPRINT "GOT R confirm - SEND I rand\n");
       
       for(j = 0 ; j < 16 ; ++j)
         sp->confirm[j] = insdat[n+j+1];  // R confirm
     
       for(j = 0 ; j < 16 ; ++j)
         sendrand[PAKHEADSIZE+10+j] = sp->irand[j];         
       sendhci(sendrand,devicen);
       }
     else if(insdat[n] == 4)
       {
       NPRINT "GOT R random\n");
       for(j = 0 ; j < 16 ; ++j)
         sp->rrand[j] = insdat[n+j+1];
       calcc1(key,sp->rrand,preq+PAKHEADSIZE+9,sp->pair,0,rp->leaddtype & 1,ia,ra,out);
       getout = 0;
       for(j = 0 ; j < 16 && getout == 0 ; ++j)
         {
         if(sp->confirm[j] != out[j])
           getout = 1;
         }
       if(getout == 0)
         {
         NPRINT "R confirm OK\n");
         sp->cryptoflag = 1;
         }
       else
         NPRINT "I confirm fail\n");
         
       NPRINT "SEND LTK encrypt %02X\n",key[1]);
       calcs1(key,sp->rrand,sp->irand,sp->linkey);
       for(j = 0 ; j < 16 ; ++j)
         ltkcrypt[j+PAKHEADSIZE+16] = sp->linkey[j];
       sendhci(ltkcrypt,devicen);
       }
     else if(sp->cryptoflag != 2 && (insdat[n] == 0x07 || insdat[n] == 0xFE))
       {
       NPRINT "PAIR OK\n");
       buf[0] = AUTO_PAIROK;
       pushins(IN_AUTOEND,devicen,1,buf);
       sp->cryptoflag = 2;
       }
     else if(sp->cryptoflag != 2 && (insdat[n] == 5 || insdat[n] == 0xFD))
       {
       NPRINT "PAIR FAIL %02X %02X %02X %02X\n",insdat[n],insdat[n+1],insdat[n+3],key[1]); 
       buf[0] = AUTO_PAIRFAIL;
       pushins(IN_AUTOEND,devicen,1,buf);
       sp->cryptoflag = 2;
       }
     }  

Got the following response:

Connect OK as LE client
GOT pair response - SEND I confirm
GOT R confirm - SEND I rand
GOT R random
I confirm fail
SEND LTK encrypt 00
PAIR FAIL FD 40 08 00
My device has disconnected
n:0, insdat[n]:5
PAIR FAIL

Also, would you have any documentations or references that explain the steps of the protocol?

If your passkey is 123456 then the value of key[] is being corrupted. Make sure that it is declared as static: at the top of the immediate() function.

static unsigned char key[16];

We got it to work with this last change! Thank you for the support!

I've opened PR #35 to merge these changes into the original code.

Would you mind deleting this issue?

OK, good. This has been very helpful in sorting out LE pairing, and it will be added as a new feature in the next release with an le_pair() function, so the changes will appear in the next few weeks.

The protocol is in the Core Specification document from here:

https://www.bluetooth.com/specifications/specs/core-specification/

Volume 3, Part H, Appendix C, Sections C1.1 and C2.1.2
Details of the HCI packets in Sections 3.5 and 3.6

Nice, thank you again!

I've deleted some of my comments as I might have shared some "non-public" information unintentionally.

I am closing this issue.