File: Crypto.k

package info (click to toggle)
kaya 0.4.4-6.2
  • links: PTS
  • area: main
  • in suites: jessie, jessie-kfreebsd
  • size: 5,200 kB
  • ctags: 2,015
  • sloc: cpp: 9,556; haskell: 7,253; sh: 3,060; yacc: 910; makefile: 816; perl: 90
file content (192 lines) | stat: -rw-r--r-- 10,024 bytes parent folder | download | duplicates (4)
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);