<!DOCTYPE HTML PUBLIC "-//W3C//DTD HTML 4.0 TRANSITIONAL//EN">
<HTML>
<HEAD>
  <META HTTP-EQUIV="Content-Type" CONTENT="text/html; CHARSET=UTF-8">
  <META NAME="GENERATOR" CONTENT="GtkHTML/4.6.6">
</HEAD>
<BODY>
Please review the patch 101-2 which addresses all the comments given by Endi, Jack and Ade.<BR>
<BR>
This patch deals with adding the feature of generating asymmetric keys in the KRA.<BR>
<BR>
Few major changes from the first patch - <BR>
- Supported algorithms - RSA and DSA.<BR>
- Permitted key sizes - RSA - 256-8192(default, is configurable). DSA 512, 768, 1024. (Since the PQG params are not yet accepted.)<BR>
- Way of storing the private key in the db. Previously, the binary data is treated as a string, but now the actual Private key object is wrapped and stored.<BR>
<BR>
Please see inline comments for any comments that are not addressed.<BR>
<BR>
On Thu, 2014-08-07 at 16:16 -0400, Ade Lee wrote:
<BLOCKQUOTE TYPE=CITE>
<PRE>
On Tue, 2014-08-05 at 11:28 -0500, Endi Sukma Dewata wrote:
<FONT COLOR="#737373">> On 8/4/2014 9:29 AM, Abhishek Koneru wrote:</FONT>
<FONT COLOR="#737373">> > Please find the attached patch which generates the asymmetric keys using</FONT>
<FONT COLOR="#737373">> > algorithms RSA and DSA (EC to be added) for a valid key sizes of 512,</FONT>
<FONT COLOR="#737373">> > 1024, 2048, 4096.</FONT>
<FONT COLOR="#737373">> ></FONT>
<FONT COLOR="#737373">> > Key Changes in the patch -</FONT>
<FONT COLOR="#737373">> ></FONT>
<FONT COLOR="#737373">> >     - Adding methods for generation of Asymmetric keys in the DRM.</FONT>
<FONT COLOR="#737373">> >     - Allowing the key-generate CLI command to accept algorithms RSA and</FONT>
<FONT COLOR="#737373">> > DSA.</FONT>
<FONT COLOR="#737373">> >     - Returning the base64 encoded public key in the KeyInfo object</FONT>
<FONT COLOR="#737373">> > (key-show CLI command).</FONT>
<FONT COLOR="#737373">> >     - Retrieving the private key using the retrieveKeyData method in the</FONT>
<FONT COLOR="#737373">> > KeyClient.</FONT>
<FONT COLOR="#737373">> ></FONT>
<FONT COLOR="#737373">> > -- Abhishek</FONT>
<FONT COLOR="#737373">> </FONT>
<FONT COLOR="#737373">> I've opened some tickets related to key management. Please take a look </FONT>
<FONT COLOR="#737373">> at them.</FONT>
<FONT COLOR="#737373">> </FONT>
<FONT COLOR="#737373">> The patch seems to be working just fine, so it's ACKed. Some comments below:</FONT>
<FONT COLOR="#737373">> </FONT>
<FONT COLOR="#737373">> 1. Not sure about the "b64" prefix in b64PublicKey and b64_public_key </FONT>
<FONT COLOR="#737373">> field names. We have some other fields that contain base-64 encoded </FONT>
<FONT COLOR="#737373">> values but they use regular field names.</FONT>
<FONT COLOR="#737373">> </FONT>
Agreed.  Lets remove the b64 parts.  What we can (and should be doing
though) is documenting both in the Java class and the python class that
base 64 encoded data is required/provided.
</PRE>
</BLOCKQUOTE>
<BR>
Done
<BLOCKQUOTE TYPE=CITE>
<PRE>

