FAQ : Communications (TCP/IP, MQ, APPC, HLLAPI, CICS) : Q: How can I use SSL to secure my application?
Q: How can I use SSL to secure my application?
Securing your VA Smalltalk Applications with SSL (Part 1)
Technote (FAQ)
Operating system(s):  
Windows, Solaris, AIX, Linux
Software version:  
6.x, VA Smalltalk 7.x, 8.x
Problem
The addition of Secure Socket Layer (SSL) capability enhances VA Smalltalk's security support.
Solution
Most of us have accessed a secure website and seen the small padlock that appears on the bottom of web browsers such as Netscape or Internet Explorer. When we shop or access account information online, the host server and our web browser are usually sending data back and forth over a secure HTTP connection. Such connections are utilizing the Secure Sockets Layer (SSL), which is a protocol developed to transmit sensitive and/or confidential documents via the Internet. In its latest incarnation as an IETF standard, this type of connection is called Transport Layer Security (TLS). With our increasing need for security when performing such transactions, the role of SSL has become even more important.
Currently, the primary use for SSL continues to be the private transmission of Internet traffic via HTTPS. Some of the many ways SSL can be utilized are publishing and/or binding to Web services, updating Lightweight Directory Access Protocol (LDAP) directories and even sending encrypted email with Secure/Multipurpose Internet Mail Extensions (S/MIME). SSL can also be used to secure Unix domain (local) sockets.
In this paper I will provide a brief introduction to SSL and explain how its design enables its use with a number of protocols. We will then go through a simple client/server example that should teach you how to create an SSL connection to provide security for your VA applications.
A Brief Introduction to SSL
SSL is a protocol that provides a secure transport between two machines. As applications implement the SSL protocol, they create a channel that is used to pass encrypted data unchanged through the connection. SSL pipes your data from one side of the connection to the other relying on the endpoints to handle the encryption/decryption. To this end, SSL associates a regular BSD-style socket with an SSL context that defines the various parameters, e.g. X.509 certificates, private keys, the encryption algorithm in use, etc. As you can see in Figure 1, SSL is located between the Application layer and the TCP layer and therefore can be used by any protocol that utilizes TCP.
Tip icon
Figure 1 - The SSL Layer
But how does SSL keep your data safe? SSL does so by satisfying the security requirements (or goals, if you will) of confidentiality (data is kept secret), message integrity (data is sent and received unchanged), and endpoint authentication (we are exchanging data with whom we intended to). In order to ensure these requirements are met, SSL makes use of certificates, public and private key cryptography, and message digests. If you want to explore the details of these concepts, I provide several references at the end of this paper. This introduction will focus on the SSL Handshake, the means by which SSL accomplishes the goals of security. Without delving into too much detail, the handshake consists of 5 steps.
1. The client sends a message to the server listing the cryptographic algorithms it is ready to support. This message includes a random number that will be used to generate private keys for later encryption.
2. The server sends several messages to the client including one that informs the client which algorithm will be used for the connection. A certificate and a random number, which will play a role in the generation of private keys, are also sent.
3. The client verifies the server's certificate (endpoint authentication) and extracts its public key. The client then generates a random string called the pre-master-secret. The client encrypts the pre-master-secret using the server's public key and sends it to the server.
4. Each side generates encryption and message authentication keys using the information they have shared thus far, namely the random values and the pre-master-secret.
5. The client and server each compute a Message Authentication Code (MAC) encompassing all of the handshake messages that have been sent and received and exchange their values. The connection Is only established if these values are identical (message integrity).
Thereafter, all data sent between the two machines is fragmented and a MAC is generated for each fragment. The data is MAC encrypted (confidentiality) using the agreed upon algorithm to form the message payload of the SSL Record. Once a header is concatenated to the payload, the message is sent. When the transactions are completed, either side can close the connection by notifying the opposite side. A few additional messages are then exchanged to ensure each side has ceased sending data. The context of the connection can be cached, however, and new keys generated should the two sides want to re-establish the connection.
SSL in VA Smalltalk
Now that you know what SSL is and what it does, you might be asking how do you make use of it in VA Smalltalk.
 
