xref: /nrf52832-nimble/packages/NimBLE-latest/nimble/host/tools/log2smtest.rb (revision 042d53a763ad75cb1465103098bb88c245d95138)
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(&params);'
1019    else
1020        puts '    ble_sm_test_util_peer_sc_good(&params);'
1021    end
1022else
1023    if $ctxt[:we_are_init]
1024        puts '    ble_sm_test_util_us_lgcy_good(&params);'
1025    else
1026        puts '    ble_sm_test_util_peer_lgcy_good(&params);'
1027    end
1028end
1029puts '}'
1030