CWIS Developer Documentation
SPTUser.php
Go to the documentation of this file.
1 <?PHP
2 
3 #
4 # FILE: SPT--SPTUser.php
5 #
6 # Part of the Collection Workflow Integration System (CWIS)
7 # Copyright 2004-2013 Edward Almasy and Internet Scout Research Group
8 # http://scout.wisc.edu/cwis/
9 #
10 
11 class SPTUser extends CWUser
12 {
13 
14  # ---- PUBLIC INTERFACE --------------------------------------------------
15 
25  public static function GetCryptKey()
26  {
27  $DB = new Database();
28 
29  # regenerate keys every day (24 * 60 * 60 = 86,400 seconds)
30  $KeyRegenInterval = 86400;
31 
32  # oldest key we want to keep
33  $MaxKeyAge = 3 * $KeyRegenInterval;
34 
35  # if the page cache was enabled, be sure to keep keys that were
36  # current (i.e. not expired) as of the oldest cache entry
37  if ($GLOBALS["AF"]->PageCacheEnabled())
38  {
39  $CacheInfo = $GLOBALS["AF"]->GetPageCacheInfo();
40  if ($CacheInfo["NumberOfEntries"]>0)
41  {
42  $MaxKeyAge += (time()-$CacheInfo["OldestTimestamp"]);
43  }
44  }
45 
46  # NOTE: One can not simply subtract two TIMESTAMPs and expect
47  # a sane result from mysql. Using a TIMESTAMP in numeric
48  # context converts it to an int, but in YYYYMMDDHHMMSS format
49  # rather than as a UNIX time. Hence the use of
50  # TIMESTAMPDIFF() below.
51 
52  # clear expired keys and replay protection tokens
53  $DB->Query("DELETE FROM UsedLoginTokens WHERE "
54  ."TIMESTAMPDIFF(SECOND, KeyCTime, NOW()) > ".$MaxKeyAge);
55 
56  $DB->Query("LOCK TABLES LoginKeys WRITE");
57  $DB->Query("DELETE FROM LoginKeys WHERE "
58  ."TIMESTAMPDIFF(SECOND, CreationTime, NOW()) > ".$MaxKeyAge);
59 
60  # get the most recently generated key
61  $DB->Query("SELECT TIMESTAMPDIFF(SECOND, CreationTime, NOW()) as Age,"
62  ."KeyPair FROM LoginKeys "
63  ."ORDER BY Age ASC LIMIT 1");
64  $Row = $DB->FetchRow();
65 
66  # if there is no key in the database, or the key is too old
67  if ( ($Row===FALSE) || ($Row["Age"]>= $KeyRegenInterval) )
68  {
69  # generate a new OpenSSL format keypair
70  $KeyPair = openssl_pkey_new(
71  array(
72  'private_key_bits' => 512, # make this a Sysadmin pref later?
73  'private_key_type' => OPENSSL_KEYTYPE_RSA ));
74 
75  # serialize it for storage
76  openssl_pkey_export($KeyPair, $KeyPairDBFormat);
77 
78  # stick it into the database
79  $DB->Query("INSERT INTO LoginKeys "
80  ."(KeyPair, CreationTime) VALUES ("
81  ."\"".addslashes($KeyPairDBFormat)."\","
82  ."NOW())");
83  }
84  else
85  {
86  # if we do have a current key in the database,
87  # convert it to openssl format for usage
88  $KeyPair = openssl_pkey_get_private( $Row["KeyPair"] );
89  }
90  $DB->Query("UNLOCK TABLES");
91 
92  return $KeyPair;
93  }
94 
102  public static function ExtractPubKeyParameters($KeyPair)
103  {
104  # Export the keypair as an ASCII signing request (which contains the data we want)
105  openssl_csr_export(openssl_csr_new(array(), $KeyPair), $Export, FALSE);
106 
107  $Modulus = "";
108  $Exponent = "";
109 
110  // @codingStandardsIgnoreStart
111  $Patterns = array(
112  '/Modulus \([0-9]+ bit\):(.*)Exponent: [0-9]+ \(0x([0-9a-f]+)\)/ms',
113  '/Public-Key: \([0-9]+ bit\).*Modulus:(.*)Exponent: [0-9]+ \(0x([0-9a-f]+)\)/ms',
114  );
115  // @codingStandardsIgnoreEnd
116 
117  foreach ($Patterns as $Pattern)
118  {
119  if (preg_match($Pattern, $Export, $Matches))
120  {
121  $Modulus = $Matches[1];
122  $Exponent = $Matches[2];
123  break;
124  }
125  }
126 
127  # Clean newlines and whitespace out of the modulus
128  $Modulus = preg_replace("/[^0-9a-f]/", "", $Modulus);
129 
130  # Return key material
131  return array( "Modulus" => $Modulus, "Exponent" => $Exponent );
132  }
133 
141  public static function DecryptPassword($UserName, $EncryptedPassword)
142  {
143  $DB = new Database();
144  $DB->Query("SELECT CreationTime, KeyPair FROM LoginKeys");
145 
146  # start off assuming that nothing will decrypt properly
147  $Password = FALSE;
148 
149  # remove the base64 encoding sent on the cyphertext
150  $Cyphertext = base64_decode($EncryptedPassword);
151 
152  # extract recently used login keys from the database
153  $Keys = $DB->FetchRows();
154 
155  # iterate through them on the cyphertext, trying to decrypt it with
156  # each key. by default, 48 hr worth of keys are kept, and fresh keys
157  # are generated every 24 hr.
158  foreach ($Keys as $Key)
159  {
160  $KeyCTime = $Key["CreationTime"];
161  $DBFormatKeyPair = $Key["KeyPair"];
162  # extract the OpenSSL format private key from the database key format
163  $Keypair = openssl_pkey_get_private($DBFormatKeyPair);
164 
165  # attempt to decrypt the cyphertext
166  if (openssl_private_decrypt($Cyphertext, $Cleartext, $Keypair))
167  {
168  # on success, extract the password portion and the random bytes
169  $Data = explode("\t", $Cleartext);
170 
171  if (count($Data) == 2)
172  {
173  list($Password, $Token) = $Data;
174 
175  # check to see if we've seen these particular bytes
176  # before for this user and this key
177  $DB->Query("SELECT * FROM UsedLoginTokens WHERE"
178  ." Token=\"".addslashes($Token)."\" AND"
179  ." KeyCTime=\"".$KeyCTime."\" AND"
180  ." UserName=\"".$UserName."\"");
181 
182  if ($DB->NumRowsSelected() > 0)
183  {
184  # if so, this is a replay attack; fail the login
185  $Password = FALSE;
186  }
187  else
188  {
189  # otherwise, record these bytes as seen, and attempt to login
190  # with the plaintext password
191  $DB->Query("INSERT INTO UsedLoginTokens"
192  ." (Token, KeyCTime, UserName) VALUES ("
193  ."\"".addslashes($Token)."\","
194  ."\"".addslashes($KeyCTime)."\","
195  ."\"".addslashes($UserName)."\")");
196  }
197 
198  # no need to try other keys when we've found one that
199  # works
200  break;
201  }
202  }
203  }
204 
205  return $Password;
206  }
207 }
$DB
Definition: User.php:1079
SQL database abstraction object with smart query caching.
Definition: Database.php:22
static ExtractPubKeyParameters($KeyPair)
Extract the modulus and exponent of the public key from an OpenSSL format keypair to send in login fo...
Definition: SPTUser.php:102
static DecryptPassword($UserName, $EncryptedPassword)
Decrypt an encrypted password.
Definition: SPTUser.php:141
static GetCryptKey()
Get/generate a cryptographic keypair for user login.
Definition: SPTUser.php:25
CWIS-specific user class.
Definition: CWUser.php:13