VA Smalltalk includes library bindings to OpenSSL, an C library implementation of the SSL protocol. While earlier versions of VA Smalltalk shipped with the OpenSSL native libraries, they proved difficult to keep updated due to issues such as export control and the fact that the OpenSSL release cycle is not aligned with VA Smalltalk. For these reasons, VA Smalltalk 8.6.2 and above no longer ship with the OpenSSL Libraries. However, OpenSSL is readily available through the package management systems on most every flavor of Unix/Linux and can be easily downloaded for use with Windows from the OpenSSL site at https://www.openssl.org/community/binaries.html
 
You must ensure that these libraries are available on the system and referenced in VA Smalltalk's configuration file (typically named abt.ini). In the configuration file there is a section called [PlatformLibrary Name Mappings]. Within this section are two key=value entries relevant to OpenSSL called CRYPTO_LIB and SSL_LIB. You will notice they already have the default names of the libraries set. If the OpenSSL libraries are already present in your system's library path, then SSL will work out of the box with no modifications required on your part. However, if this is not the case, you can change the values of both CRYPTO_LIB and SSL_LIB to reference the absolute path of where the libraries are located. If you do not wish to modify your abt.ini, you may also make a copy of the OpenSSL libraries and place them in VA Smalltalk's binary directory (i.e. the same location as the abt executable).
Now lets try an example. To run the example, a simple echo server workspace, make sure you have the ST: Socket Communications Interface feature loaded into your image. You will also need to save these certificate (cert.perm.zip) and key (key.perm.zip) files to your machine. Drop the .txt extension on both files and place them in the directory of your choice. The example uses forward slashes, so change them if needed for your platform. You should use the same files for both sides of the connection. Of course, for your applications you will need to acquire or generate your own certificates and keys. You can find this and other information about SSL at http://www.openssl.org.
SSL Echo Server Example
The key Smalltalk classes we will be using are SciSslSocketConfiguration, SciSocketAddress and SciSslSocket. The Server workspace code appears below followed by a detailed description of the steps involved in creating the SSL connection. You can grab the client workspace (ssl-client.ws.txt) but I will not describe it in detail as it is similar in content to the server-side code.
The main action lies in the following code snippet...  
 
config := SciSslSocketConfiguration new
certificateFilename: '/absolute-path/cert.pem';
privateKeyFilename: '/absolute-path/key.pem';
sslVersion: SciSslConstants::SSLv23;
yourself.
This first step creates an instance of SciSslConfiguration that will store the parameters that define an SSL Context for the connection. This configuration includes the absolute path to the certificate and key files as well as the SSL protocol version that the server is prepared to use. In this case, the server is willing to use SSLv2 or SSLv3. This information is stored in a registry and can be reused by later connections. A discussion of the differences between SSLv2 and SSLv3 is beyond the scope of this article.
Now here's the workspace in its entirety for you to follow along...  
 
"Simple SSL Echo Server Workspace"
[ | config address listenSocket secureSocket rv msg |
 
config := SciSslSocketConfiguration new
certificateFilename: '/absolute-path/cert.pem';
privateKeyFilename: '/absolute-path/key.pem';
sslVersion: SciSslConstants::SSLv23;
yourself.
 
address := (SciSocketAddress new)
address: SciSocketConstants::INADDRANY;
port: 2222.
 
listenSocket := SciSslSocket newStreamSocket.
(rv := listenSocket bind: address) isSciError
ifTrue: [ self halt ].
(rv := listenSocket listen: 5) isSciError
ifTrue: [ self halt ].
(secureSocket := listenSocket accept) isSciError
ifTrue: [ self halt ].
listenSocket close.
 
(rv := secureSocket sslInitialize: config) isSslError
ifTrue: [ secureSocket close. self halt ].
(rv := secureSocket sslAccept) isSslError
ifTrue: [ secureSocket close. self halt ].
 
msg := ByteArray new: 4096.
(rv := secureSocket
recv: msg
length: msg size
startingAt: 1 flags: 0) isSslError
ifTrue: [ self halt ].
 
Transcript cr; nextPutAll: 'SslServer Got: ', msg asString.
 
(rv := secureSocket
send: 'I hear you.' abrAsPSZ
length: 11
startingAt: 1
flags: 0) isSslError
ifTrue: [ self halt ].
 
secureSocket close. ] fork
After creating the configuration, we specify an instance of SciSocketAddress with the port on which the server will be listening (2222). We then create a stream SciSslSocket, bind the socket to the address and listen for an incoming connection. Once the incoming connection is established, which at this point is a run of the mill TCP connection with a client, the stage is set for the SSL Handshake. The configuration created at the top of the workspace is passed to the SSL socket using the SciSslSocket>>sslInitialize: method. So each side of the connection makes the following call to start the handshake sequence...
 
