Support for cryptographic algorithms varies between Prolog systems. In the following, we are using SWI-Prolog due to its extensive OpenSSL bindings and

In this text, we consider three aspects of cryptography that are extremely relevant in practice:

**hash functions**that let us assess the integrity of data**digital signatures**to declare and assess the authenticity of data**symmetric encryption**to ensure the confidentiality of data.

To try the Prolog examples of this document, you need SWI-Prolog

?- use_module(library(ssl)), current_prolog_flag(ssl_library_version, V).The convention I use in this document is that information that ought to be keptV = 'OpenSSL 1.1.0f 25 May 2017'.

In Prolog, all data is represented by Prolog terms. In SWI-Prolog, the cryptographic predicates let you encrypt, decrypt, sign and hash the following

- atoms
- strings
- lists of
*characters* - lists of
*codes*, such as Unicode code points - lists of
*bytes*, also called*octets*, which are integers between 0 and 255.

?- atom_codes(However, cryptographic methods typically work onκρυπτός, Cs). Cs = [954, 961, 965, 960, 964, 972, 962].

There are conversion predicates that let you transform every such term into any other type. Of particular interest in this context is the DCG nonterminal

?- atom_codes(Thus, if you need total control over the representation, you can always convert your data toκρυπτός, Cs), phrase(utf8_codes(Cs), UTF8). Cs = [954, 961, 965, 960, 964, 972, 962],UTF8 = [206, 186, 207, 129, 207, 133, 207, 128, 207|...].

In cryptographic applications, short lists of

?- hex_bytes(To create a list of'501ACE', Bs). Bs = [80, 26, 206]. ?- hex_bytes(Hex,[80,26,206]). Hex = '501ace'.

?- crypto_n_random_bytes(32, Bs), hex_bytes(Key, Bs). Bs = [169, 150, 152, 246, 5, 80, 194, 181, 117|...], Key =Thus, we can easily generate strong keys and unique tokens when needed.a99698f60550c2b575cb6ffc01c065b195b32a468ff046621f4db91308c1bfd3.

To more compactly store and embed binary data in your applications, also consider Base64 encoding. In SWI-Prolog, you can use

?- base64_encoded(κρυπτός, Base64, []).Base64 = "vcTFw8fMwg==".

Hash functions are needed in almost all applications of modern cryptography. In

Here is an example (click on the hash to expand it):

?- crypto_data_hash('Hello world!', Hash, [algorithm(blake2s256)]).We can useHash = c63813a8f804abece06213a46acd04a2d738c8e7a58fbf94bfe066a9c7f89197.

?- hex_bytes(c63813a8f804abece06213a46acd04a2d738c8e7a58fbf94bfe066a9c7f89197, Bytes).Bytes = [198, 56, 19, 168, 248, 4, 171, 236, 224|...].

?- crypto_data_hash('Hello world!', Hash, [algorithm(A)]). Hash = c0535e4be2b79ffd93291305436bf889314e4a3faec05ecffcbb7df31ad9e51a,This shows thatA = sha256.

A hash can be used to assess the

This raises the question: How can we be certain that such a

Two additional features make this process vastly more secure:

- First, we make computing the hash
*as slow as we can*. This counteracts brute-force attacks where an attacker tries many different passwords to find one whose hash matches the stored hash. One way to do this is to compute the*N*-fold application of the hash function. For example,*N*= 2^{17}makes brute-force attacks more than 100 000 times slower than applying the hash only once. - Second, we generate a so-called
*salt*, which is a list of random bytes that we combine with the password before it is hashed. We thus ensure (with extremely high probability) that even*identical*passwords yield*different*hashes. We store the salt together with the computed hash, so that we can use it for later reference.

For example:

