Commit | Line | Data |
---|---|---|
1cac41cb MB |
1 | #!/bin/bash |
2 | ||
3 | # fips_crypto_hmac.sh | |
4 | # | |
5 | # Author : Rohit Kothari (r.kothari@samsung.com) | |
6 | # Created on : 14 Feb 2014 | |
7 | # Copyright (c) Samsung Electronics 2014 | |
8 | ||
9 | # Given a vmlinux file and a System.map, this scripts finds bytes belonging to | |
10 | # Kernel Crypto within vmlinux file.(Under section .text, .init.text, .exit.text and .rodata) | |
11 | # After collecting all the bytes, it calculates a hmac(sha256) on those bytes. | |
12 | # Generated hmac is put back into a crypto rodata variable within vmlinux file itself. | |
13 | # This makes the build time hmac available at runtime, for integrity check. | |
14 | # | |
15 | # To find crypto bytes, this scripts heavily relies on output of arm-eabi-readelf. | |
16 | # If the output of arm-eabi-readelf changes in future, this script might need changes. | |
17 | # | |
18 | # Pre-conditions : $READELF, $HOSTCC variables are set. | |
19 | # | |
20 | # | |
21 | ||
22 | if test $# -ne 2; then | |
23 | echo "Usage: $0 vmlinux System.map" | |
24 | exit 1 | |
25 | fi | |
26 | ||
27 | vmlinux_var=$1 | |
28 | system_map_var=$2 | |
29 | ||
30 | if [[ -z "$vmlinux_var" || -z "$system_map_var" || -z "$READELF" || -z "$HOSTCC" ]]; then | |
31 | echo "$0 : variables not set" | |
32 | exit 1 | |
33 | fi | |
34 | ||
35 | if [[ ! -f $vmlinux_var || ! -f $system_map_var ]]; then | |
36 | echo "$0 : files does not exist" | |
37 | exit 1 | |
38 | fi | |
39 | ||
40 | rm -f vmlinux.elf | |
41 | $READELF -S $vmlinux_var > vmlinux.elf | |
42 | ||
43 | retval=$? | |
44 | if [ $retval -ne 0 ]; then | |
45 | echo "$0 : $READELF returned error" | |
46 | exit 1 | |
47 | fi | |
48 | ||
49 | declare -A array | |
50 | ||
51 | # FOR GENERIC CRYPTO FILES #awk fields to cut | |
52 | array[0]=".text first_crypto_text last_crypto_text \$5 \$6" | |
53 | array[1]=".rodata first_crypto_rodata last_crypto_rodata \$5 \$6" | |
54 | array[2]=".init.text first_crypto_init last_crypto_init \$4 \$5" | |
55 | ||
56 | # # FOR ASM CRYPTO FILES | |
57 | array[3]=".text first_crypto_asm_text last_crypto_asm_text \$5 \$6" | |
58 | array[4]=".rodata first_crypto_asm_rodata last_crypto_asm_rodata \$5 \$6" | |
59 | array[5]=".init.text first_crypto_asm_init last_crypto_asm_init \$4 \$5" | |
60 | ||
61 | ||
62 | rm -f offsets_sizes.txt | |
63 | ||
64 | #Addresses retrieved must be a valid hex | |
65 | reg='^[0-9A-Fa-f]+$' | |
66 | ||
67 | #Total bytes of all crypto sections scanned. Used later for error checking | |
68 | total_bytes=0; | |
69 | ||
70 | # For each type of Section : | |
71 | # first_addr = Address of first_crypto_text, first_crypto_rodata, etc. | |
72 | # last_addr = Address of last_crypto_text, last_crypto_rodata etc. | |
73 | # start_addr = Starting Address of a section within vmlinux | |
74 | # offset = Offset in vmlinux file where the section begins | |
75 | # file_offset = Offset in vmlinux file where the crypto bytes begins. | |
76 | # size = size of crypto bytes. | |
77 | ||
78 | # Output is offsets_sizes.txt, of the format | |
79 | # Section Name crypto_bytes_offset crypto_bytes_size | |
80 | # (in decimal) (in decimal) | |
81 | # .text 2531072 114576 | |
82 | # .rodata 9289648 55388 | |
83 | # : : : | |
84 | ||
85 | for i in "${array[@]}"; do | |
86 | ||
87 | var1=var2=var3=var4=var5="" | |
88 | first_addr=last_addr=start_addr=offset=file_offset=size="" | |
89 | k=1 | |
90 | #This loop creates var1, var2 etc and set them to individual strings of a row in array | |
91 | for j in $i; do | |
92 | export var$k=$j | |
93 | let k+=1 | |
94 | done | |
95 | ||
96 | first_addr=`cat $system_map_var|grep -w $var2|awk '{print $1}'` | |
97 | if [[ ! $first_addr =~ $reg ]]; then echo "$0 : first_addr invalid"; exit 1; fi | |
98 | ||
99 | last_addr=`cat $system_map_var|grep -w $var3|awk '{print $1}'` | |
100 | if [[ ! $last_addr =~ $reg ]]; then echo "$0 : last_addr invalid"; exit 1; fi | |
101 | ||
102 | start_addr=`cat vmlinux.elf |grep -w "$var1 "|grep PROGBITS|awk '{print '$var4'}'` | |
103 | if [[ ! $start_addr =~ $reg ]]; then echo "$0 : start_addr invalid"; exit 1; fi | |
104 | ||
105 | offset=`cat vmlinux.elf |grep -w "$var1 "|grep PROGBITS|awk '{print '$var5'}'` | |
106 | if [[ ! $offset =~ $reg ]]; then echo "$0 : offset invalid"; exit 1; fi | |
107 | ||
108 | if [[ $((16#$first_addr)) -lt $((16#$start_addr)) ]]; then echo "$0 : first_addr < start_addr"; exit 1; fi | |
109 | ||
110 | if [[ $((16#$last_addr)) -le $((16#$first_addr)) ]]; then echo "$0 : last_addr <= first_addr"; exit 1; fi | |
111 | ||
112 | file_offset=`expr $((16#$offset)) + $((16#$first_addr)) - $((16#$start_addr))` | |
113 | if [[ $file_offset -le 0 ]]; then echo "$0 : file_offset invalid"; exit 1; fi | |
114 | ||
115 | size=`expr $((16#$last_addr)) - $((16#$first_addr))` | |
116 | if [[ $size -le 0 ]]; then echo "$0 : crypto section size invalid"; exit 1; fi | |
117 | ||
118 | echo "$var1 " $file_offset " " $size >> offsets_sizes.txt | |
119 | ||
120 | let "total_bytes += `expr $((16#$last_addr)) - $((16#$first_addr))`" | |
121 | done | |
122 | ||
123 | if [[ ! -f offsets_sizes.txt ]]; then | |
124 | echo "$0 : offset_sizes.txt does not exist" | |
125 | exit 1 | |
126 | fi | |
127 | ||
128 | rm -f fips_crypto_utils | |
129 | $HOSTCC -o fips_crypto_utils $srctree/scripts/fips_crypto_utils.c | |
130 | retval=$? | |
131 | if [ $retval -ne 0 ]; then | |
132 | echo "$0 : $HOSTCC returned error" | |
133 | exit 1 | |
134 | fi | |
135 | ||
136 | rm -f builtime_bytes.txt #used for debugging | |
137 | rm -f builtime_bytes.bin #used for calculating hmac | |
138 | ||
139 | date_var=`date` | |
140 | echo "Created on : " $date_var > builtime_bytes.txt | |
141 | ||
142 | #Using offsets_sizes.txt, dump crypto bytes from vmlinux file into builtime_bytes.bin | |
143 | #Also gather printf's into builtime_bytes.txt, for debugging if required | |
144 | while read args; do | |
145 | ./fips_crypto_utils -g $vmlinux_var $args builtime_bytes.bin >> builtime_bytes.txt | |
146 | retval=$? | |
147 | if [ $retval -ne 0 ]; then | |
148 | echo "$0 : fips_crypto_utils : unable to gather crypto bytes from vmlinux" | |
149 | exit 1 | |
150 | fi | |
151 | echo "" >> builtime_bytes.txt | |
152 | done < offsets_sizes.txt # <================== offsets_sizes.txt | |
153 | ||
154 | if [[ ! -f builtime_bytes.bin ]]; then | |
155 | echo "$0 : builtime_bytes.bin does not exist" | |
156 | exit 1 | |
157 | fi | |
158 | ||
159 | file_size=`cat builtime_bytes.bin| wc -c` | |
160 | ||
161 | # Make sure that file size of crypto_hmac.bin is as expected | |
162 | if [ $total_bytes -ne $file_size ]; then | |
163 | echo "$0: Bytes mismatch" | |
164 | exit 1 | |
165 | fi | |
166 | ||
167 | key="The quick brown fox jumps over the lazy dog" | |
168 | ||
169 | # Now, generate the hmac. | |
170 | openssl dgst -sha256 -hmac "$key" -binary -out crypto_hmac.bin builtime_bytes.bin | |
171 | retval=$? | |
172 | if [ $retval -ne 0 ]; then | |
173 | echo "$0 : openssl dgst command returned error" | |
174 | exit 1 | |
175 | fi | |
176 | ||
177 | # Just, for debugging, print the same hmac on console | |
178 | openssl dgst -sha256 -hmac "$key" builtime_bytes.bin | |
179 | retval=$? | |
180 | if [ $retval -ne 0 ]; then | |
181 | echo "$0 : openssl dgst command returned error" | |
182 | exit 1 | |
183 | fi | |
184 | ||
185 | if [[ ! -f crypto_hmac.bin ]]; then | |
186 | echo "$0 : crypto_hmac.bin does not exist" | |
187 | exit 1 | |
188 | fi | |
189 | ||
190 | file_size=`cat crypto_hmac.bin| wc -c` | |
191 | ||
192 | # hmac(sha256) produces 32 bytes of hmac | |
193 | if [ $file_size -ne 32 ]; then | |
194 | echo "$0: Unexpected size of Hash file : " $file_size | |
195 | exit 1 | |
196 | fi | |
197 | ||
198 | ||
199 | # Now that we have the hmac, update this hmac into an rodata "builtime_crypto_hmac" varialble | |
200 | # in vmlinux file. | |
201 | # This variable has a place holder 32 bytes that will be over-written with generated hmac. | |
202 | # This way, this build time hmac, will be available as a read-only variable at run-time. | |
203 | ||
204 | first_addr=`cat $system_map_var|grep -w "builtime_crypto_hmac"|awk '{print $1}' ` | |
205 | if [[ ! $first_addr =~ $reg ]]; then echo "$0 : first_addr of hmac variable invalid"; exit 1; fi | |
206 | ||
207 | start_addr=`cat vmlinux.elf |grep -w ".rodata"|grep PROGBITS|awk '{print $5}' ` | |
208 | if [[ ! $start_addr =~ $reg ]]; then echo "$0 : start_addr of .rodata invalid"; exit 1; fi | |
209 | ||
210 | offset=`cat vmlinux.elf |grep -w ".rodata"|grep PROGBITS| awk '{print $6}' ` | |
211 | if [[ ! $offset =~ $reg ]]; then echo "$0 : offset of .rodata invalid"; exit 1; fi | |
212 | ||
213 | if [[ $((16#$first_addr)) -le $((16#$start_addr)) ]]; then echo "$0 : hmac var first_addr <= start_addr"; exit 1; fi | |
214 | ||
215 | hmac_offset=`expr $((16#$offset)) + $((16#$first_addr)) - $((16#$start_addr))` | |
216 | if [[ $hmac_offset -le 0 ]]; then echo "$0 : hmac_offset invalid"; exit 1; fi | |
217 | ||
218 | # This does the actual update of hmac into vmlinux file, at given offset | |
219 | ./fips_crypto_utils -u $vmlinux_var crypto_hmac.bin $hmac_offset | |
220 | retval=$? | |
221 | if [ $retval -ne 0 ]; then | |
222 | echo "$0 : fips_crypto_utils : unable to update hmac in vmlinux" | |
223 | exit 1 | |
224 | fi | |
225 | ||
226 | rm -f crypto_hmac.bin | |
227 | rm -f builtime_bytes.txt | |
228 | rm -f builtime_bytes.bin | |
229 | rm -f fips_crypto_utils | |
230 | rm -f vmlinux.elf | |
231 | rm -f offsets_sizes.txt | |
232 | ||
233 | # And we are done... |