secureSocket sslInitialize: config
Under the covers, the steps outlined in the "A Brief Introduction to SSL" section are being performed and the secure connection established. If the sequence succeeds, the data is sent from the client to the server, echoed back from the server to the client, and the connection is closed. Go ahead and try it out for yourself!
Summary
The Secure Sockets Layer sits between the TCP and Application Layers of the TCP stack. The power of this design lies in the fact that any protocol that uses TCP can also use SSL. The simple scenario I've described has been a brief introduction to the classes you'll need to 'SSL-orize' your VA Smalltalk applications with the SSL support in Version 6.0. As security concerns get more attention (as they should) you'll want to be prepared to secure your web applications using SSL.
References
Rescorla, Eric, "SSL and TLS: Designing and Building Secure Systems",
Addison-Wesley, 2001.
Thomas, Stephen, "SSL and TLS Essentials: Securing the Web", Wiley, 2000.
Dierks, T. and Allen, C., "The TLS Protocol, Version 1.0", RFC 2246, January 1999.
Securing your VA Smalltalk Applications with SSL (Part 2)
Part 2: Authentication and Identity Propagation
Introduction
Part one introduced the SSL protocol and the OpenSSL implementation, and demonstrated the use of the VA/Smalltalk API that enables Smalltalk applications to perform secure communications through the use of OpenSSL. This article provides additional information on transport-level authentication, using x.509 certificates with SSL. It introduces some new behavior first available with the point release VA version 6.0.2, and it provides some tips on testing your secured application.
Certificate-based Authentication
An X.509 certificate is analogous to an ID badge. It is an artifact which asserts an identity for its holder to an entity which requires proof-of-identity for access to some restricted resource. A certificate is accepted as genuine when it is recognized as having been “signed” (or “issued by”) by a trusted Certificate Authority (CA).
A certificate comprises many data attributes. The two that are germane to the discussion at hand are “Subject” and “Issuer”. These attributes correspond, respectively, to the names of the certificate holder and the CA.
Internet users may recognize some of these terms, because servers hosting secure web sites typically begin a session by presenting their x.509 certificate to the user’s browser. In most cases, this is transparent to the user. The browser receives the certificate and automatically verifies that it was signed by a trusted CA.
The browser should also compare the certificate “Common Name” (CN) attribute to the hostname of the target web site and ensure that these strings match. Though not a strict requirement, it is a best practice for web servers to use their fully-qualified-domain-name (FQDN) as their certificate “CN”. This enables a general-purpose application (such as a browser) to use a built-in universal algorithm for matching the identity asserted by the certificate to the expected identity of the target web site.
This transport-level server authentication being typically transparent to the user depends on the browser being aware of the CA that issued the certificate presented by the server. When the browser cannot match the certificate issuer to a known trusted CA, it will prompt the user for instructions as to how to proceed. At that point the user may accept the presented certificate or terminate the SSL connection.
It is possible, but less common, for a client to certificate-authenticate to an SSL server. This is a configuration option for the server. The SSL client will only send a certificate in response to a request for such from the server. The server may be configured to send a certificate request, or not. When configured to send a certificate request, the server is also configured to require a response, or not. If the server is configured to require clients to certificate-authenticate and a client does not provide a certificate, the server will terminate the SSL connection during the initial handshake.
When required, client authentication is more commonly achieved via higher-level protocols such as HTTP, IIOP, and WS-Security. This is due more to convenience and cost than to technical considerations.
Certificates from a well-known CA cost in the neighborhood of hundreds of dollars to obtain. For this reason, it is common during development and test to use “self-signed” certificates.
A self-signed certificate is a certificate that references its own Subject as its Issuer. Such a certificate cannot be trusted as proof-of-identity because it is not digitally signed by a trusted CA. Presenting such a certificate to an SSL peer is analogous to me presenting a piece of paper on which I wrote my name to a bank teller in order to make a withdrawal.
It is not uncommon for intranet sites to use “self-signed” certificates for server authentication. This is a bad practice for production sites, and is no more secure than using no server authentication at all. A better practice would be to establish an internally-trusted CA, and generate server certificates for production intranet sites using this internal CA.
The OpenSSL distribution includes utilities that enable the creation of a “demo” CA certificate that can be used to sign other certificates for use by SSL clients and servers. The use of these utilities in creating a testbed for SSL testing is demonstrated below.
Creating a Certificate Set
Using the OpenSSL utilities effectively requires a clear objective and a fair understanding of the cryptographic technologies involved. Here we present a utility, create_certificate_set, for generating certificates and keys for a specific testbed scenario. (For more information on using this utility, see the Readme file included in the utility package.)
These certificates are intended to support development and test activities only.
In the subject scenario, we want to enable mutual transport-level certificate authentication, for a simple 1-1 client/server conversation. We want the certificates in use to be issued by a CA, rather than being self-signed. We want our own CA, and we want this CA to be the issuer of our peer certificates.
In more complex environments, we might have a forest of certificates and authorities. That is, we might have multiple recognized root CAs, and zero or more intermediate CAs between a root CA and a signed client or server certificate. The ordered collection of certificates in the issuer path from a certificate to its root CA is known as the certificate’s “certificate chain”. For our purposes here, the depth of the each non-root certificate’s certificate chain will be ‘1’, since each certificate is signed by our root CA.
In targeting this scenario and generating the required certificates, we will also be prepared for some variations. In particular, choosing the appropriate subset of generated certificates and configuring our peers appropriately, we will also be able to target server-only transport authentication.
Of course, we are generating these certificates for use with a VA application. In this client/server conversation, however, we may have VA on either end – or both ends – of the SSL connection.
The create_certificate_set utility is a Linux (bash) shell script that accepts two optional parameters: a certificate set prefix and a “pass phrase” for the generated ‘*.p12’ files. So, the utility might be invoked as shown below.
> create_certificate_set vast passw0rd
This utility will result in a set of eight generated files. 
vast_ca.pem
vast_ca_key.pem
vast_client.pem
vast_client_key.pem
vast_client.p12
vast_server.pem
vast_server_key.pem
vast_server.p12
The ‘*_key.pem’ files are private key files. They contain sensitive data and, as generated here, are not encrypted. In a production system, these files must be guarded via operating system file access control mechanisms from unauthorized reading. It is the private key which enables the bearer of a certificate to successfully assert the identity expressed in the certificate to be his own.
OpenSSL does provide for generation of encrypted key files. An encrypted key file requires the use of a “pass phrase” at runtime for decryption. VAST does not support the use of encrypted key files.
The remaining ‘*.pem’ files are certificate files. These certificate and key files, with the exception of the CA key file, are deployed to the runtime filesystem. The CA key file is required for CA utility operations such as certificate signing. It should not be deployed to the runtime filesystem.
The remaining generated files – ‘*.p12’ - are used for sharing certificate information with applications. The ‘*_client.p12’ file, for example, can be used to import the client certificate into a web browser. Each of these files includes both a certificate and its private key. In this case the private key is encrypted and will require that the user present the expected pass phrase in order to complete such an import. The default pass phrase for the generated files is ‘passw0rd’.
A ‘*.p12’ file is not required to register the test CA as a trusted root CA in a web browser. The ‘*_ca.pem’ file is sufficient for this purpose.
The ‘*.p12’ files are not used in the example code here.
Deploying the generated certificates
Server Authentication
Suppose we deploy our ‘*_server*.pem’ files in a subdirectory – ‘certs’ - of our Smalltalk image start up directory. (For purposes of this discussion we do not address key file access control.)
Then the example server in Part 1 can be run otherwise unchanged by simply modifying the SSL configuration, as shown below.
SSL configuration code: 
config := SciSslSocketConfiguration new
sslVersion: SciSslConstants::SSLv3;
certificateFilename: 'certs/vast_server.pem';
privateKeyFilename: 'certs/vast_server_key.pem';
yourself.
For testing my Smalltalk SSL server with something other than a Smalltalk SSL client, the OpenSSL ‘s_client’ utility is very helpful.
Here, below, is a sample invocation. 
> openssl s_client -connect localhost:2222 \
        -CAfile certs/vast_ca.pem \
        -verify 1 \
        -ssl3 << EOF