?- crypto_password_hash(test, Hash). Hash = '$pbkdf2-sha512$t=131072$Xj6ZIfB4U+QOeZr3ymE/AA$2KYXsPFI2zJVMb9PHVtN+pVwQ6f7LleXF8ehbyqgOmkINcIYjO8IFhz8LelwMjzidEtojRHmC0B5RQJDEB2/tw'. ?- crypto_password_hash(test, Hash). Hash = '$pbkdf2-sha512$t=131072$+aXCnE1r3gAFjpQ3qHcsVw$JAiD2sLbBZPQD1/FtBJUE+iWXRF0VvC/p8etsP6JGSo2dz5U+lV3a6tFDo84mluW1BufFoGZkAuaMW+K74DIaQ'.In this case, even though the password was the

The resulting hash encapsulates everything that is necessary to later

?- crypto_password_hash(test, '$pbkdf2-sha512$t=131072$+aXCnE1r3gAFjpQ3qHcsVw$JAiD2sLbBZPQD1/FtBJUE+iWXRF0VvC/p8etsP6JGSo2dz5U+lV3a6tFDo84mluW1BufFoGZkAuaMW+K74DIaQ').Experience shows that most users choose very predictable passwords, and also reuse the same passwords for different applications. Usingtrue.?- crypto_password_hash(password, '$pbkdf2-sha512$t=131072$+aXCnE1r3gAFjpQ3qHcsVw$JAiD2sLbBZPQD1/FtBJUE+iWXRF0VvC/p8etsP6JGSo2dz5U+lV3a6tFDo84mluW1BufFoGZkAuaMW+K74DIaQ').false.

If necessary, you can use

Private and public keys can for example be loaded from PEM and DER encoding via

As a concrete example, let us consider the signatures in the

Using SWI-Prolog, it is easy to obtain the certificate chain of this domain, using

?- http_open('https://www.metalevel.at', Stream, []), ssl_peer_certificate_chain(Stream, Cs).This yields in

Note that the SSL bindings of SWI-Prolog

Let us inspect the

?- http_open('https://www.metalevel.at', Stream, []), ssl_peer_certificate_chain(Stream,This outputs the following Prolog terms:[First|_]), maplist(portray_clause, First).

