1#!/usr/bin/env ruby 2 3# 4# Licensed to the Apache Software Foundation (ASF) under one 5# or more contributor license agreements. See the NOTICE file 6# distributed with this work for additional information 7# regarding copyright ownership. The ASF licenses this file 8# to you under the Apache License, Version 2.0 (the 9# "License"); you may not use this file except in compliance 10# with the License. You may obtain a copy of the License at 11# 12# http://www.apache.org/licenses/LICENSE-2.0 13# 14# Unless required by applicable law or agreed to in writing, 15# software distributed under the License is distributed on an 16# "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY 17# KIND, either express or implied. See the License for the 18# specific language governing permissions and limitations 19# under the License. 20# 21 22### This script converts a bletiny log into a security manager unit test. The 23### input log must contain the connection establishment and complete pairing 24### procedure. 25### 26### Arguments: None 27### Stdin: bletiny log file 28 29$PAIR_ALG_STRINGS = { 30 0 => [ 'BLE_SM_PAIR_ALG_JW', 'just works', 'jw' ], 31 1 => [ 'BLE_SM_PAIR_ALG_PASSKEY', 'passkey entry', 'pk' ], 32 2 => [ 'BLE_SM_PAIR_ALG_OOB', 'out of band', 'ob' ], 33 3 => [ 'BLE_SM_PAIR_ALG_NUMCMP', 'numeric comparison', 'nc' ] 34} 35 36$ADDR_TYPE_STRINGS = { 37 0 => 'BLE_ADDR_TYPE_PUBLIC', 38 1 => 'BLE_ADDR_TYPE_RANDOM', 39 2 => 'BLE_ADDR_TYPE_RPA_PUB_DEFAULT', 40 3 => 'BLE_ADDR_TYPE_RPA_RND_DEFAULT', 41} 42 43$ACTION_STRINGS = { 44 0 => 'BLE_SM_IOACT_NONE', 45 1 => 'BLE_SM_IOACT_OOB', 46 2 => 'BLE_SM_IOACT_INPUT', 47 3 => 'BLE_SM_IOACT_DISP', 48 4 => 'BLE_SM_IOACT_NUMCMP', 49} 50 51$prev_idx = 0 52$ctxt = {} 53 54def test_case_name 55 type_str = $ctxt[:sc] ? "sc" : "lgcy" 56 init_str = $ctxt[:we_are_init] ? "us" : "peer" 57 alg_str = $PAIR_ALG_STRINGS[$ctxt[:pair_alg]][2] 58 iio_cap_str = "iio#{$ctxt[:pair_req][:io_cap]}" 59 rio_cap_str = "rio#{$ctxt[:pair_rsp][:io_cap]}" 60 bonding_str = "b#{$ctxt[:bonding] ? 1 : 0}" 61 iat_str = "iat#{$ctxt[:addrs][:init_type]}" 62 rat_str = "rat#{$ctxt[:addrs][:resp_type]}" 63 ikey_str = "ik#{$ctxt[:pair_rsp][:init_key_dist]}" 64 rkey_str = "rk#{$ctxt[:pair_rsp][:resp_key_dist]}" 65 66 "ble_sm_" + 67 "#{type_str}_#{init_str}_#{alg_str}_#{iio_cap_str}_#{rio_cap_str}_" + 68 "#{bonding_str}_#{iat_str}_#{rat_str}_#{ikey_str}_#{rkey_str}" 69end 70 71def test_case_comment 72<<-eos 73/** 74 * #{$ctxt[:sc] ? 'Secure connections' : 'Legacy'} pairing 75 * Master: #{$ctxt[:we_are_init] ? "us" : "peer"} 76 * Pair algorithm: #{$PAIR_ALG_STRINGS[$ctxt[:pair_alg]][1]} 77 * Initiator IO capabilities: #{$ctxt[:pair_req][:io_cap]} 78 * Responder IO capabilities: #{$ctxt[:pair_rsp][:io_cap]} 79 * Bonding: #{$ctxt[:bonding]} 80 * Initiator address type: #{$ADDR_TYPE_STRINGS[$ctxt[:addrs][:init_type]]} 81 * Responder address type: #{$ADDR_TYPE_STRINGS[$ctxt[:addrs][:resp_type]]} 82 * Initiator key distribution: #{$ctxt[:pair_rsp][:init_key_dist]} 83 * Responder key distribution: #{$ctxt[:pair_rsp][:resp_key_dist]} 84 */ 85eos 86end 87 88def to_hex_s(byte) 89 if byte.is_a?(String) 90 byte = s_to_i(byte) 91 end 92 93 "0x#{byte.to_s(16).rjust(2, '0')}" 94end 95 96# to_i(0) but interpret leading zeros as decimal. 97def s_to_i(s) 98 if s[0..1] == "0x" 99 return s.to_i(16) 100 else 101 return s.to_i(10) 102 end 103end 104 105def invalid_byte_line(msg, line) 106 str = "invalid byte line" 107 if msg != nil 108 str += ": #{msg}" 109 end 110 111 str += "; line=#{line}" 112 113 raise str 114end 115 116def token_string_to_bytes(line, delim = ' ') 117 tokens = line.split(delim) 118 bytes = [] 119 tokens.each do |token| 120 begin 121 byte = token.to_i(16) 122 bytes << byte 123 rescue 124 invalid_byte_line("token=#{token}", line) 125 end 126 end 127 128 return bytes 129end 130 131def txrx_prefix(is_tx) 132 if is_tx 133 return "tx" 134 else 135 return "rx" 136 end 137end 138 139def reqrsp_s(is_req) 140 reqrsp = nil 141 if is_req 142 return "req" 143 else 144 return "rsp" 145 end 146end 147 148def bytes_to_arr_body(bytes, indent) 149 lines = [] 150 151 idx = 0 152 while idx < bytes.size 153 slice_len = nil 154 if bytes.size - idx >= 8 155 slice_len = 8 156 else 157 slice_len = bytes.size - idx 158 end 159 160 slice = bytes[idx...(idx + slice_len)] 161 line = ' ' * indent + 162 slice.map{|b| to_hex_s(b)}.join(", ") + "," 163 lines << line 164 165 idx += slice_len 166 end 167 168 return lines.join("\n") << "\n" 169end 170 171def bytes_to_arr(bytes, name, indent) 172 str = "#{' ' * indent}.#{name} = {\n" 173 str << bytes_to_arr_body(bytes, indent + 4) 174 str << "#{' ' * indent}}," 175 176 return str 177end 178 179def addr_string_to_bytes(addr_string) 180 token_string_to_bytes(addr_string, ':').reverse 181end 182 183def parse_pair_cmd(line, is_req) 184 suffix = reqrsp_s(is_req) 185 re = %r{ 186 pair\s#{suffix}; 187 \s 188 conn=\d+ 189 \s 190 io_cap=(?<io_cap>\d+) 191 \s 192 oob_data_flag=(?<oob_data_flag>\d+) 193 \s 194 authreq=(?<authreq>0x[0-9a-f]+) 195 \s 196 mac_enc_key_size=(?<max_enc_key_size>\d+) 197 \s 198 init_key_dist=(?<init_key_dist>\d+) 199 \s 200 resp_key_dist=(?<resp_key_dist>\d+) 201 }x 202 203 m = re.match(line) 204 if m == nil 205 return nil 206 end 207 208 cmd = {} 209 cmd[:io_cap] = s_to_i(m[:io_cap]) 210 cmd[:oob_data_flag] = s_to_i(m[:oob_data_flag]) 211 cmd[:authreq] = s_to_i(m[:authreq]) 212 cmd[:max_enc_key_size] = s_to_i(m[:max_enc_key_size]) 213 cmd[:init_key_dist] = s_to_i(m[:init_key_dist]) 214 cmd[:resp_key_dist] = s_to_i(m[:resp_key_dist]) 215 216 return cmd 217end 218 219def parse_privkey(line) 220 if !(line =~ /our privkey=(.+)/) 221 return nil 222 end 223 return token_string_to_bytes($1) 224end 225 226def parse_public_key(line, is_tx) 227 prefix = txrx_prefix(is_tx) 228 if !(line =~ /#{prefix}ed sm command: public key; conn=\d+ x=(.+) y=(.+)/) 229 return nil 230 end 231 232 pubkey = {} 233 pubkey[:x] = token_string_to_bytes($1) 234 pubkey[:y] = token_string_to_bytes($2) 235 236 if pubkey[:x].size != 32 237 raise "invalid public key: x length incorrect; line=#{line}" 238 end 239 240 if pubkey[:y].size != 32 241 raise "invalid public key: y length incorrect; line=#{line}" 242 end 243 244 return pubkey 245end 246 247def parse_confirm(line, is_tx) 248 prefix = txrx_prefix(is_tx) 249 if !(line =~ /#{prefix}ed sm command: confirm; conn=\d+ value=(.+)/) 250 return nil 251 end 252 253 bytes = token_string_to_bytes($1) 254 if bytes.size != 16 255 raise "invalid confirm line (length mismatch): #{line}" 256 end 257 258 return { :value => bytes } 259end 260 261def parse_random(line, is_tx) 262 prefix = txrx_prefix(is_tx) 263 if !(line =~ /#{prefix}ed sm command: random; conn=\d+ value=(.+)/) 264 return nil 265 end 266 267 bytes = token_string_to_bytes($1) 268 if bytes.size != 16 269 raise "invalid random line (length mismatch): #{line}" 270 end 271 272 return { :value => bytes } 273end 274 275def parse_stk(line) 276 if !(line =~ /^ out=(.+)/) 277 return nil 278 end 279 280 bytes = token_string_to_bytes($1) 281 if bytes.size != 16 282 raise "invalid stk line (length mismatch): #{line}" 283 end 284 285 return bytes 286end 287 288def parse_dhkey_check(line, is_tx) 289 prefix = txrx_prefix(is_tx) 290 if !(line =~ /#{prefix}ed sm command: dhkey check; conn=\d+ value=(.+)/) 291 return nil 292 end 293 294 bytes = token_string_to_bytes($1) 295 if bytes.size != 16 296 raise "invalid dhkey_check line (length mismatch): #{line}" 297 end 298 299 return { :value => bytes } 300end 301 302def parse_ltk(line) 303 if !(line =~ /persisting.+ltk=([^ ]+)/) 304 return nil 305 end 306 307 bytes = $1.split(":") 308 if bytes.size != 16 309 raise "invalid ltk line (length mismatch): exp=16 got=#{bytes.size} " + 310 "line=#{line}" 311 end 312 313 return bytes 314end 315 316def parse_enc_info(line, is_tx) 317 prefix = txrx_prefix(is_tx) 318 if !(line =~ /#{prefix}ed sm command: enc info; conn=\d+ ltk=(.+)/) 319 return nil 320 end 321 322 bytes = token_string_to_bytes($1) 323 if bytes.size != 16 324 raise "invalid enc info line (length mismatch): #{line}" 325 end 326 327 return { :ltk => bytes } 328end 329 330def parse_master_id(line, is_tx) 331 prefix = txrx_prefix(is_tx) 332 if !(line =~ /#{prefix}ed sm command: master id; conn=\d+ ediv=(.+) rand=(.+)/) 333 return nil 334 end 335 336 return { 337 :ediv => s_to_i($1), 338 :rand => s_to_i($2), 339 } 340end 341 342def parse_id_info(line, is_tx) 343 prefix = txrx_prefix(is_tx) 344 if !(line =~ /#{prefix}ed sm command: id info; conn=\d+ irk=(.+)/) 345 return nil 346 end 347 348 bytes = token_string_to_bytes($1) 349 if bytes.size != 16 350 raise "invalid id info line (length mismatch): #{line}" 351 end 352 353 return { :irk => bytes } 354end 355 356def parse_id_addr_info(line, is_tx) 357 prefix = txrx_prefix(is_tx) 358 if !(line =~ /#{prefix}ed sm command: id addr info; conn=\d+ addr_type=(\d+) addr=(.+)/) 359 return nil 360 end 361 362 bytes = addr_string_to_bytes($2) 363 if bytes.size != 6 364 raise "invalid id addr info line (length mismatch): #{line}" 365 end 366 367 return { 368 :addr_type => s_to_i($1), 369 :addr => bytes, 370 } 371end 372 373def parse_sign_info(line, is_tx) 374 prefix = txrx_prefix(is_tx) 375 if !(line =~ /#{prefix}ed sm command: sign info; conn=\d+ sig_key=(.+)/) 376 return nil 377 end 378 379 bytes = token_string_to_bytes($1) 380 if bytes.size != 16 381 raise "invalid sign info line (length mismatch): #{line}" 382 end 383 384 return { 385 :sig_key => bytes, 386 } 387end 388 389def parse_passkey_info(line) 390 passkey_info = {} 391 392 case line 393 when /passkey action event; action=4 numcmp=(\d+)/ 394 passkey_info[:action] = 4 395 passkey_info[:numcmp] = $1.to_i(10) 396 when /^b passkey conn=\d+ action=1 oob=(\S+)/ 397 passkey_info[:action] = 1 398 passkey_info[:oob] = token_string_to_bytes($1, ':') 399 when /^b passkey conn=\d+ action=2 key=(\d+)/ 400 passkey_info[:action] = 2 401 passkey_info[:key] = $1.to_i(10) 402 when /b passkey conn=\d+ action=3 key=(\d+)/ 403 passkey_info[:action] = 3 404 passkey_info[:key] = $1.to_i(10) 405 else 406 return nil 407 end 408 409 return passkey_info 410end 411 412def parse_addrs(line) 413 if !(line =~ /our_ota_addr_type=(\d+) our_ota_addr=(\S+) our_id_addr_type=(\d+) our_id_addr=(\S+) peer_ota_addr_type=(\d+) peer_ota_addr=(\S+) peer_id_addr_type=(\d+) peer_id_addr=(\S+)/) 414 return nil 415 end 416 417 our_ota_addr_bytes = addr_string_to_bytes($2) 418 our_id_addr_bytes = addr_string_to_bytes($4) 419 peer_ota_addr_bytes = addr_string_to_bytes($6) 420 peer_id_addr_bytes = addr_string_to_bytes($8) 421 422 if $ctxt[:we_are_init] 423 init_id_bytes = our_id_addr_bytes 424 init_ota_bytes = our_ota_addr_bytes 425 resp_id_bytes = peer_id_addr_bytes 426 resp_ota_bytes = peer_ota_addr_bytes 427 init_addr_type = s_to_i($1) 428 resp_addr_type = s_to_i($5) 429 else 430 init_id_bytes = peer_id_addr_bytes 431 init_ota_bytes = peer_ota_addr_bytes 432 resp_id_bytes = our_id_addr_bytes 433 resp_ota_bytes = our_ota_addr_bytes 434 init_addr_type = s_to_i($5) 435 resp_addr_type = s_to_i($1) 436 end 437 438 if init_id_bytes == init_ota_bytes 439 init_ota_bytes = [0] * 6 440 end 441 if resp_id_bytes == resp_ota_bytes 442 resp_ota_bytes = [0] * 6 443 end 444 445 return { 446 :init_type => init_addr_type, 447 :resp_type => resp_addr_type, 448 :init_id_addr => init_id_bytes, 449 :resp_id_addr => resp_id_bytes, 450 :init_rpa => init_ota_bytes, 451 :resp_rpa => resp_ota_bytes, 452 } 453end 454 455def detect_initiator(lines) 456 lines.each do |line| 457 if line =~ /txed sm command: pair req/ 458 $ctxt[:we_are_init] = true 459 elsif line =~ /txed sm command: pair rsp/ 460 $ctxt[:we_are_init] = false 461 end 462 end 463 464 if $ctxt[:we_are_init] == nil 465 raise "could not detect which peer is the initiator" 466 end 467end 468 469def pair_cmd_to_s(cmd, is_req) 470 suffix = reqrsp_s(is_req) 471 return <<-eos 472 .pair_#{suffix} = { 473 .io_cap = #{to_hex_s(cmd[:io_cap])}, 474 .oob_data_flag = #{to_hex_s(cmd[:oob_data_flag])}, 475 .authreq = #{to_hex_s(cmd[:authreq])}, 476 .max_enc_key_size = #{to_hex_s(cmd[:max_enc_key_size])}, 477 .init_key_dist = #{to_hex_s(cmd[:init_key_dist])}, 478 .resp_key_dist = #{to_hex_s(cmd[:resp_key_dist])}, 479 }, 480 eos 481end 482 483def privkey_to_s(privkey) 484 return bytes_to_arr(privkey, "our_priv_key", 8) 485end 486 487def public_key_to_s(public_key, is_req) 488 suffix = reqrsp_s(is_req) 489 return <<-eos 490 .public_key_#{suffix} = { 491#{bytes_to_arr(public_key[:x], "x", 12)} 492#{bytes_to_arr(public_key[:y], "y", 12)} 493 }, 494 eos 495end 496 497def confirm_to_s(confirm, is_req, idx) 498 return <<-eos 499 .confirm_#{reqrsp_s(is_req)}[#{idx}] = { 500#{bytes_to_arr(confirm[:value], "value", 12)} 501 }, 502 eos 503end 504 505def random_to_s(random, is_req, idx) 506 return <<-eos 507 .random_#{reqrsp_s(is_req)}[#{idx}] = { 508#{bytes_to_arr(random[:value], "value", 12)} 509 }, 510 eos 511end 512 513def ltk_to_s(ltk) 514 return bytes_to_arr(ltk, "ltk", 8) 515end 516 517def stk_to_s(stk) 518 return bytes_to_arr(stk, "stk", 8) 519end 520 521def enc_info_to_s(id_info, is_req) 522 return <<-eos 523 .enc_info_#{reqrsp_s(is_req)} = { 524#{bytes_to_arr(id_info[:ltk], "ltk", 12)} 525 }, 526 eos 527end 528 529def master_id_to_s(master_id, is_req) 530 return <<-eos 531 .master_id_#{reqrsp_s(is_req)} = { 532 .ediv = 0x#{master_id[:ediv].to_s(16)}, 533 .rand_val = 0x#{master_id[:rand].to_s(16)}, 534 }, 535 eos 536end 537 538def id_info_to_s(id_info, is_req) 539 return <<-eos 540 .id_info_#{reqrsp_s(is_req)} = { 541#{bytes_to_arr(id_info[:irk], "irk", 12)} 542 }, 543 eos 544end 545 546def id_addr_info_to_s(id_addr_info, is_req) 547 return <<-eos 548 .id_addr_info_#{reqrsp_s(is_req)} = { 549 .addr_type = #{id_addr_info[:addr_type]}, 550#{bytes_to_arr(id_addr_info[:addr], "bd_addr", 12)} 551 }, 552 eos 553end 554 555def sign_info_to_s(sign_info, is_req) 556 return <<-eos 557 .sign_info_#{reqrsp_s(is_req)} = { 558#{bytes_to_arr(sign_info[:sig_key], "sig_key", 12)} 559 }, 560 eos 561end 562 563def passkey_info_fill(passkey_info) 564 case passkey_info[:action] 565 # None 566 when 0 567 $ctxt[:pair_alg] = 0 568 $ctxt[:authenticated] = false 569 570 # OOB 571 when 1 572 $ctxt[:pair_alg] = 2 573 $ctxt[:authenticated] = true 574 575 # Input 576 when 2 577 $ctxt[:pair_alg] = 1 578 $ctxt[:authenticated] = true 579 580 # Display 581 when 3 582 $ctxt[:pair_alg] = 1 583 $ctxt[:authenticated] = true 584 585 # Numeric comparison 586 when 4 587 $ctxt[:pair_alg] = 3 588 $ctxt[:authenticated] = true 589 590 else 591 raise "invalid MITM action: #{passkey_info[:action]}" 592 end 593end 594 595def passkey_info_s 596 passkey_info = $ctxt[:passkey_info] 597 action_str = $ACTION_STRINGS[passkey_info[:action]] 598 599 result = <<-eos 600 .pair_alg = #{$ctxt[:pair_alg]}, 601 .authenticated = #{$ctxt[:authenticated]}, 602 .passkey_info = { 603 .passkey = { 604 .action = #{action_str}, 605 eos 606 607 if passkey_info[:key] != nil 608 result << <<-eos 609 .passkey = #{passkey_info[:key].to_i}, 610 eos 611 end 612 if passkey_info[:oob] != nil 613 result << <<-eos 614#{bytes_to_arr(passkey_info[:oob], "oob", 16)} 615 eos 616 end 617 if passkey_info[:numcmp] != nil 618 result << <<-eos 619 .numcmp_accept = 1, 620 eos 621 end 622 623 result << <<-eos 624 }, 625 eos 626 627 if passkey_info[:numcmp] != nil 628 result << <<-eos 629 .exp_numcmp = #{passkey_info[:numcmp].to_i}, 630 eos 631 end 632 633 result << <<-eos 634 }, 635 eos 636end 637 638def addrs_to_s(addrs) 639 s = '' 640 641 init_type = addrs[:init_type] 642 resp_type = addrs[:resp_type] 643 644 if init_type != 0 645 s += " .init_addr_type = #{$ADDR_TYPE_STRINGS[init_type]},\n" 646 end 647 s += bytes_to_arr(addrs[:init_id_addr], "init_id_addr", 8) + "\n" 648 if init_type >= 2 649 s += bytes_to_arr(addrs[:init_rpa], "init_rpa", 8) + "\n" 650 end 651 652 if resp_type != 0 653 s += " .resp_addr_type = #{$ADDR_TYPE_STRINGS[resp_type]},\n" 654 end 655 s += bytes_to_arr(addrs[:resp_id_addr], "resp_id_addr", 8) + "\n" 656 if resp_type >= 2 657 s += bytes_to_arr(addrs[:resp_rpa], "resp_rpa", 8) + "\n" 658 end 659 660 return s 661end 662 663def dhkey_check_to_s(dhkey_check, is_req) 664 return <<-eos 665 .dhkey_check_#{reqrsp_s(is_req)} = { 666#{bytes_to_arr(dhkey_check[:value], "value", 12)} 667 }, 668 eos 669end 670 671def extract_one(lines, ignore_prev = false) 672 if ignore_prev 673 start = 0 674 else 675 start = $prev_idx 676 end 677 678 (start...lines.size).each do |idx| 679 line = lines[idx] 680 result = yield(line) 681 if result != nil 682 if !ignore_prev 683 $prev_idx = idx 684 end 685 return result 686 end 687 end 688 689 return nil 690end 691 692def extract_pair_req(lines) 693 return extract_one(lines) {|line| parse_pair_cmd(line, true)} 694end 695 696def extract_pair_rsp(lines) 697 return extract_one(lines) {|line| parse_pair_cmd(line, false)} 698end 699 700def extract_privkey(lines) 701 return extract_one(lines) {|line| parse_privkey(line)} 702end 703 704def extract_public_key_req(lines) 705 return extract_one(lines) do |line| 706 parse_public_key(line, $ctxt[:we_are_init]) 707 end 708end 709 710def extract_public_key_rsp(lines) 711 return extract_one(lines) do |line| 712 parse_public_key(line, !$ctxt[:we_are_init]) 713 end 714end 715 716def extract_confirm_req(lines) 717 return extract_one(lines) do |line| 718 parse_confirm(line, $ctxt[:we_are_init]) 719 end 720end 721 722def extract_confirm_rsp(lines) 723 return extract_one(lines) do |line| 724 parse_confirm(line, !$ctxt[:we_are_init]) 725 end 726end 727 728def extract_random_req(lines) 729 return extract_one(lines) do |line| 730 parse_random(line, $ctxt[:we_are_init]) 731 end 732end 733 734def extract_random_rsp(lines) 735 return extract_one(lines) do |line| 736 parse_random(line, !$ctxt[:we_are_init]) 737 end 738end 739 740def extract_confirm_random(lines) 741 confirm_reqs = [] 742 confirm_rsps = [] 743 random_reqs = [] 744 random_rsps = [] 745 746 idx = 0 747 loop do 748 confirm_req = extract_confirm_req(lines) 749 if confirm_req != nil 750 confirm_reqs << confirm_req 751 end 752 753 confirm_rsp = extract_confirm_rsp(lines) 754 break if confirm_rsp == nil 755 if idx >= 20 756 raise "too many confirm rsps (>20)" 757 end 758 confirm_rsps << confirm_rsp 759 760 random_req = extract_random_req(lines) 761 break if random_req == nil 762 random_reqs << random_req 763 764 random_rsp = extract_random_rsp(lines) 765 break if random_rsp == nil 766 random_rsps << random_rsp 767 768 idx += 1 769 end 770 771 return confirm_reqs, confirm_rsps, random_reqs, random_rsps 772end 773 774def extract_stk(lines) 775 return extract_one(lines, true) do |line| 776 parse_stk(line) 777 end 778end 779 780def extract_dhkey_check_req(lines) 781 return extract_one(lines) do |line| 782 parse_dhkey_check(line, $ctxt[:we_are_init]) 783 end 784end 785 786def extract_dhkey_check_rsp(lines) 787 return extract_one(lines) do |line| 788 parse_dhkey_check(line, !$ctxt[:we_are_init]) 789 end 790end 791 792def extract_enc_info_req(lines) 793 return extract_one(lines) do |line| 794 parse_enc_info(line, !$ctxt[:we_are_init]) 795 end 796end 797 798def extract_enc_info_rsp(lines) 799 return extract_one(lines) do |line| 800 parse_enc_info(line, $ctxt[:we_are_init]) 801 end 802end 803 804def extract_master_id_req(lines) 805 return extract_one(lines) do |line| 806 parse_master_id(line, !$ctxt[:we_are_init]) 807 end 808end 809 810def extract_master_id_rsp(lines) 811 return extract_one(lines) do |line| 812 parse_master_id(line, $ctxt[:we_are_init]) 813 end 814end 815 816def extract_id_info_req(lines) 817 return extract_one(lines) do |line| 818 parse_id_info(line, !$ctxt[:we_are_init]) 819 end 820end 821 822def extract_id_info_rsp(lines) 823 return extract_one(lines) do |line| 824 parse_id_info(line, $ctxt[:we_are_init]) 825 end 826end 827 828def extract_id_addr_info_req(lines) 829 return extract_one(lines) do |line| 830 parse_id_addr_info(line, !$ctxt[:we_are_init]) 831 end 832end 833 834def extract_id_addr_info_rsp(lines) 835 return extract_one(lines) do |line| 836 parse_id_addr_info(line, $ctxt[:we_are_init]) 837 end 838end 839 840def extract_sign_info_req(lines) 841 return extract_one(lines) do |line| 842 parse_sign_info(line, !$ctxt[:we_are_init]) 843 end 844end 845 846def extract_sign_info_rsp(lines) 847 return extract_one(lines) do |line| 848 parse_sign_info(line, $ctxt[:we_are_init]) 849 end 850end 851 852def extract_ltk(lines) 853 return extract_one(lines) do |line| 854 parse_ltk(line) 855 end 856end 857 858def extract_passkey_info(lines) 859 passkey_info = extract_one(lines, true) do |line| 860 parse_passkey_info(line) 861 end 862 863 if passkey_info == nil 864 passkey_info = { :action => 0 } 865 end 866 867 return passkey_info 868end 869 870def extract_addrs(lines) 871 return extract_one(lines) do |line| 872 parse_addrs(line) 873 end 874end 875 876 877lines = STDIN.readlines 878 879detect_initiator(lines) 880$ctxt[:pair_req] = extract_pair_req(lines) 881$ctxt[:pair_rsp] = extract_pair_rsp(lines) 882$ctxt[:privkey] = extract_privkey(lines) 883$ctxt[:public_key_req] = extract_public_key_req(lines) 884$ctxt[:public_key_rsp] = extract_public_key_rsp(lines) 885$ctxt[:confirm_reqs], $ctxt[:confirm_rsps], $ctxt[:random_reqs], $ctxt[:random_rsps] = extract_confirm_random(lines) 886$ctxt[:passkey_info] = extract_passkey_info(lines) 887$ctxt[:dhkey_check_req] = extract_dhkey_check_req(lines) 888$ctxt[:dhkey_check_rsp] = extract_dhkey_check_rsp(lines) 889$ctxt[:enc_info_req] = extract_enc_info_req(lines) 890$ctxt[:master_id_req] = extract_master_id_req(lines) 891$ctxt[:id_info_req] = extract_id_info_req(lines) 892$ctxt[:id_addr_info_req] = extract_id_addr_info_req(lines) 893$ctxt[:sign_info_req] = extract_sign_info_req(lines) 894$ctxt[:enc_info_rsp] = extract_enc_info_rsp(lines) 895$ctxt[:master_id_rsp] = extract_master_id_rsp(lines) 896$ctxt[:id_info_rsp] = extract_id_info_rsp(lines) 897$ctxt[:id_addr_info_rsp] = extract_id_addr_info_rsp(lines) 898$ctxt[:sign_info_rsp] = extract_sign_info_rsp(lines) 899$ctxt[:addrs] = extract_addrs(lines) 900$ctxt[:ltk] = extract_ltk(lines) 901$ctxt[:stk] = extract_stk(lines) 902 903expected_confirm_rsps = nil 904expected_random_reqs = nil 905expected_random_rsps = nil 906if $ctxt[:confirm_reqs].size == 0 907 expected_confirm_rsps = 1 908 expected_random_reqs = 1 909 expected_random_rsps = 1 910else 911 expected_confirm_rsps = $ctxt[:confirm_reqs].size 912 expected_random_reqs = $ctxt[:random_reqs].size 913 expected_random_rsps = $ctxt[:random_rsps].size 914end 915 916if $ctxt[:confirm_rsps].size != expected_confirm_rsps 917 raise "wrong number of confirm responses " + 918 "(exp=#{expected_confirm_rsps}; got=#{$ctxt[:confirm_rsps].size}" 919end 920 921if $ctxt[:random_reqs].size != expected_random_reqs 922 raise "wrong number of random requests " + 923 "(exp=#{expected_random_reqs}; got=#{$ctxt[:random_reqs].size}" 924end 925 926if $ctxt[:random_rsps].size != expected_random_rsps 927 raise "wrong number of random responses " + 928 "(exp=#{expected_random_rsps}; got=#{$ctxt[:random_rsps].size}" 929end 930 931passkey_info_fill($ctxt[:passkey_info]) 932 933$ctxt[:sc] = $ctxt[:public_key_req] != nil 934$ctxt[:bonding] = $ctxt[:pair_req][:authreq] & 1 == 1 && 935 $ctxt[:pair_rsp][:authreq] & 1 == 1 936 937puts test_case_comment() 938puts <<-eos 939TEST_CASE(#{test_case_name()}) 940{ 941 struct ble_sm_test_params params; 942 943 params = (struct ble_sm_test_params) { 944eos 945 946puts addrs_to_s($ctxt[:addrs]) 947 948puts pair_cmd_to_s($ctxt[:pair_req], true) 949puts pair_cmd_to_s($ctxt[:pair_rsp], false) 950 951if $ctxt[:sc] 952 puts privkey_to_s($ctxt[:privkey]) 953 puts public_key_to_s($ctxt[:public_key_req], true) 954 puts public_key_to_s($ctxt[:public_key_req], false) 955end 956 957$ctxt[:confirm_rsps].size.times do |i| 958 confirm_req = $ctxt[:confirm_reqs][i] 959 confirm_rsp = $ctxt[:confirm_rsps][i] 960 random_req = $ctxt[:random_reqs][i] 961 random_rsp = $ctxt[:random_rsps][i] 962 963 if confirm_req != nil 964 puts confirm_to_s(confirm_req, true, i) 965 end 966 967 puts confirm_to_s(confirm_rsp, false, i) 968 puts random_to_s(random_req, true, i) 969 puts random_to_s(random_rsp, false, i) 970end 971 972if $ctxt[:sc] 973 puts dhkey_check_to_s($ctxt[:dhkey_check_req], true) 974 puts dhkey_check_to_s($ctxt[:dhkey_check_rsp], false) 975end 976 977if $ctxt[:enc_info_req] != nil 978 puts enc_info_to_s($ctxt[:enc_info_req], true) 979end 980if $ctxt[:master_id_req] != nil 981 puts master_id_to_s($ctxt[:master_id_req], true) 982end 983if $ctxt[:id_info_req] != nil 984 puts id_info_to_s($ctxt[:id_info_req], true) 985end 986if $ctxt[:id_addr_info_req] != nil 987 puts id_addr_info_to_s($ctxt[:id_addr_info_req], true) 988end 989if $ctxt[:sign_info_req] != nil 990 puts sign_info_to_s($ctxt[:sign_info_req], true) 991end 992if $ctxt[:enc_info_rsp] != nil 993 puts enc_info_to_s($ctxt[:enc_info_rsp], false) 994end 995if $ctxt[:master_id_rsp] != nil 996 puts master_id_to_s($ctxt[:master_id_rsp], false) 997end 998if $ctxt[:id_info_rsp] != nil 999 puts id_info_to_s($ctxt[:id_info_rsp], false) 1000end 1001if $ctxt[:id_addr_info_rsp] != nil 1002 puts id_addr_info_to_s($ctxt[:id_addr_info_rsp], false) 1003end 1004if $ctxt[:sign_info_rsp] != nil 1005 puts sign_info_to_s($ctxt[:sign_info_rsp], false) 1006end 1007if $ctxt[:sc] 1008 puts ltk_to_s($ctxt[:ltk]) 1009else 1010 puts stk_to_s($ctxt[:stk]) 1011end 1012puts passkey_info_s() 1013 1014puts ' };' 1015 1016if $ctxt[:sc] 1017 if $ctxt[:we_are_init] 1018 puts ' ble_sm_test_util_us_sc_good(¶ms);' 1019 else 1020 puts ' ble_sm_test_util_peer_sc_good(¶ms);' 1021 end 1022else 1023 if $ctxt[:we_are_init] 1024 puts ' ble_sm_test_util_us_lgcy_good(¶ms);' 1025 else 1026 puts ' ble_sm_test_util_peer_lgcy_good(¶ms);' 1027 end 1028end 1029puts '}' 1030