> Hello World!
> EOF
Note that I specify the CA certificate file location, and recall that this is the CA that issued the server certificate. Note also that I specify a certificate chain verification depth of ‘1’, meaning that our client should go no further than one level deep in the certificate chain of a received certificate to find an issuer recognized as a trusted CA.
We also specify, for both client and server, that we want to use SSL protocol version 3 for this conversation. Version 3 is considered to be significantly more secure than previous versions, and is therefore preferred unless a known requirement for down-level compatibility exists.
The OpenSSL ‘s_client’ provides a great deal of trace and debug level output, which may be quite useful in problem diagnosis.
Note:
The generated server certificate does not comply with the practice of using the host FQDN as the value of the CN component of the Subject name. If you are using this certificate to identify a web server (for browser clients), you should modify the ‘req_input_server.txt’ file as appropriate to your site and regenerate.
Server Authentication in a Smalltalk Client
Our next step is to put a Smalltalk client in the role of s_client in the scenario above. This also requires some extensions to the sample echo client configuration, as well as a few extra lines of code for behavior that we don’t get for free.
Smalltalk code 
config := SciSslSocketConfiguration new
          sslVersion: SciSslConstants::SSLv3;
          verify: SSL_VERIFY_PEER | SSL_VERIFY_FAIL_IF_NO_PEER_CERT;
          verifyDepth: 1;
          caFile: 'certs/vast_ca.pem';
          yourself.