version(2). notbefore(1492041600). notafter(1528847999). serial('2FD9ADA774690FCFD2D69B0AF2096AE2'). subject(['CN'='www.metalevel.at']). hash("F96CDAF470FECC7B2F0FE426270EF678294935EFBE0C2245AB79442D2DFFE54B"). signature("35A5467AAFE158229AB363A88740881B853F3D1A3B6A6747998C9DC0F0CB3BD6062F266D19D88942D164EF6FF109699ABB9F8156340C8DA979AFE31B6D994DCB3B84EFBE8E5CC998D3D80B84D70917EEBF49CC7BF931A6930998C243B57F5E4C26FBF4EDC1F55F321D28116ED9ED4E41A510C0A4C663AB1B3DF324128C375476BC3A950E8E58A03BDE619566E943D04E40852253A38CC79A76A3AEFE85E7F45B06EC46D35747894BAB386BAD74761342C3FB5495DF1CE88BF61411B451A1E1FFCCCA057D3BC3CEDC69E29C0378F8AFDB010A8D89158C2778AC32D43A2BA7CA0911DCC50ABEBE9ED78779BC98BE3A3A03A73479E8D4A00B038E3633F2C38FA518"). signature_algorithm('To verify the signature of this certificate, we need the following information:RSA-SHA256'). to_be_signed("3082047DA00302010202102FD9ADA774690FCFD2D69B0AF2096AE2300D06092A864886F70D01010B05003065310B300906035504061302555331153013060355040A130C7468617774652C20496E632E311D301B060355040B1314446F6D61696E2056616C6964617465642053534C3120301E060355040313177468617774652044562053534C20534841323536204341301E170D3137303431333030303030305A170D3138303631323233353935395A301B3119301706035504030C107777772E6D6574616C6576656C2E617430820122300D06092A864886F70D01010105000382010F003082010A0282010100DC7E4EE613582D9D6293BC792B8569C15F939AC2CCC0817D48779948AA54A001B7ECF5266DD2B05A6C266595BF0F55EBE11F64081B2F58B9F65F4B3F11BCE1FF00E8E3FFEDE7B5165CEA0694925F67E757A740BD9DED6028D1080420C0DFE48E672C0759D36FCF5141647851F7FCF9081EEDA1BEB4834F21D973AF03D30DD58EDC3C1D6FA04C812B608C97F677B45A011F8DDA92E073C83DB86A18CBF7C048AE03F8145A94333DAEA1E6B87B963EED0C78C7DE5068F8A9B80D1A4DDA62DB80BBBD81041BA1CEE491D5C630E73F1F0F0CDDCD4E198B15092C3904DD71BB348F252392EBC66858767B4F9E4EE64C792C4D68D8BE48233FDDEECD78AEDEC457C3450203010001A38202893082028530290603551D110422302082107777772E6D6574616C6576656C2E6174820C6D6574616C6576656C2E617430090603551D1304023000302B0603551D1F042430223020A01EA01C861A687474703A2F2F746D2E73796D63622E636F6D2F746D2E63726C306E0603551D20046730653063060667810C0102013059302606082B06010505070201161A68747470733A2F2F7777772E7468617774652E636F6D2F637073302F06082B0601050507020230230C2168747470733A2F2F7777772E7468617774652E636F6D2F7265706F7369746F7279301F0603551D230418301680147D29312FC11E6EAE31056AB3EB1CCDA9DDAE809A300E0603551D0F0101FF0404030205A0301D0603551D250416301406082B0601050507030106082B06010505070302305706082B06010505070101044B3049301F06082B060105050730018613687474703A2F2F746D2E73796D63642E636F6D302606082B06010505073002861A687474703A2F2F746D2E73796D63622E636F6D2F746D2E63727430820105060A2B06010401D6790204020481F60481F300F1007600DDEB1D2B7A0D4FA6208B81AD8168707E2E8E9D01D55C888D3D11C4CDB6ECBECC0000015B679E9C9A0000040300473045022043D95C8201CF802751D104F1A39AD69C648A1D9F6D66C00FB7EE10B598762D46022100A73811D11680E14DDA9048C2BDB61BBD17A443F2EA67940C6594C3BE49FE733A007700A4B90990B418581487BB13A2CC67700A3C359804F91BDFB8E377CD0EC80DDC100000015B679E9DA40000040300483046022100E40047A5966BED0A726326D0E4CA5D1C1E4F8C0AFC67CBA074582C3AC779B0FF022100DED9A9A854867EEB37C464726AE3E6C57C15EB40019FF639C25413495C8D5CC6"). issuer_name(['C'='US', 'O'='thawte, Inc.', 'OU'='Domain Validated SSL', 'CN'='thawte DV SSL SHA256 CA']). key(public_key(rsa("DC7E4EE613582D9D6293BC792B8569C15F939AC2CCC0817D48779948AA54A001B7ECF5266DD2B05A6C266595BF0F55EBE11F64081B2F58B9F65F4B3F11BCE1FF00E8E3FFEDE7B5165CEA0694925F67E757A740BD9DED6028D1080420C0DFE48E672C0759D36FCF5141647851F7FCF9081EEDA1BEB4834F21D973AF03D30DD58EDC3C1D6FA04C812B608C97F677B45A011F8DDA92E073C83DB86A18CBF7C048AE03F8145A94333DAEA1E6B87B963EED0C78C7DE5068F8A9B80D1A4DDA62DB80BBBD81041BA1CEE491D5C630E73F1F0F0CDDCD4E198B15092C3904DD71BB348F252392EBC66858767B4F9E4EE64C792C4D68D8BE48233FDDEECD78AEDEC457C345", "010001", -, -, -, -, -, -))). crl(['http://tm.symcb.com/tm.crl']).

- The
*to-be-signed*portion of the certificate. This is a hex value that represents all attributes and values of the certificate that were signed. - The
*algorithm*that was used for signing the certificate. - The
*public key*of the entity that signed this certificate.

