tree checksum vpatch file split hunks

all signers: bvt diana_coman asciilifeform

antecedents: ffa_ch19_peh_tuning_and_demos.kv

press order:

ffa_ch1_genesis.kvasciilifeform bvt diana_coman
ffa_ch2_logicals.kvasciilifeform bvt diana_coman
ffa_ch3_shifts.kvasciilifeform bvt diana_coman
ffa_ch4_ffacalc.kvasciilifeform bvt diana_coman
ffa_ch5_egypt.kvasciilifeform bvt diana_coman
ffa_ch6_simplest_rsa.kvasciilifeform bvt diana_coman
ffa_ch7_turbo_egyptians.kvasciilifeform bvt diana_coman
ffa_ch8_randomism.kvasciilifeform bvt diana_coman
ffa_ch9_exodus.kvasciilifeform bvt diana_coman
ffa_ch10_karatsuba.kvasciilifeform bvt diana_coman
ffa_ch11_tuning_and_api.kvasciilifeform bvt diana_coman
ffa_ch12_karatsuba_redux.kvasciilifeform diana_coman
ffa_w_borrow_expr.kvasciilifeform diana_coman
ffa_ch13_measure_and_qshifts.kvasciilifeform diana_coman
ffa_ch14_barrett.kvasciilifeform diana_coman
ffa_ch15_gcd.kvasciilifeform diana_coman
ffa_ch16_miller_rabin.kvasciilifeform diana_coman
ffa_ch17_peh.kvasciilifeform diana_coman
ffa_ch18_subroutines.kvasciilifeform diana_coman
ffa_ch19_peh_tuning_and_demos.kvasciilifeform diana_coman
ffa_ch20_litmus.kvasciilifeform

patch:

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