Subject: [KEYPROV] Provisioning: API versus Protocol

From: Anders Rundgren <[email-masked]>
Date: Tue, 08 Jun 2010 10:01:11 +0200

If there will be a KEYPROV II you may find the following code a bit 
interesting
because it fully implements the 6 core KeyGen2 protocol passes including
the calls to the token ("sks." in the sample).

The classes "ProvisioningSessionRequestDecoder", 
"ProvisioningSessionRequestEncoder"
etc. are *entirely passive* XML object decoders and encoders respectively.

So it means that a mere 300 lines of code is all that it takes to E2ES-wise
*provision and manage* PKI, OTP, and Information Cards.  That is, *if* you
have the liberty of defining the API in parallel with the protocol.  One 
might
say that this is "cheating" but I find it rather "practical" compared to 
spending
EONs of time forcing square pegs through round holes.

Note: PKCS #11 and friends continue to work as usual, they just don't have
anything to do with the on-line provisioning and management of credentials
which isn't that surprising since they were never designed to support that.

There is work going on upgrading PKCS #11 but I think this will lead 
nowhere;
it is far easier creating a specific scheme for provisioning like SKS.

You can follow the calls by looking into the spec:
http://webpki.org/papers/keygen2/sks-api-arch.pdf

-- Anders

    class Client
      {
        XMLSchemaCache client_xml_cache;
        
        int provisioning_handle;
        
        KeyInitializationRequestDecoder key_init_request;
        
        ProvisioningSessionRequestDecoder prov_sess_req;
        
        DeviceInfo device_info;
        
        Client () throws IOException
          {
            client_xml_cache = new XMLSchemaCache ();
            client_xml_cache.addWrapper 
(ProvisioningSessionRequestDecoder.class);
            client_xml_cache.addWrapper 
(KeyInitializationRequestDecoder.class);
            client_xml_cache.addWrapper 
(CredentialDeploymentRequestDecoder.class);
          }

        private void abort (String message) throws IOException, SKSException
          {
            sks.abortProvisioningSession (provisioning_handle);
            throw new IOException (message);
          }
        
        private void postProvisioning 
(CredentialDeploymentRequestDecoder.PostOperation post_operation, int 
handle) throws IOException, GeneralSecurityException
          {
            EnumeratedProvisioningSession old = new 
EnumeratedProvisioningSession ();
            while (true)
              {
                old = sks.enumerateProvisioningSessions (old, false);
                if (!old.isValid ())
                  {
                    abort ("Old provisioning session not found:" +
                        post_operation.getClientSessionID () + "/" +
                        post_operation.getServerSessionID ());
                  }
                if (old.getClientSessionID 
().equals(post_operation.getClientSessionID ()) &&
                    old.getServerSessionID ().equals 
(post_operation.getServerSessionID ()))
                  {
                    break;
                  }
              }
            EnumeratedKey ek = new EnumeratedKey ();
            while (true)
              {
                ek = sks.enumerateKeys (ek);
                if (!ek.isValid ())
                  {
                    abort ("Old key not found");
                  }
                if (ek.getProvisioningHandle () == 
old.getProvisioningHandle ())
                  {
                    KeyAttributes ka = sks.getKeyAttributes 
(ek.getKeyHandle ());
                    if (ArrayUtil.compare (HashAlgorithms.SHA256.digest 
(ka.getCertificatePath ()[0].getEncoded ()), 
post_operation.getCertificateFingerprint ()))
                      {
                        switch (post_operation.getPostOperation ())
                          {
                            case 
CredentialDeploymentRequestDecoder.PostOperation.CLONE_KEY_PROTECTION:
                              sks.pp_cloneKeyProtection (handle, 
ek.getKeyHandle (), post_operation.getMAC ());
                              break;

                            case 
CredentialDeploymentRequestDecoder.PostOperation.UPDATE_KEY:
                              sks.pp_updateKey (handle, ek.getKeyHandle 
(), post_operation.getMAC ());
                              break;
                              
                            default:
                              sks.pp_deleteKey (handle, ek.getKeyHandle 
(), post_operation.getMAC ());
                          }
                        return;
                      }
                  }
              }
          }

///////////////////////////////////////////////////////////////////////////////////
        // Get prov sess request and respond with epheral keys and and 
attest
        
///////////////////////////////////////////////////////////////////////////////////
        byte[] provSessResponse (byte[] xmldata) throws IOException
          {
            prov_sess_req = (ProvisioningSessionRequestDecoder) 
client_xml_cache.parse (xmldata);
            Date client_time = new Date ();
            ProvisioningSession sess =
                  sks.createProvisioningSession 
(prov_sess_req.getSessionKeyAlgorithm (),
                                                 
prov_sess_req.getServerSessionID (),
                                                 
prov_sess_req.getServerEphemeralKey (),
                                                 
prov_sess_req.getSubmitURL (), /* IssuerURI */
                                                 
prov_sess_req.getSessionUpdatableFlag (),
                                                 
(int)(client_time.getTime () / 1000),
                                                 
prov_sess_req.getSessionLifeTime (),
                                                 
prov_sess_req.getSessionKeyLimit ());
            provisioning_handle = sess.getProvisioningHandle ();
            
            device_info = sks.getDeviceInfo ();
            ProvisioningSessionResponseEncoder prov_sess_response =
                  new ProvisioningSessionResponseEncoder 
(sess.getClientEphemeralKey (),
                                                          
prov_sess_req.getServerSessionID (),
                                                          
sess.getClientSessionID (),
                                                          
prov_sess_req.getServerTime (),
                                                          client_time,
                                                          
sess.getSessionAttestation (),
                                                          
device_info.getDeviceCertificatePath ());
            prov_sess_response.signRequest (new SymKeySignerInterface ()
              {
                public MacAlgorithms getMacAlgorithm () throws 
IOException, GeneralSecurityException
                  {
                    return MacAlgorithms.HMAC_SHA256;
                  }

                public byte[] signData (byte[] data) throws IOException, 
GeneralSecurityException
                  {
                    return sks.signProvisioningSessionData 
(provisioning_handle, data);
                  }
              });
            return prov_sess_response.writeXML ();
          }

///////////////////////////////////////////////////////////////////////////////////
        // Get key init request and respond with freshly generated 
public keys
        
///////////////////////////////////////////////////////////////////////////////////
        byte[] KeyInitResponse (byte[] xmldata) throws IOException
          {
            key_init_request = (KeyInitializationRequestDecoder) 
client_xml_cache.parse (xmldata);
            KeyInitializationResponseEncoder key_init_response =
                  new KeyInitializationResponseEncoder (key_init_request);
            int pin_policy_handle = 0;
            int puk_policy_handle = 0;
            for (KeyInitializationRequestDecoder.KeyObject key : 
key_init_request.getKeyObjects ())
              {
                byte[] pin_value = key.getPresetPIN ();
                if (key.getPINPolicy () == null)
                  {
                    pin_policy_handle = key.isDevicePINProtected () ? 
0xFFFFFFFF : 0;
                    puk_policy_handle = 0;
                  }
                else
                  {
                    if (key.getPINPolicy ().getUserDefinedFlag ())
                      {
                        pin_value = "015354".getBytes ("UTF-8");
                      }
                    if (key.isStartOfPINPolicy ())
                      {
                        if (key.isStartOfPUKPolicy ())
                          {
                            KeyInitializationRequestDecoder.PUKPolicy 
puk_policy = key.getPINPolicy ().getPUKPolicy ();
                            puk_policy_handle = sks.createPUKPolicy 
(provisioning_handle,
                                                                     
puk_policy.getID (),
                                                                     
puk_policy.getEncryptedValue (),
                                                                     
puk_policy.getFormat ().getSKSValue (),
                                                                     
puk_policy.getRetryLimit (),
                                                                     
puk_policy.getMAC());
                          }
                        KeyInitializationRequestDecoder.PINPolicy 
pin_policy = key.getPINPolicy ();
    
                        pin_policy_handle = sks.createPINPolicy 
(provisioning_handle,
                                                                 
pin_policy.getID (),
                                                                 
puk_policy_handle,
                                                                 
pin_policy.getUserDefinedFlag (),
                                                                 
pin_policy.getUserModifiableFlag (),
                                                                 
pin_policy.getFormat ().getSKSValue (),
                                                                 
pin_policy.getRetryLimit (),
                                                                 
pin_policy.getGrouping ().getSKSValue (),
                                                                 
PatternRestriction.getSKSValue (pin_policy.getPatternRestrictions ()),
                                                                 
pin_policy.getMinLength (),
                                                                 
pin_policy.getMaxLength (),
                                                                 
pin_policy.getInputMethod ().getSKSValue (),
                                                                 
pin_policy.getMAC ());
                      }
                  }
                KeyPair kpr = sks.createKeyPair (provisioning_handle,
                                                 key.getID (),
                                                 
key_init_request.getKeyAttestationAlgorithm (),
                                                 key.getServerSeed (),
                                                 pin_policy_handle,
                                                 pin_value,
                                                 
key.getBiometricProtection (),
                                                 
key.getPrivateKeyBackupFlag (),
                                                 key.getExportPolicy (),
                                                 key.getDeletePolicy (),
                                                 
key.getEnablePINCachingFlag (),
                                                 
key.getImportPrivateKeyFlag (),
                                                 key.getKeyUsage 
().getSKSValue (),
                                                 key.getFriendlyName (),
                                                 key.getKeyAlgorithmData 
().getSKSValue (),
                                                 key.getMAC ());
                key_init_response.addPublicKey (kpr.getPublicKey (),
                                                kpr.getKeyAttestation (),
                                                key.getID (),
                                                
kpr.getEncryptedPrivateKey ());
              }
            return key_init_response.writeXML ();
          }

///////////////////////////////////////////////////////////////////////////////////
        // Get the certificates and attributes and return a success message
        
///////////////////////////////////////////////////////////////////////////////////
        byte[] creDepResponse (byte[] xmldata) throws IOException, 
GeneralSecurityException
          {
            CredentialDeploymentRequestDecoder cred_dep_request =
                           (CredentialDeploymentRequestDecoder) 
client_xml_cache.parse (xmldata);
            /*
               Note: we could have used the saved provisioning_handle 
but that would not
               work for certifications that are delayed.  The following 
code is working
               for fully interactive and delayed scenarios by using SKS 
as state-holder
            */
            EnumeratedProvisioningSession eps = new 
EnumeratedProvisioningSession ();
            while (true)
              {
                eps = sks.enumerateProvisioningSessions (eps, true);
                if (!eps.isValid ())
                  {
                    abort ("Provisioning session not found:" +
                        cred_dep_request.getClientSessionID () + "/" +
                        cred_dep_request.getServerSessionID ());
                  }
                if (eps.getClientSessionID 
().equals(cred_dep_request.getClientSessionID ()) &&
                    eps.getServerSessionID ().equals 
(cred_dep_request.getServerSessionID ()))
                  {
                    break;
                  }
              }

//////////////////////////////////////////////////////////////////////////
            // Final check, do these keys match the request?
            
//////////////////////////////////////////////////////////////////////////
            for (CredentialDeploymentRequestDecoder.CertifiedPublicKey 
key : cred_dep_request.getCertifiedPublicKeys ())
              {
                int key_handle = sks.getKeyHandle 
(eps.getProvisioningHandle (), key.getID ());
                sks.setCertificatePath (key_handle, 
key.getCertificatePath (), key.getMAC ());
                if (key.getEncryptedSymmetricKey () != null)
                  {
                    sks.setSymmetricKey (key_handle,
                                         key.getEncryptedSymmetricKey (),
                                         
key.getSymmetricKeyEndorsedAlgorithms (),
                                         key.getSymmetricKeyMac ());
                  }
                for (CredentialDeploymentRequestDecoder.Extension 
extension : key.getExtensions ())
                  {
                    sks.addExtension (key_handle,
                                      extension.getBaseType (),
                                      extension.getQualifier (),
                                      extension.getExtensionType (),
                                      extension.getExtensionData (),
                                      extension.getMAC ());
                  }
                CredentialDeploymentRequestDecoder.PostOperation 
post_operation = key.getPostOperation ();
                if (post_operation != null)
                  {
                    postProvisioning (post_operation, key_handle);
                  }
              }
            for (CredentialDeploymentRequestDecoder.PostOperation pp_del 
: cred_dep_request.getPostProvisioningDeleteKeys ())
              {
                postProvisioning (pp_del, eps.getProvisioningHandle ());
              }
            CredentialDeploymentResponseEncoder cre_dep_response =
                      new CredentialDeploymentResponseEncoder 
(cred_dep_request,
                                                               
sks.closeProvisioningSession (eps.getProvisioningHandle (),
                                                                                             
cred_dep_request.getCloseSessionMAC ()));
            return cre_dep_response.writeXML ();
          }
      }