?- hex_bytes('3082047DA00302010202102FD9ADA774690FCFD2D69B0AF2096AE2300D06092A864886F70D01010B05003065310B300906035504061302555331153013060355040A130C7468617774652C20496E632E311D301B060355040B1314446F6D61696E2056616C6964617465642053534C3120301E060355040313177468617774652044562053534C20534841323536204341301E170D3137303431333030303030305A170D3138303631323233353935395A301B3119301706035504030C107777772E6D6574616C6576656C2E617430820122300D06092A864886F70D01010105000382010F003082010A0282010100DC7E4EE613582D9D6293BC792B8569C15F939AC2CCC0817D48779948AA54A001B7ECF5266DD2B05A6C266595BF0F55EBE11F64081B2F58B9F65F4B3F11BCE1FF00E8E3FFEDE7B5165CEA0694925F67E757A740BD9DED6028D1080420C0DFE48E672C0759D36FCF5141647851F7FCF9081EEDA1BEB4834F21D973AF03D30DD58EDC3C1D6FA04C812B608C97F677B45A011F8DDA92E073C83DB86A18CBF7C048AE03F8145A94333DAEA1E6B87B963EED0C78C7DE5068F8A9B80D1A4DDA62DB80BBBD81041BA1CEE491D5C630E73F1F0F0CDDCD4E198B15092C3904DD71BB348F252392EBC66858767B4F9E4EE64C792C4D68D8BE48233FDDEECD78AEDEC457C3450203010001A38202893082028530290603551D110422302082107777772E6D6574616C6576656C2E6174820C6D6574616C6576656C2E617430090603551D1304023000302B0603551D1F042430223020A01EA01C861A687474703A2F2F746D2E73796D63622E636F6D2F746D2E63726C306E0603551D20046730653063060667810C0102013059302606082B06010505070201161A68747470733A2F2F7777772E7468617774652E636F6D2F637073302F06082B0601050507020230230C2168747470733A2F2F7777772E7468617774652E636F6D2F7265706F7369746F7279301F0603551D230418301680147D29312FC11E6EAE31056AB3EB1CCDA9DDAE809A300E0603551D0F0101FF0404030205A0301D0603551D250416301406082B0601050507030106082B06010505070302305706082B06010505070101044B3049301F06082B060105050730018613687474703A2F2F746D2E73796D63642E636F6D302606082B06010505073002861A687474703A2F2F746D2E73796D63622E636F6D2F746D2E63727430820105060A2B06010401D6790204020481F60481F300F1007600DDEB1D2B7A0D4FA6208B81AD8168707E2E8E9D01D55C888D3D11C4CDB6ECBECC0000015B679E9C9A0000040300473045022043D95C8201CF802751D104F1A39AD69C648A1D9F6D66C00FB7EE10B598762D46022100A73811D11680E14DDA9048C2BDB61BBD17A443F2EA67940C6594C3BE49FE733A007700A4B90990B418581487BB13A2CC67700A3C359804F91BDFB8E377CD0EC80DDC100000015B679E9DA40000040300483046022100E40047A5966BED0A726326D0E4CA5D1C1E4F8C0AFC67CBA074582C3AC779B0FF022100DED9A9A854867EEB37C464726AE3E6C57C15EB40019FF639C25413495C8D5CC6', Bs), crypto_data_hash(Bs, Hash, [encoding(octet)]). Bs = [48, 130, 4, 125, 160, 3, 2, 1, 2|...], Hash = b21649782577d4328540e77232ad494415b6fe67c4524b10e5b74146405e79ba.We now fetch the

