-
Notifications
You must be signed in to change notification settings - Fork 6
/
aes.el
1454 lines (1342 loc) · 59.7 KB
/
aes.el
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
256
257
258
259
260
261
262
263
264
265
266
267
268
269
270
271
272
273
274
275
276
277
278
279
280
281
282
283
284
285
286
287
288
289
290
291
292
293
294
295
296
297
298
299
300
301
302
303
304
305
306
307
308
309
310
311
312
313
314
315
316
317
318
319
320
321
322
323
324
325
326
327
328
329
330
331
332
333
334
335
336
337
338
339
340
341
342
343
344
345
346
347
348
349
350
351
352
353
354
355
356
357
358
359
360
361
362
363
364
365
366
367
368
369
370
371
372
373
374
375
376
377
378
379
380
381
382
383
384
385
386
387
388
389
390
391
392
393
394
395
396
397
398
399
400
401
402
403
404
405
406
407
408
409
410
411
412
413
414
415
416
417
418
419
420
421
422
423
424
425
426
427
428
429
430
431
432
433
434
435
436
437
438
439
440
441
442
443
444
445
446
447
448
449
450
451
452
453
454
455
456
457
458
459
460
461
462
463
464
465
466
467
468
469
470
471
472
473
474
475
476
477
478
479
480
481
482
483
484
485
486
487
488
489
490
491
492
493
494
495
496
497
498
499
500
501
502
503
504
505
506
507
508
509
510
511
512
513
514
515
516
517
518
519
520
521
522
523
524
525
526
527
528
529
530
531
532
533
534
535
536
537
538
539
540
541
542
543
544
545
546
547
548
549
550
551
552
553
554
555
556
557
558
559
560
561
562
563
564
565
566
567
568
569
570
571
572
573
574
575
576
577
578
579
580
581
582
583
584
585
586
587
588
589
590
591
592
593
594
595
596
597
598
599
600
601
602
603
604
605
606
607
608
609
610
611
612
613
614
615
616
617
618
619
620
621
622
623
624
625
626
627
628
629
630
631
632
633
634
635
636
637
638
639
640
641
642
643
644
645
646
647
648
649
650
651
652
653
654
655
656
657
658
659
660
661
662
663
664
665
666
667
668
669
670
671
672
673
674
675
676
677
678
679
680
681
682
683
684
685
686
687
688
689
690
691
692
693
694
695
696
697
698
699
700
701
702
703
704
705
706
707
708
709
710
711
712
713
714
715
716
717
718
719
720
721
722
723
724
725
726
727
728
729
730
731
732
733
734
735
736
737
738
739
740
741
742
743
744
745
746
747
748
749
750
751
752
753
754
755
756
757
758
759
760
761
762
763
764
765
766
767
768
769
770
771
772
773
774
775
776
777
778
779
780
781
782
783
784
785
786
787
788
789
790
791
792
793
794
795
796
797
798
799
800
801
802
803
804
805
806
807
808
809
810
811
812
813
814
815
816
817
818
819
820
821
822
823
824
825
826
827
828
829
830
831
832
833
834
835
836
837
838
839
840
841
842
843
844
845
846
847
848
849
850
851
852
853
854
855
856
857
858
859
860
861
862
863
864
865
866
867
868
869
870
871
872
873
874
875
876
877
878
879
880
881
882
883
884
885
886
887
888
889
890
891
892
893
894
895
896
897
898
899
900
901
902
903
904
905
906
907
908
909
910
911
912
913
914
915
916
917
918
919
920
921
922
923
924
925
926
927
928
929
930
931
932
933
934
935
936
937
938
939
940
941
942
943
944
945
946
947
948
949
950
951
952
953
954
955
956
957
958
959
960
961
962
963
964
965
966
967
968
969
970
971
972
973
974
975
976
977
978
979
980
981
982
983
984
985
986
987
988
989
990
991
992
993
994
995
996
997
998
999
1000
;;; aes.el --- Implementation of AES -*- lexical-binding: t; -*-
;; Copyright (C) 2008-2021 Markus Sauermann
;; Author: Markus Sauermann <[email protected]>
;; Maintainer: Markus Sauermann <[email protected]>
;; Created: 15 Feb 2008
;; Version: 1.0
;; Keywords: data, tools
;; URL: https://github.com/Sauermann/emacs-aes
;; Package-Requires: ((emacs "26.1"))
;; This file is not part of GNU Emacs.
;;; Commentary:
;; This library provides support for saving files in an encrypted form
;; by using Cipher-block chaining [4] and Offset Codebook Mode [5].
;; Both use Rijndael [1] as encryption algorithm, which is implemented
;; natively in Emacs. Rijndael is a superset of the AES algorithm
;; [2]. Additionally this library provides a password generator based
;; on AES and random user input. For patent issues about OCB see [6],
;; which allows this distribution.
;; Config file
;; Insert "(require 'aes)" into your local .emacs file to load this
;; library.
;; Insert "(aes-enable-auto-decryption)" into yout local .emacs file
;; for convenient automatic recognization of encrypted files during
;; loading.
;; Whenever possible, this library should be used byte-compiled, as
;; this provides a really great performance boost!
;; Main entry points:
;; `aes-encrypt-current-buffer' / `aes-decrypt-current-buffer' Ask for
;; password and encrypt / decrypt current buffer.
;; `aes-insert-password' Generate a random password from user input.
;; For customizing this library, there is the customization group aes
;; in the applications group.
;; Emacs compatibility:
;; Version 27 is recommended
;; Version 26 has not been tested
;; Version 25 and earlier are incompatible
;; This implementation allows additionally to the AES specification
;; blocklengths of 24 and 32 bytes.
;; Nb denotes the number of 32-bit words in the state.
;; Nk denotes the number of 32-bit words comprising the cipher key.
;; Nr denotes the number of rounds.
;; We allow Nb and Nk to be 4, 6, or 8. and Nr = max(Nb, Nk) + 6
;; Since Emacs implements integers as 29 bit numbers, it is not
;; possible to use the optimization, which requires 32 bit numbers.
;; For details see [3]. This leads to an 8-bit design for this
;; implementation. So the following fitting implementation is used
;; here.
;; - Multiplication and inverting in GF(2^8) are implemented as table
;; lookups.
;; - The state is implemented as a unibyte string of length 4 * Nb.
;; - Plaintext and ciphertext are implemented as unibyte strings.
;; - The expanded key is implemented as a list of length 4 * Nb * (1 +
;; Nr) with entries '((A . B) . (C . D)), where A, B, C and D are
;; bytes. It is precalculated before the en-/decryption algorithms.
;; - The S-boxes are implemented by lookup tables.
;; - The three operations ByteSub, ShiftRow and MixColumn together
;; with round-key-addition are implemented in the functions
;; `aes-SubShiftMixKeys' and `aes-InvSubShiftMixKeys' for encryption
;; and decryption respectively.
;; - CBC mode is implemented straightforward, using a Zero [10] or
;; PKCS#7 [7] padding. The IV is appended to and saved with the
;; ciphertext.
;; - OCB mode made the implementation of a pmac, based on AES,
;; necessary, but the further details were straightforward. The IV
;; is appended to the ciphertext. During decryption the created
;; hash-value is checked.
;; - the function `aes-key-from-passwd' generates an AES key from an
;; user input string (password).
;; - Further `aes-insert-password' generates random passwords, based
;; on random user input like mousemovement, time and keyinput.
;; The version of the internal storage format of encrypted data is
;; Version 1.3. If you need to decrypt data stored in Version 1.2,
;; please use Verion 0.9 of this library or an earlier one.
;; The latest version of this package is also available via MELPA [9].
;; There are two [11] other [12] Elisp implementations of AES.
;; Known Bugs / Limitations / TODO:
;; - This implementation is not resistant against DPA attacks [8].
;; - `aes-auto-decrypt' is not completely compliant to Emacs standards.
;; - Handle CBC and OCB in two different functions instead of the
;; single function `aes-encrypt-buffer-or-string'.
;; - don't handle padding in `aes-cbc-encrypt'.
;; - refactor `aes-user-entropy'
;; - test random number generator
;; - make use of 32 bit integer support
;; References:
;; [1] https://csrc.nist.gov/csrc/media/projects/cryptographic-standards-and-guidelines/documents/aes-development/rijndael-ammended.pdf
;; [2] https://csrc.nist.gov/csrc/media/publications/fips/197/final/documents/fips-197.pdf
;; [3] https://www.openssl.org/
;; [4] https://en.wikipedia.org/wiki/Block_cipher_mode_of_operation
;; [5] https://datatracker.ietf.org/doc/html/draft-krovetz-ocb-00
;; [6] https://www.cs.ucdavis.edu/~rogaway/ocb/license.htm
;; [7] https://datatracker.ietf.org/doc/html/rfc5652#section-6.3
;; [8] https://en.wikipedia.org/wiki/Power_analysis#Differential_power_analysis
;; [9] https://melpa.org/
;; [10] https://en.wikipedia.org/wiki/Padding_(cryptography)#Zero_padding
;; [11] https://github.com/mhayashi1120/Emacs-kaesar/
;; [12] https://josefsson.org/aes/
;; This program is free software: you can redistribute it and/or
;; modify it under the terms of the GNU General Public License as
;; published by the Free Software Foundation, either version 3 of the
;; License, or (at your option) any later version.
;; This program is distributed in the hope that it will be useful, but
;; WITHOUT ANY WARRANTY; without even the implied warranty of
;; MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
;; General Public License for more details.
;; You should have received a copy of the GNU General Public License
;; along with this program. If not, see
;; <http://www.gnu.org/licenses/>.
;;; Code:
;;;; Helpers
(defun aes--xor (x y)
"Return X and Y bytewise xored.
X and Y and the return values are unibyte strings.
Y must not be shorter than X."
(let* ((l (length x))
(res (make-string l 0))
(i 0))
(while (< i l)
(aset res i (logxor (aref x i) (aref y i)))
(setq i (1+ i)))
res))
(defun aes--xor-de (x y)
"Calculate X and Y bytewise xored destructively in X.
X and Y are unibyte strings. Y must not be shorter than X.
The return value is the new value that is stored in X."
(let* ((l (length x))
(i 0))
(while (< i l)
(aset x i (logxor (aref x i) (aref y i)))
(setq i (1+ i))))
x)
(defun aes--xor-4 (x y)
"Return the 4 byte objects X and Y bytewise xored as new cons cell.
X and Y are objects of the form '((A . B) . (C . D))"
(cons (cons (logxor (car (car x)) (car (car y)))
(logxor (cdr (car x)) (cdr (car y))))
(cons (logxor (car (cdr x)) (car (cdr y)))
(logxor (cdr (cdr x)) (cdr (cdr y))))))
(defun aes--xor-4-de (x y)
"X and Y are bytewise xored destructively in X.
X and Y are objects of the form '((A . B) . (C . D))"
(setcar (car x) (logxor (car (car x)) (car (car y))))
(setcdr (car x) (logxor (cdr (car x)) (cdr (car y))))
(setcar (cdr x) (logxor (car (cdr x)) (car (cdr y))))
(setcdr (cdr x) (logxor (cdr (cdr x)) (cdr (cdr y)))))
(defun aes-pad (v bs &optional padding)
"Pad a string V to blocksize BS.
PADDING specifies the padding format. Currently only Zero-Padding
and PKCS#7 are supported. Meaningful values for PADDING are
'Zero' and 'PKCS#7'. Other values of PADDING default to
Zero-Padding."
(cond ((and padding (equal padding "PKCS#7"))
(aes-pkcs7-pad v bs))
((and padding (equal padding "Zero"))
(aes-zero-pad v bs))
(t (aes-zero-pad v bs))))
(defun aes-zero-pad (v bs)
"Apply a Zero-Padding to the unibyte string V to blocksize BS.
Append Zeros to the string until the length is a multiple of BS.
Return a new unibyte string containing the result. V is not changed."
;; For a description, see [10]
(concat v (make-string (mod (- (string-bytes v)) bs) 0)))
(defun aes--enlarge-to-multiple-num (blocksize n)
"Return the smallest multiple of BLOCKSIZE, not smaller than N."
(+ n (mod (- n) blocksize) 0))
(defun aes-pkcs7-pad (v bs)
"Apply a PKCS#7-Padding to the unibyte string V to blocksize BS.
Return a new unibyte string containing the result. V is not changed."
;; For a description, see [7]
(let ((pad (- bs (mod (string-bytes v) bs))))
(concat v (make-string pad pad))))
(defun aes--str-to-b (str)
"Convert the unibyte string STR to a list-representation.
The length of STR must be a multiple of 4.
The length of the resulting list has a quarter of the length of STR.
Elements 4*K to 4*K+3 of STR (named A, B, C and D in this order) are stored in
position K of the result as '((A . B) . (C . D))."
(let (res
(l (length str))
(i 0))
(while (< i l)
(setq res (cons
(cons (cons (aref str i) (aref str (1+ i)))
(cons (aref str (+ i 2)) (aref str (+ i 3))))
res))
(setq i (+ i 4)))
(nreverse res)))
;;;; Multiplication
(eval-when-compile
(defvar aes--l (make-string 256 0))
(defvar aes--mt (make-vector #xf 0))
(let ((aes-mul-pre
(lambda (a b)
"Multiply the bytes A and B in GF(2^8) and return their product."
;; For a description, see [1, Ch 2.1.2] or [2. Ch 4.2.1]
(let ((p 0)
(c 0))
(while (< c 8)
(if (= 1 (logand b 1))
(setq p (logxor a p)))
(if (prog1 (= #x80 (logand a #x80))
(setq a (logand #xff (lsh a 1))))
(setq a (logxor a #x1b)))
(setq b (lsh b -1))
(setq c (1+ c)))
p)))
(x 0)
i res)
(while (< x #xf)
(aset aes--mt x (make-string 256 0))
(setq x (1+ x)))
(setq x 1)
(while (< x 256)
(setq i x)
(while (< i 256)
(setq res (funcall aes-mul-pre i x))
(if (= #x01 res) (progn (aset aes--l x i) (aset aes--l i x)))
(and (< x #xf) (aset (aref aes--mt x) i res)
(and (< i #xf) (aset (aref aes--mt i) x res)))
(setq i (1+ i)))
(setq x (1+ x)))))
(defconst aes--inv-table (eval-when-compile aes--l)
"This variable contains the GF(2^8) inverting lookup table.")
;; The following 6 tables are used during the time critical
;; functions `aes-SubShiftMixKeys' and `aes-InvSubShiftMixKeys'
(defconst aes--l2 (eval-when-compile (aref aes--mt #x02)))
(defconst aes--l3 (eval-when-compile (aref aes--mt #x03)))
(defconst aes--l9 (eval-when-compile (aref aes--mt #x09)))
(defconst aes--lb (eval-when-compile (aref aes--mt #x0b)))
(defconst aes--ld (eval-when-compile (aref aes--mt #x0d)))
(defconst aes--le (eval-when-compile (aref aes--mt #x0e)))
;;;; SubBytes Transformation
(let ((l1 (make-string 256 0))
(l2 (make-string 256 0))
(x 0)
b g i)
(while (< x 256)
(setq b (aref aes--inv-table x))
(setq g 0)
(setq i 0)
(while (< i 8)
(setq g (logxor g (lsh (logand 1 (logxor (lsh (logxor b #x63) (- i))
(lsh b (- (% (+ i 4) 8)))
(lsh b (- (% (+ i 5) 8)))
(lsh b (- (% (+ i 6) 8)))
(lsh b (- (% (+ i 7) 8)))))
i)))
(setq i (1+ i)))
(aset l1 x g)
(aset l2 g x)
(setq x (1+ x)))
(defconst aes--s-boxes-enc l1
"This variable contains the encryption S-Boxes.
The S-boxes are stored as strings of length 256.")
(defconst aes--s-boxes-dec l2
"This variable contains the decryption S-Boxes.
The S-boxes are stored as strings of length 256."))
;; For a description see [1, Ch 4.2.1] or [2, Ch 5.1.1]
(defun aes-SubBytes (state)
"Apply the SubBytes transformation to each byte of the unibyte string STATE.
STATE may be of arbitrary length."
;; For a description of SubBytes see [1, Ch 4.2.1] or [2, Ch 5.1.1]
(let ((l (length state))
(i 0))
(while (< i l)
(aset state i (aref aes--s-boxes-enc (aref state i)))
(setq i (1+ i)))))
(defun aes-InvSubBytes (state)
"Apply the InvSubBytes transformation to each byte of the string STATE.
The unibyte string STATE may be of arbitrary length."
;; For a description of InvSubBytes see [1, Ch 4.2.1] or [2, Ch 5.3.2]
(let ((l (length state))
(i 0))
(while (< i l)
(aset state i (aref aes--s-boxes-dec (aref state i)))
(setq i (1+ i)))))
(defun aes-SubWord (x)
"Apply the SubBytes transformation to all 4 bytes of X.
X is of the form '((A . B) . (C . D))."
(setcar (car x) (aref aes--s-boxes-enc (car (car x))))
(setcdr (car x) (aref aes--s-boxes-enc (cdr (car x))))
(setcar (cdr x) (aref aes--s-boxes-enc (car (cdr x))))
(setcdr (cdr x) (aref aes--s-boxes-enc (cdr (cdr x)))))
;;;; ShiftRows Transformation
(defun aes-ShiftRows (state)
"Apply the shift rows transformation destructively in STATE.
The length of the unibyte string STATE must be a multiple of 4 and larger
than 12."
;; For a description of ShiftRows see [1, Ch 4.2.2] or [2, Ch 5.1.2]
(let* ((border (- (length state) 4))
(x (aref state 1))
(c 1)
(y (aref state 6))
(z (aref state 11)))
(while (< c border)
(aset state c (aref state (+ c 4)))
(setq c (+ c 4)))
(aset state c x)
(setq x (aref state 2))
(setq c 2)
(setq border (- border 4))
(while (< c border)
(aset state c (aref state (+ c 8)))
(setq c (+ c 4)))
(aset state c x)
(aset state (+ c 4) y)
(setq x (aref state 3))
(setq y (aref state 7))
(setq c 3)
(setq border (- border 4))
(while (< c border)
(aset state c (aref state (+ c 12)))
(setq c (+ c 4)))
(aset state c x)
(aset state (+ c 4) y)
(aset state (+ c 8) z)))
(defun aes-InvShiftRows (state)
"Apply the inverted shift rows transformation destructively in STATE.
The length of the unibyte string STATE must be a multiple of 4 and larger
than 12."
;; For a description of InvShiftRows see [1, Ch 4.2.2] or [2, Ch 5.3.1]
(let* ((Nb4 (length state))
(c (- Nb4 3))
(x (aref state c))
(y (aref state (- Nb4 6)))
(z (aref state (- Nb4 9))))
(while (< 4 c)
(aset state c (aref state (setq c (- c 4)))))
(aset state 1 x)
(setq x (aref state (setq c (- Nb4 2))))
(while (< 8 c)
(aset state c (aref state (- c 8)))
(setq c (- c 4)))
(aset state 6 x)
(aset state 2 y)
(setq x (aref state (setq c (- Nb4 1))))
(setq y (aref state (- c 4)))
(while (< 12 c)
(aset state c (aref state (- c 12)))
(setq c (- c 4)))
(aset state 11 x)
(aset state 7 y)
(aset state 3 z)))
;;;; Combined Single Round Transformation
(defsubst aes-SubShiftMixKeys (state copy keys)
"Apply one round of the aes encryption destructively to the string STATE.
COPY must contain a duplicate of STATE, but they must not be `eq'.
KEYS is a list containing a part of the expanded key schedule. See
`aes-KeyExpansion' for how KEYS looks like.
The relevant keys for this round are stored in the first Nb elements of KEYS,
which means that the length of KEYS is at least Nb.
In this function the 4 transformations SubBytes, ShiftRows, MixColumns and
AddRoundKey of one aes round are applied to STATE.
The length of the unibyte string STATE is a multiple of 4 and larger than 12."
;; For a description of MixColumns see [1, Ch 4.2.3] or [2, Ch 5.1.3]
;; For a description of AddRoundKey see [1, Ch 4.2.4] or [2, Ch 5.1.4]
(let* ((x4 0)
(Nb4 (length state))
s0 s1 s2 s3 keyA)
(while (< x4 Nb4)
(setq s0 (aref aes--s-boxes-enc (aref copy x4)))
(setq s1 (aref aes--s-boxes-enc (aref copy (% (+ x4 1 4) Nb4))))
(setq s2 (aref aes--s-boxes-enc (aref copy (% (+ x4 2 8) Nb4))))
(setq s3 (aref aes--s-boxes-enc (aref copy (% (+ x4 3 12) Nb4))))
(setq keyA (car keys))
(aset state x4 (logxor (aref aes--l2 s0) (aref aes--l3 s1) s2 s3
(car (car keyA))))
(aset state (1+ x4) (logxor s0 (aref aes--l2 s1) (aref aes--l3 s2) s3
(cdr (car keyA))))
(aset state (+ 2 x4) (logxor s0 s1 (aref aes--l2 s2) (aref aes--l3 s3)
(car (cdr keyA))))
(aset state (+ 3 x4) (logxor (aref aes--l3 s0) s1 s2 (aref aes--l2 s3)
(cdr (cdr keyA))))
(setq keys (cdr keys))
(setq x4 (+ x4 4)))))
(defsubst aes-InvSubShiftMixKeys (state copy keys)
"Apply the 4 inverted transformations destructively to STATE.
COPY msut contain a duplicate of STATE, but they must not be `eq'.
See `aes-SubShiftMixKeys' for additional information.
Note that the part of the key expansion KEYS is in the reverse order than it was
in `aes-SubShiftMixKeys'."
;; For a description of InvMixColumns see [1, Ch 4.2.3] or [2, Ch 5.3.3]
;; For a description of InvAddRoundKey see [1, Ch 4.2.4] or [2, Ch 5.3.4]
(let* ((Nb4 (length state))
(x4 (- Nb4 4))
s0 s1 s2 s3 keyA)
(while (<= 0 x4)
(setq keyA (car keys))
(setq s0 (logxor (aref copy x4) (car (car keyA))))
(setq s1 (logxor (aref copy (1+ x4)) (cdr (car keyA))))
(setq s2 (logxor (aref copy (+ 2 x4)) (car (cdr keyA))))
(setq s3 (logxor (aref copy (+ 3 x4)) (cdr (cdr keyA))))
(aset state x4
(aref aes--s-boxes-dec (logxor (aref aes--le s0) (aref aes--lb s1)
(aref aes--ld s2) (aref aes--l9 s3))))
(aset state (% (+ 1 4 x4) Nb4)
(aref aes--s-boxes-dec (logxor (aref aes--l9 s0) (aref aes--le s1)
(aref aes--lb s2) (aref aes--ld s3))))
(aset state (% (+ 2 8 x4) Nb4)
(aref aes--s-boxes-dec (logxor (aref aes--ld s0) (aref aes--l9 s1)
(aref aes--le s2) (aref aes--lb s3))))
(aset state (% (+ 3 12 x4) Nb4)
(aref aes--s-boxes-dec (logxor (aref aes--lb s0) (aref aes--ld s1)
(aref aes--l9 s2) (aref aes--le s3))))
(setq x4 (- x4 4))
(setq keys (cdr keys)))))
;;;; Key Expansion
(defun aes--RotWord (x)
"Rotate X by one byte.
X is of the form '((A . B) . (C . D))
Append the first byte to the end and return '((B . C) . (D . A))."
(let ((te (car (car x))))
(setcar (car x) (cdr (car x)))
(setcdr (car x) (car (cdr x)))
(setcar (cdr x) (cdr (cdr x)))
(setcdr (cdr x) te)))
(defun aes-KeyExpansion (key Nb &optional Nr)
"Return a list, containing the key expansion of KEY.
KEY is a list of NK elements with entries '((A . B) . (C . D)), where A, B, C
and D are bytes.
NB, NK and NR are defined in the Commentary section of the sourcecode.
The expanded key is a list of length 4 * Nb * (1 + Nr) with
entries of the same form as in KEY."
;; For a description of the key expansion see [1, Ch 4.3.1] or [2, Ch 5.2]
(let* ((Nk (length key))
(w (reverse key))
(i Nk)
(rcon (cons (cons 1 0) (cons 0 0)))
(border (* Nb (1+ (or Nr (+ (max Nb Nk) 6)))))
(temp (cons (cons nil nil) (cons nil nil)))
f)
(while (< i border)
(setq f (car w))
(setcar (car temp) (car (car f)))
(setcdr (car temp) (cdr (car f)))
(setcar (cdr temp) (car (cdr f)))
(setcdr (cdr temp) (cdr (cdr f)))
(if (= 0 (% i Nk))
(progn (aes--RotWord temp)
(aes-SubWord temp)
(aes--xor-4-de temp rcon)
(setcar (car rcon) (aref aes--l2 (car (car rcon)))))
(if (and (< 6 Nk) (= (% i Nk) 4))
(aes-SubWord temp)))
(setq w (cons (aes--xor-4 (nth 3 w) temp) w))
(setq i (1+ i)))
(nreverse w)))
;;;; Add Round Key
(defun aes-AddRoundKey (state keys)
"Apply one AddRoundKey transformation to the unibyte string STATE.
Use the first NB elements of the list KEYS as keys.
NB denotes the number of 32-bit words in the state.
KEYS is a part of the key expansion as defined in `aes-SubShiftMixKeys'.
The length of STATE is a multiple of 8 and larger than 12."
;; For a description of AddRoundKey see [1, Ch 4.2.4] and [1, Ch 4.3.2]
;; or [2, Ch 5.1.4]
(let ((Nb4 (length state))
(i 0)
keysA)
(while (< i Nb4)
(setq keysA (car keys))
(aset state i (logxor (aref state i) (car (car keysA))))
(aset state (1+ i) (logxor (aref state (1+ i)) (cdr (car keysA))))
(aset state (+ 2 i) (logxor (aref state (+ 2 i)) (car (cdr keysA))))
(aset state (+ 3 i) (logxor (aref state (+ 3 i)) (cdr (cdr keysA))))
(setq keysA (car (setq keys (cdr keys))))
(aset state (+ 4 i) (logxor (aref state (+ 4 i)) (car (car keysA))))
(aset state (+ 5 i) (logxor (aref state (+ 5 i)) (cdr (car keysA))))
(aset state (+ 6 i) (logxor (aref state (+ 6 i)) (car (cdr keysA))))
(aset state (+ 7 i) (logxor (aref state (+ 7 i)) (cdr (cdr keysA))))
(setq keys (cdr keys))
(setq i (+ 8 i)))))
(defun aes-InvAddRoundKey (state keys)
"Apply one AddRoundKey transformation to the unibyte string STATE.
Use the first NB elements of the list KEYS as keys.
NB denotes the number of 32-bit words in the state.
KEYS is a part of the key expansion as defined in `aes-InvSubShiftMixKeys'."
;; For a description of the inverse of AddRoundKey see [1, Ch 4.2.4] and
;; [1, Ch 4.3.2] or [2, Ch 5.3.4]
(let* ((Nb4 (length state))
(i (- Nb4 4))
keysA)
(while (<= 0 i)
(setq keysA (car keys))
(aset state i (logxor (aref state i) (car (car keysA))))
(aset state (1+ i) (logxor (aref state (1+ i)) (cdr (car keysA))))
(aset state (+ 2 i) (logxor (aref state (+ 2 i)) (car (cdr keysA))))
(aset state (+ 3 i) (logxor (aref state (+ 3 i)) (cdr (cdr keysA))))
(setq keys (cdr keys))
(setq i (- i 4)))))
;;;; AES Cipher
;;;###autoload
(defun aes-Cipher (plain keys Nb &optional Nr)
"Perform a complete aes encryption of the unibyte string PLAIN.
Return a new string containing the encrypted string PLAIN.
Use KEYS as the expanded key as defined in `aes-SubShiftMixKeys'.
NB is the number of 32-bit words in PLAIN. NR is the number of rounds.
The length of KEYS is (1 + NR) * NB."
;; For a description of the AES cipher see [1, Ch 4.4] or [2, Ch 5.1]
(let* ((state (make-string (lsh Nb 2) 0))
(copy (make-string (lsh Nb 2) 0))
(r 1))
(unless Nr (setq Nr (+ (max Nb (- (/ (length keys) Nb) 7)) 6)))
(store-substring state 0 plain)
(aes-AddRoundKey state keys)
(while (< r Nr)
(aes-SubShiftMixKeys
state (store-substring copy 0 state) (setq keys (nthcdr Nb keys)))
(setq r (1+ r)))
(aes-SubBytes state)
(aes-ShiftRows state)
(aes-AddRoundKey state (nthcdr Nb keys))
state))
;;;###autoload
(defun aes-InvCipher (cipher keys Nb &optional Nr)
"Perform a complete aes decryption of the unibyte string CIPHER.
Return a new string containing the decrypted string CIPHER.
Use KEYS as the expanded key as defined in `aes-InvSubShiftMixKeys'.
NB is the number of 32-bit words in CIPHER. NR is the number of rounds.
The length of KEYS is (1 + NR) * NB."
;; For a description of the inverted AES cipher see [1, Ch 5.3] or [2, Ch 5.3]
(let* ((state (make-string (lsh Nb 2) 0))
(copy (make-string (lsh Nb 2) 0))
(r (or Nr (+ (max Nb (- (/ (length keys) Nb) 7)) 6))))
(store-substring state 0 cipher)
(aes-InvAddRoundKey state keys)
(aes-InvShiftRows state)
(aes-InvSubBytes state)
(while (< 1 r)
(aes-InvSubShiftMixKeys
state (store-substring copy 0 state) (setq keys (nthcdr Nb keys)))
(setq r (1- r)))
(aes-InvAddRoundKey state (nthcdr Nb keys))
state))
;;;; Cipher-Block Chaining
(defun aes-cbc-encrypt (plain iv keys Nb &optional padding)
"Encrypt the string PLAIN by the cbc method using aes for encryption.
Return a new unibyte string containing the result and dont change PLAIN.
Use the unibyte string IV as initialization vector and KEYS as the complete key
expansion as defined in `aes-SubShiftMixKeys'.
The length of IV must be the blocksize NB * 4.
PADDING specifies the used padding format according to the
specification in `aes-pad'"
;; For a description of the CBC mode see [4]
(let* ((Nb4 (lsh Nb 2))
(res (aes-pad plain Nb4 padding))
(len (length res))
(p 0))
(while (< p len)
(store-substring
res p
(setq iv (aes-Cipher (aes--xor iv (substring res p (setq p (+ p Nb4))))
keys Nb))))
res))
(defun aes-cbc-decrypt (c iv keys Nb)
"Decrypt the string C by the cbc method using aes for decryption.
Return a new unibyte string containing the result and dont change C.
Use the unibyte string IV as initialization vector and KEYS as the complete key
expansion as defined in `aes-InvSubShiftMixKeys'.
The length of IV must be the blocksize NB * 4.
The length of the unibyte strings C and the result are identical and a
multiple of the blocksize.
In contrast to `aes-cbc-encrypt' this function does not handle
padding removal."
;; For a description of the CBC mode see [4]
(let* ((Nb4 (lsh Nb 2))
(len (length c))
(res (make-string len 0))
(p 0))
(while (< p len)
(store-substring
res p
(aes--xor iv (aes-InvCipher (setq iv (substring c p (setq p (+ p Nb4))))
keys Nb))))
res))
;;;; Offset Codebook Mode 2.0
(defun aes--ocb-double-de (x)
"Calculate X multiplicated by 2.
The calculation is done in a bit field according to the length of X.
This is done destructively in the unibyte string X.
The length of X is 16, 24, 32, 40, 48, 56 or 64 bytes.
The return value is the result."
;; For a description of the multiplication see [5, Ch 2]
(let* ((len (length x))
(len1 (and (or (member len '(16 24 32 40 48 56 64))
(error "%s \"%s\" is not allowed"
"The specified blocksize of string" x))
(- len 2)))
(c (* (aref [135 135 1061 27 4107 2115 293] (- (lsh len -3) 2))
(lsh (aref x 0) -7)))
(c1 (logand (lsh c -8) #xff))
(i -1))
(setq c (logand c #xff))
(while (< i len1)
(aset x (setq i (1+ i)) (logand #xff (logxor (lsh (aref x i) 1)
(lsh (aref x (+ i 1)) -7)))))
(aset x i (logxor (aref x i) c1))
(aset x (1- len) (logxor (logand #xff (lsh (aref x (1- len)) 1)) c)))
x)
(defun aes--ocb-triple-de (x)
"Return X multiplicated by 3.
The calculation is done in a bit field according to the length of X.
X and the return value area unibyte strings of arbitrary length.
This is done destructively in X.
Return X."
;; For a description of the multiplication see [5, Ch 2]
(aes--xor-de x (aes--ocb-double-de (copy-sequence x))))
(defun aes-num2str (x n)
"Calculate the N-byte representation of the number X.
Return a unibyte string of length N containing the representation of X,
where the most significant byte is at position 0."
(let ((res (make-string n 0))
(offset n))
(while (< 0 x)
(aset res (setq offset (1- offset)) (logand x #xff))
(setq x (lsh x -8)))
res))
(defun aes-ocb-pmac (header keys Nb)
"Calculate the pmac of HEADER using aes encryption.
KEYS is the expanded key as defined in `aes-KeyExpansion'.
NB * 4 denotes the blocksize.
Return the pmac of the unibyte string HEADER of arbitrary length as unibyte
string of blocksize length."
;; For a description of the pmac see [5, Ch 4]
(let* ((l (length header))
(bs (lsh Nb 2))
(whole-blocks (/ l bs))
(total-blocks (max 1 (+ whole-blocks (if (= 0 (% l bs)) 0 1))))
(border (* whole-blocks total-blocks))
(b (if (= whole-blocks total-blocks) bs (% l bs)))
(D (aes--ocb-triple-de
(aes--ocb-triple-de (aes-Cipher (make-string bs 0) keys Nb))))
(checksum (make-string bs 0))
(p 0))
(while (< p border)
(aes--ocb-double-de D)
(aes--xor-de
checksum
(aes-Cipher (aes--xor D (substring header p (setq p (+ p bs)))) keys Nb)))
(aes--ocb-triple-de (aes--ocb-double-de D))
(if (= b bs)
(aes--xor-de checksum (substring header (* bs (1- total-blocks))))
(aes--ocb-triple-de D)
(aes--xor-de checksum (concat (substring header (* bs (1- total-blocks)))
(eval-when-compile (char-to-string #x80))
(make-string (- bs b 1) 0))))
(aes-Cipher (aes--xor D checksum) keys Nb)))
(defun aes-ocb-encrypt (input header iv keys Nb)
"Encrypt the string INPUT using OCB.
Additionally generate a pmac of HEADER and INPUT.
HEADER and INPUT are unibyte strings of arbitrary length.
IV is a unibyte string of length blocksize containing the initialization vector.
KEYS contains the expanded key as described in `aes-KeyExpansion'.
NB describes the blocksize which is NB * 4 bytes.
Return a cons cell (C . P), where C is a unibyte string containing the
ciphertext and the unibyte string P of blocksize length is the hash value."
;; For a description of the ocb encryption see [5, Ch 5]
(let* ((D (aes-Cipher iv keys Nb))
(C (make-string (length input) 0))
P
(checksum (make-string (lsh Nb 2) 0))
(l (length input))
(blocksize (lsh Nb 2))
(whole-blocks (/ l blocksize))
(total-blocks (max 1 (+ whole-blocks (if (= 0 (% l blocksize)) 0 1))))
(border (* blocksize (1- total-blocks)))
(b (if (= whole-blocks total-blocks) blocksize (% l blocksize)))
(pointer 0))
(while (< pointer border)
(aes--ocb-double-de D)
(aes--xor-de checksum (substring input pointer
(+ pointer blocksize)))
(store-substring C pointer
(aes--xor D (aes-Cipher
(aes--xor D (substring
input pointer
(setq pointer
(+ pointer blocksize))))
keys Nb))))
(aes--ocb-double-de D)
(let ((pad (aes-Cipher (aes--xor D (aes-num2str (lsh b 3) blocksize))
keys
Nb))
(Mm (substring input pointer)))
(store-substring C pointer (aes--xor Mm (substring pad 0 b)))
(aes--xor-de checksum (concat Mm (substring pad b))))
(setq P (aes-Cipher (aes--xor checksum (aes--ocb-triple-de D)) keys Nb))
(if (< 0 (length header)) (aes--xor-de P (aes-ocb-pmac header keys Nb)))
(cons C P)))
(defun aes-ocb-decrypt (input header tag iv keys &optional Nb)
"Decrypt the string INPUT using OCB.
Additionally verify the pmac hash value of HEADER and INPUT with TAG.
HEADER and INPUT are unibyte strings of arbitrary length.
TAG is a unibyte string of blocksize length, that is compared to
the hashvalue generated during decryption.
IV is a unibyte string of length blocksize containing the initialization vector.
KEYS contains the expanded key as described in `aes-KeyExpansion'.
NB describes the blocksize which is NB * 4 bytes. A default value of 4 is used.
Return the plaintext as unibte string, if the hashvalue fits.
Otherwise return nil."
;; For a description of the ocb decryption see [5, Ch 6]
(unless Nb (setq Nb 4))
(let* ((D (aes-Cipher iv keys Nb))
(M (make-string (length input) 0))
(l (length input))
(blocksize (lsh Nb 2))
(checksum (make-string blocksize 0))
(whole-blocks (/ l blocksize))
(total-blocks (max 1 (+ whole-blocks (if (= 0 (% l blocksize)) 0 1))))
(border (* blocksize (1- total-blocks)))
(b (if (= whole-blocks total-blocks) blocksize (% l blocksize)))
(pointer 0)
Mi)
(setq keys (nreverse keys))
(while (< pointer border)
(aes--ocb-double-de D)
(store-substring
M pointer
(setq Mi (aes--xor D (aes-InvCipher
(aes--xor D (substring input pointer
(setq pointer
(+ pointer blocksize))))
keys Nb))))
(aes--xor-de checksum Mi))
(setq keys (nreverse keys))
(aes--ocb-double-de D)
(let* ((pad (aes-Cipher (aes--xor D (aes-num2str (* 8 b) blocksize))
keys Nb))
(Mm (aes--xor (substring
input (* blocksize (- total-blocks 1)))
(substring pad 0 b))))
(store-substring M pointer Mm)
(aes--xor-de checksum
(concat Mm (substring pad b))))
(aes--ocb-triple-de D)
(let ((T (aes-Cipher (aes--xor D checksum) keys Nb)))
(if (< 0 (length header))
(aes--xor-de T (aes-ocb-pmac header keys Nb)))
(if (equal tag (substring T 0 (length tag)))
M
nil))))
;;;; Password handling and key generation from passwords
(defgroup aes nil
"Advanced Encryption Standard implementation"
:group 'applications)
(defcustom aes-always-ask-for-passwords t
"Always ask for passwords, if non-nil.
If this variable is set to a non-nil value, then everytime a buffer or string is
encrypted/decrypted, the according password is asked from the user and no
passwords are stored in `aes--plaintext-passwords'.
Set this to nil, if you are risky."
:type 'boolean
:group 'aes)
(defcustom aes-enable-plaintext-password-storage nil
"Store passwords in emacs-memory in plaintext, if non-nil.
Enabling this feature allows everyone to read the passwords in plaintext by
accessing the variable `aes--plaintext-passwords'.
If changing the value from non-nil to nil, then the passwords stored in
`aes--plaintext-passwords' are not deleted automatically.
Set this to a non-nil value, if you are risky."
:type 'boolean
:group 'aes)
(defvar aes--plaintext-passwords ()
"Association list of plaintext passwords.
Warning: passwords are stored in plaintext and can be read by anyone with
access to the current Emacs session.
Every entry of this list consists of (A . B), where A and B are strings.
With A the password B can be refered to.")
(defun aes-clear-plaintext-keys ()
"Remove all stored plaintext passwords."
(interactive)
(setq aes--plaintext-passwords nil))
(defvar aes-idle-timer-value nil
"Reference to idle timer.
If this is non-nil, then it referes to an idle-timer function, which removes
all stored plaintext passwords.")
(defun aes-idle-clear-plaintext-keys ()
"Remove all stored plaintext passwords.
This function is called, when idle-password-clearing is activated."
(setq aes--plaintext-passwords nil)
(setq aes-idle-timer-value nil)
(message "AES Passwords cleared."))
(defcustom aes-delete-passwords-after-idle 1
"Delete the stored plaintext passwords after the given time.
This is disabled, if the value is 0. Otherwise the number is
interpreted as seconds for Emacs to be idle before the deletion
happens."
:type 'integer
:group 'aes)
(defvar aes-path-passwd-hook ()
"Hook for grouping filenames.
Functions, appended to this hook, get one argument: a path of a file to be
en- or decrypted.
According to the path the function should return a string, providing
information about the group of files, or nil otherwise.
The returned string should begin with two whitespaces, such that it is not
mixed up with buffer names.
Using this method it is possible to store the same password, used for multiple
files.")
(defun aes-exec-passws-hooks (path)
"Run the functions in the hook `aes-path-passwd-hook'.
PATH is a file system path, that is passed as argument to each function.
Return a string resulting from the first hook that returns a non-nil value.
Return nil, if every function in the hook returns nil."
(run-hook-with-args-until-success 'aes-path-passwd-hook path))
(defun aes-key-from-passwd (usage type-or-file Nk)
"Return a key, generated from a password.
This is done by encrypting the password by a key generated from the password
using a constant initialization vector.
USAGE must be a string either \"encryption\" or \"decryption\" denoting the
usage of the password.
TYPE-OR-FILE is a string describing what the password is used for. If the key
is used for a string, TYPE-OR-FILE should be \"string\". If the key is used for
a group of files (by using `aes-path-passwd-hook'), it should be a string
denoting the group. Otherwise the key is used for a file not in a group and in
this case it should be the filename.
If `aes-use-plaintext-keys' is nil and `aes-disable-global-plaintext-keys' is
non-nil, then use `aes--plaintext-passwords' for storing and reading passwords.
Passwords are only stored there, if TYPE-OR-FILE denotes a group of files.
Query the password from the user if it is not available via
`aes--plaintext-passwords'. This implementation does not test the quality of the
password.
Return the key generated from the password. The key is a string of
length NK * 4."
(if (not (member usage '("encryption" "decryption")))
(error "Wrong argument in aes-key-from-passwd: \"%S\"" usage))
(let* (passwd (p ""))
(if (and (not aes-always-ask-for-passwords)
aes-enable-plaintext-password-storage
(assoc type-or-file aes--plaintext-passwords))
(setq passwd (cdr (assoc type-or-file aes--plaintext-passwords)))
(while (equal p "")
(setq p (encode-coding-string
(read-passwd
(concat usage " Password for " type-or-file ": ")
(equal "encryption" usage))
'raw-text)))
(if (and (not aes-always-ask-for-passwords)
aes-enable-plaintext-password-storage
(not (get-buffer type-or-file))
(not (equal "string" type-or-file)))
(progn
;; store the new password
(setq aes--plaintext-passwords
(cons (cons type-or-file p) aes--plaintext-passwords))
;; reset idle timer
(if aes-idle-timer-value
(progn (cancel-timer aes-idle-timer-value)
(setq aes-idle-timer-value nil)))
;; set new idle timer
(if (< 0 aes-delete-passwords-after-idle)
(setq aes-idle-timer-value
(run-with-idle-timer
aes-delete-passwords-after-idle
nil
'aes-idle-clear-plaintext-keys)))))
(setq passwd p))
(aes-password-to-key passwd Nk)))
(defun aes-password-to-key (password Nk)
"Convert a string PASSWORD to a key.
The key is a string of length NK * 4."
; take care that the password is unibyte.
(if (multibyte-string-p password)
(setq password (encode-coding-string password 'raw-text)))
(let ((passwd (aes-zero-pad password (lsh Nk 2))))
(substring
(aes-cbc-encrypt passwd
(make-string (lsh Nk 2) 0)
(aes-KeyExpansion
(aes--str-to-b (substring passwd 0 (lsh Nk 2))) Nk)
Nk)
(- (lsh Nk 2)))))
(defcustom aes-password-char-groups
'((?a t "abcdefghjkmnopqrstuvwxyz") ; downcase letters, i and l excluded
(?A t "ABCDEFGHJKLMNPQRSTUVWXYZ") ; upcase letters, I and O excluded
(?5 t "23456789") ; numbers, 0 and 1 excluded
(?0 t "0OilI1") ; characters difficult to distinguish
(?. nil ",.!?;:_()[]{}<>") ; punctuation and brackets
(?+ nil "-+*/=") ; calculation
(?% nil "|^~#$%&'")) ; others
"Groups of characters for password generation.
The first entry in each list is a character, which can be used in the
argument TYP of `aes-generate-password' to refer to this password
group. The second entry denotes the default value of the application
of this character group; if it is non-nil, the this group is activated and used.
The third entry denotes the characters in this group used in password
generation."
:group 'aes
:type '(repeat (list character (choice (const :tag "active" t)
(const :tag "inactive" nil))
string)))
(defun aes--fisher-yates-shuffle-array (s)
"Shuffle array S randomly.
This is done destructively in S. The result is returned."
(let ((i (length s))
j)
(while (< 1 i)
(aset s (setq j (random i))
(prog1 (aref s (setq i (1- i))) (aset s i (aref s j))))))
s)
(defcustom aes-user-interaction-entropy t
"Query User for Entropy if non-nil.
If the value is non-nil, then the user must use mouse or key input to feed the
random number generator.
Otherwise use Emacs internal pseudo random number generator."
:type 'boolean
:group 'aes)
(defcustom aes-entropy-of-mousemovement 4
"The bit-entropy of a mouse movement event.
Set this to a higher value, to generate more random numbers from
less user input entropy."
:type 'integer
:group 'aes)
(defcustom aes-entropy-of-keyinput 2