<FONT COLOR="#737373">> 2. Existing issue. The KeyGenerationRequest.getKeySize() swallows </FONT>
<FONT COLOR="#737373">> NumberFormatException and returns null. I think the method should let </FONT>
<FONT COLOR="#737373">> the exception be handled by the caller. It's a RuntimeException so it </FONT>
<FONT COLOR="#737373">> doesn't need to be declared.</FONT>
</PRE>
</BLOCKQUOTE>
<BR>
There is a ticket for it. Will fix it separately
<BLOCKQUOTE TYPE=CITE>
<PRE>
<FONT COLOR="#737373">> </FONT>
<FONT COLOR="#737373">> 3. In AsymKeyGenService.serviceRequest() the request ID doesn't really </FONT>
<FONT COLOR="#737373">> need to be converted into string. The string concatenation later will do </FONT>
<FONT COLOR="#737373">> that automatically.</FONT>
<FONT COLOR="#737373">> </FONT>
<FONT COLOR="#737373">>    String id = request.getRequestId().toString</FONT>();
</PRE>
</BLOCKQUOTE>
Done
<BLOCKQUOTE TYPE=CITE>
<PRE>
<FONT COLOR="#737373">> </FONT>
<FONT COLOR="#737373">> 4. The following code in KeyRequestService might not be necessary </FONT>
<FONT COLOR="#737373">> because access to this service is already controlled by ACL, so owner is </FONT>
<FONT COLOR="#737373">> never null.</FONT>
<FONT COLOR="#737373">> </FONT>
<FONT COLOR="#737373">>    if (owner == null) {</FONT>
<FONT COLOR="#737373">>        throw new UnauthorizedException(</FONT>
<FONT COLOR="#737373">>            "Key generation must be performed by an agent");</FONT>
<FONT COLOR="#737373">>  &#-1;</FONT>&#-1; }
</PRE>
</BLOCKQUOTE>
<BR>
Removed.
<BLOCKQUOTE TYPE=CITE>
<PRE>
<FONT COLOR="#737373">> </FONT>
<FONT COLOR="#737373">> In general we shouldn't hard-code authorization logic in the code unless </FONT>
<FONT COLOR="#737373">> it's something can't be expressed via ACL.</FONT>
<FONT COLOR="#737373">> </FONT>
<FONT COLOR="#737373">> 5. Some formatting issues:</FONT>
<FONT COLOR="#737373">> </FONT>
<FONT COLOR="#737373">> Formatting issue in KeyCLI.java:</FONT>
<FONT COLOR="#737373">> </FONT>
<FONT COLOR="#737373">>    for(i=0;i<publicKey.length()/64;i++){</FONT>
<FONT COLOR="#737373">> </FONT>
<FONT COLOR="#737373">> KeyRequestService.java:</FONT>
<FONT COLOR="#737373">> </FONT>
<FONT COLOR="#737373">>    } else if (request instanceof AsymKeyGenerationRequest){</FONT>
<FONT COLOR="#737373">>    public Response generateAsymKey(AsymKeyGenerationRequest data){</FONT>

<FONT COLOR="#737373">> KeyService.java:</FONT>
<FONT COLOR="#737373">> </FONT>
<FONT COLOR="#737373">>    if(rec.getPublicKeyData() != null && getPublicKey){</FONT>
<FONT COLOR="#737373">> </FONT>
<FONT COLOR="#737373">> AsymKeyGenerationRequest.java:</FONT>
<FONT COLOR="#737373">> </FONT>
<FONT COLOR="#737373">>    public class AsymKeyGenerationRequest extends KeyGenerationRequest{</FONT>
<FONT COLOR="#737373">> </FONT>
<FONT COLOR="#737373">> KeyGenerationRequest.java:</FONT>
<FONT COLOR="#737373">> </FONT>
<FONT COLOR="#737373">>    public class KeyGenerationRequest extends ResourceMessage{</FONT>
<FONT COLOR="#737373">> </FONT>

</PRE>
</BLOCKQUOTE>
Done
<BLOCKQUOTE TYPE=CITE>
<PRE>
6. In key.py, create_request() seems to be confusingly named for me.
How about submit_request() or submit_creation_request() instead?
Same comment on createRequest() in the Java code.

</PRE>
</BLOCKQUOTE>
changed the method name to submit_request/submitRequest.
<BLOCKQUOTE TYPE=CITE>
<PRE>
7. generate_asymmetric_key() is unnecessarily wordy ..  How about
something like this (which is more likely what the final output will
look like?)

def generate_asymmetric_key(self, client_key_id, algorithm=None, size=None,
                               usages=None,
                               trans_wrapped_session_key=None):
        """ Generate and archive asymmetric keys in the DRM.

            Return a KeyRequestResponse which contains a KeyRequestInfo
            object that describes the URL for the request and generated keys.

        """
        if client_key_id is None:
            raise TypeError("Must specify Client Key ID")

        twsk = None
        if trans_wrapped_session_key is not None:
            twsk = base64.encodestring(trans_wrapped_session_key)

        request = AsymKeyGenerationRequest(
            client_key_id=client_key_id,
            key_size=size,
            key_algorithm=algorithm,
            key_usages=usages,
            trans_wrapped_session_key=twsk)
        
        if twsk is not None:
            raise NotImplementedError(
                "Returning the asymmetric keys in the same call is not yet "
                "implemented.")

        return self.create_request(request)

</PRE>
</BLOCKQUOTE>
Done
<BLOCKQUOTE TYPE=CITE>
<PRE>
8.  Formatting: Can we break up the line for XmlSeeAlso in
ResourceMessage.java ?

</PRE>
</BLOCKQUOTE>
Done
<BLOCKQUOTE TYPE=CITE>
<PRE>
9. In AsymKeyGenerationRequest.java, its useful at the end to have some
unit test code so that we can see the format of the requests.

</PRE>
</BLOCKQUOTE>
Done
<BLOCKQUOTE TYPE=CITE>
<PRE>
10.  In KeyClient.java in generateAsymmetricKey, the keySize is
hardcoded to 1024 bits.

</PRE>
</BLOCKQUOTE>
Corrected.
<BLOCKQUOTE TYPE=CITE>
<PRE>
11.  Formatting in line 264 drmtest.py  Actually, thats probably a line
that needs to be removed.

</PRE>
</BLOCKQUOTE>
Removed.
<BLOCKQUOTE TYPE=CITE>
<PRE>
12. Make sure all new files have relevant copyright/licence notices.

</PRE>
</BLOCKQUOTE>
Done.
<BLOCKQUOTE TYPE=CITE>
<PRE>
13. In AsynKeyGenService.java, it looks like you are using Hungarian
notation for your instance variables.  Please avoid this in new code.
(mKRA, mStorageUnit)

</PRE>
</BLOCKQUOTE>
Done.
<BLOCKQUOTE TYPE=CITE>
<PRE>
14. Formatting - line 92 in AsymKeygenService.java

</PRE>
</BLOCKQUOTE>
Done
<BLOCKQUOTE TYPE=CITE>
<PRE>
15. Why the change in SecurityDataService.java?

</PRE>
</BLOCKQUOTE>
To not store the algorithm of the session key in the db for the ldap entry.
<BLOCKQUOTE TYPE=CITE>
<PRE>
16. There is no Java test code in DRMTest.java?

</PRE>
</BLOCKQUOTE>
Done
<BLOCKQUOTE TYPE=CITE>
<PRE>
17.  It would be useful to have some kind of test for the validity of
the keys generated, either in drmtest.py or DRMTest.java.  Maybe some
thing that validates a public against the private key.  Something that
tells us that what is being generated is valid.

</PRE>
</BLOCKQUOTE>
<BR>
Done. Used the keys to sign and verify some data.
<BLOCKQUOTE TYPE=CITE>
<PRE>
18. Where does the list of valid key sizes come from?  We probably
should support whatever key sizes JSS/NSS supports - or at least some
reasonable subset thereof.  Furthermore, that set should be make
configurable in CS.cfg.  Are there any other parameters that are
important to key generation?  Usage/ Mode?  Or is key size (at least for
RSA and DSA the only factor needed in the constructor of the
KeyGenerator object in JSS?

</PRE>
</BLOCKQUOTE>
Done
<BLOCKQUOTE TYPE=CITE>
<PRE>
Otherwise, looks good in general.

Ade

</PRE>
</BLOCKQUOTE>
<BR>
Some comments from Jack - <BR>
<BR>
KeyGenerationRequest:<BR>
<BR>
I see we have only two usages/capabilities defined.<BR>
In TPS we support more of these as follows:<BR>
<BR>
Might want to look into expanding that.<BR>
<BR>
<BR>
op.enroll.soKey.keyGen.encryption.private.keyCapabilities.decrypt=true<BR>
op.enroll.soKey.keyGen.encryption.private.keyCapabilities.derive=false<BR>
op.enroll.soKey.keyGen.encryption.private.keyCapabilities.encrypt=false<BR>
op.enroll.soKey.keyGen.encryption.private.keyCapabilities.private=true<BR>
op.enroll.soKey.keyGen.encryption.private.keyCapabilities.sensitive=true<BR>
op.enroll.soKey.keyGen.encryption.private.keyCapabilities.sign=false<BR>
op.enroll.soKey.keyGen.encryption.private.keyCapabilities.signRecover=false<BR>
op.enroll.soKey.keyGen.encryption.private.keyCapabilities.token=true<BR>
op.enroll.soKey.keyGen.encryption.private.keyCapabilities.unwrap=true<BR>
op.enroll.soKey.keyGen.encryption.private.keyCapabilities.verify=false<BR>
op.enroll.soKey.keyGen.encryption.private.keyCapabilities.verifyRecover=false<BR>
op.enroll.soKey.keyGen.encryption.private.keyCapabilities.wrap=false<BR>
<BR>
<FONT COLOR="#ff0000">--Added all the usages except the private, sensitive and token.</FONT><BR>
<BR>
<BR>
- This code here:<BR>
<BR>
+        KeyRequestResponse response = null;<BR>
+        if (keyAlgorithm.equalsIgnoreCase(KeyRequestResource.RSA_ALGORITHM) || keyAlgorithm.equalsIgnoreCase(KeyRequestResource.DSA_ALGORITHM)) {<BR>
+            response = keyCLI.keyClient.generateAsymmetricKey(clientKeyId, keyAlgorithm,<BR>
+                    Integer.parseInt(keySize),<BR>
+                    usages, null);<BR>
+        } else {<BR>
+            response = keyCLI.keyClient.generateSymmetricKey(clientKeyId, keyAlgorithm,<BR>
+                    Integer.parseInt(keySize),<BR>
+                    usages, null);<BR>
+        }<BR>
         MainCLI.printMessage("Key generation request info");<BR>
         KeyCLI.printKeyRequestInfo(response.getRequestInfo());<BR>
     }<BR>
<BR>
So the question is, are we sure that if the alg is not RSA or DSA that we default to a symmetric key?<BR>
Are we not passing an alg for that like DES3, etc? What if ECC or some such gets passed, should we throw an exception?<BR>
<BR>
<FONT COLOR="#ff0000">--Corrected. </FONT><BR>
<BR>
- This code:<BR>
<BR>
@Override<BR>
+    public boolean serviceRequest(IRequest request) throws EBaseException {<BR>
+<BR>
+        String id = request.getRequestId().toString();<BR>
+        String clientKeyId = request.getExtDataInString(IRequest.SECURITY_DATA_CLIENT_KEY_ID);<BR>
+        String algorithm = request.getExtDataInString(IRequest.KEY_GEN_ALGORITHM);<BR>
+<BR>
+        String keySizeStr = request.getExtDataInString(IRequest.KEY_GEN_SIZE);<BR>
+        int keySize = Integer.parseInt(keySizeStr);<BR>
+<BR>
+        CMS.debug("AsymKeyGenService.serviceRequest. Request id: " + id);<BR>
+        CMS.debug("AsymKeyGenService.serviceRequest algorithm: " + algorithm);<BR>
+<BR>
+        KeyPairAlgorithm keyPairAlgorithm = KeyRequestDAO.ASYMKEY_GEN_ALGORITHMS.get(algorithm.toUpperCase());<BR>
+<BR>
+        String owner = request.getExtDataInString(IRequest.ATTR_REQUEST_OWNER);<BR>
+        String auditSubjectID = owner;<BR>
+<BR>
<BR>
Would it make sense here to do some validation and throw exceptions? If some of this data is bad, the next calls are going<BR>
to fail. If we check here we might be able to send a more specific exception back. For instance that "Integer.parseInt" could <BR>
throw up and we won't find out about it unti later. Might check to see how we handle this in other similar code. I suspect maybe this<BR>
validation is done at some higher level? I don't remember :)<BR>
<BR>
<FONT COLOR="#ff0000">-- Validation added before this statement is processed. (In the previous methods)</FONT><BR>
<BR>
IN the same method:<BR>
<BR>
if (kp == null) {<BR>
+            auditAsymKeyGenRequestProcessed(auditSubjectID, ILogger.FAILURE, request.getRequestId(),<BR>
+                    clientKeyId, null, "Failed to generate Asymmetric key");<BR>
+            throw new EBaseException("Errors in generating Asymmetric key");<BR>
+        }<BR>
<BR>
Can kp even be null here? Would not the try / catch block above catch this? I"m not sure here, just asking.<BR>
<BR>
<FONT COLOR="#ff0000">-- Just a fail-safe  null check.</FONT><BR>
<BR>
Here:<BR>
<BR>
 BigInteger serialNo = storage.getNextSerialNumber();<BR>
+<BR>
+        if (serialNo == null) {<BR>
+            mKRA.log(ILogger.LL_FAILURE,<BR>
+                    CMS.getLogMessage("CMSCORE_KRA_GET_NEXT_SERIAL"));<BR>
+            auditAsymKeyGenRequestProcessed(auditSubjectID, ILogger.FAILURE, request.getRequestId(),<BR>
+                    clientKeyId, null, "Failed to get next Key ID");<BR>
+            throw new EBaseException(CMS.getUserMessage("CMS_KRA_INVALID_STATE"));<BR>
+        }<BR>
<BR>
<BR>
Might check to see if getNextSerialNumber could ever return null.<BR>
<BR>
<FONT COLOR="#ff0000">-- Just a fail-safe  null check. We never know anything with threads. :)</FONT><BR>
<BR>
Here:<BR>
<BR>
index a2d587318efb59f0e1e7ebff0a3533468b558d10..ef353eeab018b9e3cec8f562f25d889892b830dc 100644<BR>
--- a/base/kra/src/com/netscape/kra/SecurityDataRecoveryService.java<BR>
+++ b/base/kra/src/com/netscape/kra/SecurityDataRecoveryService.java<BR>
@@ -179,7 +179,7 @@ public class SecurityDataRecoveryService implements IService {<BR>
         byte[] unwrappedSecData = null;<BR>
         if (dataType.equals(KeyRequestResource.SYMMETRIC_KEY_TYPE)) {<BR>
             symKey = recoverSymKey(keyRecord);<BR>
-        } else if (dataType.equals(KeyRequestResource.PASS_PHRASE_TYPE)) {<BR>
+        } else {<BR>
             unwrappedSecData = recoverSecurityData(keyRecord);<BR>
         }<BR>
 <BR>
Just wondering the reason for this. I"m sure it's valid, just curious..<BR>
<BR>
<FONT COLOR="#ff0000">-- It was a mistake. Corrected this time.</FONT><BR>
<BR>
Here:<BR>
<BR>
<BR>
<BR>
+<BR>
+    public Response generateAsymKey(AsymKeyGenerationRequest data){<BR>
+        if (data == null) {<BR>
+            throw new BadRequestException("Invalid key generation request.");<BR>
+        }<BR>
+<BR>
+        KeyRequestDAO dao = new KeyRequestDAO();<BR>
+        KeyRequestResponse response;<BR>
+        try {<BR>
+            String owner = servletRequest.getUserPrincipal().getName();<BR>
+            if (owner == null) {<BR>
+                throw new UnauthorizedException("Key generation must be performed by an agent");<BR>
+            }<BR>
+            response = dao.submitRequest(data, uriInfo, owner);<BR>
+            auditAsymKeyGenRequestMade(response.getRequestInfo().getRequestId(), ILogger.SUCCESS,<BR>
+                    data.getClientKeyId());<BR>
+<BR>
+            return createCreatedResponse(response, new URI(response.getRequestInfo().getRequestURL()));<BR>
+<BR>
+        } catch (EBaseException | URISyntaxException e) {<BR>
+            e.printStackTrace();<BR>
+            auditArchivalRequestMade(null, ILogger.FAILURE, data.getClientKeyId());<BR>
+            throw new PKIException(e.toString());<BR>
+        }<BR>
+    }<BR>
 }<BR>
<BR>
<BR>
Note the Auth code, with endi's realms and such, is this needed at this level of code?<BR>
<BR>
<FONT COLOR="#ff0000">-- Removed</FONT><BR>
<BR>
<BR>
--Abhishek<BR>
<BR>
<BR>
<BR>
<BR>
<BR>
</BODY>
</HTML>