?- http_open('https://www.metalevel.at', Stream, []), ssl_peer_certificate_chain(Stream, [_,ToSecond|_]), member(key(Key), Second). Stream = <stream>(0x1d7a370,0x1d75570), Second = [...],Key = public_key(rsa("B3AC0D7FADBB134D945F67426AD08971A9ED74049324C84D56A1F0919684D9846ACF5221E31AB1544CE6C69E9E4B38A996541DF5B3ED9204D06E54906E2FE97D98B48A2D12A3B442471D7F5F40E1FC7F91A601DC55A450782A633F847E2CC82B21B6C60E5EBCB8B1D41B98B3C6F8E1E828ED32441BCB7FF7E4B111EBC608B05BEEA8C2EC46AA8F29DFB9B7A403A0357A583F8B2947C1D222FA2CC6C76CCDD3F758329394D16FA92A9C0F0A2892AB140AB6DFED407A640754CEEA759732B996A075C977310274AF54774F99A2814B7959B8923FF907EA4274572E35EC558AFC613C3E5771923BABE4C1E1172C64360084B57C1A7DB041337C23F64E775A2CC14B", "010001", -, -, -, -, -, -)).

- use integer arithmetic
- use
`rsa_verify/4`from`library(crypto)`

*Exp*is the*exponent*of the public key, i.e.,(in hexadecimal notation, which is 65537 in decimal notation) in this case.**10001***M*is the*modulus*of the public key, i.e.,`B3AC0D7FADBB134D945F67426AD08971A9ED74049324C84D56A1F0919684D9846ACF5221E31AB1544CE6C69E9E4B38A996541DF5B3ED9204D06E54906E2FE97D98B48A2D12A3B442471D7F5F40E1FC7F91A601DC55A450782A633F847E2CC82B21B6C60E5EBCB8B1D41B98B3C6F8E1E828ED32441BCB7FF7E4B111EBC608B05BEEA8C2EC46AA8F29DFB9B7A403A0357A583F8B2947C1D222FA2CC6C76CCDD3F758329394D16FA92A9C0F0A2892AB140AB6DFED407A640754CEEA759732B996A075C977310274AF54774F99A2814B7959B8923FF907EA4274572E35EC558AFC613C3E5771923BABE4C1E1172C64360084B57C1A7DB041337C23F64E775A2CC14B`in this case.

?- format("~16r", [252]).Therefore, we can computefc

?- R #= 0x35A5467AAFE158229AB363A88740881B853F3D1A3B6A6747998C9DC0F0CB3BD6062F266D19D88942D164EF6FF109699ABB9F8156340C8DA979AFE31B6D994DCB3B84EFBE8E5CC998D3D80B84D70917EEBF49CC7BF931A6930998C243B57F5E4C26FBF4EDC1F55F321D28116ED9ED4E41A510C0A4C663AB1B3DF324128C375476BC3A950E8E58A03BDE619566E943D04E40852253A38CC79A76A3AEFE85E7F45B06EC46D35747894BAB386BAD74761342C3FB5495DF1CE88BF61411B451A1E1FFCCCA057D3BC3CEDC69E29C0378F8AFDB010A8D89158C2778AC32D43A2BA7CA0911DCC50ABEBE9ED78779BC98BE3A3A03A73479E8D4A00B038E3633F2C38FA518^0x10001 mod 0xB3AC0D7FADBB134D945F67426AD08971A9ED74049324C84D56A1F0919684D9846ACF5221E31AB1544CE6C69E9E4B38A996541DF5B3ED9204D06E54906E2FE97D98B48A2D12A3B442471D7F5F40E1FC7F91A601DC55A450782A633F847E2CC82B21B6C60E5EBCB8B1D41B98B3C6F8E1E828ED32441BCB7FF7E4B111EBC608B05BEEA8C2EC46AA8F29DFB9B7A403A0357A583F8B2947C1D222FA2CC6C76CCDD3F758329394D16FA92A9C0F0A2892AB140AB6DFED407A640754CEEA759732B996A075C977310274AF54774F99A2814B7959B8923FF907EA4274572E35EC558AFC613C3E5771923BABE4C1E1172C64360084B57C1A7DB041337C23F64E775A2CC14B, format("~16r", [R]). 1ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff003031300d060960864801650304020105000420b21649782577d4328540e77232ad494415b6fe67c4524b10e5b74146405e79baWe see that the hash value which we computed

In particular, we need to specify:

