ffa_ch20_litmus.kv 1 #!/bin/sh
ffa_ch20_litmus.kv 2
ffa_ch20_litmus.kv 3 ############################################################################
ffa_ch20_litmus.kv 4 # 'Litmus' Utility. Verifies traditional GPG RSA signatures using Peh. #
ffa_ch20_litmus.kv 5 # #
ffa_ch20_litmus.kv 6 # Usage: ./litmus.sh publickey.peh signature.sig datafile #
ffa_ch20_litmus.kv 7 # #
ffa_ch20_litmus.kv 8 # Currently, supports only RSA 'detached' signatures that use SHA512 hash. #
ffa_ch20_litmus.kv 9 # See instructions re: converting traditional GPG public keys for use with #
ffa_ch20_litmus.kv 10 # this program. #
ffa_ch20_litmus.kv 11 # #
ffa_ch20_litmus.kv 12 # Peh, xxd, hexdump, shasum, and a number of common utils (see EXTERNALS) #
ffa_ch20_litmus.kv 13 # must be present on your machine. #
ffa_ch20_litmus.kv 14 # #
ffa_ch20_litmus.kv 15 # (C) 2020 Stanislav Datskovskiy ( www.loper-os.org ) #
ffa_ch20_litmus.kv 16 # http://wot.deedbot.org/17215D118B7239507FAFED98B98228A001ABFFC7.html #
ffa_ch20_litmus.kv 17 # #
ffa_ch20_litmus.kv 18 # You do not have, nor can you ever acquire the right to use, copy or #
ffa_ch20_litmus.kv 19 # distribute this software ; Should you use this software for any purpose, #
ffa_ch20_litmus.kv 20 # or copy and distribute it to anyone or in any manner, you are breaking #
ffa_ch20_litmus.kv 21 # the laws of whatever soi-disant jurisdiction, and you promise to #
ffa_ch20_litmus.kv 22 # continue doing so for the indefinite future. In any case, please #
ffa_ch20_litmus.kv 23 # always : read and understand any software ; verify any PGP signatures #
ffa_ch20_litmus.kv 24 # that you use - for any purpose. #
ffa_ch20_litmus.kv 25 ############################################################################
ffa_ch20_litmus.kv 26
ffa_ch20_litmus.kv 27 # External programs that are required (if not found, will eggog) :
ffa_ch20_litmus.kv 28 EXTERNALS="peh xxd hexdump base64 shasum cut tr sed wc grep printf"
ffa_ch20_litmus.kv 29
ffa_ch20_litmus.kv 30
ffa_ch20_litmus.kv 31 # Return Codes:
ffa_ch20_litmus.kv 32
ffa_ch20_litmus.kv 33 # Signature is VALID for given Sig, Data File, and Public Key:
ffa_ch20_litmus.kv 34 RET_VALID_SIG=0
ffa_ch20_litmus.kv 35
ffa_ch20_litmus.kv 36 # Signature is INVALID:
ffa_ch20_litmus.kv 37 RET_BAD_SIG=1
ffa_ch20_litmus.kv 38
ffa_ch20_litmus.kv 39 # All Other Cases:
ffa_ch20_litmus.kv 40 RET_EGGOG=-1
ffa_ch20_litmus.kv 41
ffa_ch20_litmus.kv 42
ffa_ch20_litmus.kv 43 # Terminations:
ffa_ch20_litmus.kv 44
ffa_ch20_litmus.kv 45 # Success (Valid RSA signature) :
ffa_ch20_litmus.kv 46 done_sig_valid() {
ffa_ch20_litmus.kv 47 echo "VALID $pubkey_algo signature from $pubkey_owner"
ffa_ch20_litmus.kv 48 exit $RET_VALID_SIG
ffa_ch20_litmus.kv 49 }
ffa_ch20_litmus.kv 50
ffa_ch20_litmus.kv 51 # Failure (INVALID RSA signature) :
ffa_ch20_litmus.kv 52 done_sig_bad() {
ffa_ch20_litmus.kv 53 echo "Signature is INVALID for this public key and input file!"
ffa_ch20_litmus.kv 54 exit $RET_BAD_SIG
ffa_ch20_litmus.kv 55 }
ffa_ch20_litmus.kv 56
ffa_ch20_litmus.kv 57 # Failure in decoding 'GPG ASCII armour' :
ffa_ch20_litmus.kv 58 eggog_sig_armour() {
ffa_ch20_litmus.kv 59 echo "$SIGFILE could not decode as a GPG ASCII-Armoured Signature!" >&2
ffa_ch20_litmus.kv 60 exit $RET_EGGOG
ffa_ch20_litmus.kv 61 }
ffa_ch20_litmus.kv 62
ffa_ch20_litmus.kv 63 # Failure from corrupt signature :
ffa_ch20_litmus.kv 64 eggog_sig_corrupt() {
ffa_ch20_litmus.kv 65 echo "$SIGFILE is corrupt!" >&2
ffa_ch20_litmus.kv 66 exit $RET_EGGOG
ffa_ch20_litmus.kv 67 }
ffa_ch20_litmus.kv 68
ffa_ch20_litmus.kv 69
ffa_ch20_litmus.kv 70 # Failure from bad Peh :
ffa_ch20_litmus.kv 71 eggog_peh() {
ffa_ch20_litmus.kv 72 echo "EGGOG in executing Peh tape! Please check Public Key." >&2
ffa_ch20_litmus.kv 73 exit $RET_EGGOG
ffa_ch20_litmus.kv 74 }
ffa_ch20_litmus.kv 75
ffa_ch20_litmus.kv 76
ffa_ch20_litmus.kv 77 # Number of Arguments required by this program:
ffa_ch20_litmus.kv 78 REQD_ARGS=3
ffa_ch20_litmus.kv 79
ffa_ch20_litmus.kv 80 # If invalid arg count, print usage and abort:
ffa_ch20_litmus.kv 81 if [ "$#" -ne $REQD_ARGS ]; then
ffa_ch20_litmus.kv 82 echo "Usage: $0 publickey.peh signature.sig datafile"
ffa_ch20_litmus.kv 83 exit $RET_EGGOG
ffa_ch20_litmus.kv 84 fi
ffa_ch20_litmus.kv 85
ffa_ch20_litmus.kv 86
ffa_ch20_litmus.kv 87 # We only support SHA512. Parameters for it:
ffa_ch20_litmus.kv 88 HASHER="shasum -a 512 -b"
ffa_ch20_litmus.kv 89
ffa_ch20_litmus.kv 90 # For 'PKCS' encoding, the ASN magic turd corresponding to SHA512:
ffa_ch20_litmus.kv 91 ASN="3051300D060960864801650304020305000440"
ffa_ch20_litmus.kv 92 ASN_LEN=$((${#ASN} / 2))
ffa_ch20_litmus.kv 93 MD_LEN=64 # 512 / 8 == 64 bytes
ffa_ch20_litmus.kv 94
ffa_ch20_litmus.kv 95
ffa_ch20_litmus.kv 96 # Minimal Peh Width (used for non-arithmetical ops, e.g. 'Owner')
ffa_ch20_litmus.kv 97 MIN_PEH_WIDTH=256
ffa_ch20_litmus.kv 98
ffa_ch20_litmus.kv 99
ffa_ch20_litmus.kv 100 # The given public key file (a Peh tape, see docs)
ffa_ch20_litmus.kv 101 PUBFILE=$1
ffa_ch20_litmus.kv 102
ffa_ch20_litmus.kv 103 # The given Detached GPG Signature file to be verified
ffa_ch20_litmus.kv 104 SIGFILE=$2
ffa_ch20_litmus.kv 105
ffa_ch20_litmus.kv 106 # The given Data file to be verified against the Signature
ffa_ch20_litmus.kv 107 DATAFILE=$3
ffa_ch20_litmus.kv 108
ffa_ch20_litmus.kv 109 # Verify that each of the given input files exists:
ffa_ch20_litmus.kv 110 FILES=($PUBFILE $SIGFILE $DATAFILE)
ffa_ch20_litmus.kv 111 for f in ${FILES[@]}; do
ffa_ch20_litmus.kv 112 if ! [ -f $f ]; then
ffa_ch20_litmus.kv 113 echo "$f does not exist!" >&2
ffa_ch20_litmus.kv 114 exit $RET_EGGOG
ffa_ch20_litmus.kv 115 fi
ffa_ch20_litmus.kv 116 done
ffa_ch20_litmus.kv 117
ffa_ch20_litmus.kv 118 # Calculate length of the pubkey file:
ffa_ch20_litmus.kv 119 PUBFILE_LEN=$(wc -c $PUBFILE | cut -d ' ' -f1)
ffa_ch20_litmus.kv 120
ffa_ch20_litmus.kv 121
ffa_ch20_litmus.kv 122 # Peh's Return Codes
ffa_ch20_litmus.kv 123 PEH_YES=1
ffa_ch20_litmus.kv 124 PEH_NO=0
ffa_ch20_litmus.kv 125 PEH_MU=255
ffa_ch20_litmus.kv 126 PEH_EGGOG=254
ffa_ch20_litmus.kv 127
ffa_ch20_litmus.kv 128 # Execute given Peh tape, with given FFA Width and Height,
ffa_ch20_litmus.kv 129 # on top of the pubkey tape; returns output in $peh_res and $peh_code.
ffa_ch20_litmus.kv 130 run_peh_tape() {
ffa_ch20_litmus.kv 131 # The tape itself
ffa_ch20_litmus.kv 132 tape=$1
ffa_ch20_litmus.kv 133
ffa_ch20_litmus.kv 134 # FFA Width for the tape
ffa_ch20_litmus.kv 135 peh_width=$2
ffa_ch20_litmus.kv 136
ffa_ch20_litmus.kv 137 # FFA Stack Height for the tape
ffa_ch20_litmus.kv 138 peh_height=$3
ffa_ch20_litmus.kv 139
ffa_ch20_litmus.kv 140 # Compute the length of the given tape
ffa_ch20_litmus.kv 141 tape_len=${#tape}
ffa_ch20_litmus.kv 142
ffa_ch20_litmus.kv 143 # Add the length of the Public Key tape to the above
ffa_ch20_litmus.kv 144 tape_len=$(($tape_len + $PUBFILE_LEN))
ffa_ch20_litmus.kv 145
ffa_ch20_litmus.kv 146 # Max Peh Life for all such tapes
ffa_ch20_litmus.kv 147 peh_life=$(($tape_len * 2))
ffa_ch20_litmus.kv 148
ffa_ch20_litmus.kv 149 # Execute the tape:
ffa_ch20_litmus.kv 150 peh_res=$((cat $PUBFILE; echo $tape) | \
ffa_ch20_litmus.kv 151 peh $peh_width $peh_height $tape_len $peh_life);
ffa_ch20_litmus.kv 152 peh_code=$?
ffa_ch20_litmus.kv 153
ffa_ch20_litmus.kv 154 # # If Peh returned PEH_EGGOG:
ffa_ch20_litmus.kv 155 if [ $peh_code -eq $PEH_EGGOG ]
ffa_ch20_litmus.kv 156 then
ffa_ch20_litmus.kv 157 # Abort: likely, coarse error of pilotage in the public key tape.
ffa_ch20_litmus.kv 158 eggog_peh
ffa_ch20_litmus.kv 159 fi
ffa_ch20_litmus.kv 160 }
ffa_ch20_litmus.kv 161
ffa_ch20_litmus.kv 162 # Ask the public key about the Owner:
ffa_ch20_litmus.kv 163 run_peh_tape "@Algo!QY" $MIN_PEH_WIDTH 1
ffa_ch20_litmus.kv 164 pubkey_algo=$peh_res
ffa_ch20_litmus.kv 165
ffa_ch20_litmus.kv 166 # Ask the public key about Algo Type:
ffa_ch20_litmus.kv 167 run_peh_tape "@Owner!QY" $MIN_PEH_WIDTH 1
ffa_ch20_litmus.kv 168 pubkey_owner=$peh_res
ffa_ch20_litmus.kv 169
ffa_ch20_litmus.kv 170 # The only supported algo is GPG RSA:
ffa_ch20_litmus.kv 171 if [ "$pubkey_algo" != "GPG RSA" ]
ffa_ch20_litmus.kv 172 then
ffa_ch20_litmus.kv 173 echo "This public key specifies algo '$pubkey_algo';" >&2
ffa_ch20_litmus.kv 174 echo "The only algo supported is 'GPG RSA' !" >&2
ffa_ch20_litmus.kv 175 exit $RET_EGGOG
ffa_ch20_litmus.kv 176 fi
ffa_ch20_litmus.kv 177
ffa_ch20_litmus.kv 178 # Verify that all of the necessary external programs in fact exist:
ffa_ch20_litmus.kv 179 for i in $EXTERNALS
ffa_ch20_litmus.kv 180 do
ffa_ch20_litmus.kv 181 command -v $i >/dev/null && continue || \
ffa_ch20_litmus.kv 182 { echo "$i is required but was not found! Please install it."; \
ffa_ch20_litmus.kv 183 exit $RET_EGGOG; }
ffa_ch20_litmus.kv 184 done
ffa_ch20_litmus.kv 185
ffa_ch20_litmus.kv 186 # 'ASCII-Armoured' PGP signatures have mandatory start and end markers:
ffa_ch20_litmus.kv 187 START_MARKER="\-\-\-\-\-BEGIN PGP SIGNATURE\-\-\-\-\-"
ffa_ch20_litmus.kv 188 END_MARKER="\-\-\-\-\-END PGP SIGNATURE\-\-\-\-\-"
ffa_ch20_litmus.kv 189
ffa_ch20_litmus.kv 190 # Determine start and end line positions for payload:
ffa_ch20_litmus.kv 191 start_ln=$(grep -m 1 -n "$START_MARKER" $SIGFILE | cut -d ':' -f1)
ffa_ch20_litmus.kv 192 end_ln=$(grep -m 1 -n "$END_MARKER" $SIGFILE | cut -d ':' -f1)
ffa_ch20_litmus.kv 193
ffa_ch20_litmus.kv 194 # Both start and end markers must exist :
ffa_ch20_litmus.kv 195 if [ "$start_ln" == "" ] || [ "$end_ln" == "" ]
ffa_ch20_litmus.kv 196 then
ffa_ch20_litmus.kv 197 echo "$SIGFILE does not contain ASCII-armoured PGP Signature!" >&2
ffa_ch20_litmus.kv 198 exit $RET_EGGOG
ffa_ch20_litmus.kv 199 fi
ffa_ch20_litmus.kv 200
ffa_ch20_litmus.kv 201 # Discard the markers:
ffa_ch20_litmus.kv 202 start_ln=$(($start_ln + 1))
ffa_ch20_litmus.kv 203 end_ln=$(($end_ln - 1))
ffa_ch20_litmus.kv 204
ffa_ch20_litmus.kv 205 # If there is no payload, or the markers are misplaced, abort:
ffa_ch20_litmus.kv 206 if [ $start_ln -ge $end_ln ]
ffa_ch20_litmus.kv 207 then
ffa_ch20_litmus.kv 208 eggog_sig_armour
ffa_ch20_litmus.kv 209 fi
ffa_ch20_litmus.kv 210
ffa_ch20_litmus.kv 211 # Extract sig payload:
ffa_ch20_litmus.kv 212 sig_payload=$(sed -n "$start_ln,$end_ln p" < $SIGFILE | \
ffa_ch20_litmus.kv 213 sed -n "/^Version/!p" | sed -n "/^=/!p" | tr -d " \t\n\r")
ffa_ch20_litmus.kv 214
ffa_ch20_litmus.kv 215 # If eggog -- abort:
ffa_ch20_litmus.kv 216 if [ $? -ne 0 ]
ffa_ch20_litmus.kv 217 then
ffa_ch20_litmus.kv 218 eggog_sig_armour
ffa_ch20_litmus.kv 219 fi
ffa_ch20_litmus.kv 220
ffa_ch20_litmus.kv 221 # Obtain the sig bytes:
ffa_ch20_litmus.kv 222 sig_bytes=($(echo $sig_payload | base64 -d | hexdump -ve '1/1 "%.2x "'))
ffa_ch20_litmus.kv 223
ffa_ch20_litmus.kv 224 # If eggog -- abort:
ffa_ch20_litmus.kv 225 if [ $? -ne 0 ]
ffa_ch20_litmus.kv 226 then
ffa_ch20_litmus.kv 227 eggog_sig_armour
ffa_ch20_litmus.kv 228 fi
ffa_ch20_litmus.kv 229
ffa_ch20_litmus.kv 230 # Number of bytes in the sig file
ffa_ch20_litmus.kv 231 sig_len=${#sig_bytes[@]}
ffa_ch20_litmus.kv 232
ffa_ch20_litmus.kv 233
ffa_ch20_litmus.kv 234 # Test that certain fields in the Sig have their mandatory value
ffa_ch20_litmus.kv 235 sig_field_mandatory() {
ffa_ch20_litmus.kv 236 f_name=$1
ffa_ch20_litmus.kv 237 f_value=$2
ffa_ch20_litmus.kv 238 f_mandate=$3
ffa_ch20_litmus.kv 239 if [ "$f_value" != "$f_mandate" ]
ffa_ch20_litmus.kv 240 then
ffa_ch20_litmus.kv 241 reason="$f_name must equal $f_mandate; instead is $f_value."
ffa_ch20_litmus.kv 242 echo "$SIGFILE is UNSUPPORTED : $reason" >&2
ffa_ch20_litmus.kv 243 echo "Only RSA and SHA512 hash are supported !" >&2
ffa_ch20_litmus.kv 244 exit $RET_EGGOG
ffa_ch20_litmus.kv 245 fi
ffa_ch20_litmus.kv 246 }
ffa_ch20_litmus.kv 247
ffa_ch20_litmus.kv 248
ffa_ch20_litmus.kv 249 # Starting Position for get_sig_bytes()
ffa_ch20_litmus.kv 250 sig_pos=0
ffa_ch20_litmus.kv 251
ffa_ch20_litmus.kv 252 # Extract given # of sig bytes from the current sig_pos; advance sig_pos.
ffa_ch20_litmus.kv 253 get_sig_bytes() {
ffa_ch20_litmus.kv 254 # Number of bytes requested
ffa_ch20_litmus.kv 255 count=$1
ffa_ch20_litmus.kv 256
ffa_ch20_litmus.kv 257 # Result: $count bytes from current $sig_pos (contiguous hex string)
ffa_ch20_litmus.kv 258 r=$(echo ${sig_bytes[@]:$sig_pos:$count} | sed "s/ //g" | tr 'a-z' 'A-Z')
ffa_ch20_litmus.kv 259
ffa_ch20_litmus.kv 260 # Advance $sig_pos by $count:
ffa_ch20_litmus.kv 261 sig_pos=$(($sig_pos + $count))
ffa_ch20_litmus.kv 262
ffa_ch20_litmus.kv 263 # If more bytes were requested than were available in sig_bytes:
ffa_ch20_litmus.kv 264 if [ $sig_pos -gt $sig_len ]
ffa_ch20_litmus.kv 265 then
ffa_ch20_litmus.kv 266 # Abort. The signature was mutilated somehow.
ffa_ch20_litmus.kv 267 eggog_sig_corrupt
ffa_ch20_litmus.kv 268 fi
ffa_ch20_litmus.kv 269 }
ffa_ch20_litmus.kv 270
ffa_ch20_litmus.kv 271 # Convert the current sig component to integer
ffa_ch20_litmus.kv 272 hex_to_int() {
ffa_ch20_litmus.kv 273 r=$((16#$r))
ffa_ch20_litmus.kv 274 }
ffa_ch20_litmus.kv 275
ffa_ch20_litmus.kv 276 # Turd to be composed of certain values from the sig, per RFC4880.
ffa_ch20_litmus.kv 277 # Final hash will run on the concatenation of DATAFILE and this turd.
ffa_ch20_litmus.kv 278 turd=""
ffa_ch20_litmus.kv 279
ffa_ch20_litmus.kv 280 ## Parse all of the necessary fields in the GPG Signature:
ffa_ch20_litmus.kv 281
ffa_ch20_litmus.kv 282 # CTB (must equal 0x89)
ffa_ch20_litmus.kv 283 get_sig_bytes 1
ffa_ch20_litmus.kv 284 sig_ctb=$r
ffa_ch20_litmus.kv 285 sig_field_mandatory "Version" $sig_ctb 89
ffa_ch20_litmus.kv 286
ffa_ch20_litmus.kv 287 # Length
ffa_ch20_litmus.kv 288 get_sig_bytes 2
ffa_ch20_litmus.kv 289 hex_to_int
ffa_ch20_litmus.kv 290 sig_length=$r
ffa_ch20_litmus.kv 291
ffa_ch20_litmus.kv 292 # Version (only Version 4 -- what GPG 1.4.x outputs -- is supported)
ffa_ch20_litmus.kv 293 get_sig_bytes 1
ffa_ch20_litmus.kv 294 turd+=$r
ffa_ch20_litmus.kv 295 sig_version=$r
ffa_ch20_litmus.kv 296 sig_field_mandatory "Version" $sig_version 04
ffa_ch20_litmus.kv 297
ffa_ch20_litmus.kv 298 # Class (only class 0 is supported)
ffa_ch20_litmus.kv 299 get_sig_bytes 1
ffa_ch20_litmus.kv 300 turd+=$r
ffa_ch20_litmus.kv 301 sig_class=$r
ffa_ch20_litmus.kv 302 sig_field_mandatory "Class" $sig_class 00
ffa_ch20_litmus.kv 303
ffa_ch20_litmus.kv 304 # Public Key Algo (only RSA is supported)
ffa_ch20_litmus.kv 305 get_sig_bytes 1
ffa_ch20_litmus.kv 306 turd+=$r
ffa_ch20_litmus.kv 307 sig_pk_algo=$r
ffa_ch20_litmus.kv 308 sig_field_mandatory "Public Key Algo" $sig_pk_algo 01
ffa_ch20_litmus.kv 309
ffa_ch20_litmus.kv 310 # Digest Algo (only SHA512 is supported)
ffa_ch20_litmus.kv 311 get_sig_bytes 1
ffa_ch20_litmus.kv 312 turd+=$r
ffa_ch20_litmus.kv 313 sig_digest_algo=$r
ffa_ch20_litmus.kv 314 sig_field_mandatory "Digest Algo" $sig_digest_algo 0A
ffa_ch20_litmus.kv 315
ffa_ch20_litmus.kv 316 # Hashed Section Length
ffa_ch20_litmus.kv 317 get_sig_bytes 2
ffa_ch20_litmus.kv 318 turd+=$r
ffa_ch20_litmus.kv 319 hex_to_int
ffa_ch20_litmus.kv 320 sig_hashed_len=$r
ffa_ch20_litmus.kv 321
ffa_ch20_litmus.kv 322 # Hashed Section (typically: timestamp)
ffa_ch20_litmus.kv 323 get_sig_bytes $sig_hashed_len
ffa_ch20_litmus.kv 324 turd+=$r
ffa_ch20_litmus.kv 325 sig_hashed=$r
ffa_ch20_litmus.kv 326
ffa_ch20_litmus.kv 327 # Unhashed Section Length
ffa_ch20_litmus.kv 328 get_sig_bytes 1
ffa_ch20_litmus.kv 329 hex_to_int
ffa_ch20_litmus.kv 330 sig_unhashed_len=$r
ffa_ch20_litmus.kv 331
ffa_ch20_litmus.kv 332 # Unhashed Section (discard)
ffa_ch20_litmus.kv 333 get_sig_bytes $sig_unhashed_len
ffa_ch20_litmus.kv 334
ffa_ch20_litmus.kv 335 # Compute Byte Length of Hashed Header (for last field)
ffa_ch20_litmus.kv 336 hashed_header_len=$((${#turd} / 2))
ffa_ch20_litmus.kv 337
ffa_ch20_litmus.kv 338 # Final section of the hashed turd (not counted in hashed_header_len)
ffa_ch20_litmus.kv 339 turd+=$sig_version
ffa_ch20_litmus.kv 340 turd+="FF"
ffa_ch20_litmus.kv 341 turd+=$(printf "%08x" $hashed_header_len)
ffa_ch20_litmus.kv 342
ffa_ch20_litmus.kv 343 # Compute the hash of data file and the hashed appendix from sig :
ffa_ch20_litmus.kv 344 hash=$((cat $DATAFILE; xxd -r -p <<< $turd) | $HASHER | cut -d ' ' -f1)
ffa_ch20_litmus.kv 345 # Convert to upper case
ffa_ch20_litmus.kv 346 hash=$(echo $hash | tr 'a-z' 'A-Z')
ffa_ch20_litmus.kv 347
ffa_ch20_litmus.kv 348 # Parse the RSA Signature portion of the Sig file:
ffa_ch20_litmus.kv 349
ffa_ch20_litmus.kv 350 # RSA Packet Length (how many bytes to read)
ffa_ch20_litmus.kv 351 get_sig_bytes 1
ffa_ch20_litmus.kv 352 hex_to_int
ffa_ch20_litmus.kv 353 rsa_packet_len=$r
ffa_ch20_litmus.kv 354
ffa_ch20_litmus.kv 355 # The RSA Packet itself
ffa_ch20_litmus.kv 356 get_sig_bytes $rsa_packet_len
ffa_ch20_litmus.kv 357 rsa_packet=$r
ffa_ch20_litmus.kv 358
ffa_ch20_litmus.kv 359 # Digest Prefix (2 bytes)
ffa_ch20_litmus.kv 360 get_sig_bytes 2
ffa_ch20_litmus.kv 361 digest_prefix=$r
ffa_ch20_litmus.kv 362
ffa_ch20_litmus.kv 363 # See whether it matches the first two bytes of the actual computed hash :
ffa_ch20_litmus.kv 364 computed_prefix=$(printf "%.4s" $hash)
ffa_ch20_litmus.kv 365
ffa_ch20_litmus.kv 366 if [ "$digest_prefix" != "$computed_prefix" ]
ffa_ch20_litmus.kv 367 then
ffa_ch20_litmus.kv 368 # It didn't match, so we can return 'bad signature' immediately:
ffa_ch20_litmus.kv 369 done_sig_bad
ffa_ch20_litmus.kv 370 fi
ffa_ch20_litmus.kv 371
ffa_ch20_litmus.kv 372 # If prefix matched, we will proceed to do the actual RSA operation.
ffa_ch20_litmus.kv 373
ffa_ch20_litmus.kv 374 # RSA Bitness given in Sig
ffa_ch20_litmus.kv 375 get_sig_bytes 2
ffa_ch20_litmus.kv 376 hex_to_int
ffa_ch20_litmus.kv 377 rsa_bitness=$r
ffa_ch20_litmus.kv 378
ffa_ch20_litmus.kv 379 # Compute RSA Byteness from the above
ffa_ch20_litmus.kv 380 rsa_byteness=$((($rsa_bitness + 7) / 8))
ffa_ch20_litmus.kv 381
ffa_ch20_litmus.kv 382 # RSA Bitness for use in determining required Peh width:
ffa_ch20_litmus.kv 383 rsa_width=$(($rsa_byteness * 8))
ffa_ch20_litmus.kv 384
ffa_ch20_litmus.kv 385 # Only traditional GPG RSA widths are supported:
ffa_ch20_litmus.kv 386 if [ $rsa_width != 2048 ] && [ $rsa_width != 4096 ] && [ $rsa_width != 8192 ]
ffa_ch20_litmus.kv 387 then
ffa_ch20_litmus.kv 388 reason="Only 2048, 4096, and 8192-bit RSA are supported."
ffa_ch20_litmus.kv 389 echo "$SIGFILE is UNSUPPORTED : $reason" >&2
ffa_ch20_litmus.kv 390 exit $RET_EGGOG
ffa_ch20_litmus.kv 391 fi
ffa_ch20_litmus.kv 392
ffa_ch20_litmus.kv 393 # RSA Signature per se (final item read from sig file)
ffa_ch20_litmus.kv 394 get_sig_bytes $rsa_byteness
ffa_ch20_litmus.kv 395 rsa_sig=$r
ffa_ch20_litmus.kv 396
ffa_ch20_litmus.kv 397 # Per RFC4880, 'PKCS' encoding of hash is as follows:
ffa_ch20_litmus.kv 398 # 0 1 [PAD] 0 [ASN] [MD]
ffa_ch20_litmus.kv 399
ffa_ch20_litmus.kv 400 # First two bytes of PKCS-encoded hash will always be 00 01 :
ffa_ch20_litmus.kv 401 pkcs="0001"
ffa_ch20_litmus.kv 402
ffa_ch20_litmus.kv 403 # Compute necessary number of padding FF bytes :
ffa_ch20_litmus.kv 404 pkcs_pad_bytes=$(($rsa_byteness - $MD_LEN - $ASN_LEN - 3))
ffa_ch20_litmus.kv 405
ffa_ch20_litmus.kv 406 # Attach the padding bytes:
ffa_ch20_litmus.kv 407 for ((x=1; x<=$pkcs_pad_bytes; x++)); do
ffa_ch20_litmus.kv 408 pkcs+="FF"
ffa_ch20_litmus.kv 409 done
ffa_ch20_litmus.kv 410
ffa_ch20_litmus.kv 411 # Attach the 00 separator between the padding and the ASN:
ffa_ch20_litmus.kv 412 pkcs+="00"
ffa_ch20_litmus.kv 413
ffa_ch20_litmus.kv 414 # Attach the ASN ('magic' corresponding to the hash algo) :
ffa_ch20_litmus.kv 415 pkcs+=$ASN
ffa_ch20_litmus.kv 416
ffa_ch20_litmus.kv 417 # Finally, attach the computed (from Data file) hash itself :
ffa_ch20_litmus.kv 418 pkcs+=$hash
ffa_ch20_litmus.kv 419
ffa_ch20_litmus.kv 420 # Generate a Peh tape which will attempt to verify $rsa_sig against the pubkey,
ffa_ch20_litmus.kv 421 # computing the expression $rsa_sig ^ PUB_E mod PUB_M and comparing to $pkcs.
ffa_ch20_litmus.kv 422 # Outputs 'Valid' and returns Yes_Code (1) if and only if signature is valid.
ffa_ch20_litmus.kv 423 tape=".$rsa_sig@Public-Op!.$pkcs={[Valid]QY}{[Invalid]QN}_"
ffa_ch20_litmus.kv 424
ffa_ch20_litmus.kv 425 # Execute the tape:
ffa_ch20_litmus.kv 426 run_peh_tape $tape $rsa_width 3
ffa_ch20_litmus.kv 427
ffa_ch20_litmus.kv 428 # 'Belt and suspenders' -- test both output and return code:
ffa_ch20_litmus.kv 429 # If verification succeeded, return code will be 1, and output 'Valid':
ffa_ch20_litmus.kv 430 if [ $peh_code -eq $PEH_YES ] && [ "$peh_res" == "Valid" ]
ffa_ch20_litmus.kv 431 then
ffa_ch20_litmus.kv 432 # Valid RSA signature:
ffa_ch20_litmus.kv 433 done_sig_valid
ffa_ch20_litmus.kv 434 else
ffa_ch20_litmus.kv 435 # Signature was not valid:
ffa_ch20_litmus.kv 436 done_sig_bad
ffa_ch20_litmus.kv 437 fi
ffa_ch20_litmus.kv 438 # The end.