├── src ├── .erlang ├── lgc1.png ├── oaep.png ├── crypto1.png ├── test.erl ├── sfs.erl ├── Makefile ├── shamir.hrl ├── challenge.erl ├── demo.erl ├── stream.erl ├── crypto.tex ├── decode_rsa_keys.erl ├── color_erlang.erl ├── symmetric.erl ├── lin.erl ├── myshell.erl ├── ez_crypt_aes.erl ├── miller_rabin.erl ├── shamir.erl ├── tls.erl ├── ez_crypt_test.erl ├── plot_random.erl ├── oaep_byte_padding.erl ├── ez_crypt_rsa.erl ├── ez_crypt_primes.erl ├── ez_crypt.erl ├── ez_crypt_math.erl ├── ez_crypt_miller_rabin.erl ├── segmenting.erl ├── convert_org_to_latex.erl ├── shell.erl └── crypto_latex.org ├── crypto_tutorial.pdf ├── .gitignore ├── LICENSE └── README.md /src/.erlang: -------------------------------------------------------------------------------- 1 | io:format("local .erlang loaded~n"). 2 | -------------------------------------------------------------------------------- /src/lgc1.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/joearms/crypto_tutorial/HEAD/src/lgc1.png -------------------------------------------------------------------------------- /src/oaep.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/joearms/crypto_tutorial/HEAD/src/oaep.png -------------------------------------------------------------------------------- /src/crypto1.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/joearms/crypto_tutorial/HEAD/src/crypto1.png -------------------------------------------------------------------------------- /crypto_tutorial.pdf: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/joearms/crypto_tutorial/HEAD/crypto_tutorial.pdf -------------------------------------------------------------------------------- /.gitignore: -------------------------------------------------------------------------------- 1 | *~ 2 | *.tmp 3 | \#* 4 | *.beam 5 | *.toc 6 | *.out 7 | *.log 8 | *.aux 9 | src/expand.tex 10 | not_in_git/* 11 | src/crypto.pdf -------------------------------------------------------------------------------- /src/test.erl: -------------------------------------------------------------------------------- 1 | -module(test). 2 | -compile(export_all). 3 | 4 | test() -> 5 | Fac = fun Fac(0) -> 1; 6 | Fac(N) -> N*Fac(N-1) 7 | end, 8 | Fac(10). 9 | 10 | 11 | -------------------------------------------------------------------------------- /src/sfs.erl: -------------------------------------------------------------------------------- 1 | -module(sfs). 2 | -compile(export_all). 3 | 4 | client(Server, SHA) -> 5 | Server ! {self(), public_key_request}, 6 | receive 7 | {Server, {public_key_response, PubKey}} -> 8 | case sha1(PubKey) of 9 | SHA -> 10 | connect(Server, PubKey); 11 | _ -> 12 | exit(badKey) 13 | end 14 | end. 15 | 16 | server() -> 17 | receive 18 | {Client, public_key_request} -> 19 | Client ! {self(), 20 | {public_key_response, 21 | public_key()}}, 22 | wait_connect(Client) 23 | end. 24 | 25 | public_key() -> not_yet_implemented. 26 | wait_connect(_) -> not_yet_implemented. 27 | connect(_,_) -> not_yet_implemented. 28 | sha1(_) -> not_yet_implemented. 29 | -------------------------------------------------------------------------------- /src/Makefile: -------------------------------------------------------------------------------- 1 | .SUFFIXES: .erl .beam .yrl 2 | 3 | MODS := $(wildcard *.erl) 4 | ERLC = @echo " ERLC\t$<"; /usr/local/bin/erlc 5 | PDFLATEX = @echo " PDFLATEX\t$<"; /usr/texbin/pdflatex 6 | 7 | %.beam: %.erl 8 | $(ERLC) -W $< 9 | 10 | all: beams crypto.pdf 11 | 12 | crypto.pdf: expand.tex 13 | latexmk -pdf crypto.tex 14 | 15 | publish: 16 | make crypto.pdf 17 | cp crypto.pdf ../crypto_tutorial.pdf 18 | 19 | expand.tex: crypto_latex.org 20 | erl -oldshell -s convert_org_to_latex test -s init stop 21 | 22 | beams: ${MODS:%.erl=%.beam} 23 | 24 | clean: 25 | rm -rf lecture_*.tex *.aux *.log *.toc *.out 26 | rm -rf erl_crash.dump *.beam expand.tex 27 | rm -rf expand.tex crypto.pdf 28 | 29 | 30 | very_clean: 31 | rm -rf *.pdf 32 | 33 | 34 | 35 | 36 | -------------------------------------------------------------------------------- /src/shamir.hrl: -------------------------------------------------------------------------------- 1 | %% From: https://github.com/rnewson/shamir/tree/master/src/shamir.erl 2 | 3 | %% Copyright 2011 Robert Newson 4 | %% 5 | %% Licensed under the Apache License, Version 2.0 (the "License"); 6 | %% you may not use this file except in compliance with the License. 7 | %% You may obtain a copy of the License at 8 | %% 9 | %% http://www.apache.org/licenses/LICENSE-2.0 10 | %% 11 | %% Unless required by applicable law or agreed to in writing, software 12 | %% distributed under the License is distributed on an "AS IS" BASIS, 13 | %% WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 14 | %% See the License for the specific language governing permissions and 15 | %% limitations under the License. 16 | 17 | -record(share, { 18 | threshold, 19 | x, 20 | y 21 | }). 22 | -------------------------------------------------------------------------------- /src/challenge.erl: -------------------------------------------------------------------------------- 1 | -module(challenge). 2 | -compile(export_all). 3 | 4 | test() -> 5 | Server = spawn(?MODULE, server, []), 6 | _Client = spawn(?MODULE, client, [Server, "joe", self()]), 7 | receive X -> X end. 8 | 9 | client(Server, Who, Parent) -> 10 | Server ! {self(), {login,Who}}, 11 | receive 12 | {Server, {challenge, X}} -> 13 | Server ! {self(), {response, 14 | erlang:md5(X ++ secret(Who))}}, 15 | receive 16 | {Server, Response} -> 17 | Parent ! Response 18 | end 19 | end. 20 | 21 | server() -> 22 | receive 23 | {Pid, {login,Who}} -> 24 | Ran = binary_to_list(crypto:strong_rand_bytes(10)), 25 | Pid ! {self(), {challenge, Ran}}, 26 | receive 27 | {Pid, {response, R}} -> 28 | case erlang:md5(Ran ++ secret(Who)) of 29 | R -> 30 | Pid ! {self(), login_ok}; 31 | _ -> 32 | Pid ! {self(), login_error} 33 | end 34 | end 35 | end. 36 | 37 | secret("joe") -> 38 | "zzbingo". 39 | -------------------------------------------------------------------------------- /LICENSE: -------------------------------------------------------------------------------- 1 | Copyright (c) 2015 Joe Armstrong 2 | 3 | Permission is hereby granted, free of charge, to any person obtaining a copy 4 | of this software and associated documentation files (the "Software"), to deal 5 | in the Software without restriction, including without limitation the rights 6 | to use, copy, modify, merge, publish, distribute, sublicense, and/or sell 7 | copies of the Software, and to permit persons to whom the Software is 8 | furnished to do so, subject to the following conditions: 9 | 10 | The above copyright notice and this permission notice shall be included in 11 | all copies or substantial portions of the Software. 12 | 13 | THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 14 | IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 15 | FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 16 | AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 17 | LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, 18 | OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN 19 | THE SOFTWARE. 20 | -------------------------------------------------------------------------------- /src/demo.erl: -------------------------------------------------------------------------------- 1 | -module(demo). 2 | 3 | -compile(export_all). 4 | 5 | %% P = demo:make_prime(50). 6 | %% Q = demo:make_prime(50). 7 | %% {E,D,N} = demo:make_key(P, Q) 8 | %% M = 12345. 9 | %% Enc = lin:pow(M,E,N). 10 | %% lin:pow(Enc,D,N). 11 | 12 | %% check a prime from the audience 13 | %% is_prime 14 | %% demo:is_prime(9746347772161). (carmichael number) 15 | 16 | 17 | 18 | prime_from(N) -> 19 | N1 = case is_even(N) of 20 | true -> gen_prime(N+1); 21 | false -> gen_prime(N) 22 | end, 23 | io:format("\n"), 24 | N1. 25 | 26 | gen_prime(N) -> 27 | case miller_rabin:is_probably_prime(N) of 28 | true -> N; 29 | false -> 30 | io:format("."), 31 | gen_prime(N+1) 32 | end. 33 | 34 | is_even(K) -> 35 | K band 1 == 0. 36 | 37 | str2int(Str) -> lin:str2int(Str). 38 | 39 | int2str(N) -> lin:int2str(N). 40 | 41 | is_prime(P) -> 42 | case miller_rabin:is_probably_prime(P) of 43 | true -> probably; 44 | false-> false 45 | end. 46 | 47 | make_prime(N) -> 48 | %% N is in bits 49 | N1 = N * 3.3219, 50 | miller_rabin:make_prime(N1). 51 | 52 | make_key(P, Q) -> 53 | N = P * Q, 54 | Phi = (P-1) * (Q-1), 55 | E = 65537, 56 | D = lin:inv(E, Phi), 57 | {E,D,N}. 58 | -------------------------------------------------------------------------------- /src/stream.erl: -------------------------------------------------------------------------------- 1 | -module(stream). 2 | -compile(export_all). 3 | 4 | %% START:test 5 | test() -> 6 | Password = <<"secret">>, 7 | Server = spawn(?MODULE, server, [Password, self()]), 8 | Client = spawn(?MODULE, client, [Password, Server]), 9 | Client ! {send_server, <<"hello">>}, 10 | Client ! {send_server, <<" world">>}, 11 | Client ! stop, 12 | Got = receive X -> X end, 13 | true = (Got == <<"hello world">>), 14 | hooray. 15 | %% END:test 16 | 17 | %% START:client 18 | client(Password, Server) -> 19 | S0 = crypto:stream_init(rc4, Password), 20 | client_loop(S0, Server). 21 | 22 | client_loop(S0, Server) -> 23 | receive 24 | {send_server, Bin} -> 25 | {S1, B1}= crypto:stream_encrypt(S0, Bin), 26 | Server ! {data, B1}, 27 | client_loop(S1, Server); 28 | stop-> 29 | Server ! stop 30 | end. 31 | %% END:client 32 | 33 | %% START:server 34 | server(Password, Parent) -> 35 | S0 = crypto:stream_init(rc4, Password), 36 | Parent ! server_loop(S0, <<>>). 37 | 38 | server_loop(S0, B) -> 39 | receive 40 | {data, Bin} -> 41 | {S1, B1} = crypto:stream_decrypt(S0, Bin), 42 | server_loop(S1, <>); 43 | stop -> 44 | B 45 | end. 46 | %% END:server 47 | 48 | -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | Building the system 2 | 3 | If you want to rebuild the PDF you'll need to install 4 | TexLive and make sure that pdflatex works. 5 | 6 | I've written this in org mode in emacs with 7 | a little Erlang program to convert org mode to LaTeX. 8 | 9 | The conventions I've used are as follows: 10 | 11 | + Org mode * means a new chapter 12 | + Org mode ** means a new section 13 | + Preformed starts with 4 blanks 14 | + Line starting !! and > are expanded by an erlang program 15 | + Most sections are just LaTeX 16 | 17 | I don't use org mode ``export to LaTeX'' but do the translation myself. 18 | 19 | The resulting PDF should be readable. The filenames and 20 | code is a bit of a mess and be be refactored. 21 | 22 | To build 23 | 24 | > cd src 25 | > make 26 | 27 | When you're happy do 28 | 29 | > make publish 30 | 31 | Check ./crypto_tutorial.pdf 32 | 33 | And *then* push changes in ./src to me 34 | 35 | make_publish creates a top level PDF This is ``bad practice'' since we 36 | should only commit source code and not generated output to the 37 | repository. BUT it's there so people can read the result without 38 | having to build everything for themselves. 39 | 40 | If you suggest a change don't commit crypto_tutorial.pdf 41 | to the archive. 42 | -------------------------------------------------------------------------------- /src/crypto.tex: -------------------------------------------------------------------------------- 1 | \documentclass[article, oneside, 11pt]{memoir} 2 | 3 | \usepackage[utf8]{inputenc} 4 | \usepackage[T1]{fontenc} 5 | 6 | \usepackage{url} 7 | \usepackage{blindtext} 8 | \usepackage{color} 9 | \usepackage{graphicx} 10 | 11 | %% so I can use sout 12 | \usepackage{ulem} 13 | 14 | %% framed boxes for code 15 | \usepackage{fancyvrb} 16 | 17 | %% hyperlinks in contents 18 | \usepackage{hyperref} 19 | \hypersetup{ 20 | colorlinks=true, 21 | linktoc=all, 22 | citecolor=blue, 23 | filecolor=black, 24 | linkcolor=blue, 25 | urlcolor=blue 26 | } 27 | 28 | \usepackage{xcolor} 29 | \usepackage{newverbs} 30 | 31 | \newverbcommand{\cverb}{\color{red}}{} 32 | 33 | \newverbcommand{\bverb} 34 | {\begin{lrbox}{\verbbox}} 35 | {\end{lrbox}\colorbox{gray!30}{\box\verbbox}} 36 | 37 | \newcommand\erlFileName[1]{\textcolor[rgb]{0,0,1}{\small{\textbf{#1}}}} 38 | 39 | \reparticle 40 | 41 | \chapterstyle{hangnum} 42 | \hangsecnum 43 | 44 | \title{{\Huge\ Cryptography Tutorial}} 45 | \author{Joe Armstrong} 46 | \date{\copyright{}2015 Joe Armstrong\\ 47 | All Rights Reserved} 48 | \setlength\parindent{0pt} 49 | \setlength{\parskip}{11pt} 50 | 51 | \begin{document} 52 | \frontmatter 53 | \maketitle 54 | \tableofcontents 55 | \mainmatter 56 | \include{expand} 57 | \end{document} 58 | -------------------------------------------------------------------------------- /src/decode_rsa_keys.erl: -------------------------------------------------------------------------------- 1 | -module(decode_rsa_keys). 2 | -compile(export_all). 3 | 4 | -include_lib("public_key/include/public_key.hrl"). 5 | 6 | %% ssh-keygen -t rsa -b 1024 -C "joe@somewehere.com" 7 | %% Generating public/private rsa key pair. 8 | %% Enter file in which to save the key (/Users/joearmstrong/.ssh/id_rsa): joe_rsa 9 | %% Enter passphrase (empty for no passphrase): 10 | %% Enter same passphrase again: 11 | %% Your identification has been saved in joe_rsa. 12 | %% Your public key has been saved in joe_rsa.pub. 13 | 14 | %% START:all 15 | test() -> 16 | {E,D,N} = Key = read_pri_certificate(), 17 | {E,N} = read_pub(), 18 | io:format("Key:~p~n",[Key]), 19 | false = ez_crypt_primes:is_prime(N), 20 | check(12345, {E,D,N}), 21 | check(54321, {D,E,N}), 22 | 1024 = ez_crypt_math:bsize(N), %% modulus size 23 | wow. 24 | 25 | check(Msg, {E,D,N}) -> 26 | Crypt = ez_crypt_math:mod_pow(Msg, E, N), 27 | Decode = ez_crypt_math:mod_pow(Crypt, D, N), 28 | Msg = Decode, 29 | ok. 30 | 31 | read_pri_certificate() -> 32 | {ok, B} = file:read_file("joe_rsa"), 33 | [DSA] = public_key:pem_decode(B), 34 | X = public_key:pem_entry_decode(DSA), 35 | #'RSAPrivateKey'{modulus = N, 36 | publicExponent = E, 37 | privateExponent=D} = X, 38 | {E,D,N}. 39 | 40 | read_pub() -> 41 | {ok, B} = file:read_file("joe_rsa.pub"), 42 | [{Pub,_}] = public_key:ssh_decode(B,public_key), 43 | #'RSAPublicKey'{modulus = N, publicExponent = E} = Pub, 44 | {E, N}. 45 | %% END:all 46 | 47 | 48 | 49 | 50 | -------------------------------------------------------------------------------- /src/color_erlang.erl: -------------------------------------------------------------------------------- 1 | -module(color_erlang). 2 | -compile(export_all). 3 | 4 | string(S) -> 5 | {ok, Toks,_} = erlide_scan:string_ws(S), 6 | L = [fix(xform(I)) || I <- Toks], 7 | %% io:format("L=~p~n",[L]), 8 | L1 = [to_latex(I) || I <- L], 9 | %% io:format("L=~p~n",[lists:zip(L,L1)]), 10 | L1. 11 | 12 | xform({Tag,_,_,A}) -> {Tag, A}; 13 | xform({Tag,_,A}) -> {Tag, A}; 14 | xform({P,_Y}) -> 15 | case reserved_word(P) of 16 | true -> 17 | {keyword,P}; 18 | false -> 19 | {punc, P} 20 | end. 21 | 22 | fix({punc,dot}) -> {punc,"."}; 23 | fix({Tag,A}) when is_atom(A) -> {Tag, atom_to_list(A)}; 24 | fix(X) -> X. 25 | 26 | reserved_word('case') -> true; 27 | reserved_word('if') -> true; 28 | reserved_word('receive') -> true; 29 | reserved_word('end') -> true; 30 | reserved_word('try') -> true; 31 | reserved_word('catch') -> true; 32 | reserved_word(_) -> false. 33 | 34 | to_latex({keyword,X}) -> 35 | %% ["(*@{\\bf ",X,"}@*)"]; 36 | X; 37 | to_latex({ws,X}) -> X; 38 | to_latex({Tag,X}) -> 39 | ["(*@\\textcolor{",atom_to_list(Tag),"color}{",latex_quote(X),"}@*)"]. 40 | 41 | 42 | latex_quote("{" ++ T) -> "\\{" ++ latex_quote(T); 43 | latex_quote("}" ++ T) -> "\\}" ++ latex_quote(T); 44 | latex_quote("%" ++ T) -> "\\%" ++ latex_quote(T); 45 | latex_quote("_" ++ T) -> "\\_" ++ latex_quote(T); 46 | latex_quote("$" ++ T) -> "\\$" ++ latex_quote(T); 47 | latex_quote("#" ++ T) -> "\\#" ++ latex_quote(T); 48 | latex_quote("~" ++ T) -> "\\textasciitilde{}" ++ latex_quote(T); 49 | latex_quote("\\" ++ T) -> "$\\backslash$" ++ latex_quote(T); 50 | latex_quote([H|T]) -> [H|latex_quote(T)]; 51 | latex_quote([]) -> []. 52 | -------------------------------------------------------------------------------- /src/symmetric.erl: -------------------------------------------------------------------------------- 1 | -module(symmetric). 2 | 3 | -compile(export_all). 4 | 5 | %% https://en.wikipedia.org/wiki/Linear_congruential_generator 6 | 7 | %% 1> symmetric:encrypt_1("password", "hello joe"). 8 | %% [78,194,191,93,242,21,230,117,141] 9 | %% 2> symmetric:encrypt_1("password", "hello joe"). 10 | %% [78,194,191,93,242,21,230,117,141] 11 | 12 | %% 3> symmetric:encrypt_2("password","hello joe"). 13 | %% [53,39,155,160,85,223,148,236,31,87,240,18,5,186,151,237,33,169,206] 14 | %% 4> symmetric:encrypt_2("password","hello joe"). 15 | %% [152,15,217,156,37,174,97,136,47,207,188,187,23,77,203,209,156,185,61] 16 | 17 | test() -> 18 | Txt = "the quick brown fox jumps over the lazy dog", 19 | C0 = encrypt_0(Txt, pad()), 20 | Txt = encrypt_0(C0, pad()), 21 | C = encrypt_1("password", Txt), 22 | Txt = encrypt_1("password", C), 23 | C1 = encrypt_2("password", Txt), 24 | Txt = decrypt_2("password", C1). 25 | 26 | %% START:pad 27 | pad() -> 28 | "12ishiyr72TY873TGKY8HHAE7YT8YsadaHGFIYLIasdjasdgjasgd". 29 | 30 | encrypt_0([H|T], [H1|T1]) -> 31 | [H bxor H1 | encrypt_0(T, T1)]; 32 | encrypt_0(_, []) -> 33 | []. 34 | %% END:pad 35 | 36 | %% START:encrypt_1 37 | encrypt_1(Password, Str) -> 38 | X0 = password_to_int(Password), 39 | encrypt_1(X0, Str, []). 40 | 41 | password_to_int(Str) -> 42 | erlang:phash(Str, 4294967296). 43 | 44 | encrypt_1(X0, [H|T], L) -> 45 | X1 = lcg(X0), 46 | Ran = next_byte(X0), 47 | encrypt_1(X1, T, [H bxor Ran|L]); 48 | encrypt_1(_, [], L) -> 49 | lists:reverse(L). 50 | 51 | next_byte(N) -> 52 | (N bsr 4) band 255. 53 | 54 | lcg(X) -> 55 | A = 1664525, 56 | C = 1013904223, 57 | M = 4294967296, %% 2^32 58 | (X*A + C) rem M. 59 | %% END:encrypt_1 60 | 61 | encrypt_2(Password, Txt) -> 62 | Salt = random_session_key(), 63 | X0 = password_to_int(Salt ++ Password), 64 | encrypt_1(X0, Txt, lists:reverse(Salt)). 65 | 66 | decrypt_2(Password, Str) -> 67 | {Salt, Str1} = lists:split(10, Str), 68 | X0 = password_to_int(Salt ++ Password), 69 | encrypt_1(X0, Str1, []). 70 | 71 | random_session_key() -> 72 | binary_to_list(crypto:strong_rand_bytes(10)). 73 | 74 | 75 | 76 | 77 | -------------------------------------------------------------------------------- /src/lin.erl: -------------------------------------------------------------------------------- 1 | -module(lin). 2 | 3 | -doc([{author,'Joe Armstrong'}, 4 | {title,"Linear algebra utilities."}, 5 | {keywords, [linear,algebra]}, 6 | {date,981103}]). 7 | 8 | -export([pow/3, inv/2, solve/2, str2int/1, int2str/1, gcd/2]). 9 | 10 | %% pow(A, B, M) => (A^B) mod M 11 | %% examples pow(9726,3533,11413) = 5761 12 | %% pow(5971,6597,11413) = 9726 13 | 14 | pow(A, 1, M) -> 15 | A rem M; 16 | pow(A, 2, M) -> 17 | A*A rem M; 18 | pow(A, B, M) -> 19 | B1 = B div 2, 20 | B2 = B - B1, 21 | %% B2 = B1 or B1+1 22 | P = pow(A, B1, M), 23 | case B2 of 24 | B1 -> (P*P) rem M; 25 | _ -> (P*P*A) rem M 26 | end. 27 | 28 | %% inv(A, B) = C | no_inverse 29 | %% computes C such that 30 | %% A*C mod B = 1 31 | %% computes A^-1 mod B 32 | %% examples inv(28, 75) = 67. 33 | %% inv(3533, 11200) = 6597 34 | %% inv(6597, 11200) = 3533 35 | 36 | inv(A, B) -> 37 | case solve(A, B) of 38 | {X, _} -> 39 | if X < 0 -> X + B; 40 | true -> X 41 | end; 42 | _ -> 43 | no_inverse 44 | end. 45 | 46 | %% solve(A, B) => {X, Y} | insoluble 47 | %% solve the linear congruence 48 | %% A * X - B * Y = 1 49 | 50 | %% START:tag1 51 | solve(A, B) -> 52 | case catch s(A,B) of 53 | insoluble -> insoluble; 54 | {X, Y} -> 55 | case A * X - B * Y of 56 | 1 -> {X, Y}; 57 | _Other -> error 58 | end 59 | end. 60 | 61 | s(_, 0) -> throw(insoluble); 62 | s(_, 1) -> {0, -1}; 63 | s(_, -1) -> {0, 1}; 64 | s(A, B) -> 65 | K1 = A div B, 66 | K2 = A - K1*B, 67 | {Tmp, X} = s(B, -K2), 68 | {X, K1 * X - Tmp}. 69 | %% END:tag1 70 | 71 | 72 | %% converts a string to a base 256 integer 73 | %% converts a base 256 integer to a string 74 | 75 | %% START:tag2 76 | str2int(Str) -> str2int([$z|Str], 0). 77 | 78 | str2int([H|T], N) -> str2int(T, N*256+H); 79 | str2int([], N) -> N. 80 | 81 | int2str(N) -> 82 | "z" ++ S = int2str(N, []), 83 | S. 84 | 85 | int2str(N, L) when N =< 0 -> L; 86 | int2str(N, L) -> 87 | N1 = N div 256, 88 | H = N - N1 * 256, 89 | int2str(N1, [H|L]). 90 | %% END:tag2 91 | 92 | %% greatest common denominator 93 | 94 | gcd(A, B) when A < B -> gcd(B, A); 95 | gcd(A, 0) -> A; 96 | gcd(A, B) -> 97 | gcd(B, A rem B). 98 | -------------------------------------------------------------------------------- /src/myshell.erl: -------------------------------------------------------------------------------- 1 | -module(myshell). 2 | -compile(export_all). 3 | 4 | %% cmd(P, "XXX") -> {ok, "RRR"} | {error, "..."} 5 | 6 | test() -> 7 | LF = {eval, fun local/3}, 8 | P = start(LF), 9 | c(P, "X=1."), 10 | c(P, "Y=2."), 11 | c(P, "{X+Y,Y}."), 12 | c(P, "X=2."), 13 | c(P, "{X,Y,Y}."), 14 | stop(P), 15 | P1 = start(LF), 16 | c(P1, "Z=1."), 17 | c(P1, "{Z+Z,{a,Z}}."), 18 | c(P1, "a,b,c,d."), 19 | stop(P1). 20 | 21 | local(_M,_F,_A) -> 22 | exit(local). 23 | 24 | c(P, CmdStr) -> 25 | Out = cmd(P, CmdStr), 26 | io:format("> ~s~n~p~n",[CmdStr, Out]). 27 | 28 | start(LF) -> spawn(myshell, go, [LF]). 29 | 30 | stop(Pid) -> 31 | Ref = erlang:make_ref(), 32 | Pid ! {stop, self(), Ref}, 33 | receive 34 | {Ref, Ret} -> 35 | Ret 36 | end. 37 | 38 | 39 | go(LF) -> 40 | B = erl_eval:new_bindings(), 41 | loop(B, LF). 42 | 43 | loop(B0, LF) -> 44 | receive 45 | {stop, From, Ref} -> 46 | From ! {Ref, stopped}; 47 | {cmd, From, Ref, CmdStr} -> 48 | try 49 | {ok, Tokens, _} = erl_scan:string(CmdStr), 50 | %% many forms can be comma-separated 51 | {ok, Forms} = erl_parse:parse_exprs(Tokens), 52 | %% eval individually 53 | {value, Val, B1} = erl_eval:exprs(Forms, B0, LF), 54 | From ! {Ref, {ok, Val}}, 55 | loop(B1, LF) 56 | catch 57 | T:R -> 58 | S = erlang:get_stacktrace(), 59 | Str1 = report_exception(T, serious, {R, S}, 1), 60 | Str2 = list_to_binary(Str1), 61 | From ! {Ref, {error,Str2}}, 62 | loop(B0, LF) 63 | end 64 | end. 65 | 66 | cmd(Pid, InStr) -> 67 | Ref = erlang:make_ref(), 68 | Pid ! {cmd, self(), Ref, InStr}, 69 | receive 70 | {Ref, Ret} -> 71 | Ret 72 | end. 73 | 74 | report_exception(Class, Severity, {Reason,Stacktrace}, _RT0) -> 75 | Tag = shell:severity_tag(Severity), 76 | %% Tag = <<*>> <**>> <<"**">> 77 | I = iolist_size(Tag) + 1, 78 | RT = ets:new(a,[]), 79 | PF = fun(Term, I1) -> 80 | shell:pp(Term,I1,RT) 81 | end, 82 | SF = fun(M, _F, _A) -> (M =:= erl_eval) or (M =:= ?MODULE) end, 83 | Enc = shell:encoding(), 84 | Str = lib:format_exception(I, Class, Reason, Stacktrace, SF, PF, Enc), 85 | Str1 = [Tag,Str], 86 | lists:flatten(Str1). 87 | 88 | 89 | 90 | 91 | -------------------------------------------------------------------------------- /src/ez_crypt_aes.erl: -------------------------------------------------------------------------------- 1 | -module(ez_crypt_aes). 2 | 3 | %% Warning - this code is for illustration purposes only 4 | %% and has not been subject to a security review 5 | 6 | -export([test/0, encrypt/2, decrypt/2]). 7 | 8 | -spec encrypt(Password :: binary(), Plain :: binary()) -> 9 | Code :: binary(). 10 | 11 | -spec decrypt(Password::binary(), Code::binary()) -> 12 | Plain::binary(). 13 | 14 | test() -> 15 | test1(), 16 | test2(), 17 | test4(), 18 | ok. 19 | 20 | test1() -> 21 | Password = <<"hello">>, 22 | Plain = <<"this is plain text">>, 23 | Code = encrypt(Password, Plain), 24 | io:format("Code=~p ~p~n",[Code,size(Code)]), 25 | Plain = decrypt(Password, Code). 26 | 27 | test2() -> 28 | for(1,1000, 29 | fun(I) -> 30 | test3(I) 31 | end). 32 | 33 | test3(I) -> 34 | Password = rand_password(), 35 | Plain = crypto:rand_bytes(I), 36 | Code = encrypt(Password, Plain), 37 | Plain = decrypt(Password, Code). 38 | 39 | for(N,N,F) -> F(N); 40 | for(I, Max, F) -> F(I), for(I+1,Max,F). 41 | 42 | test4() -> 43 | Password = rand_password(), 44 | Plain = crypto:rand_bytes(1000000), 45 | {T, _} = timer:tc(?MODULE, encrypt, [Password, Plain]), 46 | io:format("1 MByte encrypted in ~w ms~n", [T div 1000]). 47 | 48 | rand_password() -> crypto:rand_bytes(10). 49 | 50 | decrypt(Password, <>) when is_binary(Password) -> 51 | P = erlang:md5(Password), 52 | Bin2 = crypto:block_decrypt(aes_cbc128, P,IV,Bin1), 53 | unpad(Bin2). 54 | 55 | unpad(B) -> 56 | Size = size(B), 57 | {_, B2} = split_binary(B, Size - 1), 58 | [Pad] = binary_to_list(B2), 59 | Len = case Pad of 60 | 0 -> 61 | %% the entire last block is padding 62 | Size - 16; 63 | _ -> 64 | Size - Pad 65 | end, 66 | {Bfinal, _} = split_binary(B, Len), 67 | Bfinal. 68 | 69 | encrypt(Password, Bin) when is_binary(Password),is_binary(Bin) -> 70 | P = erlang:md5(Password), 71 | IV = crypto:rand_bytes(16), 72 | %% or strong_rand_bytes ???? 73 | Bin1 = pad(Bin), 74 | %% io:format("~p~n",[{pass,P,size(P), 75 | %% iv,IV,size(IV),bin1,Bin1,size(Bin1)}]), 76 | Bin2 = crypto:block_encrypt(aes_cbc128, P,IV,Bin1), 77 | <>. 78 | 79 | pad(Bin) -> 80 | Extra = 16 - (size(Bin) rem 16), 81 | %% io:format("S:~p E:~p~n",[size(Bin),Extra]), 82 | pad(Extra, Bin). 83 | 84 | %% pad(K, Bin) -> Bin1 85 | %% K = 0..15 number of bytes to pad by 86 | 87 | pad(0, Bin) -> 88 | %% have to add 15 random bytes and then a zero 89 | B1 = crypto:rand_bytes(15), 90 | <>; 91 | pad(K, Bin) -> 92 | B1 = crypto:rand_bytes(K-1), 93 | <>. 94 | -------------------------------------------------------------------------------- /src/miller_rabin.erl: -------------------------------------------------------------------------------- 1 | -module(miller_rabin). 2 | 3 | -export([is_prime/1, power/2]). 4 | 5 | is_prime(1) -> false; 6 | is_prime(2) -> true; 7 | is_prime(3) -> true; 8 | is_prime(N) when N > 3, ((N rem 2) == 0) -> false; 9 | is_prime(N) when ((N rem 2) ==1), N < 341550071728321 -> 10 | is_mr_prime(N, proving_bases(N)); 11 | is_prime(N) when ((N rem 2) == 1) -> 12 | is_mr_prime(N, random_bases(N, 100)). 13 | 14 | 15 | proving_bases(N) when N < 1373653 -> 16 | [2, 3]; 17 | proving_bases(N) when N < 9080191 -> 18 | [31, 73]; 19 | proving_bases(N) when N < 25326001 -> 20 | [2, 3, 5]; 21 | proving_bases(N) when N < 3215031751 -> 22 | [2, 3, 5, 7]; 23 | proving_bases(N) when N < 4759123141 -> 24 | [2, 7, 61]; 25 | proving_bases(N) when N < 1122004669633 -> 26 | [2, 13, 23, 1662803]; 27 | proving_bases(N) when N < 2152302898747 -> 28 | [2, 3, 5, 7, 11]; 29 | proving_bases(N) when N < 3474749660383 -> 30 | [2, 3, 5, 7, 11, 13]; 31 | proving_bases(N) when N < 341550071728321 -> 32 | [2, 3, 5, 7, 11, 13, 17]. 33 | 34 | 35 | is_mr_prime(N, As) when N>2, N rem 2 == 1 -> 36 | {D, S} = find_ds(N), 37 | %% this is a test for compositeness; the two case patterns disprove 38 | %% compositeness. 39 | not lists:any(fun(A) -> 40 | case mr_series(N, A, D, S) of 41 | [1|_] -> false; % first elem of list = 1 42 | L -> not lists:member(N-1, L) % some elem of list = N-1 43 | end 44 | end, 45 | As). 46 | 47 | 48 | find_ds(N) -> 49 | find_ds(N-1, 0). 50 | 51 | 52 | find_ds(D, S) -> 53 | case D rem 2 == 0 of 54 | true -> 55 | find_ds(D div 2, S+1); 56 | false -> 57 | {D, S} 58 | end. 59 | 60 | 61 | mr_series(N, A, D, S) when N rem 2 == 1 -> 62 | Js = lists:seq(0, S), 63 | lists:map(fun(J) -> pow_mod(A, power(2, J)*D, N) end, Js). 64 | 65 | 66 | pow_mod(B, E, M) -> 67 | case E of 68 | 0 -> 1; 69 | _ -> case ((E rem 2) == 0) of 70 | true -> (power(pow_mod(B, (E div 2), M), 2)) rem M; 71 | false -> (B*pow_mod(B, E-1, M)) rem M 72 | end 73 | end. 74 | 75 | 76 | random_bases(N, K) -> 77 | [basis(N) || _ <- lists:seq(1, K)]. 78 | 79 | 80 | basis(N) when N>2 -> 81 | %% 1 + random:uniform(N-3). % random:uniform returns a single random number in range 1 -> N-3, to which is added 1, shifting the range to 2 -> N-2 82 | 83 | %% joe: want a random intere in 2 .. N - 2 84 | %% N = 10 2..8 2 9 85 | crypto:rand_uniform(2,N-1). 86 | 87 | power(B, E) -> 88 | power(B, E, 1). 89 | 90 | power(_, 0, Acc) -> 91 | Acc; 92 | power(B, E, Acc) -> 93 | power(B, E - 1, B * Acc). 94 | -------------------------------------------------------------------------------- /src/shamir.erl: -------------------------------------------------------------------------------- 1 | %% From: https://github.com/rnewson/shamir/tree/master/src/shamir.erl 2 | 3 | %% Copyright 2011 Robert Newson 4 | %% 5 | %% Licensed under the Apache License, Version 2.0 (the "License"); 6 | %% you may not use this file except in compliance with the License. 7 | %% You may obtain a copy of the License at 8 | %% 9 | %% http://www.apache.org/licenses/LICENSE-2.0 10 | %% 11 | %% Unless required by applicable law or agreed to in writing, software 12 | %% distributed under the License is distributed on an "AS IS" BASIS, 13 | %% WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 14 | %% See the License for the specific language governing permissions and 15 | %% limitations under the License. 16 | 17 | -module(shamir). 18 | 19 | -export([share/3, recover/1]). 20 | -include("shamir.hrl"). 21 | 22 | share(Secret, Threshold, Count) when is_binary(Secret) -> 23 | share(binary_to_list(Secret), Threshold, Count); 24 | 25 | share(Secret, Threshold, Count) when is_list(Secret) -> 26 | Shares = transpose([share(Byte, Threshold, Count) || Byte <- Secret]), 27 | [#share{threshold=Threshold, x=X, y=list_to_binary(lists:nth(X, Shares))} 28 | || X <- lists:seq(1, Count)]; 29 | 30 | share(Secret, Threshold, Count) when (Secret >= 0 andalso Secret =< 255) -> 31 | GF = galois:generate(8), 32 | Coeffs = binary_to_list(crypto:rand_bytes(Threshold - 1)) ++ [Secret], 33 | [horner(GF, X, Coeffs) || X <- lists:seq(1, Count)]. 34 | 35 | horner(GF, X, Coeffs) -> 36 | horner(GF, X, Coeffs, 0). 37 | 38 | horner(_, _, [], Acc) -> 39 | Acc; 40 | horner(GF, X, [Coeff|Rest], Acc) -> 41 | Mult = galois:multiply(GF, Acc, X), 42 | Add = galois:add(GF, Mult, Coeff), 43 | horner(GF, X, Rest, Add). 44 | 45 | recover([#share{threshold=Threshold}|_]=Shares0) -> 46 | Shares = lists:ukeysort(#share.x, Shares0), 47 | X = [X || #share{x=X} <- Shares], 48 | Ys = transpose(lists:map(fun(#share{y=Y}) -> 49 | binary_to_list(Y) end, Shares)), 50 | list_to_binary([recover(Threshold, Z) || Z <- [lists:zip(X, Y) || Y <- Ys]]). 51 | 52 | recover(Threshold, Shares) when length(Shares) >= Threshold -> 53 | lagrange(lists:sublist(Shares, Threshold)). 54 | 55 | lagrange(Shares) -> 56 | GF = galois:generate(8), 57 | lists:foldl(fun(Share, Acc) -> 58 | galois:add(GF, lagrange(GF, Share, Shares), Acc) end, 59 | 0, Shares). 60 | 61 | lagrange(GF, Share, Shares) -> 62 | lagrange(GF, Share, Shares, 1). 63 | 64 | lagrange(GF, {_, Y}, [], Acc) -> 65 | galois:multiply(GF, Y, Acc); 66 | lagrange(GF, {X, Y}, [{X, _} | Rest], Acc) -> 67 | lagrange(GF, {X, Y}, Rest, Acc); 68 | lagrange(GF, {X1, Y1}, [{X2, _} | Rest], Acc) -> 69 | lagrange(GF, {X1, Y1}, Rest, 70 | galois:multiply(GF, Acc, 71 | galois:divide(GF, X2, 72 | galois:subtract(GF, X1, X2)))). 73 | 74 | transpose([[X | Xs] | Xss]) -> 75 | [[X | [H || [H | _] <- Xss]] 76 | | transpose([Xs | [T || [_ | T] <- Xss]])]; 77 | transpose([[] | Xss]) -> transpose(Xss); 78 | transpose([]) -> []. 79 | -------------------------------------------------------------------------------- /src/tls.erl: -------------------------------------------------------------------------------- 1 | -module(tls). 2 | -compile(export_all). 3 | 4 | -import(ez_crypt, 5 | [random_session_key/0, 6 | make_rsa_key/1 7 | ]). 8 | 9 | test() -> 10 | Server = spawn(tls, server, []), 11 | spawn(tls, client, [Server]). 12 | 13 | client(Server) -> 14 | io:format("Client requesting key~n"), 15 | Server ! {self(), hello}, 16 | receive 17 | {Server, ServerPub} -> 18 | S1 = random_session_key(), 19 | {E,D,N} = make_rsa_key(1024), 20 | TmpPri = {D,N}, 21 | TmpPub = {E,N}, 22 | Bin1 = term_to_binary({S1,TmpPub}), 23 | Msg1 = {key1, rsa_encrypt(ServerPub, Bin1)}, 24 | io:format("Client sending S1:~p~n",[S1]), 25 | Server ! {self(),Msg1}, 26 | receive 27 | {Server, {key2, Int}} -> 28 | S2 = rsa_decrypt(TmpPri, Int), 29 | io:format("Client recovered S2:~p~n",[S2]), 30 | client_loop(S1, S2) 31 | end 32 | end. 33 | 34 | %% Note very simple. Need to make sure the key and the Pub will fit 35 | %% inside the 1024 bits whuch I did't check I just hoped it would be 36 | %% OK (it was) 37 | 38 | server() -> 39 | receive 40 | {Client, hello} -> 41 | io:format("Server sending public key~n"), 42 | Client ! {self(), server_public_key()}, 43 | receive 44 | {Client, {key1,Int}} -> 45 | Bin1 = rsa_decrypt(server_private_key(), Int), 46 | {S1, TmpPub} = binary_to_term(Bin1), 47 | io:format("Server recovered S1:~p~n",[S1]), 48 | S2 = random_session_key(), 49 | io:format("Server sending S2:~p~n",[S2]), 50 | Client ! {self(), {key2,rsa_encrypt(TmpPub, S2)}}, 51 | server_loop(S1, S2) 52 | end 53 | end. 54 | 55 | rsa_encrypt(Key, Bin) -> 56 | ez_crypt:rsa_encrypt(Key, Bin). 57 | %% ez_crypt_rsa:encrypt_1(Key, Bin). 58 | 59 | rsa_decrypt(Key, Bin) -> 60 | ez_crypt:rsa_decrypt(Key, Bin). 61 | %% ez_crypt_rsa:decrypt_1(Key, Bin). 62 | 63 | 64 | %% To make this I evaluated ez_crypt:make_rsa_key(1024) 65 | %% and cut and paste the results here 66 | 67 | keys() -> 68 | {65537, 69 | 12901971560344731623714892620385933851981310268248631655222013486395675434059634915505948561032167507464531809332480330508692843729145482423663282062688469573787107088973166278178315041330885466966016550799082934955881035405566463326776038417375846422826465488029991490557531730623662467154219829089630598553, 70 | 74927470992495584973274516407818604063562173774941122976365538135393299151259751303279871585677019223456182648491162022201878856843332519415119230531007038455201812002954362836209633061518067731325346775671448557642921454091003472671059522636286504819845200123597163430897836418492236040080954911925921110687}. 71 | 72 | server_private_key() -> 73 | {_,D,N} = keys(), 74 | {D, N}. 75 | 76 | server_public_key() -> 77 | {E,_,N} = keys(), 78 | {E, N}. 79 | 80 | client_loop(_S1, _S2) -> 81 | %% io:format("in client loop:~p ~p~n",[S1,S2]), 82 | receive 83 | after infinity -> 84 | true 85 | end. 86 | 87 | server_loop(_S1, _S2) -> 88 | %% io:format("in server loop:~p ~p~n",[S1,S2]), 89 | receive 90 | after infinity -> 91 | true 92 | end. 93 | 94 | 95 | 96 | 97 | -------------------------------------------------------------------------------- /src/ez_crypt_test.erl: -------------------------------------------------------------------------------- 1 | -module(ez_crypt_test). 2 | -compile(export_all). 3 | 4 | test() -> 5 | simple_symmetric_encryption(), 6 | aes_test(), 7 | stream_encryption1(), 8 | stream_encryption2(), 9 | rsa_test(). 10 | 11 | simple_symmetric_encryption() -> 12 | Pad = symmetric:pad(), 13 | Text = "The quick brown fox jumped over the lazy dog.", 14 | %% encrypt the text 15 | C1 = symmetric:encrypt_0(Pad, Text), 16 | %% Test we can get it back 17 | Text = symmetric:encrypt_0(Pad, C1), 18 | %% If we do it twice we get the same result 19 | C1 = symmetric:encrypt_0(Pad, Text), 20 | %% LGC 21 | Password = "password1", 22 | %% encrypt 23 | C2 = symmetric:encrypt_1(Password, Text), 24 | %% decrypt 25 | Text = symmetric:encrypt_1(Password, C2), 26 | %% always the same 27 | C2 = symmetric:encrypt_1(Password, Text), 28 | %% Now salt the value 29 | C3 = symmetric:encrypt_2(Password, Text), 30 | %% decrypt 31 | Text = symmetric:decrypt_2(Password, C3), 32 | %% Do a second time and check it's different 33 | C4 = symmetric:encrypt_2(Password, Text), 34 | false = (C3 == C4), %% different ! 35 | ok. 36 | 37 | aes_test() -> 38 | Password = <<"secret">>, 39 | Data = make_binary(2000), %% random binary 40 | C1 = ez_crypt:aes_encrypt(Password, Data), 41 | Data = ez_crypt:aes_decrypt(Password, C1), 42 | C2 = ez_crypt:aes_encrypt(Password, Data), 43 | false = (C1 == C2), 44 | ok. 45 | 46 | make_binary(K) -> 47 | crypto:strong_rand_bytes(K). 48 | 49 | stream_encryption1() -> 50 | Password = <<"secret">>, 51 | S0 = ez_crypt:stream_init(Password), 52 | {S1,C1} = ez_crypt:stream_encrypt(S0, <<"hello ">>), 53 | {_, C2} = ez_crypt:stream_encrypt(S1, <<"world">>), 54 | %% we can take the fragments and decrypt them 55 | NS0 = ez_crypt:stream_init(Password), 56 | {NS1,B1} = ez_crypt:stream_encrypt(NS0, C1), 57 | true = (B1 == <<"hello ">>), 58 | {_,B2} = ez_crypt:stream_encrypt(NS1, C2), 59 | true = (B2 == <<"world">>), 60 | ok. 61 | 62 | stream_encryption2() -> 63 | Password = <<"secret">>, 64 | Text = <<"hello brave new world">>, 65 | S0 = ez_crypt:stream_init(Password), 66 | {_,C1} = ez_crypt:stream_encrypt(S0, Text), 67 | {B1, B2} = split_binary(C1, 4), 68 | %% recombine 69 | {Sa, T1} = ez_crypt:stream_decrypt(S0, B1), 70 | {_, T2} = ez_crypt:stream_decrypt(Sa, B2), 71 | Final = <>, 72 | Final == Text. 73 | 74 | %% START:rsa_test 75 | rsa_test() -> 76 | {E,D,N} = ez_crypt:make_rsa_key(800), 77 | Secret = 1234567890, 78 | Crypt = ez_crypt:mod_pow(Secret,E,N), 79 | Secret = ez_crypt:mod_pow(Crypt,D,N), 80 | %% swap E and D 81 | Crypt1 = ez_crypt:mod_pow(Secret,D,N), 82 | Secret = ez_crypt:mod_pow(Crypt1,E,N), 83 | %% It's determanistic 84 | Crypt2 = ez_crypt:mod_pow(Secret,E,N), 85 | true = (Crypt2 == Crypt), 86 | yes. 87 | %% END:rsa_test 88 | 89 | rsa_test1(K) -> 90 | {E,D,N} = ez_crypt:make_rsa_key(1024), 91 | B = make_binary(K), 92 | C = ez_crypt:rsa_encrypt({D,N}, B), 93 | B = ez_crypt:rsa_decrypt({E,N}, C), 94 | yes. 95 | 96 | 97 | 98 | 99 | -------------------------------------------------------------------------------- /src/plot_random.erl: -------------------------------------------------------------------------------- 1 | -module(plot_random). 2 | -compile(export_all). 3 | 4 | %% +-----------------+ 5 | %% | | 6 | %% | +-----------+ | 7 | %% | | | | 8 | %% | | | | 9 | %% | +-----------+ | 10 | %% | | 11 | %% | Text | 12 | %% +-----------------+ 13 | 14 | 15 | test1() -> 16 | Margin = 30, 17 | TxtHt = 15, 18 | Width = 256 + 2*Margin, 19 | Ht = 256 + 2*Margin + TxtHt, 20 | Img = egd:create(Width,Ht), 21 | Red = egd:color({255,0,0}), 22 | Blue = egd:color({0,0,255}), 23 | egd:rectangle(Img,{Margin,Margin},{Margin+256,Margin+256},Red), 24 | Filename = filename:join([code:priv_dir(percept),"fonts","6x11_latin1.wingsfont"]), 25 | Font = egd_font:load(Filename), 26 | egd:text(Img, {Margin,Margin+256}, Font,"This is a plot",Blue), 27 | file:write_file("foo.png", [egd:render(Img, png)]), 28 | egd:destroy(Img). 29 | 30 | test2() -> 31 | %% with an iterator 32 | plot("lgc1.png", 256*256 div 5, fun lcg1/2, 33 | "Linear Congruent Generator", "silly"), 34 | plot("crypto1.png", 256*256 div 5, fun crypto1/2 , 35 | "Erlang crypto:rand_bytes()","silly"), 36 | %% takes a huge time (26 mins) 37 | io:format("wait 26 minutes~n"), 38 | plot("timer1.png", 256*256 div 5, fun timer1/2, 39 | "Erlang os:timestamp() and timer:sleep()", "silly"), 40 | ok. 41 | 42 | plot(File, K, F, Title, Password) -> 43 | Margin = 30, 44 | TxtHt = 15, 45 | Width = 256 + 2*Margin, 46 | Ht = 256 + 2*Margin + TxtHt, 47 | Img = egd:create(Width,Ht), 48 | Red = egd:color({255,0,0}), 49 | Blue = egd:color({0,0,255}), 50 | Black = egd:color({0,0,0}), 51 | S0 = F(pwd, Password), 52 | egd:rectangle(Img,{Margin,Margin},{Margin+256,Margin+256},Red), 53 | Plot = fun(B1, B2) -> 54 | XX = B1 + Margin, 55 | YY = B2 + Margin, 56 | egd:rectangle(Img,{XX,YY},{XX,YY},Black) 57 | end, 58 | 59 | Filename = filename:join([code:priv_dir(percept),"fonts","6x11_latin1.wingsfont"]), 60 | Font = egd_font:load(Filename), 61 | egd:text(Img, {Margin,Margin+256}, Font,Title,Black), 62 | add_points(K, Plot, Black, F, S0), 63 | file:write_file(File, [egd:render(Img, png)]), 64 | io:format("written:~s~n",[File]), 65 | egd:destroy(Img). 66 | 67 | add_points(0,_,_,_,_) -> 68 | true; 69 | add_points(K, Plot, Black, F, S0) -> 70 | {B1,S1} = F(next, S0), 71 | {B2,S2} = F(next, S1), 72 | Plot(B1,B2), 73 | add_points(K-1, Plot, Black, F, S2). 74 | 75 | lcg1(pwd, Password) -> 76 | password_to_int(Password); 77 | lcg1(next, X) -> 78 | X1 = lcg(X), 79 | B = next_byte(X1), 80 | {B, X1}. 81 | 82 | timer1(pwd,_) -> 1; 83 | timer1(next, S) -> 84 | {rndbyte(), S}. 85 | 86 | %% randbyte() due to Richard Carlsson [private communication] 87 | 88 | %% START:rndbyte 89 | rndbyte() -> 90 | <> = << <> || B <- rndbits(8) >>, 91 | N. 92 | 93 | rndbits(N) -> [rndbit() || _<-lists:seq(1,N)]. 94 | 95 | rndbit() -> 96 | timer:sleep(1), 97 | element(3, os:timestamp()) rem 2. 98 | %% END:rndbyte 99 | 100 | crypto1(pwd, _) -> 1; 101 | crypto1(next, S) -> 102 | <> = crypto:rand_bytes(1), 103 | {B, S}. 104 | 105 | password_to_int(Str) -> 106 | erlang:phash(Str, 4294967296). 107 | 108 | lcg(X) -> 109 | A = 1664525, 110 | C = 1013904223, 111 | M = 4294967296, %% 2^32 112 | (X*A + C) rem M. 113 | 114 | next_byte(N) -> 115 | (N bsr 4) band 255. 116 | 117 | -------------------------------------------------------------------------------- /src/oaep_byte_padding.erl: -------------------------------------------------------------------------------- 1 | -module(oaep_byte_padding). 2 | 3 | -compile(export_all). 4 | 5 | %% Optimal asymmetric encryption padding 6 | %% We assume N = 100 (an 800 bit modulus) 7 | 8 | %% smarter and faster padding 9 | %% The message is always an exact number of bytes long 10 | %% (ie not Bits) 11 | %% salt is not used 12 | %% K0 = 20 13 | 14 | %% Assume 800 bit key 15 | 16 | %% Modulus N = 100 bytes 17 | %% K0 = 20 bytes 18 | %% K1 = padding (at least 1 byte) 19 | %% Max payload = 100 - 21 bytes (= 79 bytes) 20 | 21 | %% A 64 byte message would be like this 22 | 23 | %% |<---------------------------N = 100 bytes -------------------------->| 24 | %% 25 | %% +----------------------+ +------------------+ +-------------------+ 26 | %% | Message (79 bytes) | | Pad (K1) bytes | | (K0 = 20) bytes | 27 | %% | M | | 00 00 00 04 | | R | 28 | %% +----------------------+ +------------------+ +-------------------+ 29 | %% | N-K1-K0 bytes | K1 bytes | 30 | %% | | | 31 | %% +-----------+-----------+ \|/ 32 | %% | | 33 | %% XOR ------<------- G ------<---------+ 34 | %% | | 35 | %% |------------>--- H ------>--------XOR 36 | %% | | 37 | %% X (N-K0) bytes Y K0 bytes 38 | %% 39 | 40 | 41 | %% this is for a fixed 800 bit (100 byte modulus) 42 | 43 | 44 | test() -> 45 | timer:tc(?MODULE, test1, [<<"hello world">>]). 46 | 47 | 48 | test1(Msg) -> 49 | B = pad(Msg, 100, 20), 50 | io:format("size(B) = ~p~n",[size(B)]), 51 | Msg = unpad(B, 20), 52 | yes. 53 | 54 | %% pad(Msg, N, K0) 55 | %% Msg is binary 56 | %% N is the size of the modulus in bytes 57 | %% K0 is the size in bytes of a random string that is generated for 58 | %% each evocation of the algorithm 59 | 60 | pad(Msg, N, K0) when is_binary(Msg), is_integer(N), 61 | is_integer(K0) -> 62 | NPad = N - K0 - size(Msg), 63 | Pad = make_padding(NPad), 64 | R = crypto:strong_rand_bytes(K0), 65 | G = g(N-K0, R), 66 | M0 = <>, 67 | X = xor1(G, M0), 68 | H = h(X, K0), 69 | Y = xor1(R, H), 70 | <>. 71 | 72 | unpad(B, K0) -> 73 | Len = size(B) - K0, 74 | {X, Y} = split_binary(B, Len), 75 | R = xor1(h(X, K0), Y), 76 | Mpad = xor1(X, g(Len, R)), 77 | unpad1(Mpad). 78 | 79 | unpad1(Bin) -> 80 | Len = size(Bin), 81 | {_,<>} = split_binary(Bin, Len - 1), 82 | {B1, _} = split_binary(Bin, Len - N), 83 | B1. 84 | 85 | %% h(B, K) Hash B and reduce the size to K bytes 86 | %% Note K is chosen to be 20 bytes 87 | %% so we just use sha to reduce the hash 88 | 89 | h(B, 20) -> 90 | crypto:hash(sha, B). 91 | 92 | xor1(B1, B2) -> 93 | L1 = binary_to_list(B1), 94 | L2 = binary_to_list(B2), 95 | L = lists:zip(L1, L2), 96 | list_to_binary([A bxor B || {A,B} <- L]). 97 | 98 | g(N, R) -> 99 | X0 = crypto:hash(sha, R), 100 | g1(X0, N, 1). 101 | 102 | g1(X, N, _) when size(X) > N -> 103 | <> = X, 104 | B; 105 | g1(X, N, K) -> 106 | Step = crypto:hash(sha, <>), 107 | X1 = <>, 108 | g1(X1, N, K+1). 109 | 110 | 111 | make_padding(N) -> list_to_binary(make_padding(N-1, [N])). 112 | 113 | make_padding(0, L) -> L; 114 | make_padding(N, L) -> make_padding(N-1, [0|L]). 115 | -------------------------------------------------------------------------------- /src/ez_crypt_rsa.erl: -------------------------------------------------------------------------------- 1 | -module(ez_crypt_rsa). 2 | -compile(export_all). 3 | 4 | %% Do do 5 | %% refactor so we onlt call ez_crypt 6 | %% timing and size tests 7 | 8 | test() -> 9 | {E,D,N} = keys(), 10 | Pub = {E,N}, 11 | Pri = {D,N}, 12 | Mega = mega_byte(), 13 | test1(<<"1234567890">>, Pub, Pri), 14 | test1(<<"12345678901234567890231739127397193712937917391273917397193719379173913792312379137917391739173971937197391739173971293719aaab">>, Pub, Pri), 15 | test2(<<"123456789">>, Pub, Pri), 16 | test3(<<"123456789">>, Pub, Pri), 17 | test3(Mega, Pub, Pri), 18 | yes. 19 | 20 | mega_byte() -> 21 | B1 = list_to_binary(lists:seq(0,255)), 22 | list_to_binary(lists:duplicate(4096, B1)). 23 | 24 | test1(Bin, Pub, Pri) -> 25 | I = encrypt_1(Pub, Bin), 26 | Bin = decrypt_1(Pri, I). 27 | 28 | test2(Bin, Pub, Pri) -> 29 | I = encrypt_2(Pub, Bin), 30 | Bin = decrypt_2(Pri, I). 31 | 32 | test3(Bin, Pub, Pri) -> 33 | I = encrypt_3(Pub, Bin), 34 | Bin = decrypt_3(Pri, I). 35 | 36 | 37 | %% Encrypt 1 = "text book RSA" 38 | 39 | encrypt_1({E,N}, Bin) -> 40 | Str = binary_to_list(Bin), 41 | I = ez_crypt_math:str2int(Str), 42 | if I =< N -> 43 | ez_crypt_math:mod_pow(I, E, N); 44 | true -> 45 | io:format("** size error in ez_crypt_rsa:encrypt_1 ~p ~p~n", 46 | [I,N]), 47 | exit({encrypt,size,I,N}) 48 | end. 49 | 50 | decrypt_1({D,N}, I) -> 51 | J = ez_crypt_math:mod_pow(I,D,N), 52 | list_to_binary(ez_crypt_math:int2str(J)). 53 | 54 | 55 | %% Encrypt 2 56 | %% Text book RSA with OAEP Padding 57 | 58 | %% in encrypt_2 we need to know the Modulo size it's 1024 bits 59 | %% 1024 bits = 128 bytes to play with 60 | %% 20 bytes is the random seed 61 | %% I'll set buffer to 120 62 | 63 | %% Note to self I could call bit_size(N) on the modulo to find the 64 | %% bit size. The padded binary is actually not the value 65 | %% used in encrypt_1. This has to be converted to an integer 66 | %% and the converted integer has to fit into 1024 bits 67 | %% so I'll set a lower value 68 | 69 | encrypt_2(Key, Bin) when size(Bin) =< 120 -> 70 | Bin1 = oaep_byte_padding:pad(Bin, 120, 20), 71 | encrypt_1(Key, Bin1). 72 | 73 | decrypt_2(Key, Bin) -> 74 | Bin1 = decrypt_1(Key, Bin), 75 | oaep_byte_padding:unpad(Bin1, 20). 76 | 77 | %% Now handle anything we can throw at it ... 78 | 79 | encrypt_3(Key, Bin) -> 80 | %% make a session key 81 | Session = ez_crypt:random_session_key(), 82 | %% encrypt the data with the session key 83 | EncData = ez_crypt_aes:encrypt(Session, Bin), 84 | %% encrypt the session key 85 | EncSession = encrypt_2(Key, Session), 86 | %% add a wrapper 87 | Bundle = term_to_binary({EncSession, EncData}), 88 | Bundle. 89 | 90 | decrypt_3(Key, Bundle) -> 91 | %% remove the wrapper 92 | {EncSession, EncData} = binary_to_term(Bundle), 93 | %% decrypt the session key 94 | Session = decrypt_2(Key, EncSession), 95 | %% decrypt the data 96 | Data = ez_crypt_aes:decrypt(Session, EncData), 97 | Data. 98 | 99 | keys() -> 100 | {65537, 101 | 12901971560344731623714892620385933851981310268248631655222013486395675434059634915505948561032167507464531809332480330508692843729145482423663282062688469573787107088973166278178315041330885466966016550799082934955881035405566463326776038417375846422826465488029991490557531730623662467154219829089630598553, 102 | 74927470992495584973274516407818604063562173774941122976365538135393299151259751303279871585677019223456182648491162022201878856843332519415119230531007038455201812002954362836209633061518067731325346775671448557642921454091003472671059522636286504819845200123597163430897836418492236040080954911925921110687}. 103 | -------------------------------------------------------------------------------- /src/ez_crypt_primes.erl: -------------------------------------------------------------------------------- 1 | -module(ez_crypt_primes). 2 | 3 | %% -compile(export_all). 4 | -export([make_k_bit_prime/1, is_prime/1, test/0]). 5 | 6 | -import(crypto, [strong_rand_bytes/1, rand_uniform/2]). 7 | -import(ez_crypt_math, [k_bit_random_integer/1, bsize/1]). 8 | 9 | test() -> 10 | test_small_primes(). 11 | 12 | make_k_bit_prime(Bits) when Bits > 7, Bits < 17 -> 13 | %% us a different method 14 | %% generate random number sin 1..2^N 15 | %% and test 16 | small_random_prime(Bits); 17 | make_k_bit_prime(Bits) when Bits < 8 -> 18 | exit({?MODULE,make_prime,too_few_bits,Bits}); 19 | make_k_bit_prime(Bits) -> 20 | %% DLen = length in bits 21 | N = k_bit_random_integer(Bits), 22 | %% N is an odd number 23 | {Prime,_K} = long_loop(N, 0), 24 | case bsize(Prime) of 25 | Bits -> 26 | Prime; 27 | N1 -> 28 | io:format("** something is wrong wanted ~p bit prime~n" 29 | " generated ~p bit prime~ntrying agian~n", 30 | [Bits,N1]), 31 | make_k_bit_prime(Bits) 32 | end. 33 | 34 | long_loop(P, K) -> 35 | case is_prime(P) of 36 | true -> 37 | {P,K}; 38 | false -> 39 | long_loop(P+2, K+1) 40 | end. 41 | 42 | 43 | test_small_primes() -> 44 | L = lists:seq(3,2000), 45 | S = small_primes(), 46 | lists:foreach(fun(I) -> 47 | %% io:format("I=~p is_prime:~p~n",[I,is_prime(I)]), 48 | case lists:member(I, S) of 49 | true -> 50 | true = is_prime(I); 51 | false -> 52 | false = is_prime(I) 53 | end 54 | end, L). 55 | 56 | %% START:is_prime 57 | is_prime(0) -> false; 58 | is_prime(1) -> false; 59 | is_prime(X) -> 60 | case is_multiple_of_small_prime(X) of 61 | {yes, X} -> true; 62 | {yes,_} -> false; 63 | no -> ez_crypt_miller_rabin:is_probably_prime(X) 64 | end. 65 | 66 | is_multiple_of_small_prime(N) -> 67 | is_multiple_of_small_prime(N, small_primes()). 68 | 69 | is_multiple_of_small_prime(N, [H|T]) -> 70 | case N rem H of 71 | 0 -> {yes, H}; 72 | _ -> is_multiple_of_small_prime(N, T) 73 | end; 74 | is_multiple_of_small_prime(_, []) -> 75 | no. 76 | 77 | %% END:is_prime 78 | 79 | small_primes() -> 80 | %% < 2000 81 | [3,5,7,11,13,17,19,23,29,31,37,41,43,47,53,59,61,67, 82 | 71,73,79,83,89,97,101,103,107,109,113,127,131,137,139, 83 | 149,151,157,163,167,173,179,181,191,193,197, 84 | 199,211,223,227,229,233,239,241,251,257,263,269,271,277,281,283,293,307,311, 85 | 313,317,331,337,347,349,353,359,367,373,379,383,389,397,401,409,419,421,431, 86 | 433,439,443,449,457,461,463,467,479,487,491,499,503,509,521,523,541,547,557, 87 | 563,569,571,577,587,593,599,601,607,613,617,619,631,641,643,647,653,659,661, 88 | 673,677,683,691,701,709,719,727,733,739,743,751,757,761,769,773,787,797,809, 89 | 811,821,823,827,829,839,853,857,859,863,877,881,883,887,907,911,919,929,937, 90 | 941,947,953,967,971,977,983,991,997,1009,1013,1019,1021,1031,1033,1039,1049, 91 | 1051,1061,1063,1069,1087,1091,1093,1097,1103,1109,1117,1123,1129,1151,1153, 92 | 1163,1171,1181,1187,1193,1201,1213,1217,1223,1229,1231,1237,1249,1259,1277, 93 | 1279,1283,1289,1291,1297,1301,1303,1307,1319,1321,1327,1361,1367,1373,1381, 94 | 1399,1409,1423,1427,1429,1433,1439,1447,1451,1453,1459,1471,1481,1483,1487, 95 | 1489,1493,1499,1511,1523,1531,1543,1549,1553,1559,1567,1571,1579,1583,1597, 96 | 1601,1607,1609,1613,1619,1621,1627,1637,1657,1663,1667,1669,1693,1697,1699, 97 | 1709,1721,1723,1733,1741,1747,1753,1759,1777,1783,1787,1789,1801,1811,1823, 98 | 1831,1847,1861,1867,1871,1873,1877,1879,1889,1901,1907,1913,1931,1933,1949, 99 | 1951,1973,1979,1987,1993,1997,1999]. 100 | 101 | 102 | %% Small numbers 103 | %% K = 1 bit 0 or 1 - invlaid 104 | %% K = 2 0..3 the prime is 2 or 3 105 | %% K = 3 0..7 2 3 5 106 | %% K = 4 0..15 2 3 5 7 11 107 | %% K = 5 0..31 2 3 5 7 11 13 17 19 23 29 108 | 109 | small_random_prime(K) -> 110 | N = k_bit_random_integer(K), 111 | io:format("tryimg:~p~n",[N]), 112 | case is_prime(N) of 113 | true -> N; 114 | false -> small_random_prime(K) 115 | end. 116 | 117 | 118 | -------------------------------------------------------------------------------- /src/ez_crypt.erl: -------------------------------------------------------------------------------- 1 | -module(ez_crypt). 2 | 3 | -export([ 4 | aes_decrypt/2, 5 | aes_encrypt/2, 6 | bsize/1, 7 | decrypt_message/3, 8 | encrypt_message/2, 9 | file2sha/1, 10 | is_probably_prime/1, 11 | make_k_bit_prime/1, 12 | make_rsa_key/1, 13 | make_rsa_keyfiles/4, 14 | mod_pow/3, 15 | random_session_key/0, 16 | read_private_key/2, 17 | read_public_key/1, 18 | rsa_decrypt/2, 19 | rsa_encrypt/2, 20 | sha1_checksum_of_file/1, 21 | sign_current_dir/0, 22 | sign_file/3, 23 | stream_decrypt/2, 24 | stream_encrypt/2, 25 | stream_init/1, 26 | validate_file/2, 27 | validate_this_dir/0 28 | ]). 29 | 30 | 31 | -spec mod_pow(N::integer(), P::integer(), M::integer()) -> R::integer(). 32 | -spec make_k_bit_prime(Nbits::integer()) -> Prime::integer(). 33 | 34 | %% compute N^P mod N 35 | 36 | mod_pow(N, P, M) -> ez_crypt_math:mod_pow(N, P, M). 37 | 38 | make_k_bit_prime(K) -> ez_crypt_primes:make_k_bit_prime(K). 39 | 40 | bsize(N) -> ez_crypt_math:bsize(N). 41 | 42 | make_rsa_key(LenInBits) -> ez_crypt_math:make_rsa_keypair(LenInBits). 43 | 44 | is_probably_prime(X) -> ez_crypt_miller_rabin:is_probably_prime(X). 45 | 46 | %% START:make_rsa_keyfiles 47 | make_rsa_keyfiles(Name, Email, Len, Password) -> 48 | {E,D,N} = make_rsa_key(Len), 49 | {ok, S} = file:open(Name ++ ".pub", [write]), 50 | io:format(S,"~p.~n", 51 | [#{type=>public_key, 52 | email => Email, 53 | e => E, 54 | n => N}]), 55 | file:close(S), 56 | Term = #{type => private_key, 57 | name => Name, 58 | email => Email, 59 | e => E, 60 | d => D, 61 | n => N}, 62 | Bin1 = term_to_binary(Term), 63 | Bin2 = aes_encrypt(Password, Bin1), 64 | {ok, S1} = file:open(Name ++ ".pri", [write]), 65 | io:format(S1,"~p.~n", 66 | [#{type=>encrypted_private_key, 67 | value => Bin2}]), 68 | file:close(S1). 69 | %% END:make_rsa_keyfiles 70 | -spec random_session_key() -> binary(). 71 | 72 | random_session_key() -> 73 | crypto:strong_rand_bytes(20). 74 | 75 | sign_file(File, PrivateKeyFile, Password) -> 76 | SHA = sha1_checksum_of_file(File), 77 | PriKey = read_private_key(PrivateKeyFile, Password), 78 | Sig = rsa_encrypt(PriKey, SHA), 79 | file:write_file(File ++ ".sig", term_to_binary(Sig)). 80 | 81 | validate_file(PubKeyFile, File) -> 82 | SHA1 = sha1_checksum_of_file(File), 83 | {ok, Bin} = file:read_file(File ++ ".sig"), 84 | Sig = binary_to_term(Bin), 85 | PubKey = read_public_key(PubKeyFile), 86 | io:format("PubKey=~p~n",[PubKey]), 87 | SHA2 = rsa_decrypt(PubKey, Sig), 88 | Ret = case SHA1 of 89 | SHA2 -> true; 90 | _ -> false 91 | end, 92 | io:format("Validate:~s ~p~n",[File, Ret]), 93 | Ret. 94 | 95 | sha1_checksum_of_file(File) -> 96 | {ok, Bin} = file:read_file(File), 97 | sha1_checksum(Bin). 98 | 99 | sha1_checksum(X) -> 100 | crypto:hash(sha, <>). 101 | 102 | rsa_encrypt({E,N}=Key, Bin) when is_integer(E), 103 | is_integer(N), 104 | is_binary(Bin) -> 105 | ez_crypt_rsa:encrypt_3(Key, Bin). 106 | 107 | rsa_decrypt({E,N}=Key, B) when is_integer(E), 108 | is_integer(N), 109 | is_binary(B) -> 110 | ez_crypt_rsa:decrypt_3(Key, B). 111 | 112 | sign_current_dir() -> 113 | F = erl_files(), 114 | L = [{file,I,sha1_checksum_of_file(I)} || I <- F], 115 | io:format("L=~p~n",[L]), 116 | write_term("catalog", L), 117 | sign_file("catalog", "joe.pri", <<"verysecret">>). 118 | 119 | 120 | erl_files() -> 121 | F = filelib:wildcard("*.erl"), 122 | lists:filter(fun(".#" ++ _) -> false; 123 | (_) -> true 124 | end, F). 125 | 126 | 127 | validate_this_dir() -> 128 | validate_file("joe.pub","catalog"). 129 | 130 | write_term(File, X) -> 131 | {ok, H} = file:open(File,[write]), 132 | io:format(H, "~p.~n",[X]), 133 | file:close(H). 134 | 135 | aes_encrypt(Password, Bin) when is_binary(Password), 136 | is_binary(Bin) -> 137 | ez_crypt_aes:encrypt(Password, Bin). 138 | 139 | aes_decrypt(Password, Bin) when is_binary(Password), 140 | is_binary(Bin) -> 141 | ez_crypt_aes:decrypt(Password, Bin). 142 | 143 | read_private_key(File, Password) -> 144 | {ok, [#{type := encrypted_private_key, 145 | value := Bin}]} = file:consult(File), 146 | Bin1 = aes_decrypt(Password, Bin), 147 | Term = binary_to_term(Bin1), 148 | #{d := D, n:= N} = Term, 149 | {D, N}. 150 | 151 | read_public_key(File) -> 152 | {ok, [#{type := public_key, 153 | e := E, n := N}]} = file:consult(File), 154 | {E, N}. 155 | 156 | encrypt_message(Who, Msg) -> 157 | Ran = random_session_key(), 158 | EncMessage = aes_encrypt(Ran, Msg), 159 | PubKey = read_public_key(Who), 160 | EncKey = rsa_encrypt(PubKey, Ran), 161 | term_to_binary({EncKey,EncMessage}). 162 | 163 | decrypt_message(Who, Password, Bin) -> 164 | {EncKey,EncMessage} = binary_to_term(Bin), 165 | PriKey = read_private_key(Who, Password), 166 | Key = rsa_decrypt(PriKey, EncKey), 167 | Message = aes_decrypt(Key, EncMessage), 168 | Message. 169 | 170 | stream_init(Password) -> 171 | crypto:stream_init(rc4, Password). 172 | 173 | stream_encrypt(Key, Bin) -> 174 | crypto:stream_encrypt(Key, Bin). 175 | 176 | stream_decrypt(Key, Bin) -> 177 | crypto:stream_decrypt(Key, Bin). 178 | 179 | %% START:filehash 180 | file2sha(File) -> 181 | hash_file(File, sha). 182 | 183 | hash_file(File, Method) -> 184 | % the file can be huge so read it in chunks 185 | case file:open(File, [binary,raw,read]) of 186 | {ok, P} -> hash_loop(P, crypto:hash_init(Method)); 187 | Error -> Error 188 | end. 189 | 190 | hash_loop(P, C) -> 191 | case file:read(P, 4096) of 192 | {ok, Bin} -> 193 | hash_loop(P, crypto:hash_update(C, Bin)); 194 | eof -> 195 | file:close(P), 196 | {ok, crypto:hash_final(C)} 197 | end. 198 | %% END:filehash 199 | 200 | 201 | 202 | 203 | 204 | 205 | 206 | -------------------------------------------------------------------------------- /src/ez_crypt_math.erl: -------------------------------------------------------------------------------- 1 | -module(ez_crypt_math). 2 | 3 | %% -compile(export_all). 4 | 5 | -export([bsize/1, 6 | dsize/1, 7 | gcd/2, 8 | k_bit_random_integer/1, 9 | make_rsa_keypair/1, 10 | mod_pow/3, 11 | str2int/1, 12 | int2str/1, 13 | inv/2, 14 | is_even/1, 15 | random_integer/1, 16 | test/0]). 17 | 18 | -import(crypto, [strong_rand_bytes/1, rand_uniform/2]). 19 | 20 | -spec mod_pow(N::integer(), P::integer(), M::integer()) -> R::integer(). 21 | 22 | %% random_integer(Len) -> Int. 23 | 24 | 25 | test() -> 26 | t1(<<"123">>), 27 | t1(<<0,1,2,3>>), 28 | t1(<<0,0,0,1,2,3>>), 29 | t1(<<>>), 30 | 67 = inv(28, 75), 31 | 6597 = inv(3533, 11200), 32 | 3533 = inv(6597, 11200), 33 | 5761 = mod_pow(9726,3533,11413), 34 | 4086 = mod_pow(5971,6597,11413), 35 | 1028 = bsize(k_bit_random_integer(1028)), 36 | 200 = bsize(k_bit_random_integer(200)), 37 | yes. 38 | 39 | t1(B) -> 40 | I = bin2int(B), 41 | B = int2bin(I). 42 | 43 | %% mod_pow(A, B, M) => (A^B) mod M 44 | %% examples mod_pow(9726,3533,11413) = 5761 45 | %% mod_pow(5971,6597,11413) = 4086 46 | 47 | mod_pow(A, 1, M) -> 48 | A rem M; 49 | mod_pow(A, 2, M) -> 50 | A*A rem M; 51 | mod_pow(A, B, M) -> 52 | B1 = B div 2, 53 | B2 = B - B1, 54 | %% B2 = B1 or B1+1 55 | P = mod_pow(A, B1, M), 56 | case B2 of 57 | B1 -> (P*P) rem M; 58 | _ -> (P*P*A) rem M 59 | end. 60 | 61 | %% inv(A, B) = C | no_inverse 62 | %% computes C such that 63 | %% A*C mod B = 1 64 | %% computes A^-1 mod B 65 | %% examples inv(28, 75) = 67. 66 | %% inv(3533, 11200) = 6597 67 | %% inv(6597, 11200) = 3533 68 | 69 | inv(A, B) -> 70 | case solve(A, B) of 71 | {X, _} -> 72 | if X < 0 -> X + B; 73 | true -> X 74 | end; 75 | _ -> 76 | no_inverse 77 | end. 78 | 79 | %% solve(A, B) => {X, Y} | insoluble 80 | %% solve the linear congruence 81 | %% A * X - B * Y = 1 82 | 83 | %S tag1 84 | solve(A, B) -> 85 | case catch s(A,B) of 86 | insoluble -> insoluble; 87 | {X, Y} -> 88 | case A * X - B * Y of 89 | 1 -> {X, Y}; 90 | _ -> error 91 | end 92 | end. 93 | 94 | s(_, 0) -> throw(insoluble); 95 | s(_, 1) -> {0, -1}; 96 | s(_, -1) -> {0, 1}; 97 | s(A, B) -> 98 | K1 = A div B, 99 | K2 = A - K1*B, 100 | {Tmp, X} = s(B, -K2), 101 | {X, K1 * X - Tmp}. 102 | %E tag1 103 | 104 | 105 | %% converts a string to a base 256 integer 106 | %% converts a base 256 integer to a string 107 | 108 | %% because there is a leading zero problem we stick an "z" on the beginning 109 | %% and remove it when we take the inverse 110 | 111 | %% START:str2int 112 | str2int(Str) -> str2int([$z|Str], 0). 113 | 114 | str2int([H|T], N) -> str2int(T, N*256+H); 115 | str2int([], N) -> N. 116 | 117 | int2str(N) -> 118 | "z" ++ S = int2str(N, []), 119 | S. 120 | 121 | int2str(N, L) when N =< 0 -> L; 122 | int2str(N, L) -> 123 | N1 = N div 256, 124 | H = N - N1 * 256, 125 | int2str(N1, [H|L]). 126 | %% END:str2int 127 | 128 | int2bin(Int) -> 129 | S = int2str(Int), 130 | list_to_binary(S). 131 | 132 | bin2int(B) -> str2int(binary_to_list(B)). 133 | 134 | %% greatest common denominator 135 | 136 | gcd(A, B) when A < B -> gcd(B, A); 137 | gcd(A, 0) -> A; 138 | gcd(A, B) -> 139 | gcd(B, A rem B). 140 | 141 | %% decimal size 142 | 143 | dsize(K) when K < 10 -> 1; 144 | dsize(K) -> 1 + dsize(K div 10). 145 | 146 | %% START:make_rsa_keypair 147 | make_rsa_keypair(K) -> 148 | %% K is the bitsize of the modulo N 149 | K1 = K div 2, 150 | E = 65537, 151 | P = gen_prime(K1, E), 152 | Q = gen_prime(K-K1, E), 153 | N = P*Q, 154 | Phi = (P-1)*(Q-1), 155 | D = inv(E, Phi), 156 | {E,D,N}. 157 | %% END:make_rsa_keypair 158 | 159 | %% gen_prime(K, N) 160 | %% makes a prime of K bits that is relatively prime to N 161 | 162 | gen_prime(K, N) -> 163 | P = ez_crypt_primes:make_k_bit_prime(K), 164 | case gcd(P, N) of 165 | 1 -> P; 166 | _ -> gen_prime(K, N) 167 | end. 168 | 169 | %% number of bits to represent N 170 | 171 | bsize(0) -> 1; 172 | bsize(1) -> 1; 173 | bsize(K) -> 1 + bsize(K bsr 1). 174 | 175 | k_bit_random_integer(K) -> 176 | %% makes a K bit random integer 177 | Len1 = trunc(K/8) + 2, 178 | %% start by making a larger number than necessary then removing 179 | %% some bits until it is the the correct size 180 | N = random_integer(Len1), 181 | InitialSize = bsize(N), 182 | if 183 | InitialSize =< K -> 184 | io:format("** should not happen **~n"), 185 | io:format("initial size:~p K:~p~n",[bsize(N), K]), 186 | k_bit_random_integer(K); 187 | true -> 188 | N1 = fixup_size(N, K), 189 | %% set the last bit to 1 190 | %% so it's odd 191 | %% and if adding one mucks up the bit length 192 | %% then do it all over again 193 | %% total bit length 194 | case is_even(N1) of 195 | true -> 196 | N2 = N1 + 1, 197 | case bsize(N2) of 198 | K -> N2; 199 | _ -> 200 | %% this is **very** unlikely 201 | %% but we might as well get the code right 202 | k_bit_random_integer(K) 203 | end; 204 | false -> 205 | N1 206 | end 207 | end. 208 | 209 | %% Initially N is larger than K bits wide 210 | %% reduce it one bit at a time until it is K bits wide 211 | 212 | fixup_size(N, K) -> 213 | case ez_crypt_math:bsize(N) of 214 | K -> 215 | N; 216 | B when B > K -> 217 | fixup_size(N bsr 1, K) 218 | end. 219 | 220 | random_integer(Len) -> 221 | random_integer(Len, 1000). 222 | 223 | random_integer(_, 0) -> 224 | exit(too_may_tries); 225 | random_integer(Len, K) -> 226 | case (catch strong_rand_bytes(Len)) of 227 | {'EXIT', _} -> 228 | random_integer(Len, K-1); 229 | Bin -> 230 | pint_to_int(Bin, 0) 231 | end. 232 | 233 | pint_to_int(<<>>,N) -> N; 234 | pint_to_int(<>,N) -> 235 | pint_to_int(B, N*256+X). 236 | 237 | is_even(K) -> 238 | K band 1 == 0. 239 | 240 | 241 | 242 | 243 | 244 | 245 | -------------------------------------------------------------------------------- /src/ez_crypt_miller_rabin.erl: -------------------------------------------------------------------------------- 1 | -module(ez_crypt_miller_rabin). 2 | 3 | -compile(export_all). 4 | -export([test/0, is_probably_prime/1]). 5 | 6 | 7 | %% make it obvious what we import right up front ... 8 | 9 | -import(ez_crypt_math, [bsize/1, mod_pow/3]). 10 | -import(crypto, [strong_rand_bytes/1, rand_uniform/2]). 11 | 12 | %% calls 13 | %% ez_crypt_math:mod_pow/2 14 | 15 | %% RSA-576 (174 digits) 16 | %% Note log(10)/log(2) = 3.3219 17 | %% 576/3.3219 = 173.3932 => 174 decimal digits 18 | %% each prime has 174/2 => 87 digits 19 | %% To make N = 576 (we need 288 bits) 20 | %% miller_rabin:make_prime(288). 21 | 22 | %% RSA-100 means 100 decimal digits 23 | %% RSA-200 means 200 decimal digits and is the highest ever factored 24 | %% RSA-576 means 576 binary digits (approx RSA-174 has not been factored) 25 | 26 | 27 | test() -> 28 | test1(), 29 | test2(), 30 | test576(), 31 | test_rsa(), 32 | all_tests_passed. 33 | 34 | 35 | test576() -> 36 | io:format("test576~n"), 37 | %% This is RSA-576 38 | %% First factored on December 3, 2003 by J. Franke and T. Kleinjung from 39 | %% the University of Bonn 40 | %% [cite] https://en.wikipedia.org/wiki/RSA_numbers 41 | 42 | P = 398075086424064937397125500550386491199064362342526708406385189575946388957261768583317, 43 | Q = 472772146107435302536223071973048224632914695302097116459852171130520711256363590397527, 44 | C = P*Q, 45 | true = is_probably_prime(P), 46 | true = is_probably_prime(Q), 47 | false = is_probably_prime(C), 48 | io:format("bitsize of RSA-576 = ~p~n",[bsize(C)]), 49 | C. 50 | 51 | test_rsa() -> 52 | io:format("test RSA~n"), 53 | lists:foreach(fun({_,_,C}) -> 54 | false = is_probably_prime(C) 55 | end, 56 | rsa_test_cases()). 57 | 58 | 59 | rsa_test_cases() -> 60 | %% Bit size - if factored - product of two primes 61 | [{576, yes, 188198812920607963838697239461650439807163563379417382700763356422988859715234665485319060606504743045317388011303396716199692321205734031879550656996221305168759307650257059}, 62 | {640,yes, 3107418240490043721350750035888567930037346022842727545720161948823206440518081504556346829671723286782437916272838033415471073108501919548529007337724822783525742386454014691736602477652346609}, 63 | {704, no, 74037563479561712828046796097429573142593188889231289084936232638972765034028266276891996419625117843995894330502127585370118968098286733173273108930900552505116877063299072396380786710086096962537934650563796359}, 64 | {768,no, 1230186684530117755130494958384962720772853569595334792197322452151726400507263657518745202199786469389956474942774063845925192557326303453731548268507917026122142913461670429214311602221240479274737794080665351419597459856902143413}, 65 | {896,no, 412023436986659543855531365332575948179811699844327982845455626433876445565248426198098870423161841879261420247188869492560931776375033421130982397485150944909106910269861031862704114880866970564902903653658867433731720813104105190864254793282601391257624033946373269391}, 66 | {1024,no, 135066410865995223349603216278805969938881475605667027524485143851526510604859533833940287150571909441798207282164471551373680419703964191743046496589274256239341020864383202110372958725762358509643110564073501508187510676594629205563685529475213500852879416377328533906109750544334999811150056977236890927563}, 67 | {1583,no,1847699703211741474306835620200164403018549338663410171471785774910651696711161249859337684305435744585616061544571794052229717732524660960646946071249623720442022269756756687378427562389508764678440933285157496578843415088475528298186726451339863364931908084671990431874381283363502795470282653297802934916155811881049844908319545009848393775227257052578591944993870073695755688436933812779613089230392569695253261620823676490316036551371447913932347169566988069}, 68 | {2048,no,25195908475657893494027183240048398571429282126204032027777137836043662020707595556264018525880784406918290641249515082189298559149176184502808489120072844992687392807287776735971418347270261896375014971824691165077613379859095700097330459748808428401797429100642458691817195118746121515172654632282216869987549182422433637259085141865462043576798423387184774447920739934236584823824281198163815010674810451660377306056201619676256133844143603833904414952634432190114657544454178424020924616515723350778707749817125772467962926386356373289912154831438167899885040445364023527381951378636564391212010397122822120720357} 69 | ]. 70 | 71 | 72 | %% def miller_rabin_prime?(n,k) 73 | %% return true if n == 2 74 | %% return false if n < 2 or n % 2 == 0 75 | %% d = n - 1 76 | %% s = 0 77 | %% while d % 2 == 0 78 | %% d /= 2 79 | %% s += 1 80 | %% end 81 | %% k.times do 82 | %% a = 2 + rand(n-4) 83 | %% x = (a**d) % n 84 | %% next if x == 1 or x == n-1 85 | %% for r in (1 .. s-1) 86 | %% x = (x**2) % n 87 | %% return false if x == 1 88 | %% break if x == n-1 89 | %% end 90 | %% return false if x != n-1 91 | %% end 92 | %% true # probably 93 | %% end 94 | 95 | test1() -> 96 | io:format("test1~n"), 97 | [I || I <- lists:seq(1, 1000), is_probably_prime(I)]. 98 | 99 | test2() -> 100 | io:format("test2~n"), 101 | true = is_probably_prime(643808006803554439230129854961492699151386107534013432918073439524138264842370630061369715394739134090922937332590384720397133335969549256322620979036686633213903952966175107096769180017646161851573147596390153), 102 | false = is_probably_prime(743808006803554439230129854961492699151386107534013432918073439524138264842370630061369715394739134090922937332590384720397133335969549256322620979036686633213903952966175107096769180017646161851573147596390153), 103 | yes. 104 | 105 | 106 | %% 200 decimal digits * 200 decimal digits = 400 decimal digits 107 | %% = 400*3.32 = 1328 bits 108 | 109 | %% say 154 decimal digits = 154*3.32 => 511 bits Two numbers = 1024 bits) 110 | 111 | %% 768 bit keys are the current record 112 | %% call with 384 113 | 114 | %% two 384 bit keys takes about 240 ms (0.2 seconds) 115 | %% 1024 bit (2 * 512) takes about 0.4 seconds 116 | %% 2048 bit (2*1024) takes 2-3 seconds 117 | 118 | 119 | digits(P, K) when P < 10 -> K+1; 120 | digits(P, K) -> digits(P div 10, K+1). 121 | 122 | -spec is_probably_prime(integer()) -> boolean(). 123 | 124 | %% runs the rabbin miller rabit test 40 times 125 | %% to test for a prime number 126 | 127 | is_probably_prime(2) -> true; 128 | is_probably_prime(N) when N < 2 -> false; 129 | is_probably_prime(3) -> true; 130 | is_probably_prime(5) -> true; 131 | is_probably_prime(N) -> 132 | case is_even(N) of 133 | true -> false; 134 | false -> 135 | {S, D} = sd(N-1, 0), 136 | loop(40, N, S, D) 137 | end. 138 | 139 | loop(0,_,_,_) -> 140 | true; 141 | loop(K, N, S, D) -> 142 | A = 2 + rand_uniform(1,N-4), 143 | X = ez_crypt_math:mod_pow(A,D,N), 144 | N1 = N - 1, 145 | case X of 146 | 1 -> loop(K-1, N, S, D); 147 | N1 -> loop(K-1, N,S,D); 148 | _ -> inner(S-1, K, N, X, S, D) 149 | end. 150 | 151 | inner(0, K, N, X, S, D) -> 152 | case N-1 of 153 | X -> loop(K-1, N, S, D); 154 | _ -> false 155 | end; 156 | inner(R, K, N, X, S, D) -> 157 | X1 = ez_crypt_math:mod_pow(X,2,N), 158 | N1 = N - 1, 159 | case X1 of 160 | 1 -> false; 161 | N1 -> inner(0, K, N, X1, S, D); 162 | _ -> inner(R-1,K,N,X1,S,D) 163 | end. 164 | 165 | sd(K, N) -> 166 | case is_even(K) of 167 | true -> sd(K div 2, N+1); 168 | false -> {N, K} 169 | end. 170 | 171 | is_even(K) -> 172 | K band 1 == 0. 173 | 174 | 175 | 176 | 177 | -------------------------------------------------------------------------------- /src/segmenting.erl: -------------------------------------------------------------------------------- 1 | -module(segmenting). 2 | %% this is the module comment 3 | 4 | -compile(export_all). 5 | -import(lists,[reverse/1]). 6 | 7 | %% crude attempt to segment a file 8 | %% We only consider files that are compilable 9 | %% The first step is to parse all forms etc. 10 | 11 | test() -> 12 | segment("./segmenting.erl"). 13 | 14 | %% this comment is for erl2emod 15 | 16 | -spec erl2emod(string()) -> boolean(). 17 | 18 | erl2emod(File) -> 19 | J = segment(File), 20 | elib1_misc:dump(Z = File ++ ".tmp", J), 21 | io:format("look in:~p~n",[Z]), 22 | K = [reconstruct(I) || I <- J], 23 | elib1_misc:dump(File ++ ".emod.tmp", K). 24 | 25 | reconstruct({{module, M}, _}) -> 26 | {module, M}; 27 | reconstruct({comment,L}) -> 28 | {comment, flatten_comment(L)}; 29 | reconstruct({{compile,export_all}, _}) -> 30 | {export_all, true}; 31 | reconstruct({{import,I},_}) -> 32 | {import, I}; 33 | reconstruct({{Tag,_}=F,Body}) when Tag == spec; Tag == function -> 34 | B1 = flatten_body(Body), 35 | {F, B1}. 36 | 37 | flatten_body(L) -> 38 | list_to_binary([fb(I) || I <- L]). 39 | 40 | fb({_Tag,Str}) -> Str. 41 | 42 | 43 | flatten_comment(L) -> 44 | list_to_binary([fc(I) || I <- L]). 45 | 46 | fc({str,S}) -> S; 47 | fc({white_space,W}) -> W. 48 | 49 | start() -> 50 | L1 = filelib:wildcard(elib2_misc:root_dir() ++ "/lib/src/*.erl"), 51 | %% L2 = filelib:wildcard(code:lib_dir() ++ "/*/src/*.erl"), 52 | [segment(I) || I<- L1]. 53 | 54 | %% Here are some test cases 55 | 56 | segment(File) -> 57 | %% io:format("segment:~p~n",[File]), 58 | Z = read_raw_forms(File), 59 | %5 elib2_misc:dump("raw_forms.tmp",Z), 60 | Z1 = partition(Z), 61 | %% elib2_misc:dump("raw_forms1.tmp",Z1), 62 | Segments = [zapit(I) || I <- Z1], 63 | %% elib2_misc:dump("segments.tmp",Segments), 64 | Reduced = reduce_segments(Segments, []), 65 | %% elib2_misc:dump("reduced.tmp",Reduced), 66 | Reduced. 67 | 68 | partition(L) -> partition(L, []). 69 | 70 | partition([H|T], L) -> 71 | L1 = add_forms(H, L), 72 | partition(T, L1); 73 | partition([], L) -> 74 | reverse(L). 75 | 76 | add_forms([], L) -> 77 | L; 78 | add_forms(T, L) -> 79 | {T1, Form} = split_form(T), 80 | add_forms(T1, [Form|L]). 81 | 82 | split_form([{white_space,_,_}=H|T]) -> split_ws(T, [H]); 83 | split_form([{comment,_,_}=H|T]) -> split_ws(T, [H]); 84 | split_form(X) -> collect_form(X, []). 85 | 86 | split_ws([{white_space,_,_}=H|T], L) -> split_ws(T, [H|L]); 87 | split_ws([{comment,_,_}=H|T], L) -> split_ws(T, [H|L]); 88 | split_ws(X, L) -> {X, {ws, reverse(L)}}. 89 | 90 | collect_form([{dot,_}=H|T], L) -> {T, {form,reverse([H|L])}}; 91 | collect_form([H|T], L) -> collect_form(T, [H|L]). 92 | 93 | collect_segments([{Min,Max,Tag}|T], I, Lines, L) -> 94 | {Segment, I1, Lines1} = collect_segement(Min,Max,I,Lines,[]), 95 | collect_segments(T, I1, Lines1, [{Tag,Min,Max,Segment}|L]); 96 | collect_segments([],_,_,L) -> 97 | reverse(L). 98 | 99 | collect_segement(Min,Max,I,[_|T], B) when I 100 | collect_segement(Min,Max,I+1,T, B); 101 | collect_segement(Min,Max,I,[H|T], B) when Min =< I, I =< Max -> 102 | collect_segement(Min, Max, I+1, T, [H|B]); 103 | collect_segement(_, Max, I, T, B) when I > Max -> 104 | {reverse(B), I, T}. 105 | 106 | zapit({ws,_}=X) -> X; 107 | zapit({form,Toks}) -> 108 | Toks2 = remove_whitespace(Toks), 109 | Parse = erl_parse:parse_form(Toks2), 110 | %% summarise classifies the parse tree 111 | %% fallback is used if the form is illegal and cannot be used 112 | Val = case Parse of 113 | {ok, Form} -> summarise(Form); 114 | {error, _} -> fallback_method(Toks2) 115 | end, 116 | Toks3 = [prune(I) || I <- Toks], 117 | Toks4 = find_func_calls(Toks3), 118 | {form1, Val, Toks4}. 119 | 120 | find_func_calls([{atom,X},{'(',"("}|T]) -> 121 | [{func_symbol,X},{'(',"("}|find_func_calls(T)]; 122 | find_func_calls([{atom,M},{':',":"},{atom,F},{'(',"("}|T]) -> 123 | [{mod_symbol,M},{':',":"},{func_symbol,F},{'(',"("}|find_func_calls(T)]; 124 | find_func_calls([H|T]) -> 125 | [H|find_func_calls(T)]; 126 | find_func_calls([]) -> 127 | []. 128 | 129 | prune({Tag, L}) -> {Tag,proplists:get_value(text,L)}; 130 | prune({Tag,L,_}) -> {Tag, proplists:get_value(text,L)}. 131 | 132 | summarise({attribute,_,spec,{S,_}}) -> {spec, S}; 133 | summarise({attribute,_,Tag,Val}) -> {Tag,Val}; 134 | summarise({function,_,Name,Arity,_}) -> {function,{Name,Arity}}; 135 | summarise(X) -> {cannot_summarise,X}. 136 | 137 | fallback_method([{'-',_},{atom,_,define},{'(',_},{var,_,Name},{'(',_}|T]) -> 138 | case (catch get_define_arity(T, 0)) of 139 | {'EXIT', _} -> error; 140 | Arity -> {define, {Name,Arity}} 141 | end; 142 | fallback_method(Toks) -> 143 | Toks1 = sneaky(Toks), 144 | %% sneaky replaces ?DEF by the atom sneaky -- 145 | %% and then tries to parse the result 146 | %% this is poor man's macro expansion 147 | case erl_parse:parse_form(Toks1) of 148 | {ok, Form} -> summarise(Form); 149 | {error, _} -> error 150 | end. 151 | 152 | sneaky([{'?',_},{var,Ln,_}|T]) -> [{atom,Ln,'sneaky'}|sneaky(T)]; 153 | sneaky([H|T]) -> [H|sneaky(T)]; 154 | sneaky([]) -> []. 155 | 156 | get_define_arity([{var,_,_}|T], N) -> get_define_arity(T, N+1); 157 | get_define_arity([{',',_}|T], N) -> get_define_arity(T, N); 158 | get_define_arity([{')',_}|_], N) -> N. 159 | 160 | %% in this context whitespace is viewed as a comment 161 | 162 | remove_whitespace([{white_space,_,_}|T]) -> remove_whitespace(T); 163 | remove_whitespace([{comment, _,_}|T]) -> remove_whitespace(T); 164 | remove_whitespace([H|T]) -> [H|remove_whitespace(T)]; 165 | remove_whitespace([]) -> []. 166 | 167 | header() -> 168 | [" 169 | 173 | "]. 174 | 175 | pass1({Type,_,_,I}) -> 176 | [header(Type),"
",quote(I),"
"]. 177 | 178 | header(X) -> 179 | ["