- The
*public key*of the (purported) signing entity. - The
*data*that was signed. In our case, it is the*hash*of the*to-be-signed*portion that must have been signed. Recall from above that this hash is`b21649782577d4328540e77232ad494415b6fe67c4524b10e5b74146405e79ba`. - The
*signature*. *Options*that specify the used algorithm and, if necessary, the*encoding*of the data.

?- Key = public_key(rsa("B3AC0D7FADBB134D945F67426AD08971A9ED74049324C84D56A1F0919684D9846ACF5221E31AB1544CE6C69E9E4B38A996541DF5B3ED9204D06E54906E2FE97D98B48A2D12A3B442471D7F5F40E1FC7F91A601DC55A450782A633F847E2CC82B21B6C60E5EBCB8B1D41B98B3C6F8E1E828ED32441BCB7FF7E4B111EBC608B05BEEA8C2EC46AA8F29DFB9B7A403A0357A583F8B2947C1D222FA2CC6C76CCDD3F758329394D16FA92A9C0F0A2892AB140AB6DFED407A640754CEEA759732B996A075C977310274AF54774F99A2814B7959B8923FF907EA4274572E35EC558AFC613C3E5771923BABE4C1E1172C64360084B57C1A7DB041337C23F64E775A2CC14B", "010001", -, -, -, -, -, -)), Hash = 'b21649782577d4328540e77232ad494415b6fe67c4524b10e5b74146405e79ba', Signature = "35A5467AAFE158229AB363A88740881B853F3D1A3B6A6747998C9DC0F0CB3BD6062F266D19D88942D164EF6FF109699ABB9F8156340C8DA979AFE31B6D994DCB3B84EFBE8E5CC998D3D80B84D70917EEBF49CC7BF931A6930998C243B57F5E4C26FBF4EDC1F55F321D28116ED9ED4E41A510C0A4C663AB1B3DF324128C375476BC3A950E8E58A03BDE619566E943D04E40852253A38CC79A76A3AEFE85E7F45B06EC46D35747894BAB386BAD74761342C3FB5495DF1CE88BF61411B451A1E1FFCCCA057D3BC3CEDC69E29C0378F8AFDB010A8D89158C2778AC32D43A2BA7CA0911DCC50ABEBE9ED78779BC98BE3A3A03A73479E8D4A00B038E3633F2C38FA518",Since thisrsa_verify(Key, Hash, Signature, [type(sha256)]).

SWI-Prolog makes it extremely easy to encrypt arbitrary data in a secure way, using the predicate

In addition to the data you want to encrypt, you must provide:

- the
**algorithm**you want to use - the
**key**that is used for encryption - the
**initialization vector**(IV).

For a start, let us use a widely known and very secure encryption algorithm:

Ideally,

We can specify keys and IVs as lists of

With this knowledge, we can already try an encryption! For example:

?- crypto_n_random_bytes(16, Ks), crypto_n_random_bytes(16, IV), crypto_data_encrypt(The result depends on which random numbers are actually generated when you run it. For example, I get:test, 'aes-128-cbc', Ks, IV, CipherText, []).

Ks = [31, 240, 156, 156, 161, 198, 9, 230, 109|...], IV = [165, 227, 153, 21, 246, 53, 136, 179, 201|...],TheCipherText = "uç'þß\026\nåÉ®#º(î\nâ".

Before we continue, a few test runs are highly appropriate. For example, let us see whether we can

?-As result, we get:repeat, PlainText ="test", Algorithm = 'aes-128-cbc', crypto_n_random_bytes(16, Ks), crypto_n_random_bytes(16, IV), portray_clause(verifying), crypto_data_encrypt(PlainText, Algorithm, Ks, IV, CipherText, []), crypto_data_decrypt(CipherText, Algorithm, Ks, IV, PlainText, []), portray_clause(ok),false.

verifying.After a few hundred thousand iterations of this, we can be reasonably confident that what is encrypted can also be decrypted. When experimenting with different algorithms, it is a common error to specify keys or IVs that are shorter than what the chosen algorithm requires, and such test cases help us to detect these mistakes.ok.verifying.ok.verifying.ok.etc.