These additional configuration parameters specify that we want OpenSSL to require the server to present a certificate, that it must be verified as being issued by a CA that we trust, and that we expect verification to succeed on the first hop up the server’s certificate chain. To enable this verification to occur, we specify the path to a file containing a copy of the certificate for the CA that we require the server’s certificate to have been issued by.
Following the send of SciSslSocket#>>sslConnect, we perform the following three steps..
steps 
(rv := secureSocket sslVerifyCertificate) ~= 0
        ifTrue: [ rv halt ].
 
(rv := secureSocket sslGetPeerCertificate) isSslError
        ifTrue: [ rv halt ].
 
(rv := rv getSubjectName) isSslError
        ifTrue: [ rv halt ].       
What we are after here are two things. First, we want to determine whether verification succeeded according to our configured constraints. If so, then we are assured that the server is who it claims to be. Second, we want to verify that server is who we want it to be. This is done by comparing the server certificate Subject name (or some component of it) to the value that we expect it to be.
Now let’s take a look at another OpenSSL utility that is useful in validating our client configuration – ‘s_server’. Here is a sample invocation.
openssl s_server -accept 7443 \
      -cert vast_server.pem \
      -key vast_server_key.pem \
      -ssl3 \
      -state
This will run an SSL server listening on port 7443, identifying itself according to the certificate file ‘vast_server.pem’. We’ve also added a ‘-state’ option, to get a little more helpful diagnostic output.
Client Authentication
While we noted above that client certificate authentication is not common, it is also not unusual. Consider, for example, a distributed system architecture where it is a requirement for multiple internal components to mutually-authenticate for the purpose of collaborating via secure TCP connections. In this case the SSL client is not an end-user application (such as a web browser), and client certificate authentication is quite reasonable and not at all uncommon.
To configure s_server for client authentication, we invoke it with a few additional parameters. 
 
openssl s_server -accept 7443 \
      -cert sst_server.pem \
      -key sst_server_key.pem \
      -Verify 1 \
      -CAfile sst_ca.pem \
      -ssl3 \
      -state
The idea here is by now fairly clear. We want to the server to require clients to present a certificate, and we want to be able to verify the certificate authenticity in one hop up the certificate chain. And of course we have to specify a location for the CA certificate to use in this procedure.
With our Smalltalk client, we simply need to add the parameters which specify the location of our certificate and key files.
Client code 
config := SciSslSocketConfiguration new
          sslVersion: SciSslConstants::SSLv3;
          certificateFilename: 'certs\sst_client.pem';
          privateKeyFilename: 'certs\sst_client_key.pem';
          verify: SSL_VERIFY_PEER | SSL_VERIFY_FAIL_IF_NO_PEER_CERT;
          verifyDepth: 1;
          caFile: 'certs/vast_ca.pem';
          yourself.
Client Authentication in a Smalltalk Server
Completing the circle, now, we begin by extending our Smalltalk server configuration to enable client authentication.
Client authentication code: 
 