",quote(io_lib:format("~p",[X])),"

"]. 180 | 181 | classify_form(Str) -> 182 | R = case (catch classify_form0(Str)) of 183 | {'EXIT', _Why} -> 184 | unknown; 185 | Val -> 186 | Val 187 | end, 188 | %% io:format("R=~p~n",[R]), 189 | R. 190 | 191 | classify_form0(Str) -> 192 | case erl_scan:string(Str, 1) of 193 | {ok, Tokens, _} -> 194 | %% io:format("Tokens=~p~n",[Tokens]), 195 | summary(erl_parse:parse_form(Tokens)); 196 | _ -> 197 | oops 198 | end. 199 | 200 | summary({ok,{function,_,Name,Arity,_}}) -> {function,{Name,Arity}}; 201 | summary({ok,{attribute,_,spec,{S,_}}}) -> {spec, S}; 202 | summary({ok,{attribute,_,export,L}}) -> {export,L}; 203 | summary({ok,{attribute,_,type,{S,_,_}}}) -> {type,S}; 204 | summary({ok,{attribute,_,compile,_}}) -> compile; 205 | summary({ok, X}) -> {funny, X}; 206 | summary(X) -> {bad,X}. 207 | 208 | quote(I) -> 209 | elib2_misc:string2html(I). 210 | 211 | add_line_numbers(Form) -> 212 | L = [I || {line, I} <- elib2_misc:deep_find(fun is_line/1, Form)], 213 | {lists:min(L), lists:max(L)}. 214 | 215 | is_line({line,_}) -> true; 216 | is_line(_) -> false. 217 | 218 | %%---------------------------------------------------------------------- 219 | read_raw_forms(File) -> 220 | Str = elib2_misc:file2string(File), 221 | loop(erl_scan:tokens([], Str, {1,1}, [return,text]), []). 222 | 223 | loop({done, {eof,_}, eof}, L) -> 224 | reverse(L); 225 | loop({done, {ok, Toks, _}, eof}, L) -> 226 | reverse([Toks|L]); 227 | loop({done, {ok, Toks, Ln}, Str1}, L) -> 228 | loop(erl_scan:tokens([], Str1, Ln, [return,text]), 229 | [Toks|L]); 230 | loop({more, X}, L) -> 231 | loop(erl_scan:tokens(X, eof, {1,1}, [return,text]), L). 232 | 233 | %%---------------------------------------------------------------------- 234 | 235 | normalize_toks(Toks) -> 236 | [normalize_tok(I) || I <- Toks]. 237 | 238 | normalize_tok(Tok) -> 239 | %% this is the portable way ... 240 | [{_,Txt}] = erl_scan:token_info(Tok, [text]), 241 | Txt. 242 | 243 | %%---------------------------------------------------------------------- 244 | 245 | reduce_segments([{ws,X}|T], L) -> 246 | %% if it's all white_space then drop_this 247 | case remove_leading_ws(X) of 248 | [] -> reduce_segments(T, L); 249 | X1 -> reduce_segments(T, [{comment,reduce_comment(X1)}|L]) 250 | end; 251 | reduce_segments([{form1,K,V}|T], L) -> 252 | reduce_segments(T, [{K,V}|L]); 253 | reduce_segments([], L) -> 254 | reverse(L). 255 | 256 | remove_leading_ws([{white_space,_,_}|T]) -> remove_leading_ws(T); 257 | remove_leading_ws(X) -> X. 258 | 259 | reduce_comment(L) -> 260 | [reduce_comment1(I) || I <- L]. 261 | 262 | reduce_comment1({comment,_,C}) -> {str,C}; 263 | reduce_comment1({white_space,_,S}) -> {white_space,S}. 264 | 265 | 266 | -------------------------------------------------------------------------------- /src/convert_org_to_latex.erl: -------------------------------------------------------------------------------- 1 | -module(convert_org_to_latex). 2 | %% -compile(export_all). 3 | -import(lists, [reverse/1]). 4 | 5 | -export([test/0]). 6 | 7 | %% Note: rapidly hacked together - not beautiful 8 | %% no documentation 9 | 10 | %% first step break the entire thing into lines 11 | 12 | test() -> 13 | io:format("yes~n"), 14 | transform("./crypto_latex.org", "expand.tex"). 15 | 16 | transform(In, Out) -> 17 | io:format("Transforming:~p~n",[In]), 18 | Lines = elib2_misc:file2lines(In), 19 | Blocks0 = parse(Lines, []), 20 | _Root = filename:rootname(In), 21 | %% elib2_misc:dump(Root++"_blocks0.tmp", Blocks0), 22 | Blocks1 = expand_cmds(Blocks0), 23 | %% elib2_misc:dump(Root++"_blocks1.tmp", Blocks1), 24 | Blocks2 = pass1_code_includes(Blocks1), 25 | %% elib2_misc:dump(Root++"_blocks2.tmp", Blocks2), 26 | Blocks4 = merge_li(Blocks2, []), 27 | %% elib2_misc:dump(Root++"_blocks4.tmp", Blocks4), 28 | Z = to_tex(Blocks4), 29 | elib2_misc:check_io_list(Z), 30 | file:write_file(Out, Z), 31 | io:format("Written: ~s~n",[Out]). 32 | 33 | parse(["** " ++ H|T], L) -> parse(T, [{h2,remove_trailing_nl(H)}|L]); 34 | parse(["* " ++ H|T], L) -> parse(T, [{h1,remove_trailing_nl(H)}|L]); 35 | parse(["+ " ++ H|T], L) -> parse(T, [{li,H}|L]); 36 | parse([">" ++ H|T], L) -> parse(T, [{cmd,H}|L]); 37 | parse(["!!" ++ H|T], L) -> 38 | Val = do_bangs(H), 39 | parse(T, [Val|L]); 40 | parse([" " ++ H|T], L) -> 41 | {Pre, T1} = get_pre(T, [H]), 42 | parse(T1, [Pre|L]); 43 | parse(["\\begin{verbatim}" ++ H|T], L) -> 44 | {Pre, T1} = get_verbatim(T, [H]), 45 | parse(T1, [Pre|L]); 46 | 47 | parse([H|T], L) -> parse(T, [{str,H}|L]); 48 | parse([], L) -> reverse(L). 49 | 50 | merge_li([{li,_}|_]=A, L) -> 51 | {Li, B} = lists:splitwith(fun is_li/1, A), 52 | merge_li(B, [{ul,Li}|L]); 53 | merge_li([H|T], L) -> 54 | merge_li(T, [H|L]); 55 | merge_li([], L) -> 56 | reverse(L). 57 | 58 | do_bangs(X) -> 59 | X1 = elib2_misc:skip_blanks(X), 60 | case string:tokens(X1,":") of 61 | ["include_tagged",File,Tag|_] -> 62 | Str = get_erl_section(File, Tag), 63 | {boxed, File, Str}; 64 | Y -> 65 | io:format("Cannot do ~p~n",[Y]), 66 | exit(1) 67 | end. 68 | 69 | 70 | pass1_code_includes(B) -> 71 | %% do the code includes 72 | L = [{M,F,A} || {include_function,M,F,A} <- B], 73 | %% parse all the modules 74 | Mods = [M || {M,_,_} <- L], 75 | Mods1 = elib2_misc:remove_duplicates(Mods), 76 | io:format("Mods1:~p~n",[Mods1]), 77 | L1 = [{F, segment(F)} || F <- Mods1], 78 | [do_includes(I, L1) || I <- B]. 79 | 80 | segment(File) -> 81 | case filelib:is_file(File) of 82 | true -> 83 | segmenting:segment(File); 84 | false -> 85 | io:format("Missing file:~p~n",[File]), 86 | error 87 | end. 88 | 89 | do_includes({include_function,File,F,A}, L) -> 90 | case (catch fetch_code(File,F,A,L)) of 91 | {'EXIT', Why} -> 92 | io:format("Cannot find:~p/~p in ~p::~p~n",[F,A,File,Why]), 93 | {pre,"**** error ***"}; 94 | Toks -> 95 | {erl_tokens, #{file => File, 96 | func => F, 97 | arity=> A, 98 | toks => Toks}} 99 | end; 100 | do_includes(X, _) -> 101 | X. 102 | 103 | fetch_code(File, Func, Arity, L) -> 104 | {value,{_, S}} = lists:keysearch(File, 1, L), 105 | {value,{_, Val}} = lists:keysearch({function,{Func,Arity}}, 1, S), 106 | Val. 107 | 108 | add_if_non_nil([], L) -> L; 109 | add_if_non_nil(X, L) -> [X|L]. 110 | 111 | get_verbatim(["\\end{verbatim}" ++ _|T], L) -> 112 | {{pre, lists:append(reverse(L))}, T}; 113 | get_verbatim([H|T], L) -> 114 | get_verbatim(T, [H|L]); 115 | get_verbatim([], L) -> 116 | {{pre, lists:append(reverse(L))}, []}. 117 | 118 | 119 | get_pre([], L) -> 120 | {make_pre(L), []}; 121 | get_pre([H|T]=A, L) -> 122 | case is_blank(H) of 123 | true -> 124 | get_pre(T, ["\n"|L]); 125 | false -> 126 | case H of 127 | " " ++ Two -> 128 | get_pre(T, [Two|L]); 129 | _ -> 130 | {make_pre(L), A} 131 | end 132 | end. 133 | 134 | make_pre(L) -> 135 | Content = lists:append(reverse(L)), %% {node,pre,#{}, Content}. 136 | {pre, Content}. 137 | 138 | 139 | is_blank([$\s|T]) -> is_blank(T); 140 | is_blank([$\n|T]) -> is_blank(T); 141 | is_blank([]) -> true; 142 | is_blank(_) -> false. 143 | 144 | 145 | %%---------------------------------------------------------------------- 146 | 147 | remove_trailing_nl([$\n]) -> []; 148 | remove_trailing_nl([H|T]) -> [H|remove_trailing_nl(T)]; 149 | remove_trailing_nl([]) -> []. 150 | 151 | %%---------------------------------------------------------------------- 152 | 153 | expand_cmds(L) -> 154 | LF = {eval, fun local/3}, 155 | P = myshell:start(LF), 156 | expand_cmds(L, P, 1, []). 157 | 158 | expand_cmds([{cmd,"#" ++ X}|T], P, K, L) -> 159 | %% # command: Exprs is evaluated and the result inserted 160 | %% into the stream 161 | case cmd(P, X) of 162 | {ok, Val} -> 163 | L1 = add_if_non_nil(Val, L), 164 | expand_cmds(T, P, K, L1); 165 | {error, E} -> 166 | io:format("Command failed:~p~n",[E]), 167 | exit({badCommand,X}) 168 | end; 169 | expand_cmds([{cmd, "!" ++ X}|T], P, K, L) -> 170 | %% evaluate the command and throw away the 171 | %% result 172 | Obj = string2exprs(X), 173 | case Obj of 174 | [{atom,_,new}] -> 175 | %% reset the bindings and set the command 176 | %% number to 1 177 | myshell:stop(P), 178 | P1 = myshell:start({eval, fun local/3}), 179 | expand_cmds(T, P1, 1, L); 180 | _Exprs -> 181 | cmd(P, X), 182 | expand_cmds(T, P, K, L) 183 | end; 184 | expand_cmds([{cmd, X}|T], P, K, L) -> 185 | %% Print the command, then evaluate the command 186 | %% print the results 187 | Str = case cmd(P, X) of 188 | {ok, Val} -> 189 | lists:flatten(io_lib:format("~p~n",[Val])); 190 | {error, EBin} -> 191 | binary_to_list(EBin) 192 | end, 193 | expand_cmds(T, P, K+1, [{pre,integer_to_list(K) ++ ">" ++ X 194 | ++ Str}|L]); 195 | expand_cmds([H|T], P, K, L) -> 196 | expand_cmds(T, P, K, [H|L]); 197 | expand_cmds([], P, _, L) -> 198 | myshell:stop(P), 199 | reverse(L). 200 | 201 | cmd(P, X) -> 202 | myshell:cmd(P,X). 203 | 204 | 205 | string2exprs(Str) -> 206 | case erl_scan:tokens([], Str, 1) of 207 | {done, {ok, Toks, _}, []} -> 208 | case erl_parse:parse_exprs(Toks) of 209 | {ok, Exprs} -> 210 | Exprs; 211 | {error,{Line,Mod,Arg}} -> 212 | EStr = io_lib:format("~s",[apply(Mod,format_error,[Arg])]), 213 | Msg = lists:flatten(EStr), 214 | io:format("~n***PARSE ERROR in line:~w ~s~n", [Line,Msg]), 215 | io:format("Str=~s~n",[Str]), 216 | exit({badExpr,Str}) 217 | end; 218 | Other -> 219 | io:format("~n***SCAN ERROR:~p~n", [Other]), 220 | error 221 | end. 222 | 223 | eval_erl(Exprs, B0) -> 224 | case (catch erl_eval:exprs(Exprs, B0, {eval, fun local/3})) of 225 | {'EXIT', Why} -> 226 | io:format("Error:~p~n",[Why]), 227 | {value, "

