|
37 | 37 | #include <bluefruit.h>
|
38 | 38 | #include "HAPUuid.h"
|
39 | 39 | #include "HAPPairing.h"
|
| 40 | +#include <Nffs.h> |
40 | 41 |
|
41 | 42 | #include "crypto/crypto.h"
|
42 | 43 |
|
| 44 | +/* |
| 45 | +The following is a description of SRP-6 and 6a, the latest versions of SRP: |
| 46 | +
|
| 47 | + N A large safe prime (N = 2q+1, where q is prime) |
| 48 | + All arithmetic is done modulo N. |
| 49 | + g A generator modulo N |
| 50 | + k Multiplier parameter (k = H(N, g) in SRP-6a, k = 3 for legacy SRP-6) |
| 51 | + s User's salt |
| 52 | + I Username |
| 53 | + p Cleartext Password |
| 54 | + H() One-way hash function |
| 55 | + ^ (Modular) Exponentiation |
| 56 | + u Random scrambling parameter |
| 57 | + a,b Secret ephemeral values |
| 58 | + A,B Public ephemeral values |
| 59 | + x Private key (derived from p and s) |
| 60 | + v Password verifier |
| 61 | +
|
| 62 | +The host stores passwords using the following formula: |
| 63 | + x = H(s, p) (s is chosen randomly) |
| 64 | + v = g^x (computes password verifier) |
| 65 | +The host then keeps {I, s, v} in its password database. The authentication protocol itself goes as follows: |
| 66 | +User -> Host: I, A = g^a (identifies self, a = random number) |
| 67 | +Host -> User: s, B = kv + g^b (sends salt, b = random number) |
| 68 | +
|
| 69 | + Both: u = H(A, B) |
| 70 | +
|
| 71 | + User: x = H(s, p) (user enters password) |
| 72 | + User: S = (B - kg^x) ^ (a + ux) (computes session key) |
| 73 | + User: K = H(S) |
| 74 | +
|
| 75 | + Host: S = (Av^u) ^ b (computes session key) |
| 76 | + Host: K = H(S) |
| 77 | +Now the two parties have a shared, strong session key K. To complete authentication, they need to prove to each other that their keys match. One possible way: |
| 78 | +User -> Host: M = H(H(N) xor H(g), H(I), s, A, B, K) |
| 79 | +Host -> User: H(A, M, K) |
| 80 | +The two parties also employ the following safeguards: |
| 81 | +The user will abort if he receives B == 0 (mod N) or u == 0. |
| 82 | +The host will abort if it detects that A == 0 (mod N). |
| 83 | +The user must show his proof of K first. If the server detects that the user's proof is incorrect, it must abort without showing its own proof of K. |
| 84 | + */ |
| 85 | + |
43 | 86 | // kTLV type for pairing
|
44 | 87 | enum {
|
45 | 88 | PAIRING_TYPE_METHOD = 0,
|
@@ -131,11 +174,33 @@ err_t HAPPairing::begin(void)
|
131 | 174 | VERIFY_STATUS( _pairing.begin() );
|
132 | 175 |
|
133 | 176 | // Init cryptography
|
| 177 | + Nffs.mkdir_p("/adafruit/homekit"); |
134 | 178 | crypto_init();
|
135 | 179 |
|
136 | 180 | return ERROR_NONE;
|
137 | 181 | }
|
138 | 182 |
|
| 183 | +HAPResponse_t* createSrpResponse(uint8_t tid, uint8_t status, TLV8_t ktlv[], uint8_t count) |
| 184 | +{ |
| 185 | + HAPResponse_t* hap_resp = NULL; |
| 186 | + |
| 187 | + uint16_t srplen = tlv8_calculate_encode_len(ktlv, count); |
| 188 | + uint8_t* srpbuf = (uint8_t*) rtos_malloc(srplen); |
| 189 | + VERIFY( srpbuf != NULL, NULL ); |
| 190 | + |
| 191 | + if( srplen == tlv8_encode_n(srpbuf, srplen, ktlv, count) ) |
| 192 | + { |
| 193 | + LOG_LV2_BUFFER("PAIR-SETUP", srpbuf, srplen); |
| 194 | + TLV8_t tlv = { .type = HAP_PARAM_VALUE, .len = srplen, .value = srpbuf }; |
| 195 | + hap_resp = createHapResponse(tid, status, &tlv, 1); |
| 196 | + } |
| 197 | + |
| 198 | +// LOG_LV2_BUFFER("PAIR-SETUP", hap_resp, hap_resp->body_len + sizeof(HAPResponseHeader_t) + 2); |
| 199 | + |
| 200 | + rtos_free(srpbuf); |
| 201 | + return hap_resp; |
| 202 | +} |
| 203 | + |
139 | 204 |
|
140 | 205 | static HAPResponse_t* pairing_setup_write_cb (HAPCharacteristic* chr, ble_gatts_evt_write_t const* gatt_req, HAPRequest_t const* hap_req)
|
141 | 206 | {
|
@@ -185,19 +250,38 @@ static HAPResponse_t* pairing_setup_write_cb (HAPCharacteristic* chr, ble_gatts_
|
185 | 250 | LOG_LV2("HAP", "Method %s", pairing_method_str[ *((uint8_t const*)ktlv.value) ]);
|
186 | 251 | break;
|
187 | 252 |
|
| 253 | + // TODO multiple pairing support |
188 | 254 | case PAIRING_TYPE_STATE:
|
189 | 255 | {
|
190 | 256 | uint8_t state = *((uint8_t const*)ktlv.value);
|
191 | 257 | LOG_LV2("HAP", "State = M%d", state);
|
| 258 | + |
192 | 259 | switch (state)
|
193 | 260 | {
|
194 | 261 | case 1: // M1
|
195 | 262 | // if paired return PAIRING_ERROR_UNAVAILABLE
|
196 | 263 | // tries more than 100 time return PAIRING_ERROR_MAX_TRIES
|
197 | 264 | // pairing with other iOS return PAIRING_ERROR_BUSY
|
198 | 265 |
|
199 |
| - break; |
| 266 | + // step 4 |
| 267 | + srp_start(); |
200 | 268 |
|
| 269 | + // step 5 : username (I = "Pair-Setup" |
| 270 | + // step 6 : 16 bytes salt already created in srp_init() |
| 271 | + // step 7,8 : password (p = setup code) done in srp_init() |
| 272 | + // step 9 : public key (B) done in srp_init() |
| 273 | + |
| 274 | + uint8_t mstate = 2; |
| 275 | + |
| 276 | + TLV8_t tlv_para[] = |
| 277 | + { |
| 278 | + { .type = PAIRING_TYPE_STATE , .len = 1 , .value = &mstate }, |
| 279 | + { .type = PAIRING_TYPE_PUBLIC_KEY , .len = 384, .value = srp_getB() }, |
| 280 | + { .type = PAIRING_TYPE_SALT , .len = 16 , .value = srp_getSalt() }, |
| 281 | + }; |
| 282 | + |
| 283 | + hap_resp = createSrpResponse(hap_req->header.tid, HAP_STATUS_SUCCESS, tlv_para, arrcount(tlv_para)); |
| 284 | + break; |
201 | 285 | }
|
202 | 286 | }
|
203 | 287 | break;
|
|
0 commit comments