There are several ways to solve this. We start—and end—with the

To illustrate the idea, we now use a powerful and efficient algorithm denoted by the atom

This cipher uses a 256-bit

For example, here is a concrete encryption with ChaCha20-Poly1305, using a random key and nonce:

?- crypto_n_random_bytes(32, Ks), crypto_n_random_bytes(12, IV), crypto_data_encrypt(In response, we get the encrypted text, and a 128-bittest, 'chacha20-poly1305', Ks, IV, CipherText, [tag(Ts)]).

Ks = [84, 148, 85, 236, 235, 183, 51, 68, 144|...], IV = [182, 70, 102, 111, 6, 170, 45, 76, 148|...],Again, the key must be kept completely secret. In contrast, the tag and nonce (IV) can be safely stored and shared in plain text.CipherText = "Pç,õ",Ts = [119, 23, 173, 207, 167, 255, 29, 135, 101|...].

Decryption only works if the correct

?- crypto_data_decrypt($CipherText, 'chacha20-poly1305', $Ks, $IV, PlainText, [In contrast, even if we onlytag($Ts)]). PlainText = "test", CipherText = "Pç,õ", Ks = [84, 148, 85, 236, 235, 183, 51, 68, 144|...], IV = [182, 70, 102, 111, 6, 170, 45, 76, 148|...], Ts = [119, 23, 173, 207, 167, 255, 29, 135, 101|...].

?- crypto_data_decrypt($CipherText, 'chacha20-poly1305', $Ks, $IV, PlainText, [tag([Thus, an attacker who changes the ciphertext must also make a1|$Ts])]).ERROR: SSL(00000000) func(0): reason(0)

Prolog is well-suited for studying how such algorithms work by prototyping their implementations. For example, here is a Prolog implementation of the Poly1305 authenticator:

A second example of an authenticated cipher is

For example, in the Diffie-Hellman-Merkle key exchange, we first negotiate a

There is a standard algorithm that lets us derive keys and IVs from arbitrary input data. It is called

For example, let us now derive a key and an IV from a

Using HKDF, we

Therefore, instead of

Thus, we will now

To ensure that

From the generated

Taking all these considerations into account, we obtain for example the following predicate to derive a 128-bit (i.e., 16 bytes) key and IV from a given password and salt:

Sample usage, with a fresh 128-bit salt:password_salt_key_iv(Password, Salt, Ks, IV) :- crypto_password_hash(Password, Hash, [algorithm('pbkdf2-sha512'), cost(19), salt(Salt)]), crypto_data_hkdf(Hash, 16, Ks, [info(key),algorithm(sha256)]), crypto_data_hkdf(Hash, 16, IV, [info(iv),algorithm(sha256)]).

?- crypto_n_random_bytes(16, Salt), password_salt_key_iv(This yields:test,Salt, Ks, IV).

Salt = [203, 81, 172, 46, 86, 244, 37, 2, 215|...],When we later use the same password and salt, exactly the same results are derived:Ks= [38, 141, 86, 95, 83, 22, 243, 31, 38|...],IV= [36, 149, 175, 179, 48, 192, 213, 175, 71|...].

?- password_salt_key_iv(Thus, we only need to store thetest,$Salt, Ks, IV). Salt = [203, 81, 172, 46, 86, 244, 37, 2, 215|...].Ks= [38, 141, 86, 95, 83, 22, 243, 31, 38|...],IV= [36, 149, 175, 179, 48, 192, 213, 175, 71|...],

The SWI documentation contains an example for negotiating a shared secret via ECDH. Again, once such a secret is established, you can derive keys from it using

Sometimes, keys are themselves derived from keys. For example, you may have a

In many cases, you can benefit from cryptographic features even without knowing how they work internally. For example, if you simply want to enable encrypted traffic for your web applications, see

In other cases, you can use the available functionality to implement specific applications on your own. For example, you can use Prolog to reason about

The cryptographic functionality of SWI-Prolog is subject to continuous improvements. Please see the SSL Roadmap for more information.