summaryrefslogtreecommitdiff
path: root/src/libs/mynewt-nimble/nimble/host/tools/log2smtest.rb
diff options
context:
space:
mode:
Diffstat (limited to 'src/libs/mynewt-nimble/nimble/host/tools/log2smtest.rb')
-rwxr-xr-xsrc/libs/mynewt-nimble/nimble/host/tools/log2smtest.rb1029
1 files changed, 1029 insertions, 0 deletions
diff --git a/src/libs/mynewt-nimble/nimble/host/tools/log2smtest.rb b/src/libs/mynewt-nimble/nimble/host/tools/log2smtest.rb
new file mode 100755
index 00000000..e253e69f
--- /dev/null
+++ b/src/libs/mynewt-nimble/nimble/host/tools/log2smtest.rb
@@ -0,0 +1,1029 @@
+#!/usr/bin/env ruby
+
+#
+# Licensed to the Apache Software Foundation (ASF) under one
+# or more contributor license agreements. See the NOTICE file
+# distributed with this work for additional information
+# regarding copyright ownership. The ASF licenses this file
+# to you under the Apache License, Version 2.0 (the
+# "License"); you may not use this file except in compliance
+# with the License. You may obtain a copy of the License at
+#
+# http://www.apache.org/licenses/LICENSE-2.0
+#
+# Unless required by applicable law or agreed to in writing,
+# software distributed under the License is distributed on an
+# "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+# KIND, either express or implied. See the License for the
+# specific language governing permissions and limitations
+# under the License.
+#
+
+### This script converts a bletiny log into a security manager unit test. The
+### input log must contain the connection establishment and complete pairing
+### procedure.
+###
+### Arguments: None
+### Stdin: bletiny log file
+
+$PAIR_ALG_STRINGS = {
+ 0 => [ 'BLE_SM_PAIR_ALG_JW', 'just works', 'jw' ],
+ 1 => [ 'BLE_SM_PAIR_ALG_PASSKEY', 'passkey entry', 'pk' ],
+ 2 => [ 'BLE_SM_PAIR_ALG_OOB', 'out of band', 'ob' ],
+ 3 => [ 'BLE_SM_PAIR_ALG_NUMCMP', 'numeric comparison', 'nc' ]
+}
+
+$ADDR_TYPE_STRINGS = {
+ 0 => 'BLE_ADDR_TYPE_PUBLIC',
+ 1 => 'BLE_ADDR_TYPE_RANDOM',
+ 2 => 'BLE_ADDR_TYPE_RPA_PUB_DEFAULT',
+ 3 => 'BLE_ADDR_TYPE_RPA_RND_DEFAULT',
+}
+
+$ACTION_STRINGS = {
+ 0 => 'BLE_SM_IOACT_NONE',
+ 1 => 'BLE_SM_IOACT_OOB',
+ 2 => 'BLE_SM_IOACT_INPUT',
+ 3 => 'BLE_SM_IOACT_DISP',
+ 4 => 'BLE_SM_IOACT_NUMCMP',
+}
+
+$prev_idx = 0
+$ctxt = {}
+
+def test_case_name
+ type_str = $ctxt[:sc] ? "sc" : "lgcy"
+ init_str = $ctxt[:we_are_init] ? "us" : "peer"
+ alg_str = $PAIR_ALG_STRINGS[$ctxt[:pair_alg]][2]
+ iio_cap_str = "iio#{$ctxt[:pair_req][:io_cap]}"
+ rio_cap_str = "rio#{$ctxt[:pair_rsp][:io_cap]}"
+ bonding_str = "b#{$ctxt[:bonding] ? 1 : 0}"
+ iat_str = "iat#{$ctxt[:addrs][:init_type]}"
+ rat_str = "rat#{$ctxt[:addrs][:resp_type]}"
+ ikey_str = "ik#{$ctxt[:pair_rsp][:init_key_dist]}"
+ rkey_str = "rk#{$ctxt[:pair_rsp][:resp_key_dist]}"
+
+ "ble_sm_" +
+ "#{type_str}_#{init_str}_#{alg_str}_#{iio_cap_str}_#{rio_cap_str}_" +
+ "#{bonding_str}_#{iat_str}_#{rat_str}_#{ikey_str}_#{rkey_str}"
+end
+
+def test_case_comment
+<<-eos
+/**
+ * #{$ctxt[:sc] ? 'Secure connections' : 'Legacy'} pairing
+ * Master: #{$ctxt[:we_are_init] ? "us" : "peer"}
+ * Pair algorithm: #{$PAIR_ALG_STRINGS[$ctxt[:pair_alg]][1]}
+ * Initiator IO capabilities: #{$ctxt[:pair_req][:io_cap]}
+ * Responder IO capabilities: #{$ctxt[:pair_rsp][:io_cap]}
+ * Bonding: #{$ctxt[:bonding]}
+ * Initiator address type: #{$ADDR_TYPE_STRINGS[$ctxt[:addrs][:init_type]]}
+ * Responder address type: #{$ADDR_TYPE_STRINGS[$ctxt[:addrs][:resp_type]]}
+ * Initiator key distribution: #{$ctxt[:pair_rsp][:init_key_dist]}
+ * Responder key distribution: #{$ctxt[:pair_rsp][:resp_key_dist]}
+ */
+eos
+end
+
+def to_hex_s(byte)
+ if byte.is_a?(String)
+ byte = s_to_i(byte)
+ end
+
+ "0x#{byte.to_s(16).rjust(2, '0')}"
+end
+
+# to_i(0) but interpret leading zeros as decimal.
+def s_to_i(s)
+ if s[0..1] == "0x"
+ return s.to_i(16)
+ else
+ return s.to_i(10)
+ end
+end
+
+def invalid_byte_line(msg, line)
+ str = "invalid byte line"
+ if msg != nil
+ str += ": #{msg}"
+ end
+
+ str += "; line=#{line}"
+
+ raise str
+end
+
+def token_string_to_bytes(line, delim = ' ')
+ tokens = line.split(delim)
+ bytes = []
+ tokens.each do |token|
+ begin
+ byte = token.to_i(16)
+ bytes << byte
+ rescue
+ invalid_byte_line("token=#{token}", line)
+ end
+ end
+
+ return bytes
+end
+
+def txrx_prefix(is_tx)
+ if is_tx
+ return "tx"
+ else
+ return "rx"
+ end
+end
+
+def reqrsp_s(is_req)
+ reqrsp = nil
+ if is_req
+ return "req"
+ else
+ return "rsp"
+ end
+end
+
+def bytes_to_arr_body(bytes, indent)
+ lines = []
+
+ idx = 0
+ while idx < bytes.size
+ slice_len = nil
+ if bytes.size - idx >= 8
+ slice_len = 8
+ else
+ slice_len = bytes.size - idx
+ end
+
+ slice = bytes[idx...(idx + slice_len)]
+ line = ' ' * indent +
+ slice.map{|b| to_hex_s(b)}.join(", ") + ","
+ lines << line
+
+ idx += slice_len
+ end
+
+ return lines.join("\n") << "\n"
+end
+
+def bytes_to_arr(bytes, name, indent)
+ str = "#{' ' * indent}.#{name} = {\n"
+ str << bytes_to_arr_body(bytes, indent + 4)
+ str << "#{' ' * indent}},"
+
+ return str
+end
+
+def addr_string_to_bytes(addr_string)
+ token_string_to_bytes(addr_string, ':').reverse
+end
+
+def parse_pair_cmd(line, is_req)
+ suffix = reqrsp_s(is_req)
+ re = %r{
+ pair\s#{suffix};
+ \s
+ conn=\d+
+ \s
+ io_cap=(?<io_cap>\d+)
+ \s
+ oob_data_flag=(?<oob_data_flag>\d+)
+ \s
+ authreq=(?<authreq>0x[0-9a-f]+)
+ \s
+ mac_enc_key_size=(?<max_enc_key_size>\d+)
+ \s
+ init_key_dist=(?<init_key_dist>\d+)
+ \s
+ resp_key_dist=(?<resp_key_dist>\d+)
+ }x
+
+ m = re.match(line)
+ if m == nil
+ return nil
+ end
+
+ cmd = {}
+ cmd[:io_cap] = s_to_i(m[:io_cap])
+ cmd[:oob_data_flag] = s_to_i(m[:oob_data_flag])
+ cmd[:authreq] = s_to_i(m[:authreq])
+ cmd[:max_enc_key_size] = s_to_i(m[:max_enc_key_size])
+ cmd[:init_key_dist] = s_to_i(m[:init_key_dist])
+ cmd[:resp_key_dist] = s_to_i(m[:resp_key_dist])
+
+ return cmd
+end
+
+def parse_privkey(line)
+ if !(line =~ /our privkey=(.+)/)
+ return nil
+ end
+ return token_string_to_bytes($1)
+end
+
+def parse_public_key(line, is_tx)
+ prefix = txrx_prefix(is_tx)
+ if !(line =~ /#{prefix}ed sm command: public key; conn=\d+ x=(.+) y=(.+)/)
+ return nil
+ end
+
+ pubkey = {}
+ pubkey[:x] = token_string_to_bytes($1)
+ pubkey[:y] = token_string_to_bytes($2)
+
+ if pubkey[:x].size != 32
+ raise "invalid public key: x length incorrect; line=#{line}"
+ end
+
+ if pubkey[:y].size != 32
+ raise "invalid public key: y length incorrect; line=#{line}"
+ end
+
+ return pubkey
+end
+
+def parse_confirm(line, is_tx)
+ prefix = txrx_prefix(is_tx)
+ if !(line =~ /#{prefix}ed sm command: confirm; conn=\d+ value=(.+)/)
+ return nil
+ end
+
+ bytes = token_string_to_bytes($1)
+ if bytes.size != 16
+ raise "invalid confirm line (length mismatch): #{line}"
+ end
+
+ return { :value => bytes }
+end
+
+def parse_random(line, is_tx)
+ prefix = txrx_prefix(is_tx)
+ if !(line =~ /#{prefix}ed sm command: random; conn=\d+ value=(.+)/)
+ return nil
+ end
+
+ bytes = token_string_to_bytes($1)
+ if bytes.size != 16
+ raise "invalid random line (length mismatch): #{line}"
+ end
+
+ return { :value => bytes }
+end
+
+def parse_stk(line)
+ if !(line =~ /^ out=(.+)/)
+ return nil
+ end
+
+ bytes = token_string_to_bytes($1)
+ if bytes.size != 16
+ raise "invalid stk line (length mismatch): #{line}"
+ end
+
+ return bytes
+end
+
+def parse_dhkey_check(line, is_tx)
+ prefix = txrx_prefix(is_tx)
+ if !(line =~ /#{prefix}ed sm command: dhkey check; conn=\d+ value=(.+)/)
+ return nil
+ end
+
+ bytes = token_string_to_bytes($1)
+ if bytes.size != 16
+ raise "invalid dhkey_check line (length mismatch): #{line}"
+ end
+
+ return { :value => bytes }
+end
+
+def parse_ltk(line)
+ if !(line =~ /persisting.+ltk=([^ ]+)/)
+ return nil
+ end
+
+ bytes = $1.split(":")
+ if bytes.size != 16
+ raise "invalid ltk line (length mismatch): exp=16 got=#{bytes.size} " +
+ "line=#{line}"
+ end
+
+ return bytes
+end
+
+def parse_enc_info(line, is_tx)
+ prefix = txrx_prefix(is_tx)
+ if !(line =~ /#{prefix}ed sm command: enc info; conn=\d+ ltk=(.+)/)
+ return nil
+ end
+
+ bytes = token_string_to_bytes($1)
+ if bytes.size != 16
+ raise "invalid enc info line (length mismatch): #{line}"
+ end
+
+ return { :ltk => bytes }
+end
+
+def parse_master_id(line, is_tx)
+ prefix = txrx_prefix(is_tx)
+ if !(line =~ /#{prefix}ed sm command: master id; conn=\d+ ediv=(.+) rand=(.+)/)
+ return nil
+ end
+
+ return {
+ :ediv => s_to_i($1),
+ :rand => s_to_i($2),
+ }
+end
+
+def parse_id_info(line, is_tx)
+ prefix = txrx_prefix(is_tx)
+ if !(line =~ /#{prefix}ed sm command: id info; conn=\d+ irk=(.+)/)
+ return nil
+ end
+
+ bytes = token_string_to_bytes($1)
+ if bytes.size != 16
+ raise "invalid id info line (length mismatch): #{line}"
+ end
+
+ return { :irk => bytes }
+end
+
+def parse_id_addr_info(line, is_tx)
+ prefix = txrx_prefix(is_tx)
+ if !(line =~ /#{prefix}ed sm command: id addr info; conn=\d+ addr_type=(\d+) addr=(.+)/)
+ return nil
+ end
+
+ bytes = addr_string_to_bytes($2)
+ if bytes.size != 6
+ raise "invalid id addr info line (length mismatch): #{line}"
+ end
+
+ return {
+ :addr_type => s_to_i($1),
+ :addr => bytes,
+ }
+end
+
+def parse_sign_info(line, is_tx)
+ prefix = txrx_prefix(is_tx)
+ if !(line =~ /#{prefix}ed sm command: sign info; conn=\d+ sig_key=(.+)/)
+ return nil
+ end
+
+ bytes = token_string_to_bytes($1)
+ if bytes.size != 16
+ raise "invalid sign info line (length mismatch): #{line}"
+ end
+
+ return {
+ :sig_key => bytes,
+ }
+end
+
+def parse_passkey_info(line)
+ passkey_info = {}
+
+ case line
+ when /passkey action event; action=4 numcmp=(\d+)/
+ passkey_info[:action] = 4
+ passkey_info[:numcmp] = $1.to_i(10)
+ when /^b passkey conn=\d+ action=1 oob=(\S+)/
+ passkey_info[:action] = 1
+ passkey_info[:oob] = token_string_to_bytes($1, ':')
+ when /^b passkey conn=\d+ action=2 key=(\d+)/
+ passkey_info[:action] = 2
+ passkey_info[:key] = $1.to_i(10)
+ when /b passkey conn=\d+ action=3 key=(\d+)/
+ passkey_info[:action] = 3
+ passkey_info[:key] = $1.to_i(10)
+ else
+ return nil
+ end
+
+ return passkey_info
+end
+
+def parse_addrs(line)
+ 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+)/)
+ return nil
+ end
+
+ our_ota_addr_bytes = addr_string_to_bytes($2)
+ our_id_addr_bytes = addr_string_to_bytes($4)
+ peer_ota_addr_bytes = addr_string_to_bytes($6)
+ peer_id_addr_bytes = addr_string_to_bytes($8)
+
+ if $ctxt[:we_are_init]
+ init_id_bytes = our_id_addr_bytes
+ init_ota_bytes = our_ota_addr_bytes
+ resp_id_bytes = peer_id_addr_bytes
+ resp_ota_bytes = peer_ota_addr_bytes
+ init_addr_type = s_to_i($1)
+ resp_addr_type = s_to_i($5)
+ else
+ init_id_bytes = peer_id_addr_bytes
+ init_ota_bytes = peer_ota_addr_bytes
+ resp_id_bytes = our_id_addr_bytes
+ resp_ota_bytes = our_ota_addr_bytes
+ init_addr_type = s_to_i($5)
+ resp_addr_type = s_to_i($1)
+ end
+
+ if init_id_bytes == init_ota_bytes
+ init_ota_bytes = [0] * 6
+ end
+ if resp_id_bytes == resp_ota_bytes
+ resp_ota_bytes = [0] * 6
+ end
+
+ return {
+ :init_type => init_addr_type,
+ :resp_type => resp_addr_type,
+ :init_id_addr => init_id_bytes,
+ :resp_id_addr => resp_id_bytes,
+ :init_rpa => init_ota_bytes,
+ :resp_rpa => resp_ota_bytes,
+ }
+end
+
+def detect_initiator(lines)
+ lines.each do |line|
+ if line =~ /txed sm command: pair req/
+ $ctxt[:we_are_init] = true
+ elsif line =~ /txed sm command: pair rsp/
+ $ctxt[:we_are_init] = false
+ end
+ end
+
+ if $ctxt[:we_are_init] == nil
+ raise "could not detect which peer is the initiator"
+ end
+end
+
+def pair_cmd_to_s(cmd, is_req)
+ suffix = reqrsp_s(is_req)
+ return <<-eos
+ .pair_#{suffix} = {
+ .io_cap = #{to_hex_s(cmd[:io_cap])},
+ .oob_data_flag = #{to_hex_s(cmd[:oob_data_flag])},
+ .authreq = #{to_hex_s(cmd[:authreq])},
+ .max_enc_key_size = #{to_hex_s(cmd[:max_enc_key_size])},
+ .init_key_dist = #{to_hex_s(cmd[:init_key_dist])},
+ .resp_key_dist = #{to_hex_s(cmd[:resp_key_dist])},
+ },
+ eos
+end
+
+def privkey_to_s(privkey)
+ return bytes_to_arr(privkey, "our_priv_key", 8)
+end
+
+def public_key_to_s(public_key, is_req)
+ suffix = reqrsp_s(is_req)
+ return <<-eos
+ .public_key_#{suffix} = {
+#{bytes_to_arr(public_key[:x], "x", 12)}
+#{bytes_to_arr(public_key[:y], "y", 12)}
+ },
+ eos
+end
+
+def confirm_to_s(confirm, is_req, idx)
+ return <<-eos
+ .confirm_#{reqrsp_s(is_req)}[#{idx}] = {
+#{bytes_to_arr(confirm[:value], "value", 12)}
+ },
+ eos
+end
+
+def random_to_s(random, is_req, idx)
+ return <<-eos
+ .random_#{reqrsp_s(is_req)}[#{idx}] = {
+#{bytes_to_arr(random[:value], "value", 12)}
+ },
+ eos
+end
+
+def ltk_to_s(ltk)
+ return bytes_to_arr(ltk, "ltk", 8)
+end
+
+def stk_to_s(stk)
+ return bytes_to_arr(stk, "stk", 8)
+end
+
+def enc_info_to_s(id_info, is_req)
+ return <<-eos
+ .enc_info_#{reqrsp_s(is_req)} = {
+#{bytes_to_arr(id_info[:ltk], "ltk", 12)}
+ },
+ eos
+end
+
+def master_id_to_s(master_id, is_req)
+ return <<-eos
+ .master_id_#{reqrsp_s(is_req)} = {
+ .ediv = 0x#{master_id[:ediv].to_s(16)},
+ .rand_val = 0x#{master_id[:rand].to_s(16)},
+ },
+ eos
+end
+
+def id_info_to_s(id_info, is_req)
+ return <<-eos
+ .id_info_#{reqrsp_s(is_req)} = {
+#{bytes_to_arr(id_info[:irk], "irk", 12)}
+ },
+ eos
+end
+
+def id_addr_info_to_s(id_addr_info, is_req)
+ return <<-eos
+ .id_addr_info_#{reqrsp_s(is_req)} = {
+ .addr_type = #{id_addr_info[:addr_type]},
+#{bytes_to_arr(id_addr_info[:addr], "bd_addr", 12)}
+ },
+ eos
+end
+
+def sign_info_to_s(sign_info, is_req)
+ return <<-eos
+ .sign_info_#{reqrsp_s(is_req)} = {
+#{bytes_to_arr(sign_info[:sig_key], "sig_key", 12)}
+ },
+ eos
+end
+
+def passkey_info_fill(passkey_info)
+ case passkey_info[:action]
+ # None
+ when 0
+ $ctxt[:pair_alg] = 0
+ $ctxt[:authenticated] = false
+
+ # OOB
+ when 1
+ $ctxt[:pair_alg] = 2
+ $ctxt[:authenticated] = true
+
+ # Input
+ when 2
+ $ctxt[:pair_alg] = 1
+ $ctxt[:authenticated] = true
+
+ # Display
+ when 3
+ $ctxt[:pair_alg] = 1
+ $ctxt[:authenticated] = true
+
+ # Numeric comparison
+ when 4
+ $ctxt[:pair_alg] = 3
+ $ctxt[:authenticated] = true
+
+ else
+ raise "invalid MITM action: #{passkey_info[:action]}"
+ end
+end
+
+def passkey_info_s
+ passkey_info = $ctxt[:passkey_info]
+ action_str = $ACTION_STRINGS[passkey_info[:action]]
+
+ result = <<-eos
+ .pair_alg = #{$ctxt[:pair_alg]},
+ .authenticated = #{$ctxt[:authenticated]},
+ .passkey_info = {
+ .passkey = {
+ .action = #{action_str},
+ eos
+
+ if passkey_info[:key] != nil
+ result << <<-eos
+ .passkey = #{passkey_info[:key].to_i},
+ eos
+ end
+ if passkey_info[:oob] != nil
+ result << <<-eos
+#{bytes_to_arr(passkey_info[:oob], "oob", 16)}
+ eos
+ end
+ if passkey_info[:numcmp] != nil
+ result << <<-eos
+ .numcmp_accept = 1,
+ eos
+ end
+
+ result << <<-eos
+ },
+ eos
+
+ if passkey_info[:numcmp] != nil
+ result << <<-eos
+ .exp_numcmp = #{passkey_info[:numcmp].to_i},
+ eos
+ end
+
+ result << <<-eos
+ },
+ eos
+end
+
+def addrs_to_s(addrs)
+ s = ''
+
+ init_type = addrs[:init_type]
+ resp_type = addrs[:resp_type]
+
+ if init_type != 0
+ s += " .init_addr_type = #{$ADDR_TYPE_STRINGS[init_type]},\n"
+ end
+ s += bytes_to_arr(addrs[:init_id_addr], "init_id_addr", 8) + "\n"
+ if init_type >= 2
+ s += bytes_to_arr(addrs[:init_rpa], "init_rpa", 8) + "\n"
+ end
+
+ if resp_type != 0
+ s += " .resp_addr_type = #{$ADDR_TYPE_STRINGS[resp_type]},\n"
+ end
+ s += bytes_to_arr(addrs[:resp_id_addr], "resp_id_addr", 8) + "\n"
+ if resp_type >= 2
+ s += bytes_to_arr(addrs[:resp_rpa], "resp_rpa", 8) + "\n"
+ end
+
+ return s
+end
+
+def dhkey_check_to_s(dhkey_check, is_req)
+ return <<-eos
+ .dhkey_check_#{reqrsp_s(is_req)} = {
+#{bytes_to_arr(dhkey_check[:value], "value", 12)}
+ },
+ eos
+end
+
+def extract_one(lines, ignore_prev = false)
+ if ignore_prev
+ start = 0
+ else
+ start = $prev_idx
+ end
+
+ (start...lines.size).each do |idx|
+ line = lines[idx]
+ result = yield(line)
+ if result != nil
+ if !ignore_prev
+ $prev_idx = idx
+ end
+ return result
+ end
+ end
+
+ return nil
+end
+
+def extract_pair_req(lines)
+ return extract_one(lines) {|line| parse_pair_cmd(line, true)}
+end
+
+def extract_pair_rsp(lines)
+ return extract_one(lines) {|line| parse_pair_cmd(line, false)}
+end
+
+def extract_privkey(lines)
+ return extract_one(lines) {|line| parse_privkey(line)}
+end
+
+def extract_public_key_req(lines)
+ return extract_one(lines) do |line|
+ parse_public_key(line, $ctxt[:we_are_init])
+ end
+end
+
+def extract_public_key_rsp(lines)
+ return extract_one(lines) do |line|
+ parse_public_key(line, !$ctxt[:we_are_init])
+ end
+end
+
+def extract_confirm_req(lines)
+ return extract_one(lines) do |line|
+ parse_confirm(line, $ctxt[:we_are_init])
+ end
+end
+
+def extract_confirm_rsp(lines)
+ return extract_one(lines) do |line|
+ parse_confirm(line, !$ctxt[:we_are_init])
+ end
+end
+
+def extract_random_req(lines)
+ return extract_one(lines) do |line|
+ parse_random(line, $ctxt[:we_are_init])
+ end
+end
+
+def extract_random_rsp(lines)
+ return extract_one(lines) do |line|
+ parse_random(line, !$ctxt[:we_are_init])
+ end
+end
+
+def extract_confirm_random(lines)
+ confirm_reqs = []
+ confirm_rsps = []
+ random_reqs = []
+ random_rsps = []
+
+ idx = 0
+ loop do
+ confirm_req = extract_confirm_req(lines)
+ if confirm_req != nil
+ confirm_reqs << confirm_req
+ end
+
+ confirm_rsp = extract_confirm_rsp(lines)
+ break if confirm_rsp == nil
+ if idx >= 20
+ raise "too many confirm rsps (>20)"
+ end
+ confirm_rsps << confirm_rsp
+
+ random_req = extract_random_req(lines)
+ break if random_req == nil
+ random_reqs << random_req
+
+ random_rsp = extract_random_rsp(lines)
+ break if random_rsp == nil
+ random_rsps << random_rsp
+
+ idx += 1
+ end
+
+ return confirm_reqs, confirm_rsps, random_reqs, random_rsps
+end
+
+def extract_stk(lines)
+ return extract_one(lines, true) do |line|
+ parse_stk(line)
+ end
+end
+
+def extract_dhkey_check_req(lines)
+ return extract_one(lines) do |line|
+ parse_dhkey_check(line, $ctxt[:we_are_init])
+ end
+end
+
+def extract_dhkey_check_rsp(lines)
+ return extract_one(lines) do |line|
+ parse_dhkey_check(line, !$ctxt[:we_are_init])
+ end
+end
+
+def extract_enc_info_req(lines)
+ return extract_one(lines) do |line|
+ parse_enc_info(line, !$ctxt[:we_are_init])
+ end
+end
+
+def extract_enc_info_rsp(lines)
+ return extract_one(lines) do |line|
+ parse_enc_info(line, $ctxt[:we_are_init])
+ end
+end
+
+def extract_master_id_req(lines)
+ return extract_one(lines) do |line|
+ parse_master_id(line, !$ctxt[:we_are_init])
+ end
+end
+
+def extract_master_id_rsp(lines)
+ return extract_one(lines) do |line|
+ parse_master_id(line, $ctxt[:we_are_init])
+ end
+end
+
+def extract_id_info_req(lines)
+ return extract_one(lines) do |line|
+ parse_id_info(line, !$ctxt[:we_are_init])
+ end
+end
+
+def extract_id_info_rsp(lines)
+ return extract_one(lines) do |line|
+ parse_id_info(line, $ctxt[:we_are_init])
+ end
+end
+
+def extract_id_addr_info_req(lines)
+ return extract_one(lines) do |line|
+ parse_id_addr_info(line, !$ctxt[:we_are_init])
+ end
+end
+
+def extract_id_addr_info_rsp(lines)
+ return extract_one(lines) do |line|
+ parse_id_addr_info(line, $ctxt[:we_are_init])
+ end
+end
+
+def extract_sign_info_req(lines)
+ return extract_one(lines) do |line|
+ parse_sign_info(line, !$ctxt[:we_are_init])
+ end
+end
+
+def extract_sign_info_rsp(lines)
+ return extract_one(lines) do |line|
+ parse_sign_info(line, $ctxt[:we_are_init])
+ end
+end
+
+def extract_ltk(lines)
+ return extract_one(lines) do |line|
+ parse_ltk(line)
+ end
+end
+
+def extract_passkey_info(lines)
+ passkey_info = extract_one(lines, true) do |line|
+ parse_passkey_info(line)
+ end
+
+ if passkey_info == nil
+ passkey_info = { :action => 0 }
+ end
+
+ return passkey_info
+end
+
+def extract_addrs(lines)
+ return extract_one(lines) do |line|
+ parse_addrs(line)
+ end
+end
+
+
+lines = STDIN.readlines
+
+detect_initiator(lines)
+$ctxt[:pair_req] = extract_pair_req(lines)
+$ctxt[:pair_rsp] = extract_pair_rsp(lines)
+$ctxt[:privkey] = extract_privkey(lines)
+$ctxt[:public_key_req] = extract_public_key_req(lines)
+$ctxt[:public_key_rsp] = extract_public_key_rsp(lines)
+$ctxt[:confirm_reqs], $ctxt[:confirm_rsps], $ctxt[:random_reqs], $ctxt[:random_rsps] = extract_confirm_random(lines)
+$ctxt[:passkey_info] = extract_passkey_info(lines)
+$ctxt[:dhkey_check_req] = extract_dhkey_check_req(lines)
+$ctxt[:dhkey_check_rsp] = extract_dhkey_check_rsp(lines)
+$ctxt[:enc_info_req] = extract_enc_info_req(lines)
+$ctxt[:master_id_req] = extract_master_id_req(lines)
+$ctxt[:id_info_req] = extract_id_info_req(lines)
+$ctxt[:id_addr_info_req] = extract_id_addr_info_req(lines)
+$ctxt[:sign_info_req] = extract_sign_info_req(lines)
+$ctxt[:enc_info_rsp] = extract_enc_info_rsp(lines)
+$ctxt[:master_id_rsp] = extract_master_id_rsp(lines)
+$ctxt[:id_info_rsp] = extract_id_info_rsp(lines)
+$ctxt[:id_addr_info_rsp] = extract_id_addr_info_rsp(lines)
+$ctxt[:sign_info_rsp] = extract_sign_info_rsp(lines)
+$ctxt[:addrs] = extract_addrs(lines)
+$ctxt[:ltk] = extract_ltk(lines)
+$ctxt[:stk] = extract_stk(lines)
+
+expected_confirm_rsps = nil
+expected_random_reqs = nil
+expected_random_rsps = nil
+if $ctxt[:confirm_reqs].size == 0
+ expected_confirm_rsps = 1
+ expected_random_reqs = 1
+ expected_random_rsps = 1
+else
+ expected_confirm_rsps = $ctxt[:confirm_reqs].size
+ expected_random_reqs = $ctxt[:random_reqs].size
+ expected_random_rsps = $ctxt[:random_rsps].size
+end
+
+if $ctxt[:confirm_rsps].size != expected_confirm_rsps
+ raise "wrong number of confirm responses " +
+ "(exp=#{expected_confirm_rsps}; got=#{$ctxt[:confirm_rsps].size}"
+end
+
+if $ctxt[:random_reqs].size != expected_random_reqs
+ raise "wrong number of random requests " +
+ "(exp=#{expected_random_reqs}; got=#{$ctxt[:random_reqs].size}"
+end
+
+if $ctxt[:random_rsps].size != expected_random_rsps
+ raise "wrong number of random responses " +
+ "(exp=#{expected_random_rsps}; got=#{$ctxt[:random_rsps].size}"
+end
+
+passkey_info_fill($ctxt[:passkey_info])
+
+$ctxt[:sc] = $ctxt[:public_key_req] != nil
+$ctxt[:bonding] = $ctxt[:pair_req][:authreq] & 1 == 1 &&
+ $ctxt[:pair_rsp][:authreq] & 1 == 1
+
+puts test_case_comment()
+puts <<-eos
+TEST_CASE(#{test_case_name()})
+{
+ struct ble_sm_test_params params;
+
+ params = (struct ble_sm_test_params) {
+eos
+
+puts addrs_to_s($ctxt[:addrs])
+
+puts pair_cmd_to_s($ctxt[:pair_req], true)
+puts pair_cmd_to_s($ctxt[:pair_rsp], false)
+
+if $ctxt[:sc]
+ puts privkey_to_s($ctxt[:privkey])
+ puts public_key_to_s($ctxt[:public_key_req], true)
+ puts public_key_to_s($ctxt[:public_key_req], false)
+end
+
+$ctxt[:confirm_rsps].size.times do |i|
+ confirm_req = $ctxt[:confirm_reqs][i]
+ confirm_rsp = $ctxt[:confirm_rsps][i]
+ random_req = $ctxt[:random_reqs][i]
+ random_rsp = $ctxt[:random_rsps][i]
+
+ if confirm_req != nil
+ puts confirm_to_s(confirm_req, true, i)
+ end
+
+ puts confirm_to_s(confirm_rsp, false, i)
+ puts random_to_s(random_req, true, i)
+ puts random_to_s(random_rsp, false, i)
+end
+
+if $ctxt[:sc]
+ puts dhkey_check_to_s($ctxt[:dhkey_check_req], true)
+ puts dhkey_check_to_s($ctxt[:dhkey_check_rsp], false)
+end
+
+if $ctxt[:enc_info_req] != nil
+ puts enc_info_to_s($ctxt[:enc_info_req], true)
+end
+if $ctxt[:master_id_req] != nil
+ puts master_id_to_s($ctxt[:master_id_req], true)
+end
+if $ctxt[:id_info_req] != nil
+ puts id_info_to_s($ctxt[:id_info_req], true)
+end
+if $ctxt[:id_addr_info_req] != nil
+ puts id_addr_info_to_s($ctxt[:id_addr_info_req], true)
+end
+if $ctxt[:sign_info_req] != nil
+ puts sign_info_to_s($ctxt[:sign_info_req], true)
+end
+if $ctxt[:enc_info_rsp] != nil
+ puts enc_info_to_s($ctxt[:enc_info_rsp], false)
+end
+if $ctxt[:master_id_rsp] != nil
+ puts master_id_to_s($ctxt[:master_id_rsp], false)
+end
+if $ctxt[:id_info_rsp] != nil
+ puts id_info_to_s($ctxt[:id_info_rsp], false)
+end
+if $ctxt[:id_addr_info_rsp] != nil
+ puts id_addr_info_to_s($ctxt[:id_addr_info_rsp], false)
+end
+if $ctxt[:sign_info_rsp] != nil
+ puts sign_info_to_s($ctxt[:sign_info_rsp], false)
+end
+if $ctxt[:sc]
+ puts ltk_to_s($ctxt[:ltk])
+else
+ puts stk_to_s($ctxt[:stk])
+end
+puts passkey_info_s()
+
+puts ' };'
+
+if $ctxt[:sc]
+ if $ctxt[:we_are_init]
+ puts ' ble_sm_test_util_us_sc_good(&params);'
+ else
+ puts ' ble_sm_test_util_peer_sc_good(&params);'
+ end
+else
+ if $ctxt[:we_are_init]
+ puts ' ble_sm_test_util_us_lgcy_good(&params);'
+ else
+ puts ' ble_sm_test_util_peer_lgcy_good(&params);'
+ end
+end
+puts '}'