config := SciSslSocketConfiguration new
          sslVersion: SciSslConstants::SSLv3;
          certificateFilename: 'certs/vast_server.pem';
          privateKeyFilename: 'certs/vast_server_key.pem';
          verify: SSL_VERIFY_PEER | SSL_VERIFY_FAIL_IF_NO_PEER_CERT;
          verifyDepth: 1;
          caFile: 'certs/vast_ca.pem';
          yourself.
Then we add exactly the same verification steps in our server code, following the #sslAccept, that we added above to our client after it’s #sslConnect.
With client authentication, however, application-specific validation of the client’s authority to perform the in-process request is generally more involved than with server authentication. That is to say, the code that makes use of the peer certificate Subject name is likely to be more involved on the server side than on the client side.
Error Handling
Here are a few common error scenarios.
Missing files 
One or more files specified in the configuration is missing or misnamed. For example, suppose this is the case for our server’s certificate file. The error is detected during #sslInitialize:, which returns an SciSslError.
errorCode: ENOENT (2)
errorObject: ‘certs\bogus.pem'
errorHint: 'No such file or directory'
alertString: nil
alertCode: INTERNAL_ERROR
It’s possible to have this error detected earlier in the startup procedure. As written, the sample server performs lazy initialization of its SSL Context. That is, Context initialization does not occur until the server detects the first attempted client connection. It is during this Context initialization that this error is actually detected. This initialization can be forced by explicitly executing the following bit of code.
(rv := config getSciSslContext) isSslError
      ifTrue: [“your error handling here”].
 
In this way, configuration errors like that above could be detected before opening of the listening socket.
Note the use of an #alertCode and #alertString. If #alertCode is other than INTERNAL_ERROR, then it represents an SSL protocol error and #alertString is the error description.
Key and certificate mismatch 
Suppose the private key and certificate used for the sample server do not match. This is detected during handshaking for the first attempted client connection, where #sslAccept returns the following error.
errorCode: 185073780
errorObject: certs\vast_server_key.pem'
errorHint: 'X509_check_private_key:key values mismatch'
alertString: nil
alertCode: INTERNAL_ERROR
The #errorCode here is an OpenSSL-internal code.
No certificate provided when required 
Our server is configured to require that the client present a certificate, but the client fails to do so.
With our sample client, this is indicated by an #sslConnect failure. The actual SSL alert code is ‘40’, which maps to an SSL alert description of ‘SSL3_READ_BYTES: sslv3 alert handshake failure’.
errorCode: 336151568
errorObject: certs\vast_server_key.pem'
errorHint: 'SSL3_READ_BYTES'
alertString: ‘sslv3 alert handshake failure’
alertCode: HANDSHAKE_FAILURE
Certificate verification failure 
The server presents a certificate which does not satisfy the client’s verification constraints.
With our sample client configured for peer certificate verification, this error is indicated by an #sslConnect failure.
errorCode: 336134278
errorObject: nil
errorHint: ‘SSL3_GET_SERVER_CERTIFICATE:certificate verify failed’
alertString: nil
alertCode: INTERNAL_ERROR
 
It’s also possible to handle this error scenario at the application layer. If we initialize our security configuration with “verify: nil”, (but do not change #verifyDepth) then the #sslConnect: succeeds. If we test with #sslVerifyCertificate, however, we get a non-zero certificate verification status. This status code generally provides a more precise indication of the verification failure, and is one of the codes documented as X509_V_ERR.
References
VA Smalltalk download page
Certificate Set Utilities
Sample Test Certificates
 
In addition to the references cited in Part 1…
Viega, John, “Network Security with OpenSSL”, O’Reilly, 2002. Essential.
Linux Documentation Project: SSL Certificates HOWTO http://www.tldp.org/HOWTO/SSL-Certificates-HOWTO/index.html
Readers may find this site very helpful as a practical guide to accomplishing specific tasks. The details are somewhat Linux-specific, but Windows users will find it valuable as well.
OpenSSL.org: CA.pl manpage http://www.openssl.org/docs/apps/CA.pl.html
The “CA” utility wraps the core openssl commands much the way the “create_certificate_set” utility does, but with a more general objective.
This directory contains a “certificates” how-to and a “keys” how-to.
Last modified date: 12/20/2017