** error see the log for why **

", B0}; 228 | {redirect, X} -> 229 | throw({redirect,X}); 230 | Other -> 231 | io:format("Here:~p~n",[Other]), 232 | Other 233 | end. 234 | 235 | local(def,[{atom,_,Name},{integer,_,Arity},{'fun',_,{clauses,Clauses}}], B0) -> 236 | put({func,Name,Arity}, Clauses), 237 | {value, true, B0}; 238 | local(bindings, [], B0) -> 239 | {value, B0, B0}; 240 | local(pre, [Expr], B0) -> 241 | {value, Val, B1} = eval_erl([Expr], B0), 242 | {value, pre(Val), B1}; 243 | local(show_all_bindings,[],B0) -> 244 | {value, pre(B0), B0}; 245 | local(show_bindings,[],B0) -> 246 | B1 = lists:sort(lists:keydelete('SYS', 1, B0)), 247 | {value, ["

Bindings

",pre(B1)], B0}; 248 | local(redirect, [Expr], B0) -> 249 | {value, Val, _B1} = eval_erl([Expr], B0), 250 | io:format("Redirecting to:~p~n",[Val]), 251 | throw({redirect, Val}); 252 | local(Name, Args, B0) -> 253 | %% io:format("Local:::::~p~n",[{Name,Args,B0}]), 254 | LF = {eval, fun local/3}, 255 | {Vals, _B1} = eval1(Args, B0, LF), 256 | %% io:format("Vals=~p~n",[Vals]), 257 | CCC = apply(orgmode_local, Name, Vals), 258 | %% io:format("CCC=~p~n",[CCC]), 259 | {value, CCC, B0}. 260 | 261 | %% local(Name, Args, B0) -> 262 | %% io:format("Local:~p~n",[{Name,Args,B0}]), 263 | %% Arity = length(Args), 264 | %% Clauses = get({func,Name,Arity}), 265 | %% LF = {eval, fun local/3}, 266 | %% {Vals, _B1} = eval1(Args, B0, LF), 267 | %% case erl_eval:match_clause(Clauses, Vals, [], LF) of 268 | %% {Body, Bs1} -> 269 | %% {value, ZZ, _} = erl_eval:exprs(Body, Bs1, LF), 270 | %% {value, ZZ, B0}; 271 | %% nomatch -> 272 | %% erlang:error({function_clause,[{local,Name,Args}]}) 273 | %% end. 274 | 275 | eval1([H|T], B0, LF) -> 276 | {value, H1, B1} = erl_eval:expr(H, B0, LF), 277 | {T1, B2} = eval1(T, B1, LF), 278 | {[H1|T1], B2}; 279 | eval1([], B, _) -> 280 | {[], B}. 281 | 282 | pre(X) -> 283 | ["
\n",quote(lists:flatten(io_lib:format("~p",[X]))), "
"]. 284 | 285 | quote("<" ++ T) -> "<" ++ quote(T); 286 | quote("&" ++ T) -> "&" ++ quote(T); 287 | quote([H|T]) -> [H|quote(T)]; 288 | quote([]) -> []. 289 | 290 | is_li({li,_}) -> true; 291 | is_li(_) -> false. 292 | 293 | 294 | %%---------------------------------------------------------------------- 295 | 296 | to_tex([{h1,X}|T]) -> 297 | ["\\chapter{",string2latex(X),"}\n" | to_tex(T)]; 298 | to_tex([{h2,X}|T]) -> 299 | ["\\section{",string2latex(X),"}\n" | to_tex(T)]; 300 | to_tex([{str,X}|T]) -> 301 | [X,to_tex(T)]; 302 | to_tex([{ul,L}|T]) -> 303 | ["\\begin{itemize}\n", 304 | to_tex(L), 305 | "\\end{itemize}\n", 306 | to_tex(T)]; 307 | to_tex([{li, X}|T]) -> 308 | ["\\item ", X, to_tex(T)]; 309 | to_tex([{pre, X}|T]) -> 310 | ["\\begin{verbatim}", X, "\\end{verbatim}\n", to_tex(T)]; 311 | to_tex([{boxed, File, X}|T]) -> 312 | %% very hacky -- but it works 313 | ["\\hfill\\erlFileName{",quote_under(File),"}", 314 | "{\\parskip=0pt\\begin{Verbatim}[frame=single]\n", 315 | X, 316 | "\\end{Verbatim}\n}\n", to_tex(T)]; 317 | to_tex([{erl_tokens, #{toks:=X,file:=File}}|T]) -> 318 | %% io:format("Tokens:~p~n",[X]), 319 | Vals = [Str || {_Tag,Str} <- X], 320 | to_tex([{boxed,File,Vals}|T]); 321 | to_tex([{img,#{src:=File, height:=H}}|T]) -> 322 | ["\n\n \\includegraphics[height=",H,"]{",File,"}\n\n",to_tex(T)]; 323 | to_tex([]) -> 324 | []. 325 | 326 | quote_under("_" ++ T) -> "\\_" ++ quote_under(T); 327 | quote_under([H|T]) -> [H|quote_under(T)]; 328 | quote_under([]) -> []. 329 | 330 | 331 | 332 | -spec string2latex(string()) -> string(). 333 | 334 | string2latex("%" ++ T) -> "\\%" ++ string2latex(T); 335 | string2latex("$" ++ T) -> "\\$" ++ string2latex(T); 336 | string2latex("#" ++ T) -> "\\#" ++ string2latex(T); 337 | string2latex("{" ++ T) -> "\\{" ++ string2latex(T); 338 | string2latex("}" ++ T) -> "\\}" ++ string2latex(T); 339 | string2latex("_" ++ T) -> "\\_" ++ string2latex(T); 340 | string2latex("<" ++ T) -> "$<$" ++ string2latex(T); 341 | string2latex(">" ++ T) -> "$>$" ++ string2latex(T); 342 | string2latex("\\" ++ T) -> "\\\\" ++ string2latex(T); 343 | string2latex("<" ++ T) -> "$<$" ++ string2latex(T); 344 | string2latex("&" ++ T) -> "\\&" ++ string2latex(T); 345 | string2latex("&" ++ T) -> "\\&" ++ string2latex(T); 346 | string2latex([{ent,"ldquo"}|T]) -> "``" ++ string2latex(T); 347 | string2latex([{ent,"rdquo"}|T]) -> "''" ++ string2latex(T); 348 | 349 | string2latex([{em,X}|T]) -> 350 | X1 = string2latex(X), 351 | ["{\\it ",X1,"}", string2latex(T)]; 352 | string2latex([{yellow,X}|T]) -> 353 | X1 = string2latex(X), 354 | ["{\\colorbox{yellow}{",X1,"}", string2latex(T)]; 355 | string2latex([{strike,X}|T]) -> 356 | X1 = string2latex(X), 357 | ["{\\sout{ ",X1,"}", string2latex(T)]; 358 | string2latex([{code,X}|T]) -> 359 | %% fixme if we have a plus 360 | ["{\\verb+",X,"+", string2latex(T)]; 361 | string2latex([H|T]) when is_integer(H) -> [H|string2latex(T)]; 362 | string2latex([]) -> []. 363 | 364 | 365 | 366 | -spec get_erl_section(FileName::string(), Tag::string()) -> string(). 367 | 368 | get_erl_section(File, Tag) -> 369 | case file:read_file(File) of 370 | {ok, B} -> 371 | Stuff = get_stuff(binary_to_list(B), File, Tag), 372 | Stuff1 = expand_tabs(Stuff), 373 | %% io:format("Stuff=~p~n",[Stuff2]), 374 | Stuff1 375 | ; 376 | {error, _} -> 377 | Str = lists:flatten(io_lib:format( 378 | "*** ERROR: Missing File: ~s~n",[File])), 379 | exit({eGetErlSection,File,Str}) 380 | end. 381 | 382 | get_stuff(Str, _, all) -> Str; 383 | get_stuff(Str, File, Tag) -> get_stuff1(Str, File, Tag). 384 | 385 | get_stuff1("%% START:" ++ T, File, Tag) -> 386 | case matches(T, Tag) of 387 | {yes, T1} -> get_content(T1, File, Tag, []); 388 | no -> get_stuff1(T, File, Tag) 389 | end; 390 | get_stuff1([_|T], File, Tag) -> 391 | get_stuff1(T, File, Tag); 392 | get_stuff1([], File, Tag) -> 393 | throw({eGetErlSection, noStartTag, File, Tag}). 394 | 395 | get_content("%% START:" ++ T, File, Tag, L) -> 396 | get_content(skip_to_nl(T), File, Tag, L); 397 | get_content("%% END:" ++ T, File, Tag, L) -> 398 | case matches(T, Tag) of 399 | {yes, _} -> 400 | reverse(L); 401 | no -> 402 | get_content(skip_to_nl(T), File, Tag, L) 403 | end; 404 | get_content([H|T], File, Tag, L) -> 405 | get_content(T, File, Tag, [H|L]); 406 | get_content([], File, Tag, _) -> 407 | throw({eGetErlSection, eofInTag, File, Tag}). 408 | 409 | matches(T, []) -> {yes, skip_to_nl(T)}; 410 | matches([H|T1], [H|T2]) -> matches(T1, T2); 411 | matches(_, _) -> no. 412 | 413 | expand_tabs(Xs) -> expand_tabs(0,Xs). 414 | 415 | %% N is the current column 416 | expand_tabs(_N,[]) -> 417 | []; 418 | expand_tabs(_, [$\n|T]) -> 419 | [$\n|expand_tabs(0, T)]; 420 | expand_tabs(N,[$\t|Xs]) -> 421 | N1 = 8*(N div 8) + 8, 422 | [$\s || _ <- lists:seq(N,N1-1)] ++ expand_tabs(N1,Xs); 423 | expand_tabs(N,[X|Xs]) -> 424 | [X|expand_tabs(N+1,Xs)]. 425 | %%---------------------------------------------------------------------- 426 | %% @doc skip all characters up to and including new line 427 | 428 | -spec skip_to_nl(string()) -> string(). 429 | 430 | skip_to_nl([$\n|T]) -> T; 431 | skip_to_nl([]) -> []; 432 | skip_to_nl([_|T]) -> skip_to_nl(T). 433 | -------------------------------------------------------------------------------- /src/shell.erl: -------------------------------------------------------------------------------- 1 | %% 2 | %% %CopyrightBegin% 3 | %% 4 | %% Copyright Ericsson AB 1996-2013. All Rights Reserved. 5 | %% 6 | %% The contents of this file are subject to the Erlang Public License, 7 | %% Version 1.1, (the "License"); you may not use this file except in 8 | %% compliance with the License. You should have received a copy of the 9 | %% Erlang Public License along with this software. If not, it can be 10 | %% retrieved online at http://www.erlang.org/. 11 | %% 12 | %% Software distributed under the License is distributed on an "AS IS" 13 | %% basis, WITHOUT WARRANTY OF ANY KIND, either express or implied. See 14 | %% the License for the specific language governing rights and limitations 15 | %% under the License. 16 | %% 17 | %% %CopyrightEnd% 18 | %% 19 | -module(shell). 20 | 21 | 22 | -export([start/0, start/1, start/2, server/1, server/2, history/1, results/1]). 23 | -export([whereis_evaluator/0, whereis_evaluator/1]). 24 | -export([start_restricted/1, stop_restricted/0]). 25 | -export([local_allowed/3, non_local_allowed/3]). 26 | -export([prompt_func/1, strings/1]). 27 | -export([report_exception/4]). 28 | -export([pp/3,enc/0,severity_tag/1,encoding/0]). 29 | 30 | 31 | -define(LINEMAX, 30). 32 | -define(CHAR_MAX, 60). 33 | -define(DEF_HISTORY, 20). 34 | -define(DEF_RESULTS, 20). 35 | -define(DEF_CATCH_EXCEPTION, false). 36 | -define(DEF_PROMPT_FUNC, default). 37 | -define(DEF_STRINGS, true). 38 | 39 | -define(RECORDS, shell_records). 40 | 41 | -define(MAXSIZE_HEAPBINARY, 64). 42 | 43 | %% When used as the fallback restricted shell callback module... 44 | local_allowed(q,[],State) -> 45 | {true,State}; 46 | local_allowed(_,_,State) -> 47 | {false,State}. 48 | 49 | non_local_allowed({init,stop},[],State) -> 50 | {true,State}; 51 | non_local_allowed(_,_,State) -> 52 | {false,State}. 53 | 54 | -spec start() -> pid(). 55 | 56 | start() -> 57 | start(false, false). 58 | 59 | start(init) -> 60 | start(false, true); 61 | start(NoCtrlG) -> 62 | start(NoCtrlG, false). 63 | 64 | start(NoCtrlG, StartSync) -> 65 | _ = code:ensure_loaded(user_default), 66 | spawn(fun() -> server(NoCtrlG, StartSync) end). 67 | 68 | %% Find the pid of the current evaluator process. 69 | -spec whereis_evaluator() -> 'undefined' | pid(). 70 | 71 | whereis_evaluator() -> 72 | %% locate top group leader, always registered as user 73 | %% can be implemented by group (normally) or user 74 | %% (if oldshell or noshell) 75 | case whereis(user) of 76 | undefined -> 77 | undefined; 78 | User -> 79 | %% get user_drv pid from group, or shell pid from user 80 | case group:interfaces(User) of 81 | [] -> % old- or noshell 82 | case user:interfaces(User) of 83 | [] -> 84 | undefined; 85 | [{shell,Shell}] -> 86 | whereis_evaluator(Shell) 87 | end; 88 | [{user_drv,UserDrv}] -> 89 | %% get current group pid from user_drv 90 | case user_drv:interfaces(UserDrv) of 91 | [] -> 92 | undefined; 93 | [{current_group,Group}] -> 94 | %% get shell pid from group 95 | GrIfs = group:interfaces(Group), 96 | case lists:keyfind(shell, 1, GrIfs) of 97 | {shell, Shell} -> 98 | whereis_evaluator(Shell); 99 | false -> 100 | undefined 101 | end 102 | end 103 | end 104 | end. 105 | 106 | -spec whereis_evaluator(pid()) -> 'undefined' | pid(). 107 | 108 | whereis_evaluator(Shell) -> 109 | case process_info(Shell, dictionary) of 110 | {dictionary,Dict} -> 111 | case lists:keyfind(evaluator, 1, Dict) of 112 | {_, Eval} when is_pid(Eval) -> 113 | Eval; 114 | _ -> 115 | undefined 116 | end; 117 | _ -> 118 | undefined 119 | end. 120 | 121 | %% Call this function to start a user restricted shell 122 | %% from a normal shell session. 123 | -spec start_restricted(Module) -> {'error', Reason} when 124 | Module :: module(), 125 | Reason :: code:load_error_rsn(). 126 | 127 | start_restricted(RShMod) when is_atom(RShMod) -> 128 | case code:ensure_loaded(RShMod) of 129 | {module,RShMod} -> 130 | application:set_env(stdlib, restricted_shell, RShMod), 131 | exit(restricted_shell_started); 132 | {error,What} = Error -> 133 | error_logger:error_report( 134 | lists:flatten( 135 | io_lib:fwrite( 136 | "Restricted shell module ~w not found: ~tp\n", 137 | [RShMod,What]))), 138 | Error 139 | end. 140 | 141 | -spec stop_restricted() -> no_return(). 142 | 143 | stop_restricted() -> 144 | application:unset_env(stdlib, restricted_shell), 145 | exit(restricted_shell_stopped). 146 | 147 | -spec server(boolean(), boolean()) -> 'terminated'. 148 | 149 | server(NoCtrlG, StartSync) -> 150 | put(no_control_g, NoCtrlG), 151 | server(StartSync). 152 | 153 | 154 | %%% The shell should not start until the system is up and running. 155 | %%% We subscribe with init to get a notification of when. 156 | 157 | %%% In older releases we didn't syncronize the shell with init, but let it 158 | %%% start in parallell with other system processes. This was bad since 159 | %%% accessing the shell too early could interfere with the boot procedure. 160 | %%% Still, by means of a flag, we make it possible to start the shell the 161 | %%% old way (for backwards compatibility reasons). This should however not 162 | %%% be used unless for very special reasons necessary. 163 | 164 | -spec server(boolean()) -> 'terminated'. 165 | 166 | server(StartSync) -> 167 | case init:get_argument(async_shell_start) of 168 | {ok,_} -> 169 | ok; % no sync with init 170 | _ when not StartSync -> 171 | ok; 172 | _ -> 173 | case init:notify_when_started(self()) of 174 | started -> 175 | ok; 176 | _ -> 177 | init:wait_until_started() 178 | end 179 | end, 180 | %% Our spawner has fixed the process groups. 181 | Bs = erl_eval:new_bindings(), 182 | 183 | %% Use an Ets table for record definitions. It takes too long to 184 | %% send a huge term to and from the evaluator. Ets makes it 185 | %% possible to have thousands of record definitions. 186 | RT = ets:new(?RECORDS, [public,ordered_set]), 187 | _ = initiate_records(Bs, RT), 188 | process_flag(trap_exit, true), 189 | 190 | %% Check if we're in user restricted mode. 191 | RShErr = 192 | case application:get_env(stdlib, restricted_shell) of 193 | {ok,RShMod} when is_atom(RShMod) -> 194 | io:fwrite(<<"Restricted ">>, []), 195 | case code:ensure_loaded(RShMod) of 196 | {module,RShMod} -> 197 | undefined; 198 | {error,What} -> 199 | {RShMod,What} 200 | end; 201 | {ok, Term} -> 202 | {Term,not_an_atom}; 203 | undefined -> 204 | undefined 205 | end, 206 | 207 | case get(no_control_g) of 208 | true -> 209 | io:fwrite(<<"Eshell V~s\n">>, [erlang:system_info(version)]); 210 | _undefined_or_false -> 211 | io:fwrite(<<"Eshell V~s (abort with ^G)\n">>, 212 | [erlang:system_info(version)]) 213 | end, 214 | erase(no_control_g), 215 | 216 | case RShErr of 217 | undefined -> 218 | ok; 219 | {RShMod2,What2} -> 220 | io:fwrite( 221 | ("Warning! Restricted shell module ~w not found: ~tp.\n" 222 | "Only the commands q() and init:stop() will be allowed!\n"), 223 | [RShMod2,What2]), 224 | application:set_env(stdlib, restricted_shell, ?MODULE) 225 | end, 226 | 227 | {History,Results} = check_and_get_history_and_results(), 228 | server_loop(0, start_eval(Bs, RT, []), Bs, RT, [], History, Results). 229 | 230 | server_loop(N0, Eval_0, Bs00, RT, Ds00, History0, Results0) -> 231 | N = N0 + 1, 232 | {Eval_1,Bs0,Ds0,Prompt} = prompt(N, Eval_0, Bs00, RT, Ds00), 233 | {Res,Eval0} = get_command(Prompt, Eval_1, Bs0, RT, Ds0), 234 | case Res of 235 | {ok,Es0} -> 236 | case expand_hist(Es0, N) of 237 | {ok,Es} -> 238 | {V,Eval,Bs,Ds} = shell_cmd(Es, Eval0, Bs0, RT, Ds0, cmd), 239 | {History,Results} = check_and_get_history_and_results(), 240 | add_cmd(N, Es, V), 241 | HB1 = del_cmd(command, N - History, N - History0, false), 242 | HB = del_cmd(result, N - Results, N - Results0, HB1), 243 | %% The following test makes sure that large binaries 244 | %% (outside of the heap) are garbage collected as soon 245 | %% as possible. 246 | if 247 | HB -> 248 | garb(self()); 249 | true -> 250 | ok 251 | end, 252 | server_loop(N, Eval, Bs, RT, Ds, History, Results); 253 | {error,E} -> 254 | fwrite_severity(benign, <<"~ts">>, [E]), 255 | server_loop(N0, Eval0, Bs0, RT, Ds0, History0, Results0) 256 | end; 257 | {error,{Line,Mod,What}} -> 258 | fwrite_severity(benign, <<"~w: ~ts">>, 259 | [Line, Mod:format_error(What)]), 260 | server_loop(N0, Eval0, Bs0, RT, Ds0, History0, Results0); 261 | {error,terminated} -> %Io process terminated 262 | exit(Eval0, kill), 263 | terminated; 264 | {error,interrupted} -> %Io process interrupted us 265 | exit(Eval0, kill), 266 | {_,Eval,_,_} = shell_rep(Eval0, Bs0, RT, Ds0), 267 | server_loop(N0, Eval, Bs0, RT, Ds0, History0, Results0); 268 | {error,tokens} -> %Most probably character > 255 269 | fwrite_severity(benign, <<"~w: Invalid tokens.">>, 270 | [N]), 271 | server_loop(N0, Eval0, Bs0, RT, Ds0, History0, Results0); 272 | eof -> 273 | fwrite_severity(fatal, <<"Terminating erlang (~w)">>, [node()]), 274 | halt() 275 | end. 276 | 277 | get_command(Prompt, Eval, Bs, RT, Ds) -> 278 | Parse = 279 | fun() -> 280 | exit( 281 | case 282 | io:scan_erl_exprs(group_leader(), Prompt, 1) 283 | of 284 | {ok,Toks,_EndPos} -> 285 | erl_parse:parse_exprs(Toks); 286 | {eof,_EndPos} -> 287 | eof; 288 | {error,ErrorInfo,_EndPos} -> 289 | %% Skip the rest of the line: 290 | Opts = io:getopts(), 291 | TmpOpts = lists:keyreplace(echo, 1, Opts, 292 | {echo, false}), 293 | _ = io:setopts(TmpOpts), 294 | _ = io:get_line(''), 295 | _ = io:setopts(Opts), 296 | {error,ErrorInfo}; 297 | Else -> 298 | Else 299 | end 300 | ) 301 | end, 302 | Pid = spawn_link(Parse), 303 | get_command1(Pid, Eval, Bs, RT, Ds). 304 | 305 | get_command1(Pid, Eval, Bs, RT, Ds) -> 306 | receive 307 | {'EXIT', Pid, Res} -> 308 | {Res, Eval}; 309 | {'EXIT', Eval, {Reason,Stacktrace}} -> 310 | report_exception(error, {Reason,Stacktrace}, RT), 311 | get_command1(Pid, start_eval(Bs, RT, Ds), Bs, RT, Ds); 312 | {'EXIT', Eval, Reason} -> 313 | report_exception(error, {Reason,[]}, RT), 314 | get_command1(Pid, start_eval(Bs, RT, Ds), Bs, RT, Ds) 315 | end. 316 | 317 | prompt(N, Eval0, Bs0, RT, Ds0) -> 318 | case get_prompt_func() of 319 | {M,F} -> 320 | L = [{history,N}], 321 | C = {call,1,{remote,1,{atom,1,M},{atom,1,F}},[{value,1,L}]}, 322 | {V,Eval,Bs,Ds} = shell_cmd([C], Eval0, Bs0, RT, Ds0, pmt), 323 | {Eval,Bs,Ds,case V of 324 | {pmt,Val} -> 325 | Val; 326 | _ -> 327 | bad_prompt_func({M,F}), 328 | default_prompt(N) 329 | end}; 330 | default -> 331 | {Eval0,Bs0,Ds0,default_prompt(N)} 332 | end. 333 | 334 | get_prompt_func() -> 335 | case application:get_env(stdlib, shell_prompt_func) of 336 | {ok,{M,F}=PromptFunc} when is_atom(M), is_atom(F) -> 337 | PromptFunc; 338 | {ok,default=Default} -> 339 | Default; 340 | {ok,Term} -> 341 | bad_prompt_func(Term), 342 | default; 343 | undefined -> 344 | default 345 | end. 346 | 347 | bad_prompt_func(M) -> 348 | fwrite_severity(benign, "Bad prompt function: ~tp", [M]). 349 | 350 | default_prompt(N) -> 351 | %% Don't bother flattening the list irrespective of what the 352 | %% I/O-protocol states. 353 | case is_alive() of 354 | true -> io_lib:format(<<"(~s)~w> ">>, [node(), N]); 355 | false -> io_lib:format(<<"~w> ">>, [N]) 356 | end. 357 | 358 | %% expand_hist(Expressions, CommandNumber) 359 | %% Preprocess the expression list replacing all history list commands 360 | %% with their expansions. 361 | 362 | expand_hist(Es, C) -> 363 | catch {ok,expand_exprs(Es, C)}. 364 | 365 | expand_exprs([E|Es], C) -> 366 | [expand_expr(E, C)|expand_exprs(Es, C)]; 367 | expand_exprs([], _C) -> 368 | []. 369 | 370 | expand_expr({cons,L,H,T}, C) -> 371 | {cons,L,expand_expr(H, C),expand_expr(T, C)}; 372 | expand_expr({lc,L,E,Qs}, C) -> 373 | {lc,L,expand_expr(E, C),expand_quals(Qs, C)}; 374 | expand_expr({bc,L,E,Qs}, C) -> 375 | {bc,L,expand_expr(E, C),expand_quals(Qs, C)}; 376 | expand_expr({tuple,L,Elts}, C) -> 377 | {tuple,L,expand_exprs(Elts, C)}; 378 | expand_expr({map,L,Es}, C) -> 379 | {map,L,expand_exprs(Es, C)}; 380 | expand_expr({map,L,Arg,Es}, C) -> 381 | {map,L,expand_expr(Arg, C),expand_exprs(Es, C)}; 382 | expand_expr({map_field_assoc,L,K,V}, C) -> 383 | {map_field_assoc,L,expand_expr(K, C),expand_expr(V, C)}; 384 | expand_expr({map_field_exact,L,K,V}, C) -> 385 | {map_field_exact,L,expand_expr(K, C),expand_expr(V, C)}; 386 | expand_expr({record_index,L,Name,F}, C) -> 387 | {record_index,L,Name,expand_expr(F, C)}; 388 | expand_expr({record,L,Name,Is}, C) -> 389 | {record,L,Name,expand_fields(Is, C)}; 390 | expand_expr({record_field,L,R,Name,F}, C) -> 391 | {record_field,L,expand_expr(R, C),Name,expand_expr(F, C)}; 392 | expand_expr({record,L,R,Name,Ups}, C) -> 393 | {record,L,expand_expr(R, C),Name,expand_fields(Ups, C)}; 394 | expand_expr({record_field,L,R,F}, C) -> %This is really illegal! 395 | {record_field,L,expand_expr(R, C),expand_expr(F, C)}; 396 | expand_expr({block,L,Es}, C) -> 397 | {block,L,expand_exprs(Es, C)}; 398 | expand_expr({'if',L,Cs}, C) -> 399 | {'if',L,expand_cs(Cs, C)}; 400 | expand_expr({'case',L,E,Cs}, C) -> 401 | {'case',L,expand_expr(E, C),expand_cs(Cs, C)}; 402 | expand_expr({'try',L,Es,Scs,Ccs,As}, C) -> 403 | {'try',L,expand_exprs(Es, C),expand_cs(Scs, C), 404 | expand_cs(Ccs, C),expand_exprs(As, C)}; 405 | expand_expr({'receive',L,Cs}, C) -> 406 | {'receive',L,expand_cs(Cs, C)}; 407 | expand_expr({'receive',L,Cs,To,ToEs}, C) -> 408 | {'receive',L,expand_cs(Cs, C), expand_expr(To, C), expand_exprs(ToEs, C)}; 409 | expand_expr({call,L,{atom,_,e},[N]}, C) -> 410 | case get_cmd(N, C) of 411 | {undefined,_,_} -> 412 | no_command(N); 413 | {[Ce],_V,_CommandN} -> 414 | Ce; 415 | {Ces,_V,_CommandN} when is_list(Ces) -> 416 | {block,L,Ces} 417 | end; 418 | expand_expr({call,_L,{atom,_,v},[N]}, C) -> 419 | case get_cmd(N, C) of 420 | {_,undefined,_} -> 421 | no_command(N); 422 | {Ces,V,CommandN} when is_list(Ces) -> 423 | {value,CommandN,V} 424 | end; 425 | expand_expr({call,L,F,Args}, C) -> 426 | {call,L,expand_expr(F, C),expand_exprs(Args, C)}; 427 | expand_expr({'catch',L,E}, C) -> 428 | {'catch',L,expand_expr(E, C)}; 429 | expand_expr({match,L,Lhs,Rhs}, C) -> 430 | {match,L,Lhs,expand_expr(Rhs, C)}; 431 | expand_expr({op,L,Op,Arg}, C) -> 432 | {op,L,Op,expand_expr(Arg, C)}; 433 | expand_expr({op,L,Op,Larg,Rarg}, C) -> 434 | {op,L,Op,expand_expr(Larg, C),expand_expr(Rarg, C)}; 435 | expand_expr({remote,L,M,F}, C) -> 436 | {remote,L,expand_expr(M, C),expand_expr(F, C)}; 437 | expand_expr({'fun',L,{clauses,Cs}}, C) -> 438 | {'fun',L,{clauses,expand_exprs(Cs, C)}}; 439 | expand_expr({named_fun,L,Name,Cs}, C) -> 440 | {named_fun,L,Name,expand_exprs(Cs, C)}; 441 | expand_expr({clause,L,H,G,B}, C) -> 442 | %% Could expand H and G, but then erl_eval has to be changed as well. 443 | {clause,L,H, G, expand_exprs(B, C)}; 444 | expand_expr({bin,L,Fs}, C) -> 445 | {bin,L,expand_bin_elements(Fs, C)}; 446 | expand_expr(E, _C) -> % Constants. 447 | E. 448 | 449 | expand_cs([{clause,L,P,G,B}|Cs], C) -> 450 | [{clause,L,P,G,expand_exprs(B, C)}|expand_cs(Cs, C)]; 451 | expand_cs([], _C) -> 452 | []. 453 | 454 | expand_fields([{record_field,L,F,V}|Fs], C) -> 455 | [{record_field,L,expand_expr(F, C),expand_expr(V, C)}| 456 | expand_fields(Fs, C)]; 457 | expand_fields([], _C) -> []. 458 | 459 | expand_quals([{generate,L,P,E}|Qs], C) -> 460 | [{generate,L,P,expand_expr(E, C)}|expand_quals(Qs, C)]; 461 | expand_quals([{b_generate,L,P,E}|Qs], C) -> 462 | [{b_generate,L,P,expand_expr(E, C)}|expand_quals(Qs, C)]; 463 | expand_quals([E|Qs], C) -> 464 | [expand_expr(E, C)|expand_quals(Qs, C)]; 465 | expand_quals([], _C) -> []. 466 | 467 | expand_bin_elements([], _C) -> 468 | []; 469 | expand_bin_elements([{bin_element,L,E,Sz,Ts}|Fs], C) -> 470 | [{bin_element,L,expand_expr(E, C),Sz,Ts}|expand_bin_elements(Fs, C)]. 471 | 472 | no_command(N) -> 473 | throw({error, 474 | io_lib:fwrite(<<"~ts: command not found">>, 475 | [erl_pp:expr(N, enc())])}). 476 | 477 | %% add_cmd(Number, Expressions, Value) 478 | %% get_cmd(Number, CurrentCommand) 479 | %% del_cmd(Number, NewN, OldN, HasBin0) -> bool() 480 | 481 | add_cmd(N, Es, V) -> 482 | put({command,N}, Es), 483 | put({result,N}, V). 484 | 485 | getc(N) -> 486 | {get({command,N}), get({result,N}), N}. 487 | 488 | get_cmd(Num, C) -> 489 | case catch erl_eval:expr(Num, erl_eval:new_bindings()) of 490 | {value,N,_} when N < 0 -> getc(C+N); 491 | {value,N,_} -> getc(N); 492 | _Other -> {undefined,undefined,undefined} 493 | end. 494 | 495 | del_cmd(_Type, N, N0, HasBin) when N < N0 -> 496 | HasBin; 497 | del_cmd(Type, N, N0, HasBin0) -> 498 | T = erase({Type,N}), 499 | HasBin = HasBin0 orelse has_binary(T), 500 | del_cmd(Type, N-1, N0, HasBin). 501 | 502 | has_binary(T) -> 503 | try has_bin(T), false 504 | catch true=Thrown -> Thrown 505 | end. 506 | 507 | has_bin(T) when is_tuple(T) -> 508 | has_bin(T, tuple_size(T)); 509 | has_bin([E | Es]) -> 510 | has_bin(E), 511 | has_bin(Es); 512 | has_bin(B) when byte_size(B) > ?MAXSIZE_HEAPBINARY -> 513 | throw(true); 514 | has_bin(T) -> 515 | T. 516 | 517 | has_bin(T, 0) -> 518 | T; 519 | has_bin(T, I) -> 520 | has_bin(element(I, T)), 521 | has_bin(T, I - 1). 522 | 523 | %% shell_cmd(Sequence, Evaluator, Bindings, RecordTable, Dictionary, What) 524 | %% shell_rep(Evaluator, Bindings, RecordTable, Dictionary) -> 525 | %% {Value,Evaluator,Bindings,Dictionary} 526 | %% Send a command to the evaluator and wait for the reply. Start a new 527 | %% evaluator if necessary. 528 | %% What = pmt | cmd. When evaluating a prompt ('pmt') the evaluated value 529 | %% must not be displayed, and it has to be returned. 530 | 531 | shell_cmd(Es, Eval, Bs, RT, Ds, W) -> 532 | Eval ! {shell_cmd,self(),{eval,Es}, W}, 533 | shell_rep(Eval, Bs, RT, Ds). 534 | 535 | shell_rep(Ev, Bs0, RT, Ds0) -> 536 | receive 537 | {shell_rep,Ev,{value,V,Bs,Ds}} -> 538 | {V,Ev,Bs,Ds}; 539 | {shell_rep,Ev,{command_error,{Line,M,Error}}} -> 540 | fwrite_severity(benign, <<"~w: ~ts">>, 541 | [Line, M:format_error(Error)]), 542 | {{'EXIT',Error},Ev,Bs0,Ds0}; 543 | {shell_req,Ev,get_cmd} -> 544 | Ev ! {shell_rep,self(),get()}, 545 | shell_rep(Ev, Bs0, RT, Ds0); 546 | {shell_req,Ev,exit} -> 547 | Ev ! {shell_rep,self(),exit}, 548 | exit(normal); 549 | {shell_req,Ev,{update_dict,Ds}} -> % Update dictionary 550 | Ev ! {shell_rep,self(),ok}, 551 | shell_rep(Ev, Bs0, RT, Ds); 552 | {ev_exit,{Ev,Class,Reason0}} -> % It has exited unnaturally 553 | receive {'EXIT',Ev,normal} -> ok end, 554 | report_exception(Class, Reason0, RT), 555 | Reason = nocatch(Class, Reason0), 556 | {{'EXIT',Reason},start_eval(Bs0, RT, Ds0), Bs0, Ds0}; 557 | {ev_caught,{Ev,Class,Reason0}} -> % catch_exception is in effect 558 | report_exception(Class, benign, Reason0, RT), 559 | Reason = nocatch(Class, Reason0), 560 | {{'EXIT',Reason},Ev,Bs0,Ds0}; 561 | {'EXIT',_Id,interrupt} -> % Someone interrupted us 562 | exit(Ev, kill), 563 | shell_rep(Ev, Bs0, RT, Ds0); 564 | {'EXIT',Ev,{Reason,Stacktrace}} -> 565 | report_exception(exit, {Reason,Stacktrace}, RT), 566 | {{'EXIT',Reason},start_eval(Bs0, RT, Ds0), Bs0, Ds0}; 567 | {'EXIT',Ev,Reason} -> 568 | report_exception(exit, {Reason,[]}, RT), 569 | {{'EXIT',Reason},start_eval(Bs0, RT, Ds0), Bs0, Ds0}; 570 | {'EXIT',_Id,R} -> 571 | exit(Ev, R), 572 | exit(R); 573 | _Other -> % Ignore everything else 574 | shell_rep(Ev, Bs0, RT, Ds0) 575 | end. 576 | 577 | nocatch(throw, {Term,Stack}) -> 578 | {{nocatch,Term},Stack}; 579 | nocatch(error, Reason) -> 580 | Reason; 581 | nocatch(exit, Reason) -> 582 | Reason. 583 | 584 | report_exception(Class, Reason, RT) -> 585 | %% always servius 586 | report_exception(Class, serious, Reason, RT). 587 | 588 | report_exception(Class, Severity, {Reason,Stacktrace}, RT) -> 589 | %% typically Class=error, Severity=serious Reason = {badmatch,4} 590 | %% Tacktrace = [{erl-eval,expr,3,[]}] 591 | Tag = severity_tag(Severity), 592 | %% Tag = <<*>> <**>> <<"**">> 593 | I = iolist_size(Tag) + 1, %% 4 or something 594 | PF = fun(Term, I1) -> pp(Term, I1, RT) end, 595 | SF = fun(M, _F, _A) -> (M =:= erl_eval) or (M =:= ?MODULE) end, 596 | Enc = encoding(), 597 | Str = lib:format_exception(I, Class, Reason, Stacktrace, SF, PF, Enc), 598 | io:requests([{put_chars, latin1, Tag}, 599 | {put_chars, unicode, Str}, 600 | nl]). 601 | 602 | start_eval(Bs, RT, Ds) -> 603 | Self = self(), 604 | Eval = spawn_link(fun() -> evaluator(Self, Bs, RT, Ds) end), 605 | put(evaluator, Eval), 606 | Eval. 607 | 608 | %% evaluator(Shell, Bindings, RecordTable, ProcessDictionary) 609 | %% Evaluate expressions from the shell. Use the "old" variable bindings 610 | %% and dictionary. 611 | 612 | evaluator(Shell, Bs, RT, Ds) -> 613 | init_dict(Ds), 614 | case application:get_env(stdlib, restricted_shell) of 615 | undefined -> 616 | eval_loop(Shell, Bs, RT); 617 | {ok,RShMod} -> 618 | case get(restricted_shell_state) of 619 | undefined -> put(restricted_shell_state, []); 620 | _ -> ok 621 | end, 622 | put(restricted_expr_state, []), 623 | restricted_eval_loop(Shell, Bs, RT, RShMod) 624 | end. 625 | 626 | eval_loop(Shell, Bs0, RT) -> 627 | receive 628 | {shell_cmd,Shell,{eval,Es},W} -> 629 | Ef = {value, 630 | fun(MForFun, As) -> apply_fun(MForFun, As, Shell) end}, 631 | Lf = local_func_handler(Shell, RT, Ef), 632 | Bs = eval_exprs(Es, Shell, Bs0, RT, Lf, Ef, W), 633 | eval_loop(Shell, Bs, RT) 634 | end. 635 | 636 | restricted_eval_loop(Shell, Bs0, RT, RShMod) -> 637 | receive 638 | {shell_cmd,Shell,{eval,Es}, W} -> 639 | {LFH,NLFH} = restrict_handlers(RShMod, Shell, RT), 640 | put(restricted_expr_state, []), 641 | Bs = eval_exprs(Es, Shell, Bs0, RT, {eval,LFH}, {value,NLFH}, W), 642 | restricted_eval_loop(Shell, Bs, RT, RShMod) 643 | end. 644 | 645 | eval_exprs(Es, Shell, Bs0, RT, Lf, Ef, W) -> 646 | try 647 | {R,Bs2} = exprs(Es, Bs0, RT, Lf, Ef, W), 648 | Shell ! {shell_rep,self(),R}, 649 | Bs2 650 | catch 651 | exit:normal -> 652 | exit(normal); 653 | Class:Reason -> 654 | Stacktrace = erlang:get_stacktrace(), 655 | M = {self(),Class,{Reason,Stacktrace}}, 656 | case do_catch(Class, Reason) of 657 | true -> 658 | Shell ! {ev_caught,M}, 659 | Bs0; 660 | false -> 661 | %% We don't want the ERROR REPORT generated by the 662 | %% emulator. Note: exit(kill) needs nothing special. 663 | {links,LPs} = process_info(self(), links), 664 | ER = nocatch(Class, {Reason,Stacktrace}), 665 | lists:foreach(fun(P) -> exit(P, ER) end, LPs--[Shell]), 666 | Shell ! {ev_exit,M}, 667 | exit(normal) 668 | end 669 | end. 670 | 671 | do_catch(exit, restricted_shell_stopped) -> 672 | false; 673 | do_catch(exit, restricted_shell_started) -> 674 | false; 675 | do_catch(_Class, _Reason) -> 676 | case application:get_env(stdlib, shell_catch_exception) of 677 | {ok, true} -> 678 | true; 679 | _ -> 680 | false 681 | end. 682 | 683 | exprs(Es, Bs0, RT, Lf, Ef, W) -> 684 | exprs(Es, Bs0, RT, Lf, Ef, Bs0, W). 685 | 686 | exprs([E0|Es], Bs1, RT, Lf, Ef, Bs0, W) -> 687 | UsedRecords = used_record_defs(E0, RT), 688 | RBs = record_bindings(UsedRecords, Bs1), 689 | case check_command(prep_check([E0]), RBs) of 690 | ok -> 691 | E1 = expand_records(UsedRecords, E0), 692 | {value,V0,Bs2} = expr(E1, Bs1, Lf, Ef), 693 | Bs = orddict:from_list([VV || {X,_}=VV <- erl_eval:bindings(Bs2), 694 | not is_expand_variable(X)]), 695 | if 696 | Es =:= [] -> 697 | VS = pp(V0, 1, RT), 698 | case W of 699 | cmd -> io:requests([{put_chars, unicode, VS}, nl]); 700 | pmt -> ok 701 | end, 702 | %% Don't send the result back if it will be 703 | %% discarded anyway. 704 | V = if 705 | W =:= pmt -> 706 | {W,V0}; 707 | true -> case result_will_be_saved() of 708 | true -> V0; 709 | false -> ignored 710 | end 711 | end, 712 | {{value,V,Bs,get()},Bs}; 713 | true -> 714 | exprs(Es, Bs, RT, Lf, Ef, Bs0, W) 715 | end; 716 | {error,Error} -> 717 | {{command_error,Error},Bs0} 718 | end. 719 | 720 | is_expand_variable(V) -> 721 | case catch atom_to_list(V) of 722 | "rec" ++ _Integer -> true; 723 | _ -> false 724 | end. 725 | 726 | result_will_be_saved() -> 727 | case get_history_and_results() of 728 | {_, 0} -> false; 729 | _ -> true 730 | end. 731 | 732 | used_record_defs(E, RT) -> 733 | %% Be careful to return a list where used records come before 734 | %% records that use them. The linter wants them ordered that way. 735 | UR = case used_records(E, [], RT) of 736 | [] -> 737 | []; 738 | L0 -> 739 | L1 = lists:zip(L0, lists:seq(1, length(L0))), 740 | L2 = lists:keysort(2, lists:ukeysort(1, L1)), 741 | [R || {R, _} <- L2] 742 | end, 743 | record_defs(RT, UR). 744 | 745 | used_records(E, U0, RT) -> 746 | case used_records(E) of 747 | {name,Name,E1} -> 748 | U = used_records(ets:lookup(RT, Name), [Name | U0], RT), 749 | used_records(E1, U, RT); 750 | {expr,[E1 | Es]} -> 751 | used_records(Es, used_records(E1, U0, RT), RT); 752 | _ -> 753 | U0 754 | end. 755 | 756 | used_records({record_index,_,Name,F}) -> 757 | {name, Name, F}; 758 | used_records({record,_,Name,Is}) -> 759 | {name, Name, Is}; 760 | used_records({record_field,_,R,Name,F}) -> 761 | {name, Name, [R | F]}; 762 | used_records({record,_,R,Name,Ups}) -> 763 | {name, Name, [R | Ups]}; 764 | used_records({record_field,_,R,F}) -> % illegal 765 | {expr, [R | F]}; 766 | used_records({call,_,{atom,_,record},[A,{atom,_,Name}]}) -> 767 | {name, Name, A}; 768 | used_records({call,_,{atom,_,is_record},[A,{atom,_,Name}]}) -> 769 | {name, Name, A}; 770 | used_records({call,_,{remote,_,{atom,_,erlang},{atom,_,is_record}}, 771 | [A,{atom,_,Name}]}) -> 772 | {name, Name, A}; 773 | used_records({call,_,{atom,_,record_info},[A,{atom,_,Name}]}) -> 774 | {name, Name, A}; 775 | used_records({call,Line,{tuple,_,[M,F]},As}) -> 776 | used_records({call,Line,{remote,Line,M,F},As}); 777 | used_records(T) when is_tuple(T) -> 778 | {expr, tuple_to_list(T)}; 779 | used_records(E) -> 780 | {expr, E}. 781 | 782 | fwrite_severity(Severity, S, As) -> 783 | io:fwrite(<<"~ts\n">>, [format_severity(Severity, S, As)]). 784 | 785 | format_severity(Severity, S, As) -> 786 | add_severity(Severity, io_lib:fwrite(S, As)). 787 | 788 | add_severity(Severity, S) -> 789 | [severity_tag(Severity), S]. 790 | 791 | severity_tag(fatal) -> <<"*** ">>; 792 | severity_tag(serious) -> <<"** ">>; 793 | severity_tag(benign) -> <<"* ">>. 794 | 795 | restrict_handlers(RShMod, Shell, RT) -> 796 | { fun(F,As,Binds) -> 797 | local_allowed(F, As, RShMod, Binds, Shell, RT) 798 | end, 799 | fun(MF,As) -> 800 | non_local_allowed(MF, As, RShMod, Shell) 801 | end }. 802 | 803 | -define(BAD_RETURN(M, F, V), 804 | try erlang:error(reason) 805 | catch _:_ -> erlang:raise(exit, {restricted_shell_bad_return,V}, 806 | [{M,F,3} | erlang:get_stacktrace()]) 807 | end). 808 | 809 | local_allowed(F, As, RShMod, Bs, Shell, RT) when is_atom(F) -> 810 | {LFH,NLFH} = restrict_handlers(RShMod, Shell, RT), 811 | case not_restricted(F, As) of % Not restricted is the same as builtin. 812 | % variable and record manipulations local 813 | % to the shell process. Those are never 814 | % restricted. 815 | true -> 816 | local_func(F, As, Bs, Shell, RT, {eval,LFH}, {value,NLFH}); 817 | false -> 818 | {AsEv,Bs1} = expr_list(As, Bs, {eval,LFH}, {value,NLFH}), 819 | case RShMod:local_allowed(F, AsEv, {get(restricted_shell_state), 820 | get(restricted_expr_state)}) of 821 | {Result,{RShShSt,RShExprSt}} -> 822 | put(restricted_shell_state, RShShSt), 823 | put(restricted_expr_state, RShExprSt), 824 | if not Result -> 825 | shell_req(Shell, {update_dict,get()}), 826 | exit({restricted_shell_disallowed,{F,AsEv}}); 827 | true -> % This is never a builtin, 828 | % those are handled above. 829 | non_builtin_local_func(F,AsEv,Bs1) 830 | end; 831 | Unexpected -> % The user supplied non conforming module 832 | ?BAD_RETURN(RShMod, local_allowed, Unexpected) 833 | end 834 | end. 835 | 836 | non_local_allowed(MForFun, As, RShMod, Shell) -> 837 | case RShMod:non_local_allowed(MForFun, As, {get(restricted_shell_state), 838 | get(restricted_expr_state)}) of 839 | {Result,{RShShSt,RShExprSt}} -> 840 | put(restricted_shell_state, RShShSt), 841 | put(restricted_expr_state, RShExprSt), 842 | case Result of 843 | false -> 844 | shell_req(Shell, {update_dict,get()}), 845 | exit({restricted_shell_disallowed,{MForFun,As}}); 846 | {redirect, NewMForFun, NewAs} -> 847 | apply_fun(NewMForFun, NewAs, Shell); 848 | _ -> 849 | apply_fun(MForFun, As, Shell) 850 | end; 851 | Unexpected -> % The user supplied non conforming module 852 | ?BAD_RETURN(RShMod, non_local_allowed, Unexpected) 853 | end. 854 | 855 | %% The commands implemented in shell should not be checked if allowed 856 | %% This *has* to correspond to the function local_func/7! 857 | %% (especially true for f/1, the argument must not be evaluated). 858 | not_restricted(f, []) -> 859 | true; 860 | not_restricted(f, [_]) -> 861 | true; 862 | not_restricted(h, []) -> 863 | true; 864 | not_restricted(b, []) -> 865 | true; 866 | not_restricted(history, [_]) -> 867 | true; 868 | not_restricted(results, [_]) -> 869 | true; 870 | not_restricted(catch_exception, [_]) -> 871 | true; 872 | not_restricted(exit, []) -> 873 | true; 874 | not_restricted(rd, [_,_]) -> 875 | true; 876 | not_restricted(rf, []) -> 877 | true; 878 | not_restricted(rf, [_]) -> 879 | true; 880 | not_restricted(rl, []) -> 881 | true; 882 | not_restricted(rl, [_]) -> 883 | true; 884 | not_restricted(rp, [_]) -> 885 | true; 886 | not_restricted(rr, [_]) -> 887 | true; 888 | not_restricted(rr, [_,_]) -> 889 | true; 890 | not_restricted(rr, [_,_,_]) -> 891 | true; 892 | not_restricted(_, _) -> 893 | false. 894 | 895 | %% When erlang:garbage_collect() is called from the shell, 896 | %% the shell process process that spawned the evaluating 897 | %% process is garbage collected as well. 898 | %% To garbage collect the evaluating process only the command 899 | %% garbage_collect(self()). can be used. 900 | apply_fun({erlang,garbage_collect}, [], Shell) -> 901 | garb(Shell); 902 | apply_fun({M,F}, As, _Shell) -> 903 | apply(M, F, As); 904 | apply_fun(MForFun, As, _Shell) -> 905 | apply(MForFun, As). 906 | 907 | prep_check({call,Line,{atom,_,f},[{var,_,_Name}]}) -> 908 | %% Do not emit a warning for f(V) when V is unbound. 909 | {atom,Line,ok}; 910 | prep_check({value,_CommandN,_Val}) -> 911 | %% erl_lint cannot handle the history expansion {value,_,_}. 912 | {atom,0,ok}; 913 | prep_check(T) when is_tuple(T) -> 914 | list_to_tuple(prep_check(tuple_to_list(T))); 915 | prep_check([E | Es]) -> 916 | [prep_check(E) | prep_check(Es)]; 917 | prep_check(E) -> 918 | E. 919 | 920 | expand_records([], E0) -> 921 | E0; 922 | expand_records(UsedRecords, E0) -> 923 | RecordDefs = [Def || {_Name,Def} <- UsedRecords], 924 | L = 1, 925 | E = prep_rec(E0), 926 | Forms = RecordDefs ++ [{function,L,foo,0,[{clause,L,[],[],[E]}]}], 927 | [{function,L,foo,0,[{clause,L,[],[],[NE]}]}] = 928 | erl_expand_records:module(Forms, [strict_record_tests]), 929 | prep_rec(NE). 930 | 931 | prep_rec({value,_CommandN,_V}=Value) -> 932 | %% erl_expand_records cannot handle the history expansion {value,_,_}. 933 | {atom,Value,ok}; 934 | prep_rec({atom,{value,_CommandN,_V}=Value,ok}) -> 935 | %% Undo the effect of the previous clause... 936 | Value; 937 | prep_rec(T) when is_tuple(T) -> list_to_tuple(prep_rec(tuple_to_list(T))); 938 | prep_rec([E | Es]) -> [prep_rec(E) | prep_rec(Es)]; 939 | prep_rec(E) -> E. 940 | 941 | init_dict([{K,V}|Ds]) -> 942 | put(K, V), 943 | init_dict(Ds); 944 | init_dict([]) -> true. 945 | 946 | %% local_func(Function, Args, Bindings, Shell, RecordTable, 947 | %% LocalFuncHandler, ExternalFuncHandler) -> {value,Val,Bs} 948 | %% Evaluate local functions, including shell commands. 949 | %% 950 | %% Note that the predicate not_restricted/2 has to correspond to what's 951 | %% handled internally - it should return 'true' for all local functions 952 | %% handled in this module (i.e. those that are not eventually handled by 953 | %% non_builtin_local_func/3 (user_default/shell_default). 954 | 955 | local_func(h, [], Bs, Shell, RT, _Lf, _Ef) -> 956 | Cs = shell_req(Shell, get_cmd), 957 | Cs1 = lists:filter(fun({{command, _},_}) -> true; 958 | ({{result, _},_}) -> true; 959 | (_) -> false 960 | end, 961 | Cs), 962 | Cs2 = lists:map(fun({{T, N}, V}) -> {{N, T}, V} end, 963 | Cs1), 964 | Cs3 = lists:keysort(1, Cs2), 965 | {value,list_commands(Cs3, RT),Bs}; 966 | local_func(b, [], Bs, _Shell, RT, _Lf, _Ef) -> 967 | {value,list_bindings(erl_eval:bindings(Bs), RT),Bs}; 968 | local_func(f, [], _Bs, _Shell, _RT, _Lf, _Ef) -> 969 | {value,ok,erl_eval:new_bindings()}; 970 | local_func(f, [{var,_,Name}], Bs, _Shell, _RT, _Lf, _Ef) -> 971 | {value,ok,erl_eval:del_binding(Name, Bs)}; 972 | local_func(f, [_Other], _Bs, _Shell, _RT, _Lf, _Ef) -> 973 | erlang:raise(error, function_clause, [{shell,f,1}]); 974 | local_func(rd, [{atom,_,RecName},RecDef0], Bs, _Shell, RT, _Lf, _Ef) -> 975 | RecDef = expand_value(RecDef0), 976 | RDs = lists:flatten(erl_pp:expr(RecDef)), 977 | Attr = lists:concat(["-record('", RecName, "',", RDs, ")."]), 978 | {ok, Tokens, _} = erl_scan:string(Attr), 979 | case erl_parse:parse_form(Tokens) of 980 | {ok,AttrForm} -> 981 | [RN] = add_records([AttrForm], Bs, RT), 982 | {value,RN,Bs}; 983 | {error,{_Line,M,ErrDesc}} -> 984 | ErrStr = io_lib:fwrite(<<"~ts">>, [M:format_error(ErrDesc)]), 985 | exit(lists:flatten(ErrStr)) 986 | end; 987 | local_func(rd, [_,_], _Bs, _Shell, _RT, _Lf, _Ef) -> 988 | erlang:raise(error, function_clause, [{shell,rd,2}]); 989 | local_func(rf, [], Bs, _Shell, RT, _Lf, _Ef) -> 990 | true = ets:delete_all_objects(RT), 991 | {value,initiate_records(Bs, RT),Bs}; 992 | local_func(rf, [A], Bs0, _Shell, RT, Lf, Ef) -> 993 | {[Recs],Bs} = expr_list([A], Bs0, Lf, Ef), 994 | if '_' =:= Recs -> 995 | true = ets:delete_all_objects(RT); 996 | true -> 997 | lists:foreach(fun(Name) -> true = ets:delete(RT, Name) 998 | end, listify(Recs)) 999 | end, 1000 | {value,ok,Bs}; 1001 | local_func(rl, [], Bs, _Shell, RT, _Lf, _Ef) -> 1002 | {value,list_records(ets:tab2list(RT)),Bs}; 1003 | local_func(rl, [A], Bs0, _Shell, RT, Lf, Ef) -> 1004 | {[Recs],Bs} = expr_list([A], Bs0, Lf, Ef), 1005 | {value,list_records(record_defs(RT, listify(Recs))),Bs}; 1006 | local_func(rp, [A], Bs0, _Shell, RT, Lf, Ef) -> 1007 | {[V],Bs} = expr_list([A], Bs0, Lf, Ef), 1008 | Cs = io_lib_pretty:print(V, ([{column, 1}, 1009 | {line_length, columns()}, 1010 | {depth, -1}, 1011 | {max_chars, ?CHAR_MAX}, 1012 | {record_print_fun, record_print_fun(RT)}] 1013 | ++ enc())), 1014 | io:requests([{put_chars, unicode, Cs}, nl]), 1015 | {value,ok,Bs}; 1016 | local_func(rr, [A], Bs0, _Shell, RT, Lf, Ef) -> 1017 | {[File],Bs} = expr_list([A], Bs0, Lf, Ef), 1018 | {value,read_and_add_records(File, '_', [], Bs, RT),Bs}; 1019 | local_func(rr, [_,_]=As0, Bs0, _Shell, RT, Lf, Ef) -> 1020 | {[File,Sel],Bs} = expr_list(As0, Bs0, Lf, Ef), 1021 | {value,read_and_add_records(File, Sel, [], Bs, RT),Bs}; 1022 | local_func(rr, [_,_,_]=As0, Bs0, _Shell, RT, Lf, Ef) -> 1023 | {[File,Sel,Options],Bs} = expr_list(As0, Bs0, Lf, Ef), 1024 | {value,read_and_add_records(File, Sel, Options, Bs, RT),Bs}; 1025 | local_func(history, [{integer,_,N}], Bs, _Shell, _RT, _Lf, _Ef) -> 1026 | {value,history(N),Bs}; 1027 | local_func(history, [_Other], _Bs, _Shell, _RT, _Lf, _Ef) -> 1028 | erlang:raise(error, function_clause, [{shell,history,1}]); 1029 | local_func(results, [{integer,_,N}], Bs, _Shell, _RT, _Lf, _Ef) -> 1030 | {value,results(N),Bs}; 1031 | local_func(results, [_Other], _Bs, _Shell, _RT, _Lf, _Ef) -> 1032 | erlang:raise(error, function_clause, [{shell,results,1}]); 1033 | local_func(catch_exception, [{atom,_,Bool}], Bs, _Shell, _RT, _Lf, _Ef) 1034 | when Bool; not Bool -> 1035 | {value,catch_exception(Bool),Bs}; 1036 | local_func(catch_exception, [_Other], _Bs, _Shell, _RT, _Lf, _Ef) -> 1037 | erlang:raise(error, function_clause, [{shell,catch_exception,1}]); 1038 | local_func(exit, [], _Bs, Shell, _RT, _Lf, _Ef) -> 1039 | shell_req(Shell, exit), %This terminates us 1040 | exit(normal); 1041 | local_func(F, As0, Bs0, _Shell, _RT, Lf, Ef) when is_atom(F) -> 1042 | {As,Bs} = expr_list(As0, Bs0, Lf, Ef), 1043 | non_builtin_local_func(F,As,Bs). 1044 | 1045 | non_builtin_local_func(F,As,Bs) -> 1046 | Arity = length(As), 1047 | case erlang:function_exported(user_default, F, Arity) of 1048 | true -> 1049 | {eval,erlang:make_fun(user_default, F, Arity),As,Bs}; 1050 | false -> 1051 | shell_default(F,As,Bs) 1052 | end. 1053 | 1054 | shell_default(F,As,Bs) -> 1055 | M = shell_default, 1056 | A = length(As), 1057 | case code:ensure_loaded(M) of 1058 | {module, _} -> 1059 | case erlang:function_exported(M,F,A) of 1060 | true -> 1061 | {eval,erlang:make_fun(M, F, A),As,Bs}; 1062 | false -> 1063 | shell_undef(F,A) 1064 | end; 1065 | {error, _} -> 1066 | shell_undef(F,A) 1067 | end. 1068 | 1069 | shell_undef(F,A) -> 1070 | erlang:error({shell_undef,F,A,[]}). 1071 | 1072 | local_func_handler(Shell, RT, Ef) -> 1073 | H = fun(Lf) -> 1074 | fun(F, As, Bs) -> 1075 | local_func(F, As, Bs, Shell, RT, {eval,Lf(Lf)}, Ef) 1076 | end 1077 | end, 1078 | {eval,H(H)}. 1079 | 1080 | record_print_fun(RT) -> 1081 | fun(Tag, NoFields) -> 1082 | case ets:lookup(RT, Tag) of 1083 | [{_,{attribute,_,record,{Tag,Fields}}}] 1084 | when length(Fields) =:= NoFields -> 1085 | record_fields(Fields); 1086 | _ -> 1087 | no 1088 | end 1089 | end. 1090 | 1091 | record_fields([{record_field,_,{atom,_,Field}} | Fs]) -> 1092 | [Field | record_fields(Fs)]; 1093 | record_fields([{record_field,_,{atom,_,Field},_} | Fs]) -> 1094 | [Field | record_fields(Fs)]; 1095 | record_fields([]) -> 1096 | []. 1097 | 1098 | initiate_records(Bs, RT) -> 1099 | RNs1 = init_rec(shell_default, Bs, RT), 1100 | RNs2 = case code:is_loaded(user_default) of 1101 | {file,_File} -> 1102 | init_rec(user_default, Bs, RT); 1103 | false -> 1104 | [] 1105 | end, 1106 | lists:usort(RNs1 ++ RNs2). 1107 | 1108 | init_rec(Module, Bs, RT) -> 1109 | case read_records(Module, []) of 1110 | RAs when is_list(RAs) -> 1111 | case catch add_records(RAs, Bs, RT) of 1112 | {'EXIT',_} -> 1113 | []; 1114 | RNs -> 1115 | RNs 1116 | end; 1117 | _Error -> 1118 | [] 1119 | end. 1120 | 1121 | read_and_add_records(File, Selected, Options, Bs, RT) -> 1122 | case read_records(File, Selected, Options) of 1123 | RAs when is_list(RAs) -> 1124 | add_records(RAs, Bs, RT); 1125 | Error -> 1126 | Error 1127 | end. 1128 | 1129 | read_records(File, Selected, Options) -> 1130 | case read_records(File, listify(Options)) of 1131 | Error when is_tuple(Error) -> 1132 | Error; 1133 | RAs when Selected =:= '_' -> 1134 | RAs; 1135 | RAs -> 1136 | Sel = listify(Selected), 1137 | [RA || {attribute,_,_,{Name,_}}=RA <- RAs, 1138 | lists:member(Name, Sel)] 1139 | end. 1140 | 1141 | add_records(RAs, Bs0, RT) -> 1142 | Recs = [{Name,D} || {attribute,_,_,{Name,_}}=D <- RAs], 1143 | Bs1 = record_bindings(Recs, Bs0), 1144 | case check_command([], Bs1) of 1145 | {error,{_Line,M,ErrDesc}} -> 1146 | %% A source file that has not been compiled. 1147 | ErrStr = io_lib:fwrite(<<"~ts">>, [M:format_error(ErrDesc)]), 1148 | exit(lists:flatten(ErrStr)); 1149 | ok -> 1150 | true = ets:insert(RT, Recs), 1151 | lists:usort([Name || {Name,_} <- Recs]) 1152 | end. 1153 | 1154 | listify(L) when is_list(L) -> 1155 | L; 1156 | listify(E) -> 1157 | [E]. 1158 | 1159 | check_command(Es, Bs) -> 1160 | erl_eval:check_command(Es, Bs). 1161 | 1162 | expr(E, Bs, Lf, Ef) -> 1163 | erl_eval:expr(E, Bs, Lf, Ef). 1164 | 1165 | expr_list(Es, Bs, Lf, Ef) -> 1166 | erl_eval:expr_list(Es, Bs, Lf, Ef). 1167 | 1168 | %% Note that a sequence number is used here to make sure that if a 1169 | %% record is used by another record, then the first record is parsed 1170 | %% before the second record. (erl_eval:check_command() calls the 1171 | %% linter which needs the records in a proper order.) 1172 | record_bindings([], Bs) -> 1173 | Bs; 1174 | record_bindings(Recs0, Bs0) -> 1175 | {Recs1, _} = lists:mapfoldl(fun ({Name,Def}, I) -> {{Name,I,Def},I+1} 1176 | end, 0, Recs0), 1177 | Recs2 = lists:keysort(2, lists:ukeysort(1, Recs1)), 1178 | lists:foldl(fun ({Name,I,Def}, Bs) -> 1179 | erl_eval:add_binding({record,I,Name}, Def, Bs) 1180 | end, Bs0, Recs2). 1181 | 1182 | %%% Read record information from file(s) 1183 | 1184 | read_records(FileOrModule, Opts0) -> 1185 | Opts = lists:delete(report_warnings, Opts0), 1186 | case find_file(FileOrModule) of 1187 | {files,[File]} -> 1188 | read_file_records(File, Opts); 1189 | {files,Files} -> 1190 | lists:flatmap(fun(File) -> 1191 | case read_file_records(File, Opts) of 1192 | RAs when is_list(RAs) -> RAs; 1193 | _ -> [] 1194 | end 1195 | end, Files); 1196 | Error -> 1197 | Error 1198 | end. 1199 | 1200 | -include_lib("kernel/include/file.hrl"). 1201 | 1202 | find_file(Mod) when is_atom(Mod) -> 1203 | case code:which(Mod) of 1204 | File when is_list(File) -> 1205 | {files,[File]}; 1206 | preloaded -> 1207 | {_M,_Bin,File} = code:get_object_code(Mod), 1208 | {files,[File]}; 1209 | _Else -> % non_existing, interpreted, cover_compiled 1210 | {error,nofile} 1211 | end; 1212 | find_file(File) -> 1213 | case catch filelib:wildcard(File) of 1214 | {'EXIT',_} -> 1215 | {error,invalid_filename}; 1216 | Files -> 1217 | {files,Files} 1218 | end. 1219 | 1220 | read_file_records(File, Opts) -> 1221 | case filename:extension(File) of 1222 | ".beam" -> 1223 | case beam_lib:chunks(File, [abstract_code,"CInf"]) of 1224 | {ok,{_Mod,[{abstract_code,{Version,Forms}},{"CInf",CB}]}} -> 1225 | case record_attrs(Forms) of 1226 | [] when Version =:= raw_abstract_v1 -> 1227 | []; 1228 | [] -> 1229 | %% If the version is raw_X, then this test 1230 | %% is unnecessary. 1231 | try_source(File, CB); 1232 | Records -> 1233 | Records 1234 | end; 1235 | {ok,{_Mod,[{abstract_code,no_abstract_code},{"CInf",CB}]}} -> 1236 | try_source(File, CB); 1237 | Error -> 1238 | %% Could be that the "Abst" chunk is missing (pre R6). 1239 | Error 1240 | end; 1241 | _ -> 1242 | parse_file(File, Opts) 1243 | end. 1244 | 1245 | %% This is how the debugger searches for source files. See int.erl. 1246 | try_source(Beam, CB) -> 1247 | Os = case lists:keyfind(options, 1, binary_to_term(CB)) of 1248 | false -> []; 1249 | {_, Os0} -> Os0 1250 | end, 1251 | Src0 = filename:rootname(Beam) ++ ".erl", 1252 | case is_file(Src0) of 1253 | true -> parse_file(Src0, Os); 1254 | false -> 1255 | EbinDir = filename:dirname(Beam), 1256 | Src = filename:join([filename:dirname(EbinDir), "src", 1257 | filename:basename(Src0)]), 1258 | case is_file(Src) of 1259 | true -> parse_file(Src, Os); 1260 | false -> {error, nofile} 1261 | end 1262 | end. 1263 | 1264 | is_file(Name) -> 1265 | case filelib:is_file(Name) of 1266 | true -> 1267 | not filelib:is_dir(Name); 1268 | false -> 1269 | false 1270 | end. 1271 | 1272 | parse_file(File, Opts) -> 1273 | Cwd = ".", 1274 | Dir = filename:dirname(File), 1275 | IncludePath = [Cwd,Dir|inc_paths(Opts)], 1276 | case epp:parse_file(File, IncludePath, pre_defs(Opts)) of 1277 | {ok,Forms} -> 1278 | record_attrs(Forms); 1279 | Error -> 1280 | Error 1281 | end. 1282 | 1283 | pre_defs([{d,M,V}|Opts]) -> 1284 | [{M,V}|pre_defs(Opts)]; 1285 | pre_defs([{d,M}|Opts]) -> 1286 | [M|pre_defs(Opts)]; 1287 | pre_defs([_|Opts]) -> 1288 | pre_defs(Opts); 1289 | pre_defs([]) -> []. 1290 | 1291 | inc_paths(Opts) -> 1292 | [P || {i,P} <- Opts, is_list(P)]. 1293 | 1294 | record_attrs(Forms) -> 1295 | [A || A = {attribute,_,record,_D} <- Forms]. 1296 | 1297 | %%% End of reading record information from file(s) 1298 | 1299 | shell_req(Shell, Req) -> 1300 | Shell ! {shell_req,self(),Req}, 1301 | receive 1302 | {shell_rep,Shell,Rep} -> Rep 1303 | end. 1304 | 1305 | list_commands([{{N,command},Es0}, {{N,result}, V} |Ds], RT) -> 1306 | Es = prep_list_commands(Es0), 1307 | VS = pp(V, 4, RT), 1308 | Ns = io_lib:fwrite(<<"~w: ">>, [N]), 1309 | I = iolist_size(Ns), 1310 | io:requests([{put_chars, latin1, Ns}, 1311 | {format,<<"~ts\n">>,[erl_pp:exprs(Es, I, enc())]}, 1312 | {format,<<"-> ">>,[]}, 1313 | {put_chars, unicode, VS}, 1314 | nl]), 1315 | list_commands(Ds, RT); 1316 | list_commands([{{N,command},Es0} |Ds], RT) -> 1317 | Es = prep_list_commands(Es0), 1318 | Ns = io_lib:fwrite(<<"~w: ">>, [N]), 1319 | I = iolist_size(Ns), 1320 | io:requests([{put_chars, latin1, Ns}, 1321 | {format,<<"~ts\n">>,[erl_pp:exprs(Es, I, enc())]}]), 1322 | list_commands(Ds, RT); 1323 | list_commands([_D|Ds], RT) -> 1324 | list_commands(Ds, RT); 1325 | list_commands([], _RT) -> ok. 1326 | 1327 | list_bindings([{Name,Val}|Bs], RT) -> 1328 | case erl_eval:fun_data(Val) of 1329 | {fun_data,_FBs,FCs0} -> 1330 | FCs = expand_value(FCs0), % looks nicer 1331 | F = {'fun',0,{clauses,FCs}}, 1332 | M = {match,0,{var,0,Name},F}, 1333 | io:fwrite(<<"~ts\n">>, [erl_pp:expr(M, enc())]); 1334 | {named_fun_data,_FBs,FName,FCs0} -> 1335 | FCs = expand_value(FCs0), % looks nicer 1336 | F = {named_fun,0,FName,FCs}, 1337 | M = {match,0,{var,0,Name},F}, 1338 | io:fwrite(<<"~ts\n">>, [erl_pp:expr(M, enc())]); 1339 | false -> 1340 | Namel = io_lib:fwrite(<<"~s = ">>, [Name]), 1341 | Nl = iolist_size(Namel)+1, 1342 | ValS = pp(Val, Nl, RT), 1343 | io:requests([{put_chars, latin1, Namel}, 1344 | {put_chars, unicode, ValS}, 1345 | nl]) 1346 | end, 1347 | list_bindings(Bs, RT); 1348 | list_bindings([], _RT) -> 1349 | ok. 1350 | 1351 | list_records(Records) -> 1352 | lists:foreach(fun({_Name,Attr}) -> 1353 | io:fwrite(<<"~ts">>, [erl_pp:attribute(Attr, enc())]) 1354 | end, Records). 1355 | 1356 | record_defs(RT, Names) -> 1357 | lists:flatmap(fun(Name) -> ets:lookup(RT, Name) 1358 | end, Names). 1359 | 1360 | expand_value(E) -> 1361 | substitute_v1(fun({value,CommandN,V}) -> try_abstract(V, CommandN) 1362 | end, E). 1363 | 1364 | %% There is no abstract representation of funs. 1365 | try_abstract(V, CommandN) -> 1366 | try erl_parse:abstract(V) 1367 | catch _:_ -> {call,0,{atom,0,v},[{integer,0,CommandN}]} 1368 | end. 1369 | 1370 | %% Rather than listing possibly huge results the calls to v/1 are shown. 1371 | prep_list_commands(E) -> 1372 | substitute_v1(fun({value,CommandN,_V}) -> 1373 | {call,0,{atom,0,v},[{integer,0,CommandN}]} 1374 | end, E). 1375 | 1376 | substitute_v1(F, {value,_,_}=Value) -> 1377 | F(Value); 1378 | substitute_v1(F, T) when is_tuple(T) -> 1379 | list_to_tuple(substitute_v1(F, tuple_to_list(T))); 1380 | substitute_v1(F, [E | Es]) -> 1381 | [substitute_v1(F, E) | substitute_v1(F, Es)]; 1382 | substitute_v1(_F, E) -> 1383 | E. 1384 | 1385 | check_and_get_history_and_results() -> 1386 | check_env(shell_history_length), 1387 | check_env(shell_saved_results), 1388 | get_history_and_results(). 1389 | 1390 | get_history_and_results() -> 1391 | History = get_env(shell_history_length, ?DEF_HISTORY), 1392 | Results = get_env(shell_saved_results, ?DEF_RESULTS), 1393 | {History, erlang:min(Results, History)}. 1394 | 1395 | pp(V, I, RT) -> 1396 | pp(V, I, RT, enc()). 1397 | 1398 | pp(V, I, RT, Enc) -> 1399 | Strings = 1400 | case application:get_env(stdlib, shell_strings) of 1401 | {ok, false} -> 1402 | false; 1403 | _ -> 1404 | true 1405 | end, 1406 | io_lib_pretty:print(V, ([{column, I}, {line_length, columns()}, 1407 | {depth, ?LINEMAX}, {max_chars, ?CHAR_MAX}, 1408 | {strings, Strings}, 1409 | {record_print_fun, record_print_fun(RT)}] 1410 | ++ Enc)). 1411 | 1412 | columns() -> 1413 | case io:columns() of 1414 | {ok,N} -> N; 1415 | _ -> 80 1416 | end. 1417 | encoding() -> 1418 | [{encoding, Encoding}] = enc(), 1419 | Encoding. 1420 | enc() -> 1421 | case lists:keyfind(encoding, 1, io:getopts()) of 1422 | false -> [{encoding,latin1}]; % should never happen 1423 | Enc -> [Enc] 1424 | end. 1425 | 1426 | garb(Shell) -> 1427 | erlang:garbage_collect(Shell), 1428 | catch erlang:garbage_collect(whereis(user)), 1429 | catch erlang:garbage_collect(group_leader()), 1430 | erlang:garbage_collect(). 1431 | 1432 | get_env(V, Def) -> 1433 | case application:get_env(stdlib, V) of 1434 | {ok, Val} when is_integer(Val), Val >= 0 -> 1435 | Val; 1436 | _ -> 1437 | Def 1438 | end. 1439 | 1440 | check_env(V) -> 1441 | case application:get_env(stdlib, V) of 1442 | undefined -> 1443 | ok; 1444 | {ok, Val} when is_integer(Val), Val >= 0 -> 1445 | ok; 1446 | {ok, Val} -> 1447 | Txt = io_lib:fwrite 1448 | ("Invalid value of STDLIB configuration parameter" 1449 | "~w: ~tp\n", [V, Val]), 1450 | error_logger:info_report(lists:flatten(Txt)) 1451 | end. 1452 | 1453 | set_env(App, Name, Val, Default) -> 1454 | Prev = case application:get_env(App, Name) of 1455 | undefined -> 1456 | Default; 1457 | {ok, Old} -> 1458 | Old 1459 | end, 1460 | application_controller:set_env(App, Name, Val), 1461 | Prev. 1462 | 1463 | -spec history(N) -> non_neg_integer() when 1464 | N :: non_neg_integer(). 1465 | 1466 | history(L) when is_integer(L), L >= 0 -> 1467 | set_env(stdlib, shell_history_length, L, ?DEF_HISTORY). 1468 | 1469 | -spec results(N) -> non_neg_integer() when 1470 | N :: non_neg_integer(). 1471 | 1472 | results(L) when is_integer(L), L >= 0 -> 1473 | set_env(stdlib, shell_saved_results, L, ?DEF_RESULTS). 1474 | 1475 | -spec catch_exception(Bool) -> boolean() when 1476 | Bool :: boolean(). 1477 | 1478 | catch_exception(Bool) -> 1479 | set_env(stdlib, shell_catch_exception, Bool, ?DEF_CATCH_EXCEPTION). 1480 | 1481 | -spec prompt_func(PromptFunc) -> PromptFunc2 when 1482 | PromptFunc :: 'default' | {module(),atom()}, 1483 | PromptFunc2 :: 'default' | {module(),atom()}. 1484 | 1485 | prompt_func(String) -> 1486 | set_env(stdlib, shell_prompt_func, String, ?DEF_PROMPT_FUNC). 1487 | 1488 | -spec strings(Strings) -> Strings2 when 1489 | Strings :: boolean(), 1490 | Strings2 :: boolean(). 1491 | 1492 | strings(Strings) -> 1493 | set_env(stdlib, shell_strings, Strings, ?DEF_STRINGS). 1494 | -------------------------------------------------------------------------------- /src/crypto_latex.org: -------------------------------------------------------------------------------- 1 | * Introduction 2 | 3 | This is ``work in progress'' - if you wish to contribute please 4 | refer to the archive at 5 | \url{http://github.com/joearms/crypto_tutorial}. All the code for 6 | this tutorial is in the archive. 7 | 8 | \textsl{Warning: Do not use this code in production} -- Code like this 9 | is fine for \textsl{understanding} cryptographic algorithms but it is 10 | not intended for large-scale production. For production system more 11 | in-depth knowledge of cryptography is required. I'm fully aware that 12 | AES256 and 1024 bit RSA keys are not considered ``state of the 13 | art''\footnote{So don't bother to tell me, I know!} but for the 14 | purposes of \textsl{understanding the algorithms} a 32 bit RSA key suffices! 15 | 16 | Always remember that the security of a system depends upon the weakest 17 | link, which in many cases boils down to key-management\footnote{So 18 | don't loose your passwords, or give them away!}. 19 | 20 | 21 | ** Understanding not code 22 | 23 | In this tutorial, I'm going to introduce a number of simple 24 | cryptographic techniques that can be used to build secure 25 | applications. The main goal of the tutorial is to understand a small 26 | number of well-known cryptographic techniques and to be able to apply 27 | that knowledge. 28 | 29 | Wherever possible I have tried to implement the algorithms in pure 30 | Erlang in as few lines of code as possible. This is possible for 31 | algorithms such as RSA, where the core RSA algorithm can be coded in a 32 | handful of lines of code. This fits in with my philosophy of 33 | programming. I think it is better to completely understand RSA 34 | implemented using Erlang bignums, rather than not understand a fast 35 | RSA algorithm written in thousands of lines of assembler and using 36 | hardware acceleration which I cannot myself validate. 37 | 38 | Algorithms such as AES256 are not written in Erlang (since the algorithm 39 | itself is not very exciting) and I assume that the implementation in 40 | the Erlang Crypto libraries are correct.\footnote{Actually this claim 41 | would be difficult to verify, since the Erlang crypto libraries make 42 | use of OpenSSL and this library is many thousands of lines of (to me) 43 | incomprehensible C code, and has also had buggy code in it in the past.} 44 | 45 | Once you have understood the algorithms you will be able to make 46 | informed decisions over which algorithm to use and how to combine them. 47 | If efficiency is your goal over personal understanding, then you might 48 | like to trust some third party library, so I'll wind up with a 49 | introduction to NACL. 50 | 51 | In a tutorial like this the problem is ``what to leave out'' rather 52 | than ``what to include'' -- so I've left out a lot of detail. 53 | 54 | In many cases, distributed algorithms which involve the interaction 55 | between a client and server have been programmed as interactions 56 | between pairs of Erlang processes. This is so we can concentrate on 57 | the algorithms themselves and not be concerned with details of how 58 | data is transported between clients and servers. For our purposes it 59 | suffices to assume that data can be sent between clients and servers 60 | and not \textsl{how} that data is transmitted. Turning distributed 61 | algorithms, involving message passing between processes, can easily be 62 | transformed to algorithms that uses TCP sockets or some other 63 | communication mechanism to transmit the data. 64 | 65 | ** The Basic Methods 66 | I'll go into these in detail: 67 | 68 | \begin{itemize} 69 | \item RSA. 70 | \item AES256. 71 | \item SHA1. 72 | \item Padding. 73 | \item Salting. 74 | \item Secret sharing. 75 | \end{itemize} 76 | 77 | ** Applications 78 | 79 | Applications are made by combining the basic methods. I'll deal with: 80 | 81 | + Challenge response algorithms 82 | + Authentication 83 | + Secret message streams 84 | + Transport Layer Security 85 | + Self-certifying File Systems 86 | 87 | As you'll see, no one technique dominates. To make an application, 88 | we will have to make several design choices. We'll choose a 89 | cryptographic strength\footnote{How many bits in the keys, how 90 | paranoid are we?} and a set of algorithms that work together. 91 | 92 | ** Warning 93 | 94 | I guess, I should give a word of warning before starting. I'm not a 95 | cryptography expert and the code I present has not been subject to 96 | review - on the other hand the algorithms are so simple and they 97 | closely follow the math that hopefully, they are correct. 98 | 99 | I had a choice - I could \textsl{trust} other people's code that I 100 | cannot review and understand myself, or I can write my own code that 101 | I do understand. For algorithms like RSA, where the math is simple and 102 | the code borders on the trivial, I choose the latter. 103 | 104 | For algorithms like AES and SHA1, I'll trust that the reference 105 | implementations are correct and secure. 106 | 107 | In defense of my position, I'd point out that many well known crypto 108 | algorithms in the public domain have been subject to 109 | large scale peer-review \textsl{despite this, they have been found to contain 110 | security holes}. One of the responses to this (not the only) is that 111 | they are written in imperative language like C \textsl{which are 112 | extremely difficult to understand}.\footnote{In my opinion virtually 113 | all C is extremely difficult to understand, FPLs with no mutable state 114 | that closely follow the crypto math are far easier to understand. By 115 | the time you get to the end of this tutorial I hope you'll agree with 116 | me.} 117 | 118 | As a final note, I'd say that no cryptographic system is perfect -- you 119 | just have to decide how much effort an adversary is prepared to put into 120 | breaking your system. If you were designing a system to manage 121 | financial transactions you should put a lot of effort into making it 122 | secure. If you want to build a flower-arranging website you'd be less 123 | bothered. 124 | 125 | * Techniques 126 | 127 | Certain common techniques crop up over and over again in different 128 | crypto applications. Real applications are made by 129 | combining small fragments of code which do some rather specific 130 | things. The whole can appear complicated, but this is mainly because 131 | combining many small things can create the illusion of complexity, when 132 | in fact the systems are in principle rather simple. We'll see how this 133 | works as we progress throughout this tutorial. 134 | 135 | ** Salting 136 | 137 | If we encode the same thing many times we would like to get different 138 | crypto text for each encoding. This is to make the attacker's life more 139 | difficult. A common way to do this is to add ``salt'' to the 140 | password that is used to encrypt the data. The salt (which is a 141 | random number) is transmitted together with the crypto text; this 142 | ensures that if the same text is encrypted many times each encryption 143 | will result in different crypto text. 144 | 145 | Note: Encrypting known fragments of text in the input text helped crack 146 | the enigma machine in WWII and lead to the development of computers. 147 | 148 | 149 | Encrypt with: Key ++ Salt 150 | Transmit: Salt ++ Encrypted text 151 | 152 | 153 | ** Envelopes 154 | 155 | Often we apply several transformations to the input data. We salt 156 | the data, then pad it and encrypt it and so on. To recover the data we 157 | perform the steps in the opposite order to which the transformations 158 | were applied. This is very simple if each step is itself 159 | invertible. So, if we encode data by performing a set of 160 | transformations: 161 | 162 | $ Out = F(G(H(I(In)))) $ 163 | 164 | Then all we have to do is invert each step: 165 | 166 | $ In = I^{-1}(H^{-1}(G^{-1}(F^{-1}(Out)))) $ 167 | 168 | In Erlang to encode some data we might do something like: 169 | 170 | \begin{verbatim} 171 | Bin1 = encrypt(Bin, SymKey), 172 | Sha = sha1(Bin1), 173 | Bin2 = term_to_binary({packet, Sha, Bin1}), 174 | \end{verbatim} 175 | 176 | To decoding this we'd do the steps in the opposite order: 177 | 178 | \begin{verbatim} 179 | {packet, Sha, Bin1} = binary_to_term(Bin2), 180 | Bin = decode(Bin1, SymKey), 181 | case sha1(Bin1) of 182 | Sha -> ...; 183 | _ -> exit(bad_packet) 184 | end 185 | \end{verbatim} 186 | 187 | * Symmetric Algorithms 188 | 189 | We'll start with the simplest of algorithms. These use the same key 190 | for both encryption and decryption. These are called ``symmetric'' 191 | algorithms. We'll look at a number of different symmetric algorithms, 192 | the first few (one-time pads and LCGs) are ``toy'' implementations and 193 | just here for illustrative purposes. For production applications, some 194 | AES variant or RC4 would be a better choice. 195 | 196 | ** One time pad 197 | 198 | A one time pad is a pre-computed sequence of random bytes that 199 | both the sender and receiver have agreed upon. It is used once 200 | xoring the bits in the message with the bytes in the one-time pad. 201 | 202 | Here's an example of a one time pad: 203 | 204 | !! include_tagged:symmetric.erl:pad: 205 | 206 | The one time pad is the return value of the function \verb+pad/0+. 207 | The function \verb+encrypt_0(Pad, Str)+ encrypts the \verb+Str+ using the 208 | characters in \verb+Pad+: 209 | 210 | > Pad = symmetric:pad(). 211 | 212 | > C = symmetric:encrypt_0(Pad, "hello joe"). 213 | 214 | And we decrypt using the same pad: 215 | 216 | > symmetric:encrypt_0(Pad, C). 217 | 218 | One time pads are extremely secure provided we can securely distribute the pad to 219 | both parties in advance. There is no algorithm to crack. 220 | 221 | ** Xor Text with a stream of random bytes 222 | 223 | Our next method generates a stream of random bytes using a linear 224 | congruent generator (LCG) and then XORS the byte stream with the data 225 | to be encrypted. 226 | 227 | To decrypt the data we just XOR the encrypted data with the same byte 228 | stream to recover the original data. This works because: 229 | 230 | (M xor R) xor R = M 231 | 232 | A LCG generates random numbers with a recurrence relation of the form 233 | \verb|X[k+1] = (aX[k] + c) mod n| the WikiPedia page 234 | \url{https://en.wikipedia.org/wiki/Linear_congruential_generator} gives a 235 | number of values for \verb+a+ \verb+c+ and \verb+n+ 236 | 237 | \verb+mod+ is called \verb+rem+ in Erlang. 238 | 239 | We can easily turn a LCG into an encryption routine like this: 240 | 241 | !! include_tagged:symmetric.erl:encrypt_1: 242 | 243 | Note this code is for illustration only, this would be very easy to 244 | break so don't use it in practice. 245 | 246 | To get an idea of how good the LCG we have used, we can 247 | generate pairs of random bytes, and use them as the \verb+X+ and 248 | \verb+Y+ coordinates of points in a 2-D 256 x 256 scatter plot. The 249 | resulting plot is as follows: 250 | 251 | \includegraphics[width=8.0cm]{lgc1.png} 252 | 253 | As you can see, the result is not very random. 254 | Using \verb+crypto:rand_bytes(K)+ we obtain: 255 | 256 | \includegraphics[width=8.0cm]{crypto1.png} 257 | 258 | Which looks much better. The code for this can be found in 259 | \verb+plot_random.erl+ in the project archive. 260 | 261 | ** Adding Salt 262 | 263 | The problem with the previous algorithm is that if we encrypt the 264 | same text many times with the same password the encrypted text is 265 | always the same. 266 | 267 | To remedy this, we generate a random string each time and 268 | append it to the password: 269 | 270 | ># {include_function, "symmetric.erl", encrypt_2, 2}. 271 | 272 | To decrypt the data we need to extract the salt before decryption: 273 | 274 | ># {include_function, "symmetric.erl", decrypt_2, 2}. 275 | 276 | Note that in both encryption and decryption we reused the 277 | code that encrypted the original data -- salting is done with a small 278 | wrapper around the original code. 279 | 280 | ** AES256 281 | 282 | LCGs are pretty poor sources of random numbers, I've just used them 283 | here for illustration. A better symmetric algorithm is AES256 which is 284 | part of the Advanced Encryption Standard. AES assumes the data to be 285 | encrypted is a multiple of 16 bytes log and requires salting. A simple 286 | interface the the Erlang crypto application is in the module 287 | \verb+ez_crypt_aes.erl+ 288 | 289 | ># {include_function, "ez_crypt_test.erl", aes_test, 0}. 290 | 291 | Encrypting the same data twice gives a different crypto text, so the AES 292 | library is ``self salting''. 293 | 294 | ** Stream Encryption 295 | 296 | Encryption can operate in two modes: 297 | 298 | \begin{itemize} 299 | \item Batch encryption -- all the data to be encrypted is available at 300 | the same time and the data size is relatively small. 301 | \item Stream encryption -- 302 | the data is encrypted in chunks and we use a synchronized pair of senders and receivers. 303 | \end{itemize} 304 | 305 | Stream encryption is typically used when the data to be encrypted is 306 | huge or for things like streaming media -- where the stream can be 307 | considered ``infinite.'' 308 | 309 | This kind of code has an initialization step: 310 | 311 | S0 = crypto:stream_init(Type, Password) 312 | 313 | \verb+S0+ is an initial state. When new data \verb+Bin+ is to be encrypted 314 | we call: 315 | 316 | {S1, C1} = crypto:stream_encrypt(S0, Bin) 317 | 318 | \verb+C1+ is the crypto text and \verb+S1+ is the new state of the 319 | encrypter which must be used in the next encryption call.\footnote{Yes 320 | it's a Monad!} 321 | 322 | To illustrate stream encryption we can set up a pair of processes and 323 | set up a stream encryption channel between them: 324 | 325 | Typical client code looks like this: 326 | 327 | !! include_tagged:stream.erl:client: 328 | 329 | The server code follows the same pattern, only now we use 330 | \verb+stream_decrypt+ instead of \verb+stream_encrypt+ 331 | 332 | !! include_tagged:stream.erl:server: 333 | 334 | I've include a small test hardness so we can run the code. 335 | 336 | !! include_tagged:stream.erl:test: 337 | 338 | This code is very simple. To run this in a real application 339 | we'd use a socket TCP interface and a ``middle man'' 340 | pattern.\footnote{Read my Erlang Book to see how :-)} 341 | 342 | * Hashing and Padding 343 | 344 | Before we move to public key algorithms, we'll have a quick look at hashing 345 | and padding, since we'll need these in the next chapter. 346 | 347 | ** Hashing 348 | 349 | \begin{tabular}{|p{10cm}} 350 | A cryptographic hash function is a hash function which is considered 351 | practically impossible to invert, that is, to recreate the input data 352 | from its hash value alone. These one-way hash functions have been 353 | called "the workhorses of modern cryptography".[1] The input data is 354 | often called the message, and the hash value is often called the 355 | message digest or simply the digest. 356 | 357 | The ideal cryptographic hash function has four main properties: 358 | 359 | \begin{itemize} 360 | \item it is easy to compute the hash value for any given message. 361 | \item it is infeasible to generate a message that has a given hash. 362 | \item it is infeasible to modify a message without changing the hash. 363 | \item it is infeasible to find two different messages with the same hash. 364 | \end{itemize} 365 | 366 | Quote From: \verb+http://en.wikipedia.org/wiki/Cryptographic_hash_function+ 367 | 368 | \end{tabular} 369 | 370 | SHA1 is one of the most commonly used cryptographic hash algorithms. 371 | It produces a 120 bit hash of a data set. SHA1 is part of the Erlang 372 | standard libraries. 373 | 374 | There are two ways of calling it: 375 | 376 | 377 | digest1() -> 378 | crypto:hash(sha, "hello world"). 379 | 380 | digest2() -> 381 | S0 = crypto:hash_init(sha), 382 | S1 = crypto:hash_update(S0, "hello "), 383 | S2 = crypto:hash_update(S1, "world"), 384 | crypto:hash_final(S2). 385 | 386 | The first example can be used when the data involved is small. The 387 | second where the data concerned is large. For example, if we wanted to 388 | compare digital images of a few MBytes we could use the first method, 389 | but to compute the SHA1 checksum of a GByte movie we would use the 390 | second method with code like the following: 391 | 392 | !! include_tagged:ez_crypt.erl:filehash: 393 | 394 | ** Applications of hashing 395 | 396 | The single most important application of cryptographic hashing is in 397 | \textsl{validation}. Two data sets can be considered identical if they 398 | have the same checksum. 399 | 400 | \textsl{Note: This is not a mathematical certainty. If we have more than $2^{120}$ 401 | different files then two will have the same SHA1 402 | checksum.\footnote{Since an SHA1 checksum has 120 bits.}} 403 | 404 | ** Padding 405 | 406 | Suppose we have a \textsl{fixed length buffer}, containing salt and 407 | encrypted data. Something like this: 408 | 409 | \begin{verbatim} 410 | <---------------- fixed length -----------------> 411 | +-------+----------------+----------------------+ 412 | | Salt | Encrypted text | unused area | 413 | +-------+----------------+----------------------+ 414 | \end{verbatim} 415 | 416 | There is a problem with the unused area. If it contains some constant 417 | (like padding with zeros) we will leak information about the encrypted 418 | text, like, for example, the length of the text. A ``padding scheme'' 419 | fills the unused area with random bits. Something like: 420 | 421 | 422 | \begin{verbatim} 423 | <---------------- fixed length -----------------> 424 | +-------+----------------+----------------------+ 425 | | Salt | Encrypted text | random bits | 426 | +-------+----------------+----------------------+ 427 | \end{verbatim} 428 | 429 | The padding scheme I use in this tutorial is called OAEP Padding 430 | An explanation and the following diagram 431 | can be found at \url{https://en.wikipedia.org/wiki/Optimal_asymmetric_encryption_padding} 432 | 433 | \includegraphics[width=10.cm]{oaep.png} 434 | 435 | The Erlang implementation is straightforward and follows the diagram: 436 | 437 | ># {include_function, "oaep_byte_padding.erl", pad, 3}. 438 | 439 | * Public Key Systems 440 | 441 | In a public key system two different keys are used. One key is used to 442 | encrypt the data and a different key is used to decrypt the data. 443 | Use of different keys is called \textsl{Asymmetric Encryption}. 444 | 445 | In this tutorial I'll take a detailed look at 446 | The RSA\footnote{Named after Don Rivest, Adi Shamir and Leonard Adleman.} 447 | algorithm. RSA makes use of two keys \verb+{E,N}+ and 448 | \verb+{D,N}+. 449 | 450 | ** RSA in a nutshell 451 | 452 | Here's a simple test that illustrates how to use RSA: 453 | 454 | ># {include_function, "ez_crypt_test.erl", rsa_test, 0}. 455 | 456 | \verb+{E,D,N}+ is a triplet of three integers and 457 | \verb+mod_pow(X, P, N)+ computes $X^P mod \ N$: 458 | 459 | ># {include_function, "ez_crypt_math.erl", mod_pow, 3}. 460 | 461 | Note1: the algorithm is defined over \textsl{integers} not strings 462 | or binaries. 463 | 464 | Note2: either \verb+E+ or \verb+D+ can be used for encryption, provided we use 465 | the other value for decryption. So we can use \verb+D+ to encrypt and \verb+E+ 466 | to decrypt: 467 | 468 | This works because $(X^E)^D mod\ N$ is the same as $(X^D)^E mod\ N$ The 469 | $mod \ N$ bit is irrelevant, and obviously $(X^E)^D = (X^D)^E = X^{D*E}$ 470 | 471 | Using a \verb+N+ bit key we can encrypt any integer whose binary 472 | representation is less than or equal to \verb+N+ bits.\footnote{Note: 473 | It is not a good idea to encrypt either very small 474 | values or values whose size approaches the bit size of the key. This 475 | is since we need to have enough free space in the key for some 476 | ``salt'' and some ``padding''.} In practice I usually encrypt 477 | something like an SHA1 checksum (160 bits) with a 800 bit key. 800 478 | bits is fast enough for me and sufficiently difficult to crack that 479 | for most purposes can be considered secure. 480 | 481 | Creating a key pair is very easy: 482 | 483 | !! include_tagged:ez_crypt_math.erl:make_rsa_keypair: 484 | 485 | \verb+gen_prime(K, N)+ makes a prime of \verb+K+ bits that is 486 | relatively prime to \verb+N+. 487 | 488 | \verb+inv(A, B)+ computes \verb+C+ if it exists such that 489 | \verb+A*C mod B = 1+ (ie $A^{-1} mod \ B$).\footnote{This is called the modular 490 | inverse, and is computed using the extended Euclidean algorithm.} 491 | 492 | Note: If we know that \verb+P+ and \verb+Q+ are prime 493 | we can can compute \verb+N = P * Q+ but given \verb+N+ we cannot 494 | easily recover \verb+P+ and \verb+Q+. 495 | 496 | For example: 497 | 498 | \begin{verbatim} 499 | 1> P = 3760483207475282540887. 500 | 3760483207475282540887 501 | 2> Q = 3760483207475282540887. 502 | 3760483207475282540887 503 | 3> N = P*Q. 504 | 14141233953703588876397602262890002826746769 505 | 4> ez_crypt:is_probably_prime(N). 506 | false 507 | 5> ez_crypt:is_probably_prime(P). 508 | true. 509 | \end{verbatim} 510 | 511 | ** Text-book RSA 512 | 513 | RSA encrypts and decrypts integers but not strings. To encrypt a 514 | string we first convert it to a integer.\footnote{A string can be 515 | considered a base 256 integer.} 516 | 517 | The functions \verb+str2int+ 518 | and the inverse \verb+int2str+ convert between strings and integers. 519 | 520 | !! include_tagged:ez_crypt_math.erl:str2int: 521 | 522 | Note: I have appended a \verb+z+ character so that leading zeros in the 523 | string get correctly converted. 524 | 525 | Now we can defined the simplest version of RSA encode: 526 | 527 | ># {include_function, "ez_crypt_rsa.erl", encrypt_1, 2}. 528 | 529 | And the inverse: 530 | 531 | ># {include_function, "ez_crypt_rsa.erl", decrypt_1, 2}. 532 | 533 | We convert the binary \verb+Bin+ to an integer \verb+I+ then compute 534 | \verb+I^E mod N+. \verb+I+ has to be less than \verb+N+. The number of 535 | bits in \verb+I+ is approximately \verb|8*(size(Bin) + 1)| which means 536 | the maximum size of \verb+Bin+ is about 127 bytes. Provided we 537 | use this algorithm for small binaries we won't have any 538 | problems.\footnote{We'll exit if we can't encode the data.} 539 | 540 | ** RSA for small data with padding 541 | 542 | Our second algorithm pads the binary with random numbers using OEAP 543 | padding which we explained earlier: 544 | 545 | ># {include_function, "ez_crypt_rsa.erl", encrypt_2, 2}. 546 | 547 | The padding extends the size of the data to be encrypted (which is a 548 | good thing) and adds salt (which is also good) -- double goodness! 549 | 550 | The \verb+120+ and \verb+20+ specify the size of the buffers in the 551 | OAEP algorithm. \verb+120+ is the total buffer size in bytes. When 552 | converted to an integer this must be less than the modulus used in the 553 | RSA algorithm.\footnote{Note that there is a slight mismatch here. RSA 554 | is conventionally described in terms of a fix bit size modulus - this 555 | fits nicely with languages like C, but is a conceptual mismatch with 556 | Erlang which happily uses bignums. Given the size of a binary we don't 557 | know the exact size in bits of the integer returned by 558 | str2int. This could be fixed - but us an irrelevant detail as 559 | far as this tutorial is concerned.} 560 | 561 | And the inverse: 562 | 563 | ># {include_function, "ez_crypt_rsa.erl", decrypt_2, 2}. 564 | 565 | As you can see all this does is add a small wrapper round 566 | ``text book RSA.''\footnote{We saw this phenomena earlier, crypto 567 | software gets lay-on-layer of abstractions, so we have to keep a clear 568 | head when writing it.} 569 | 570 | ** RSA with large data volumes 571 | 572 | RSA is \textsl{slow} and \textsl{can only encrypt a small amount of 573 | data} (ie some value less than the modulus \verb+N+ in the key). This 574 | is not a problem since we typically use it to encrypt an SHA1 checksum 575 | (120 bits) or a short password. 576 | 577 | To speed up modulo arithmetic we might use ``Montgomery reduction'' 578 | (ie computations module N are time consuming, so we do this modulo 579 | $2^K$ which is easier, then do some transformation to compute modulo 580 | $N$). 581 | 582 | 2048 bit modulus are considered secure.\footnote{The world record is 583 | RSA-768 (2000 years on single code 2.2GHz AMD Opteron.} 584 | 585 | RSA should only be used to encrypt small integers (ie less than the modulus) 586 | If we want to encode a large value, we use two steps. First we generate 587 | a session key and use a fast symmetric encryption algorithm such as 588 | AES256 to encrypt the data, then we encrypt the session key with RSA. 589 | So we transmit: 590 | 591 | +---------------------------+---------------------------------+ 592 | | RSA encrypted session Key | Data encrypted with session key | 593 | +---------------------------+---------------------------------+ 594 | 595 | I've chosen random 160 bit session keys (The same bit length as 596 | SHA1), with this design choice the code is very simple: 597 | 598 | ># {include_function, "ez_crypt_rsa.erl", encrypt_3, 2}. 599 | 600 | Calling \verb+encrypt_2+ make the code \textsl{very} simple since 601 | \verb+encrypt_2+ adds padding (and indirectly salting). 602 | 603 | and decrypting is easy: 604 | 605 | ># {include_function, "ez_crypt_rsa.erl", decrypt_3, 2}. 606 | 607 | Again note how this code just uses a small wrapper round 608 | \verb+encrypt_2+ and \verb+decrypt_2+. Also observe how the encryption 609 | and decryption code mirror each other. \verb+term_to_binary+ and 610 | \verb+binary_to_term+ are used to pack and unpack the data avoiding 611 | the use of complex envelopes (like ASN.1).\footnote{Isn't this 612 | nice. Pity all crypto code isn't this easy to understand.} 613 | 614 | This is the most robust version of the RSA encryption routines 615 | so I've aliased these from \verb+ez_crypt.erl+: 616 | 617 | ># {include_function, "ez_crypt.erl", rsa_encrypt, 2}. 618 | 619 | and 620 | 621 | ># {include_function, "ez_crypt.erl", rsa_decrypt, 2}. 622 | 623 | Now we're done with RSA. But what about the keys? How should we manage these? 624 | 625 | ** Storing keys in files 626 | 627 | The next problem we'll look at is storing and distributing keys. 628 | We can create a key pair with 629 | 630 | >! new. 631 | 632 | > ez_crypt:make_rsa_key(128). 633 | 634 | This makes a key pair. But what we want to do is create two files 635 | from this. A plain text file with the public key which anybody can read 636 | and an encrypted file with the private key that is password protected: 637 | 638 | !! include_tagged:ez_crypt.erl:make_rsa_keyfiles: 639 | 640 | We can run this: 641 | 642 | 643 | > ez_crypt:make_rsa_keyfiles("joe", "erlang@gmail.com", 644 | 1024, <<"verysecret">>). 645 | ok 646 | 647 | This creates two files \verb+joe.pub+ which contains 648 | something like this: 649 | 650 | #{e => 65537, 651 | email => "erlang@gmail.com", 652 | n => 1080693449566203084629677149 ... 751495357, 653 | type => public_key}. 654 | 655 | We can recover the key with: 656 | 657 | ># {include_function, "ez_crypt.erl", read_public_key, 1}. 658 | 659 | What do we do with this file? We can either distribute this file 660 | together with our application, or we can cut-and paste the contents 661 | into some Erlang code which returns the public key. This way the key 662 | will be loaded without touching the file system. 663 | 664 | The public key contains something like this: 665 | 666 | #{type => encrypted_public_key, 667 | value => <<81,15,195,174,78,46,109,191,197,49,53,174,17, 668 | ... 103,199,16,138,86,27,184,52>>} 669 | 670 | The value has been encrypted with the password we supplied when we 671 | created the key. We can read the key with the following: 672 | 673 | ># {include_function, "ez_crypt.erl", read_private_key, 2}. 674 | 675 | ** RSA Open SSL key pairs 676 | 677 | RSA is essentially a pair of keys \verb+{E,N}+ and \verb+{D,N}+ and 678 | some modular arithmetic $M^{E}mod \ N$ so how come Open SSL is so 679 | complex? 680 | 681 | It turns out to be rather simple if we dig a little. 682 | 683 | We start by generating an RSA key pair: 684 | 685 | \begin{verbatim} 686 | ssh-keygen -t rsa -b 1024 -C "joe@somewehere.com" 687 | Generating public/private rsa key pair. 688 | Enter file in which to save the key (/Users/joearmstrong/.ssh/id_rsa): joe_rsa 689 | Enter passphrase (empty for no passphrase): 690 | Enter same passphrase again: 691 | Your identification has been saved in joe_rsa. 692 | Your public key has been saved in joe_rsa.pub. 693 | \end{verbatim} 694 | 695 | \verb+joe_rsa+ contains the \verb+{E,D,N}+ tuple that is the source of all 696 | goodness, and some other stuff that is less exciting. We can pull out 697 | this data as follows: 698 | 699 | !! include_tagged:decode_rsa_keys.erl:all: 700 | 701 | And have some fun! 702 | 703 | > decode_rsa_keys:test(). 704 | Key:{65537, 705 | 8465878345925402733279....971822495488001, 706 | 1071945495772 ... 1194367526413419199174309} 707 | wow 708 | 709 | So it was easy after all. 710 | 711 | * Secret sharing 712 | 713 | \url{https://en.wikipedia.org/wiki/Shamir%27s_Secret_Sharing} algorithm. 714 | 715 | Shami'r secret sharing algorithm is a \verb+K+, \verb+N+ algorithm. 716 | The key is split into \verb+N+ fragments. Any \verb+K+ of them can 717 | used to reconstruct the key. 718 | 719 | The implementation here is due to Robert Newson and was 720 | published at \url{https://github.com/rnewson/shamir/}. 721 | 722 | As an example, suppose we want to share the secret \verb+hello+ using 723 | seven shares, so that any three shares unlock the secret. We can 724 | generate the shares like this: 725 | 726 | > L=shamir:share(<<"hello">>, 3,7). 727 | [{share,3,1,<<206,145,84,97,217>>}, 728 | {share,3,2,<<229,208,230,155,102>>}, 729 | {share,3,3,<<67,36,222,150,208>>}, 730 | {share,3,4,<<56,15,170,12,128>>}, 731 | {share,3,5,<<158,251,146,1,54>>}, 732 | {share,3,6,<<181,186,32,251,137>>}, 733 | {share,3,7,<<19,78,24,246,63>>}] 734 | 735 | Using shares 1 2 and 5 we can reconstruct the secret as follows: 736 | 737 | > shamir:recover([lists:nth(1,L),lists:nth(2,L),lists:nth(5,L)]). 738 | <<"hello">> 739 | 740 | The algorithm fails if we don't give it three different shares: 741 | 742 | > shamir:recover([lists:nth(1,L),lists:nth(2,L),lists:nth(2,L)]). 743 | ** exception error: no function clause matching 744 | shamir:recover(3,[{1,10},{2,199}]) (shamir.erl, line 50) 745 | in function shamir:'-recover/1-lc$^2/1-1-'/2 (shamir.erl, line 48) 746 | in call from shamir:recover/1 (shamir.erl, line 48) 747 | 748 | Again the shared secret should be a password that unlocks or 749 | validates the content of another file. 750 | 751 | * Applications 752 | 753 | ** Application 1: Challenge-Response 754 | 755 | The Challeng-Response algorithm ensures that no plain text passwords 756 | are sent over the wire. 757 | 758 | Here's an example. Assume that \verb+joe+ has password 759 | \verb+"bingo"+. The interaction between a client and server is as follows: 760 | 761 | \begin{verbatim} 762 | {login,"joe"} 763 | Client --------->----------- Server 764 | 765 | {challenge,"zq12i3"} 766 | Client ----------<---------- Server 767 | 768 | {response,md5("bingo"++"zq12i3")} 769 | Client ------------->---------------------- Server 770 | 771 | login_ok | login_error 772 | Client --------------<--------------------- Server 773 | \end{verbatim} 774 | 775 | 776 | In response to a request \verb+{login,"joe"}+ the server generates a random 777 | string and sends it to the client. The client responds by computing 778 | the MD5 checksum of the random string and the shared secret. The server 779 | can check the responds using the shared secret and authenticate the 780 | user. 781 | 782 | ># {include_function, "challenge.erl", client, 3}. 783 | 784 | And the server is like this: 785 | 786 | ># {include_function, "challenge.erl", server, 0}. 787 | 788 | 789 | This is called ``one-way authentication'' - the server has validated 790 | the identity of the client, but not the other way around. The server 791 | has not proved to the client that it \textbf{is} the server. In ``two way 792 | authentication'' the algorithm is run twice, once in each 793 | direction. In the first pass the server authenticates the client, in 794 | the second pass the roles of the client and server are reversed and 795 | the client authenticates the server. 796 | 797 | The main problem with this is that the server needs to store plain 798 | text passwords - better methods exist. 799 | 800 | ** Application 2: Authentication Algorithms 801 | 802 | We'll first talk about how to authenticate a single file. 803 | 804 | The easiest way to authenticate something is to generate a checksum of 805 | the file and sign the checksum with your private key. I'll assume the 806 | public and private keys are stored in files: 807 | 808 | ># {include_function, "ez_crypt.erl", sign_file, 3}. 809 | 810 | To validate the file, a user needs the file \verb+File+ and the signature 811 | \verb+Sig+ and the public key of the signer. The file is authenticated 812 | with: 813 | 814 | ># {include_function, "ez_crypt.erl", validate_file, 2}. 815 | 816 | We can validate several files by storing the filenames and their 817 | checksums catalog and, then signing the catalog. For example the 818 | cataloger could be a list of Erlang terms: 819 | 820 | \begin{verbatim} 821 | {file,"this.erl", "a23121tsu128368136"}. 822 | {file,"that.erl", "1293879127391732"}. 823 | \end{verbatim} 824 | 825 | Making this is easy: 826 | 827 | ># {include_function, "ez_crypt.erl", sign_current_dir, 0}. 828 | 829 | To validate this we first validate the catalog, and if it is correct 830 | we know the SHA1 checksums of the individual files. Then we check 831 | the SHA1's of each of the files. 832 | 833 | ** Application 3: Secret message streams 834 | 835 | Secret message streams are streams of messages sent to a server where 836 | only the sever can decode the messages. The clients are supplied with 837 | a pre-compiled version of the server public key. This has the 838 | advantage that no password management in the client is necessary. 839 | 840 | Each message is encrypted with a new random key. The key is encoded with 841 | the public key of the server. 842 | 843 | ># {include_function, "ez_crypt.erl", encrypt_message, 2}. 844 | 845 | Decoding the message is easy: 846 | 847 | ># {include_function, "ez_crypt.erl", decrypt_message, 3}. 848 | 849 | We can do a quick test to show that this works 850 | 851 | 1> C=ez_crypt:encrypt_message("joe.pub",<<"hello">>). 852 | <<131,104,2,109,0,0,0,187,131,104,2,110,128,0,170,153,221, 853 | 253,81,86,2,138,59,10,204,163,156,185,191,...>> 854 | 2> ez_crypt:decrypt_message("joe.pri",<<"verysecret">>,C). 855 | <<"hello">> 856 | 857 | ** Application 4: TLS (Transport Layer Security) 858 | 859 | TLS, very much simplified works like this: 860 | 861 | \begin{verbatim} 862 | Client Server 863 | 864 | 865 | ---->---- 866 | ClientHello 867 | 868 | ----------<----- 869 | {hello, ServerPub} 870 | 871 | 872 | {Rpub,Rpri} = random_rsa_key() 873 | S1 = random_session_key(), 874 | 875 | -------->-------- 876 | {key1, enc(ServerPub,{S1,Rpub})} 877 | 878 | Server decodes message 879 | and recovers S1, Rpub 880 | (only server can do this) 881 | S2 = random_session_key() 882 | 883 | {key2, enc(Rpub, S2}) 884 | ------------<-------- 885 | 886 | 887 | Client decode message 888 | and recovers S2 889 | \end{verbatim} 890 | 891 | After the key exchange is over both sides know \verb+S1+ and 892 | \verb+S2+. \verb+S1+ is used to encrypt \verb+client->server+ 893 | messages and \verb+S2+ for \verb+server->client+ messages. 894 | 895 | These are the basic ideas involved. The actual protocol is far more 896 | messy than this simple diagram might imply. In the real TSL there is a 897 | phase of protocol negotiation, and packet envelopes and 898 | encoding/decoding of data has to be agreed upon. A pure Erlang 899 | implementation of a subset of the protocol is very easy to implement 900 | and understand. 901 | 902 | We use RSA to encode and decode the session keys, we don't negotiate 903 | the protocols and we use \verb+term_to_binary+ to encode the massages. 904 | 905 | Here's the entire thing in a few lines of Erlang: 906 | 907 | ># {include_function, "tls.erl", client,1}. 908 | 909 | And the server: 910 | 911 | ># {include_function, "tls.erl", server,0}. 912 | 913 | And we can run it like this:\footnote{Take a look in tls.erl for more details}. 914 | 915 | \begin{verbatim} 916 | 4> tls:test(). 917 | Client requesting key 918 | Server sending public key 919 | <0.49.0> 920 | Client sending S1:<<106,76,214,102,3,172,229,70,86,129,223, 921 | 156,134,223,14,104,6,88,7,242>> 922 | Server recovered S1:<<106,76,214,102,3,172,229,70,86,129,223, 923 | 156,134,223,14,104,6,88,7,242>> 924 | Server sending S2:<<192,132,4,3,69,169,132,252,71,60,111,200, 925 | 29,166,75,59,170,181,250,129>> 926 | Client recovered S2:<<192,132,4,3,69,169,132,252,71,60,111,200, 927 | 29,166,75,59,170,181,250,129>> 928 | \end{verbatim} 929 | 930 | ** Application 5: SFS Self-certifying File System 931 | 932 | The Self-certifying File System (SFS) is a distributed file system using 933 | a protocol described in the David Mazière's PhD Thesis 934 | \url{http://pdos.csail.mit.edu/~ericp/doc/sfs-thesis.ps}. 935 | 936 | The key idea in this thesis is to publish the SHA1 checksum of the public 937 | key of a server rather than the public key itself. It's conceptually 938 | similar TLS but with a simple twist. The client does not initially 939 | know the public key of the server. Instead it knows the SHA1 checksum 940 | of the public key of the server. 941 | 942 | It's called ``Self certifying'' since the server provides a public 943 | key which is not signed by a certification authority. 944 | 945 | The client can then request the public key from anywhere that claims 946 | to know what the public key of the server is. Once it has obtained 947 | a response to the public key request it can check the key using the 948 | SHA1 checksum to make sure that the key is correct. Anybody can provide 949 | the key and it can be cached by the client. 950 | 951 | Only the server can decode messages encoded with the public key. 952 | 953 | The advantage of this is that we only need to distribute the SHA1 954 | checksum of the public key of the server and NOT the public key 955 | itself. This is splendid since the checksum is only 20 bytes and can 956 | be easily communicated by any out of band method.\footnote{It's short 957 | enough so you can write it down on a sheet of paper.} This is not true 958 | of the public key itself which is several KBytes long. 959 | 960 | The client code to request the key is: 961 | 962 | ># {include_function, "sfs.erl", client, 2}. 963 | 964 | and the corresponding server code: 965 | 966 | ># {include_function, "sfs.erl", server, 0}. 967 | 968 | To flesh this out into a functioning server we need to combine the TSL 969 | code with this code and add some key manipulation.\footnote{This is 970 | left as an exercise -- if you've been paying attention this should be 971 | easy!} 972 | 973 | * Experiments 974 | ** Experiment 1 - Make some random integers 975 | 976 | Making random integers is a difficult problem. We can either trust 977 | some library to provide good random numbers, or use a combination of a 978 | software random number generator together with a physical source of 979 | randomness. We could for example, get the user to type in keystrokes 980 | and take the low-order bits in the time intervals between keystrokes, 981 | or take a digital photo and take the low order bits of the image, then 982 | destroy the image. No method of generating random integers is 983 | foolproof and indeed systems have made less secure by hacking into the 984 | part of the system that creates random numbers. 985 | 986 | In this tutorial I'll assume that the random number generator provided 987 | in the \verb+crypto+ application is sound. As an exercise you can 988 | combine this with (say) a keystroke timing algorithm to make a better 989 | algorithm. If you do this you can check the result into the project 990 | archive and send me a push request and I'll take a look at it. 991 | 992 | \verb+ez_crypto_math:random_integer(Len)+ calls the crypto random 993 | number generator to generate a random binary of \verb+Len+ bytes and 994 | converts it to an integer. For example: 995 | 996 | 1> ez_crypt_math:random_integer(20). 997 | 16288231860616810451978163722812339303633551557 998 | 999 | ** Experiment 2 - Generating a random integer that can be represented in a specific number of bits 1000 | 1001 | The next thing we might want to do is create a random integer of 1002 | \textsl{exactly} \verb+K+ bits. This is done with 1003 | \verb+ez_crypt_math:k_bit_random_integer+. For example: 1004 | 1005 | 1> I = ez_crypt_math:k_bit_random_integer(40). 1006 | 792296059411 1007 | 2> ez_crypt_math:bsize(I). 1008 | 40 1009 | 3> ez_crypt_math:bsize(ez_crypt_math:k_bit_random_integer(100)). 1010 | 100 1011 | 4> ez_crypt_math:bsize(ez_crypt_math:k_bit_random_integer(2048)). 1012 | 2048 1013 | 1014 | \verb+bsize(N)+ returns the number of bits necessary to represent the 1015 | integer \verb+N+. 1016 | 1017 | We use \verb+ez_crypt_math:k_bit_random_integer+ to generate 1018 | RSA keys integers. 1019 | 1020 | 1021 | ** Experiment 3 - Test an integer to see if it's a prime 1022 | 1023 | Prime number testing is tricky. 1024 | 1025 | First we test the number to see if it is a multiple of a small prime. 1026 | The function \verb+small_primes()+ returns a hard-wired list of the 1027 | first 2000 primes. If the number being tested is in this list 1028 | then we can say it definitely is a prime or if it's a multiple of a 1029 | number in the list then we know its not a prime so we can return 1030 | \verb+true+ or \verb+false+. If it's not a multiple of a small prime 1031 | we perform the Miller-Rabin test and call 1032 | \verb+ez_crypt_miller_rabin:is_probably_prime+ 1033 | 1034 | Miller-Rabin is an expensive test, which is why we tested against a list 1035 | of known primes before performing the test. 1036 | 1037 | !! include_tagged:ez_crypt_primes.erl:is_prime: 1038 | 1039 | Note that \verb+is_prime(N)+ willfully lies and returns \verb+true+ or 1040 | \verb+false+ and not (\verb+true+, \verb+false+ or 1041 | \verb+probably+).\footnote{Some things we'll never be sure about!} 1042 | 1043 | ** Experiment 4 - Make some random integers with a fixed bit length 1044 | 1045 | Many algorithms want to have primes with a fixed bit length. For 1046 | example primes whose binary representation fits into to exactly \verb+K+ 1047 | bits. Why is this? - the main reason is (I think) to make analysis of 1048 | the algorithms simpler and to know in advance how much space needs to 1049 | be allocated in fixed length data structures. 1050 | 1051 | Making a random prime with an exact number of bits is a tad more 1052 | tricky than making a random prime. 1053 | 1054 | I start by making a random number of exactly \verb+K+ bits. This is done by 1055 | generating a random integer known to have more than \verb+K+ bits, then 1056 | computing the length and chopping of one bit at a time until the 1057 | integer has the required number of bits. 1058 | 1059 | The first odd integer \verb+P+ of \verb+K+ bits is used to initialize 1060 | a generate and test algorithm. Every time the test on a prime 1061 | \verb+P+ fails we test \verb|P+2| until we hit a prime. Hopefully 1062 | \verb|P+2| will have \verb+K+ bits if \verb+P+ has \verb+K+ bits (why 1063 | is this?) - for large \verb+P+ this is true, but for small \verb+P+ it 1064 | is false, so for small \verb+P+ I use an entirely different algorithm. 1065 | 1066 | To be absolutely sure the generated prime has \verb+K+ bits I do a 1067 | final test before returning and if the generated prime does not have 1068 | exactly \verb+K+ bits I start over and do everything again. This is 1069 | highly unlikely, but it could happen and I do want some guarantees 1070 | here. The return value \textsl{must have exactly K bits}.\footnote{I 1071 | can't prove this mathematically, but I can empirically test it in 1072 | code, so I'm a happy bunny.} 1073 | 1074 | * ez_crypt.erl 1075 | 1076 | \verb+ez_crypt.erl+ is ``work in progress'' - it's not finished and 1077 | does not contain production quality code. If you want to base code on 1078 | it then fine - do so, but don't blame me if it has bugs. 1079 | 1080 | My intention here is to teach cryptographic techniques using small 1081 | understandable code fragments, it's not to produce production quality 1082 | code. 1083 | 1084 | * Miscellaneous 1085 | ** A little math 1086 | 1087 | I'm not going to tell you what a prime number is, if you don't know 1088 | you're probably reading the wrong tutorial. 1089 | 1090 | A lot of crypto algorithms involve computing $A^B$ and $A^B\ mod\ C$. 1091 | 1092 | Why is this? 1093 | 1094 | Let's meet Alice and Bob.\footnote{Cryptographers always talk about 1095 | Alice, Bob, Carol, Dan and on. Can you guess why?} Alice and Bob 1096 | both know two numbers $x$ and $y$. 1097 | 1098 | Alice chooses some random number $R$ and computes a message $M = R^x$. 1099 | Alice sends $M$ to Bob. Bob receives $M$ and computes $S=M^y$. Alice can also 1100 | compute $S=M^y$. $S$ is now a ``shared secret'' - both Alice and Bob 1101 | know $S$ anybody watching the communication sees only $M$ so they 1102 | cannot figure out the value of $S$ without knowing both $x$ and $y$. 1103 | 1104 | In its various forms the fact that $(K^x)^y = (K^y)^x$ pops up in 1105 | many algorithms and is the basic reason why RSA and Diffie-Hellman 1106 | work. 1107 | 1108 | In practice we'll compute $A^B mod \ N$ since values of $A^B$ can be 1109 | extremely large - taking the exponentiation modulo $N$ bounds all the 1110 | values to a maximum value or $N$ which makes things easier to work 1111 | with. 1112 | 1113 | ** A couple of theorems 1114 | 1115 | The correctness of RSA depends upon two theorems: 1116 | 1117 | Fermat's little theorem: $a^p \equiv \ a \ mod \ p$ if $p$ is a prime 1118 | number. 1119 | 1120 | Eulers theorem: $a^{\phi(N)}\ \equiv 1 (mod\ n)$ if $a$ and $n$ are 1121 | coprime. $\phi(N)$ is Eulers totient function.\footnote{the number of integers 1122 | from 1 to $N$ that are coprime to $N$.} 1123 | 1124 | ** Prime number testing 1125 | Prime number testing makes use of Fermats little theorem. 1126 | 1127 | Recall that 1128 | $a^p \equiv \ a \ mod \ p$ if $p$ is a prime 1129 | number. 1130 | 1131 | So to test if $p$ is prime we choose several different values of $a$ 1132 | and apply the Fermat test. Unfortunately if $p$ is composite for 1133 | certain values of $a$ the Fermat equivalence is obeyed. And for some 1134 | particularly nasty values of $p$\footnote{The Carmichael numbers} the 1135 | equivalence is obeyed for all values of $a$. 1136 | 1137 | Tests where the Fermat equivalence is obeyed but when $p$ is composite 1138 | are called ``false witnesses.'' 1139 | 1140 | The Miller Rabin test is a probabilistic variant of the Fermat 1141 | test. Each iteration of the test decreases the probability of error 1142 | this is why we can say that a large number $p$ is ``probably prime'' 1143 | not that it is ``definitely a prime.'' We can however say that a 1144 | large number is composite without being able to compute the factors. 1145 | 1146 | This is one of the frustrating things about RSA - we can easily prove 1147 | that the modules is composite - but we can't compute the factors. 1148 | This is a good thing \texttrademark - if it were false the worlds financial system 1149 | would break down. 1150 | 1151 | ** Why RSA works 1152 | 1153 | By construction 1154 | 1155 | \hspace{15pt} $ed \equiv 1 \ mod \ \phi(N) $ 1156 | 1157 | So there exists some $k$ such that: 1158 | 1159 | \hspace{15pt} $ed \equiv 1 + k \phi(N) $ 1160 | 1161 | Suppose: 1162 | 1163 | \hspace{15pt} $c = m^e \ mod \ N$ 1164 | 1165 | Then: 1166 | 1167 | $c^{d} = m^{ed} \ mod \ N$\\ 1168 | $= m^{1 + k\phi(N)} \ mod\ N$\\ 1169 | $= m^{1}.(m^{\phi(N)} \ mod \ N)^k$\\ 1170 | 1171 | But $m^{\phi(N)} mod N = 1$ is Euler's theorm\footnote{also known as the Fermat-Euler 1172 | theorem of Eulers's totient theorem}, thus: 1173 | 1174 | $c^{d} = m^{1}.(1)^k$\\ 1175 | $= m$\\ 1176 | 1177 | Also $N$ is the product of two primes $P$ and $Q$ then $\phi(N) = (P-1)*(Q-1)$ 1178 | 1179 | Note: this proof is not quite correct, since we must also show that 1180 | $gcd(m,N) = 1$ which requires a longer explanation ... 1181 | 1182 | ** Further reading 1183 | 1184 | + Montague arithmetic 1185 | + Galoir Fields GF(256) 1186 | 1187 | ** How big RSA keys? 1188 | 1189 | How large should an RSA key be? The WikiPedia says the world record 1190 | was set for RSA-768\footnote{The modulus is 768 bits.} and has stood 1191 | since 2009. This took the equivalent of 2000 years on a single-core 1192 | 2.2 GHZ AMD Operon. 1193 | 1194 | Adding additional bits to the modulus increases the complexity of the 1195 | problem. The difficulty of attacking RSA reduces to the difficulty 1196 | of factoring the RSA modulus which is known to be a hard problem. 1197 | 1198 | 2048 bits is recommended if you're expecting aliens.\footnote{I've seen 1199 | Mars Attacks, but since we won, I'm not particularly worried about 1200 | them.} 1201 | For my purposes I reckon a hundred bits more than the world 1202 | record is good enough for most purposes.\footnote{I'd check this every 1203 | year or so, to see what's happening here.} 1204 | 1205 | At high levels of security guarding against ``side channel attacks'' and 1206 | software bugs is far more important than agonizing over bit lengths. 1207 | 1208 | ** How big Hashes, which hash should I choose? 1209 | 1210 | I've used SHA1 in this tutorial. And \textsl{Yes I know it's not 1211 | recommended.} SHA256 is now recommended. 1212 | 1213 | ** What is a good symmetric encryption algorithm? 1214 | 1215 | I've used AES256 -- I think it's ok. I'm not actually writing 1216 | ``super duper secure systems'' so I just want to stop \textsl{script 1217 | kiddies} from messing with my stuff - not professional attackers. 1218 | 1219 | ** Sidechannel attacks 1220 | 1221 | Most crypto systems are broken not because somebody managed to break 1222 | the crypto system but because some property of the system 1223 | was exploited that the creator of the system had not thought of. 1224 | 1225 | A crypto system is as strong as its weakest link, so although the 1226 | math in the crypto algorithms might be sound side channel attacks are 1227 | possible. 1228 | 1229 | Here are some example of side channels: 1230 | 1231 | + Bribing a sysadmin who knows the system passwords 1232 | + Spying on memory while a crypto program is running 1233 | + Planting a password sniffer in the firmware of the keyboard 1234 | + Measuring the timing of internal signals when a password is entered 1235 | + Trying all the small strings on your hard disk to see if they are passwords 1236 | + Analyzing the swap area of your disk 1237 | + Torture 1238 | 1239 | As an example of side channels you might like to consult the 1240 | validation procedure SET transactions (ref). 1241 | 1242 | True story: A few years ago I was involved in an pre-study for an 1243 | ``electronic wallet'' project. We wanted to put a crypto-chip into our 1244 | phones and partner with a major bank to make a secure payment system. 1245 | 1246 | The bank said - ``we won't trust your chip. We can make the chip which 1247 | you put in your phones'' - we said ``You're not going to put your chip 1248 | in our phones, we don't trust your chip, it might mess up our 1249 | phones.'' So the project didn't happen. 1250 | * lin.erl 1251 | 1252 | This crypto tutorial started off many years ago with a module called 1253 | \verb+lin.erl+ it has a sub-set of the routines in 1254 | \verb+ez_crypt_math.erl+ and is somewhat easier to understand so I've 1255 | included it here. 1256 | 1257 | Many cryptography algorithms need some bignum integer functions. In 1258 | particular we need some support for the \verb+inv+ function which is 1259 | used to compute the RSA exponents. 1260 | 1261 | For example, suppose we want to find $X$ such that $28 * X \equiv 1 1262 | mod 75$ The solution is $X = 28^{-1} mod 75$. This we can compute with 1263 | the \verb+inv/2+ function in the Erlang shell: 1264 | 1265 | 1> lin:inv(28, 75) 1266 | 67. 1267 | 1268 | So $67$ is the ``modular multiplicative inverse of 28 modulo 75'' -- 1269 | we can check this in the Erlang shell: 1270 | 1271 | 2> 28*67 rem 75. 1272 | 1 1273 | 1274 | Modular arithmetic in the integer domain keeps all values constrained 1275 | to the integer domain which is why it is nice for 1276 | cryptography.\footnote{Even more fun is the finite field GF(256) used 1277 | in the AES standard. In this field values are constrained to the 1278 | 0..255 domain -- but this is way too complex for an introductory 1279 | tutorial.} 1280 | 1281 | \verb+lin.erl+ exports the following functions: 1282 | 1283 | \begin{description} 1284 | \item \verb+pow(A, B, M) -> V+\\ 1285 | Computes \verb+V = (A^B) mod M+ 1286 | 1287 | \item \verb+inv(A, B) -> C | no_inverse+\\ 1288 | Computes \verb+C+ such that \verb+A*C mod B = 1+ 1289 | If such C exists. 1290 | 1291 | \item \verb+solve(A, B) => {X, Y} | insoluble+\\ 1292 | Solves the linear congruence \verb+A * X - B * Y = 1+ if it is solvable. 1293 | 1294 | \item \verb+str2int(Str)+\\ 1295 | Converts a string to a base 256 integer. 1296 | 1297 | \item \verb+int2str(N)+\\ 1298 | Converts a base 256 integer to a string 1299 | 1300 | \item \verb+gcd(A, B)+\\ 1301 | Computes the greater common denominator of \verb+A+ and \verb+B+ 1302 | \end{description} 1303 | 1304 | Some of these are pretty simple, \verb+pow(A, B, M)+ computes 1305 | \verb+A^B mod M+. To compute this we proceed as follows: if 1306 | \verb+B+ is even we compute \verb+pow(A, B div 2, M)+ and square the result 1307 | (modulo \verb+M+). If \verb+B+ is odd and greater than one 1308 | we compute \verb+P = pow(A, (B-1)div 2, M)+ 1309 | and then \verb+P*P*A mod M+: 1310 | 1311 | ># {include_function,"lin.erl", pow, 3}. 1312 | 1313 | \verb+gcd+ is also easy: 1314 | 1315 | ># {include_function,"lin.erl", gcd, 2}. 1316 | 1317 | As are conversions between strings and integers: 1318 | 1319 | !! include_tagged:lin.erl:tag2: 1320 | 1321 | \verb+solve/2+ requires some thought, before launching into the code 1322 | we give some examples: 1323 | 1324 | Solve \verb|12x - 5y = 1| for integer \verb+x+ and \verb+y+, solution 1325 | \verb+x = -2+ and \verb+y = -5+ (check \verb|12.-2 -5.-5 = 1| as required. 1326 | 1327 | Solve \verb|28x - 25y = 1| for integer \verb+x+ and \verb+y+, solution 1328 | \verb+x = -8+ and \verb+y = -9+ (check \verb|28.-8 - (25.-9) = 1| as required. 1329 | 1330 | These solutions are computed as follows: 1331 | 1332 | \begin{verbatim} 1333 | > lin:solve(12,5). 1334 | {-2,-5} 1335 | > lin:solve(28,25). 1336 | {-8,-9} 1337 | \end{verbatim} 1338 | 1339 | To see how to solve these congruences we give a simple example 1340 | 1341 | \begin{verbatim} 1342 | solve 12x - 5y = 1 (1) 1343 | or (2*5 + 2)x - 5y = 1 1344 | regrouping 2x + 5(2x - y) = 1 1345 | 1346 | let a = 2x - y (2) 1347 | 1348 | then 2x + 5a = 1 (3) 1349 | or 2x + (2*2 + 1)a = 1 1350 | regrouping 2(x + 2a) + a = 1 1351 | 1352 | let b = x + 2a (4) 1353 | 1354 | then 2b + a = 1 (5) 1355 | 1356 | A solution to this is b = 1, a = -1 1357 | 1358 | Then from (4) x = b - 2a = 1 - 2(-1) = 3 (6) 1359 | and from (2) y = 2x - a = 2*3 -(-1) = 7. (7) 1360 | 1361 | So a solution is (x, y) = (3, 7) 1362 | 1363 | Check 12*3 - 5*7 = 1 as required 1364 | \end{verbatim} 1365 | 1366 | This gives us the key idea as to how to solve linear congruences. 1367 | 1368 | In order to solve \verb+12x - 5y = 1+ (equation 1) we make a 1369 | substitution (equation 2) to reduce this to a simpler form, then we 1370 | have to solve the simpler sub problem which is \verb|2x + 5a = 1| 1371 | (equation 3) . This is a simpler problem because the magnitude of the 1372 | arguments are less. Eventually the process terminates when a trivial 1373 | subproblem (equation 5) is encountered. Having found the solution to 1374 | the sub-problem we back substitute (equations 6 and 7) to obtain the 1375 | final result. 1376 | 1377 | Note that some linear congruences are not solvable; 1378 | \verb+Ax - By = 1+ is not soluble if \verb+A mod B = 0+ 1379 | 1380 | The above algorithm is easily encoded as: 1381 | 1382 | ># {include_function,"lin.erl", gcd, 2}. 1383 | 1384 | !! include_tagged:lin.erl:tag1: 1385 | 1386 | Fortunately Erlang has bignums so that: 1387 | 1388 | \begin{verbatim} 1389 | > lin:solve(2812971937912739173,2103789173917397193791739173). 1390 | {-997308564012181922485842000,-1333499116160234237} 1391 | \end{verbatim} 1392 | 1393 | Finally \verb+inv(A, B)+ which computes 1394 | \verb+C+ such that \verb+A*C mod B = 1+ if such an inverse exists. 1395 | 1396 | ># {include_function,"lin.erl", inv, 2}. 1397 | 1398 | * LIBNACL 1399 | 1400 | This is an ``easy to use'' crypto library. 1401 | 1402 | Here is a quote from the paper of Bernstein, Lange and Schwabe 1403 | which describes the library: 1404 | 1405 | \begin{tabular}{|p{10cm}} 1406 | \begin{verbatim} 1407 | A typical cryptographic library uses several steps to authenticate and encrypt 1408 | a packet. Consider, for example, the following typical combination of RSA, AES, 1409 | etc.: 1410 | 1411 | – Alice generates a random AES key. 1412 | – Alice uses the AES key to encrypt the packet. 1413 | – Alice hashes the encrypted packet using SHA-256. 1414 | – Alice reads her RSA secret key from “wire format.” 1415 | – Alice uses her RSA secret key to sign the hash. 1416 | – Alice reads Bob’s RSA public key from wire format. 1417 | – Alice uses Bob’s public key to encrypt the AES key, hash, and signature. 1418 | – Alice converts the encrypted key, hash, and signature to wire format. 1419 | – Alice concatenates with the encrypted packet. 1420 | \end{verbatim} 1421 | Quote from: \url{http://cr.yp.to/highspeed/coolnacl-20120725.pdf} 1422 | \end{tabular} 1423 | 1424 | You'll notice the similarity between this list and some of the 1425 | contortions we've been through earlier in this tutorial. 1426 | 1427 | + Erlang binding to NaCl in the form of libsodium \url{https://github.com/jlouis/erlang-nacl}. 1428 | + NACL \url{http://nacl.cr.yp.to/}. 1429 | 1430 | * Things I have not talked about 1431 | 1432 | ** Keyrings - Certificate Chains, Certifying authorities. 1433 | 1434 | Once we've understood the ideas of public key encryption and 1435 | authentication the ideas of a key-chain or certifying authority is 1436 | rather easy. 1437 | 1438 | We start with a ``root certificate'' (\textsl{The Key of the world}) 1439 | and use this to sign sub-certificates. The sub-certificates can in 1440 | their turn create child certificates. The certificate contain 1441 | backwards pointer to their parents and a ``best before'' date. 1442 | 1443 | ** Galois Arithmetic 1444 | 1445 | Integer multiplication is problematic. If we multiply two positive 1446 | integer together where both is in the range \verb+0..255+ we sometimes 1447 | get an integer that is outside this range. 1448 | 1449 | Integer division is even more horrible. If we divide two integers 1450 | we sometimes get an integer, we sometimes get a float, and sometimes it's 1451 | impossible.\footnote{When we try to divide by zero.} 1452 | 1453 | A \textsl{Galois field} is an algebraic structure where the following 1454 | rules apply: 1455 | 1456 | \begin{itemize} 1457 | 1458 | \item Multiplication, division, addition and subtraction of any two 1459 | elements in the field result in a third value that is within the field. 1460 | 1461 | \item Each element in the field has an inverse. So if $A$ is in the 1462 | field there exists some element $B$ such that $A*B = 1$. Usually we 1463 | write $B$ as $A^{-1}$ 1464 | \end{itemize} 1465 | 1466 | The Galois Field GF(256) contains the integers \verb+0..255+ if we 1467 | perform any arithmetic operation on integers in the Galois field we 1468 | get another integer in the field. - Amazingly algorithms for inverting 1469 | matrices and solving linear equations which were first used in the 1470 | integer domain also work in the Galois field. In the Galois field 1471 | things like division of elements within the field stay within the 1472 | field.\footnote{Unlike the division of integers which can take you 1473 | outside the integer domain and into the domain of real numbers.} 1474 | 1475 | Arithmetic in the Galois field cannot overflow or fail with precision 1476 | problems and is used widely in various crypto-algorithms. 1477 | 1478 | Note: The math behind RSA is relatively straightforward.\footnote{Which is 1479 | why I like it.} The math behind ``Elliptic Curve Cryptography'' is 1480 | however not so simple.\footnote{This is an understatement, a PhD in 1481 | number theory would be a good prerequisite here.} 1482 | 1483 | We can either use thigns like RSA where the math is reasonable simple 1484 | and code uses Erlang bignums and ``understand it ourselves'' OR we 1485 | can trust math we do not understand and programs we cannot reasonably 1486 | be expected to validate.\footnote{And yes, we are programmers, and 1487 | the code is open source and we can read it, but it is beyond the state 1488 | of the art to prove that it is correct and even if we could prove that 1489 | the program corresponds to the math, we probably could not understand 1490 | the math.} 1491 | 1492 | So this is where I end. 1493 | 1494 | To delve deeper I can recommend the following: 1495 | 1496 | \begin{itemize} 1497 | \item \url{https://www.schneier.com/books/applied_cryptography/} 1498 | \item \url{https://www.crypto101.io/} 1499 | \end{itemize} 1500 | 1501 | 1502 | 1503 | --------------------------------------------------------------------------------