1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 100 101 102 103 104 105 106 107 108 109 110 111 112 113 114 115 116 117 118 119 120 121 122 123 124 125 126 127 128 129 130 131 132 133 134 135 136 137 138 139 140 141 142 143 144 145 146 147 148 149 150 151 152 153 154 155 156 157 158 159 160 161 162 163 164 165 166 167 168 169 170 171 172 173 174 175 176 177 178 179 180 181 182 183 184 185 186 187 188 189 190 191 192
|
/** -*-C-*-ish
Kaya standard library
Copyright (C) 2004, 2005 Edwin Brady
This file is distributed under the terms of the GNU Lesser General
Public Licence. See COPYING for licence.
*/
"<summary>High level cryptography/hashing functions</summary>
<prose>This module contains the encryption and decryption wrappers for Kaya's built-in encryption used to secure web applications. Currently this encryption uses AES 256 encryption via the <moduleref>Gcrypt</moduleref> module and libgcrypt</prose>
<prose>The encryption key used in the built-in encryption is normally contained within the application binary. If you are using this internal key, you must therefore keep the application binary safe from unauthorised reading (just as you would protect a config file containing database passwords). For convenience of administration, application may choose to use an externally provided key with the built-in encryption functions.</prose>"
module Crypto;
import Builtins;
import Strings;
import Gcrypt;
import Binary;
import Prelude;
import Regex;
%include "crypto_glue.h";
%include "unistd.h";
%imported "crypto_glue";
foreign "crypto_glue.o" {
[Int] getInternalKey() = getKey;
// the Ivec isn't secret, but it might have good reason to be constant!
[Int] getInternalIvec() = getIVec;
}
data KeyType = InternalKey | ExternalKey([Int] key);
globals {
KeyType appkey = InternalKey;
KeyType appivec = InternalKey;
}
[Int] getKey() {
case appkey of {
InternalKey -> key = getInternalKey();
appkey = ExternalKey(copy(key)); // so can't set key after use!
| ExternalKey(key) -> ;
}
return key;
}
[Int] getIVec() {
case appivec of {
InternalKey -> key = getInternalIvec();
appivec = ExternalKey(copy(key)); // so can't set key after use!
| ExternalKey(key) -> ;
}
return key;
}
"<argument name='key'>The new encryption key. This must be an array of integers, containing exactly 32 integers between 0 and 255. An Exception will be thrown if the parameter is unsuitable. For security, encryption keys should be generated from a good-quality random number source.</argument>
<summary>Set application secret key</summary>
<prose>Sets the application secret key used by <functionref>encode</functionref> and <functionref>decode</functionref> to a new value for the remainder of program execution. You can obtain the key from a variety of sources (e.g. a flat file, or a database entry) - the easiest way is probably to read 32 bytes from a file with <functionref>IO::getChar</functionref>. Using an external application key removes the requirement that the application binary be strongly protected (by moving the requirement to whatever the key source is, of course). This may be useful for installing common web application binaries to a shared folder.</prose>
<prose>Web applications should call this function from within their <code>webconfig</code> function to avoid unpredictable results. Also to avoid unpredictable results, this function may only be called once per program run, and must be called before any other function that uses the key. An Exception will be thrown if this is called for a second time or too late.</prose>
<prose>Note: While an unscrupulous application writer could use this function to ensure that the effective application key could not be changed by <code>kaya-rekey</code>, they could find far easier and less easily detectable ways to put a backdoor into any application that users were unable to recompile or verify.</prose>"
public Void setEncryptionKey([Int] key) {
case appkey of {
ExternalKey(ek) -> throw(InvalidExternalKey("External key already set"));
| InternalKey -> if (size(key) != 32) {
throw(InvalidExternalKey("Key must have a length of 32"));
}
for k@i in key {
if (k < 0 || k > 255) {
throw(InvalidExternalKey("Each key digit must be in the range 0..255 (error at digit "+i+")"));
}
}
// copy to make especially sure that there's no problem with references.
appkey = ExternalKey(copy(key));
}
}
"<argument name='ivec'>The new ivec. This must be an array of integers, containing exactly 16 integers between 0 and 255. An Exception will be thrown if the parameter is unsuitable. The ivec is not secret and does not need to be generated in any particular way.</argument>
<summary>Set application Ivec</summary>
<prose>Sets the application Ivec used by <functionref>encode</functionref> and <functionref>decode</functionref> to a new value for the remainder of program execution. You can obtain the key from a variety of sources (e.g. a flat file, or a database entry) - the easiest way is probably to read 16 bytes from a file with <functionref>IO::getChar</functionref>. The Ivec is not secret, but in some cases you may need it to be consistent.</prose>
<prose>Web applications should call this function from within their <code>webconfig</code> function to avoid unpredictable results. Also to avoid unpredictable results, this function may only be called once per program run, and must be called before any other function that uses the key. An Exception will be thrown if this is called for a second time or too late.</prose>"
public Void setEncryptionIvec([Int] ivec) {
case appivec of {
ExternalKey(ek) -> throw(InvalidExternalKey("External Ivec already set"));
| InternalKey -> if (size(ivec) != 16) {
throw(InvalidExternalKey("Ivec must have a length of 16"));
}
for k@i in ivec {
if (k < 0 || k > 255) {
throw(InvalidExternalKey("Each Ivec digit must be in the range 0..255 (error at digit "+i+")"));
}
}
// copy to make especially sure that there's no problem with references.
appivec = ExternalKey(copy(ivec));
}
}
"<summary>Invalid external key or Ivec</summary>
<prose>This Exception is thrown when the key or Ivec supplied to <functionref>setEncryptionKey</functionref> or <functionref>setEncryptionIvec</functionref> is invalid. The parameter describes the reason.</prose>"
Exception InvalidExternalKey(String reason);
"<summary>Invalid hash</summary>
<prose>This Exception is thrown when the hash supplied with the data to decrypt doesn't match the decrypted data. In webapps and CGI programs this guards against data corruption.</prose>"
Exception InvalidDecryptionHash();
"<summary>Invalid encrypted data</summary>
<prose>This Exception is thrown when the data to decrypt isn't valid data generated with <functionref>encode</functionref>.</prose>"
Exception InvalidEncryptedData();
"<argument name='msg'>The String to encode</argument>
<summary>Encode a String using the AES256 algorithm.</summary>
<prose>Encode a String using the AES256 algorithm.
The value is encoded with the application's secret key, which is compiled
in automatically (unless overridden with <functionref>setEncryptionKey</functionref>). The return value is base64 encoded and also includes
a hash which will be verified when the String is decoded. The value will only be decodable by the Kaya application that encoded it, provided that the application secret key is not revealed.</prose>
<prose>You must therefore ensure no-one has read access to the application key (whether this is in the Kaya binary or an external source) if you need to rely on the integrity of the data (for example, in CGI or webapps). Should you suspect the key is compromised, you can change an internal key by recompiling the binary, or using the <code>kaya-rekey</code> utility.</prose>
<related><functionref>decode</functionref></related>"
public String encode(String msg)
{
h = openCipher(AES256, CBC, createArray(1));
setCipherKey(h,getKey);
setCipherIVec(h,getIVec);
// putStrLn(msg);
enc = encryptString(h,msg);
// putStrLn("Encoded: "+enc);
closeCipher(h);
hh = openHash(SHA1, createArray(1));
hashString(hh,msg);
msghash = Binary::base64Encode(getHash(hh));
// putStrLn("Hash: "+msghash);
closeHash(hh);
encoded = enc+","+msghash;
//marshal((enc,msghash),9);
// putStrLn(encoded);
return encoded;
//Strings::base64encode(encoded);
}
// old version:
// public String encode(String str) = doencode(getVM(),str);
"<argument name='msgin'>The String to decode</argument>
<summary>Decode a String using the AES256 algorithm.</summary>
<prose>Decode a String using the AES256 algorithm.
The value to decode must have been generated by the <code>encode</code> function in an application with the same secret key (which usually requires it to be the same application). If decoding fails (due to bad input) an Exception is thrown.</prose>
<related><functionref>encode</functionref></related>"
public String decode(String msgin)
{
strs = split(",",msgin);
if (size(strs) != 2) {
throw(InvalidEncryptedData);
}
msg = strs[0];
hash = strs[1];
// (String,String) todecode = unmarshal(Strings::base64decode(msgin),9);
// msg = todecode.fst;
// hash = todecode.snd;
h = openCipher(AES256, CBC, createArray(1));
setCipherKey(h,getKey);
setCipherIVec(h,getIVec);
dec = decryptString(h,msg);
// putStrLn("Decoded: "+dec);
closeCipher(h);
hh = openHash(SHA1,createArray(1));
hashString(hh,dec);
dechash = base64Encode(getHash(hh));
// putStrLn("Hash: "+dechash+" ("+(dechash==hash)+")");
if (dechash!=hash) {
// putStrLn(dechash+" "+hash);
// putStrLn(dec);
throw(InvalidDecryptionHash);
}
closeHash(hh);
return dec;
}
"<argument name='pwd'>The String to hash</argument>
<summary>Generate a secure hash from a String.</summary>
<prose>Use the SHA256 algorithm to generate a secure hash from a String,
for example for encrypting passwords. Unlike the other <moduleref>Crypto</moduleref> functions, this function is not application-dependent.</prose>"
public String secureHash(String pwd)
{
h = openHash(SHA256,createArray(1));
hashString(h,pwd);
return base64Encode(getHash(h));
}
// old version
// public String decode(String str) = dodecode(getVM(),str);
|