security/nss/lib/crmf/crmfpop.c

Thu, 22 Jan 2015 13:21:57 +0100

author
Michael Schloh von Bennewitz <michael@schloh.com>
date
Thu, 22 Jan 2015 13:21:57 +0100
branch
TOR_BUG_9701
changeset 15
b8a032363ba2
permissions
-rw-r--r--

Incorporate requested changes from Mozilla in review:
https://bugzilla.mozilla.org/show_bug.cgi?id=1123480#c6

     1 /* -*- Mode: C; tab-width: 8 -*-*/
     2 /* This Source Code Form is subject to the terms of the Mozilla Public
     3  * License, v. 2.0. If a copy of the MPL was not distributed with this
     4  * file, You can obtain one at http://mozilla.org/MPL/2.0/. */
     7 #include "crmf.h"
     8 #include "crmfi.h"
     9 #include "secasn1.h"
    10 #include "keyhi.h"
    11 #include "cryptohi.h"
    13 #define CRMF_DEFAULT_ALLOC_SIZE 1024
    15 SECStatus
    16 crmf_init_encoder_callback_arg (struct crmfEncoderArg *encoderArg, 
    17 				SECItem               *derDest) 
    18 {
    19     derDest->data = PORT_ZNewArray(unsigned char, CRMF_DEFAULT_ALLOC_SIZE);
    20     if (derDest->data == NULL) {
    21         return SECFailure;
    22     }
    23     derDest->len = 0;
    24     encoderArg->allocatedLen = CRMF_DEFAULT_ALLOC_SIZE;
    25     encoderArg->buffer = derDest;
    26     return SECSuccess;
    28 }
    30 /* Caller should release or unmark the pool, instead of doing it here.
    31 ** But there are NO callers of this function at present...
    32 */
    33 SECStatus 
    34 CRMF_CertReqMsgSetRAVerifiedPOP(CRMFCertReqMsg *inCertReqMsg)
    35 {
    36     SECItem               *dummy;
    37     CRMFProofOfPossession *pop;
    38     PLArenaPool           *poolp;
    39     void                  *mark;
    41     PORT_Assert(inCertReqMsg != NULL && inCertReqMsg->pop == NULL);
    42     poolp = inCertReqMsg->poolp;
    43     mark = PORT_ArenaMark(poolp);
    44     if (CRMF_CertReqMsgGetPOPType(inCertReqMsg) != crmfNoPOPChoice) {
    45         return SECFailure;
    46     }
    47     pop = PORT_ArenaZNew(poolp, CRMFProofOfPossession);
    48     if (pop == NULL) {
    49         goto loser;
    50     }
    51     pop->popUsed = crmfRAVerified;
    52     pop->popChoice.raVerified.data = NULL;
    53     pop->popChoice.raVerified.len  = 0;
    54     inCertReqMsg->pop = pop;
    55     dummy = SEC_ASN1EncodeItem(poolp, &(inCertReqMsg->derPOP),
    56 			       &(pop->popChoice.raVerified),
    57 			       CRMFRAVerifiedTemplate);
    58     return SECSuccess;
    59  loser:
    60     PORT_ArenaRelease(poolp, mark);
    61     return SECFailure;
    62 }
    64 static SECOidTag
    65 crmf_get_key_sign_tag(SECKEYPublicKey *inPubKey)
    66 {
    67     /* maintain backward compatibility with older
    68      * implementations */
    69     if (inPubKey->keyType == rsaKey) {
    70         return SEC_OID_PKCS1_SHA1_WITH_RSA_ENCRYPTION;
    71     }
    72     return SEC_GetSignatureAlgorithmOidTag(inPubKey->keyType, SEC_OID_UNKNOWN);
    73 }
    75 static SECAlgorithmID*
    76 crmf_create_poposignkey_algid(PLArenaPool      *poolp,
    77 			      SECKEYPublicKey  *inPubKey)
    78 {
    79     SECAlgorithmID *algID;
    80     SECOidTag       tag;
    81     SECStatus       rv;
    82     void           *mark;
    84     mark = PORT_ArenaMark(poolp);
    85     algID = PORT_ArenaZNew(poolp, SECAlgorithmID);
    86     if (algID == NULL) {
    87         goto loser;
    88     }
    89     tag = crmf_get_key_sign_tag(inPubKey);
    90     if (tag == SEC_OID_UNKNOWN) {
    91         goto loser;
    92     }
    93     rv = SECOID_SetAlgorithmID(poolp, algID, tag, NULL);
    94     if (rv != SECSuccess) {
    95         goto loser;
    96     }
    97     PORT_ArenaUnmark(poolp, mark);
    98     return algID;
    99  loser:
   100     PORT_ArenaRelease(poolp, mark);
   101     return NULL;
   102 }
   104 static CRMFPOPOSigningKeyInput*
   105 crmf_create_poposigningkeyinput(PLArenaPool *poolp, CERTCertificate *inCert,
   106 				CRMFMACPasswordCallback fn, void *arg)
   107 {
   108   /* PSM isn't going to do this, so we'll fail here for now.*/
   109      return NULL;
   110 }
   112 void
   113 crmf_generic_encoder_callback(void *arg, const char* buf, unsigned long len,
   114 			      int depth, SEC_ASN1EncodingPart data_kind)
   115 {
   116     struct crmfEncoderArg *encoderArg = (struct crmfEncoderArg*)arg;
   117     unsigned char *cursor;
   119    if (encoderArg->buffer->len + len > encoderArg->allocatedLen) {
   120         int newSize = encoderArg->buffer->len+CRMF_DEFAULT_ALLOC_SIZE;
   121         void *dummy = PORT_Realloc(encoderArg->buffer->data, newSize);
   122 	if (dummy == NULL) {
   123 	    /* I really want to return an error code here */
   124 	    PORT_Assert(0);
   125 	    return;
   126 	}
   127 	encoderArg->buffer->data = dummy;
   128 	encoderArg->allocatedLen = newSize;
   129     }
   130     cursor = &(encoderArg->buffer->data[encoderArg->buffer->len]);
   131     PORT_Memcpy (cursor, buf, len);
   132     encoderArg->buffer->len += len;    
   133 }
   135 static SECStatus
   136 crmf_encode_certreq(CRMFCertRequest *inCertReq, SECItem *derDest)
   137 {
   138     struct crmfEncoderArg encoderArg;
   139     SECStatus             rv;
   141     rv = crmf_init_encoder_callback_arg (&encoderArg, derDest);
   142     if (rv != SECSuccess) {
   143         return SECFailure;
   144     }
   145     return SEC_ASN1Encode(inCertReq, CRMFCertRequestTemplate, 
   146 			  crmf_generic_encoder_callback, &encoderArg);
   147 }
   149 static SECStatus
   150 crmf_sign_certreq(PLArenaPool        *poolp,
   151 		  CRMFPOPOSigningKey *crmfSignKey, 
   152 		  CRMFCertRequest    *certReq,
   153 		  SECKEYPrivateKey   *inKey,
   154 		  SECAlgorithmID     *inAlgId)
   155 {
   156     SECItem                      derCertReq = { siBuffer, NULL, 0 };
   157     SECItem                      certReqSig = { siBuffer, NULL, 0 };
   158     SECStatus                    rv = SECSuccess;
   160     rv = crmf_encode_certreq(certReq, &derCertReq);
   161     if (rv != SECSuccess) {
   162        goto loser;
   163     }
   164     rv = SEC_SignData(&certReqSig, derCertReq.data, derCertReq.len,
   165 		      inKey,SECOID_GetAlgorithmTag(inAlgId));
   166     if (rv != SECSuccess) {
   167         goto loser;
   168     }
   170     /* Now make it a part of the POPOSigningKey */
   171     rv = SECITEM_CopyItem(poolp, &(crmfSignKey->signature), &certReqSig);
   172     /* Convert this length to number of bits */
   173     crmfSignKey->signature.len <<= 3; 
   175  loser:
   176     if (derCertReq.data != NULL) {
   177         PORT_Free(derCertReq.data);
   178     }
   179     if (certReqSig.data != NULL) {
   180         PORT_Free(certReqSig.data);
   181     }
   182     return rv;
   183 }
   185 static SECStatus
   186 crmf_create_poposignkey(PLArenaPool             *poolp,
   187 			CRMFCertReqMsg          *inCertReqMsg, 
   188 			CRMFPOPOSigningKeyInput *signKeyInput, 
   189 			SECKEYPrivateKey        *inPrivKey,
   190 			SECAlgorithmID          *inAlgID,
   191 			CRMFPOPOSigningKey      *signKey) 
   192 {
   193     CRMFCertRequest    *certReq;
   194     void               *mark;
   195     PRBool              useSignKeyInput;
   196     SECStatus           rv;
   198     PORT_Assert(inCertReqMsg != NULL && inCertReqMsg->certReq != NULL);
   199     mark = PORT_ArenaMark(poolp);
   200     if (signKey == NULL) {
   201         goto loser;
   202     }
   203     certReq = inCertReqMsg->certReq;
   204     useSignKeyInput = !(CRMF_DoesRequestHaveField(certReq,crmfSubject)  &&
   205 			CRMF_DoesRequestHaveField(certReq,crmfPublicKey));
   207     if (useSignKeyInput) {
   208         goto loser; 
   209     } else {
   210         rv = crmf_sign_certreq(poolp, signKey, certReq,inPrivKey, inAlgID);
   211 	if (rv != SECSuccess) {
   212 	    goto loser;
   213 	}
   214     }
   215     PORT_ArenaUnmark(poolp,mark);
   216     return SECSuccess;
   217  loser:
   218     PORT_ArenaRelease(poolp,mark);
   219     return SECFailure;
   220 }
   222 SECStatus
   223 CRMF_CertReqMsgSetSignaturePOP(CRMFCertReqMsg   *inCertReqMsg,
   224 			       SECKEYPrivateKey *inPrivKey,
   225 			       SECKEYPublicKey  *inPubKey,
   226 			       CERTCertificate  *inCertForInput,
   227 			       CRMFMACPasswordCallback  fn,
   228 			       void                    *arg)
   229 {
   230     SECAlgorithmID  *algID;
   231     PLArenaPool     *poolp;
   232     SECItem          derTemp = {siBuffer, NULL, 0};
   233     void            *mark;
   234     SECStatus        rv;
   235     CRMFPOPOSigningKeyInput *signKeyInput = NULL;
   236     CRMFCertRequest         *certReq;
   237     CRMFProofOfPossession   *pop;
   238     struct crmfEncoderArg    encoderArg;
   240     PORT_Assert(inCertReqMsg != NULL && inCertReqMsg->certReq != NULL &&
   241 		inCertReqMsg->pop == NULL);
   242     certReq = inCertReqMsg->certReq;
   243     if (CRMF_CertReqMsgGetPOPType(inCertReqMsg) != crmfNoPOPChoice || 
   244 	!CRMF_DoesRequestHaveField(certReq, crmfPublicKey)) {
   245         return SECFailure;
   246     } 
   247     poolp = inCertReqMsg->poolp;
   248     mark = PORT_ArenaMark(poolp);
   249     algID = crmf_create_poposignkey_algid(poolp, inPubKey);
   251     if(!CRMF_DoesRequestHaveField(certReq,crmfSubject)) {
   252         signKeyInput = crmf_create_poposigningkeyinput(poolp, inCertForInput,
   253 						       fn, arg);
   254 	if (signKeyInput == NULL) {
   255 	    goto loser;
   256 	}
   257     }
   259     pop = PORT_ArenaZNew(poolp, CRMFProofOfPossession);
   260     if (pop == NULL) {
   261         goto loser;
   262     }
   264     rv = crmf_create_poposignkey(poolp, inCertReqMsg, 
   265 				 signKeyInput, inPrivKey, algID,
   266 				 &(pop->popChoice.signature));
   267     if (rv != SECSuccess) {
   268         goto loser;
   269     }
   271     pop->popUsed = crmfSignature;
   272     pop->popChoice.signature.algorithmIdentifier = algID;
   273     inCertReqMsg->pop = pop;
   275     rv = crmf_init_encoder_callback_arg (&encoderArg, &derTemp);
   276     if (rv != SECSuccess) {
   277         goto loser;
   278     }
   279     rv = SEC_ASN1Encode(&pop->popChoice.signature, 
   280 			CRMFPOPOSigningKeyTemplate,
   281 			crmf_generic_encoder_callback, &encoderArg);
   282     if (rv != SECSuccess) {
   283         goto loser;
   284     }
   285     rv = SECITEM_CopyItem(poolp, &(inCertReqMsg->derPOP), &derTemp);
   286     if (rv != SECSuccess) {
   287         goto loser;
   288     }
   289     PORT_Free (derTemp.data);
   290     PORT_ArenaUnmark(poolp,mark);
   291     return SECSuccess;
   293  loser:
   294     PORT_ArenaRelease(poolp,mark);
   295     if (derTemp.data != NULL) {
   296         PORT_Free(derTemp.data);
   297     }
   298     return SECFailure;
   299 }
   301 static const SEC_ASN1Template*
   302 crmf_get_popoprivkey_subtemplate(CRMFPOPOPrivKey *inPrivKey) 
   303 {
   304     const SEC_ASN1Template *retTemplate = NULL;
   306     switch (inPrivKey->messageChoice) {
   307     case crmfThisMessage:
   308         retTemplate = CRMFThisMessageTemplate;
   309 	break;
   310     case crmfSubsequentMessage:
   311         retTemplate = CRMFSubsequentMessageTemplate;
   312 	break;
   313     case crmfDHMAC:
   314         retTemplate = CRMFDHMACTemplate;
   315 	break;
   316     default:
   317         retTemplate = NULL;
   318     }
   319     return retTemplate;
   320 }
   322 static SECStatus
   323 crmf_encode_popoprivkey(PLArenaPool            *poolp,
   324 			CRMFCertReqMsg         *inCertReqMsg,
   325 			CRMFPOPOPrivKey        *popoPrivKey,
   326 			const SEC_ASN1Template *privKeyTemplate)
   327 {
   328     struct crmfEncoderArg   encoderArg;
   329     SECItem                 derTemp = { siBuffer, NULL, 0 };
   330     SECStatus               rv;
   331     void                   *mark;
   332     const SEC_ASN1Template *subDerTemplate;
   334     mark = PORT_ArenaMark(poolp);
   335     rv = crmf_init_encoder_callback_arg(&encoderArg, &derTemp);
   336     if (rv != SECSuccess) {
   337         goto loser;
   338     }
   339     subDerTemplate = crmf_get_popoprivkey_subtemplate(popoPrivKey);
   340     /* We've got a union, so a pointer to one item is a pointer to 
   341      * all the items in the union.
   342      */
   343     rv = SEC_ASN1Encode(&popoPrivKey->message.thisMessage, 
   344 			subDerTemplate,
   345 			crmf_generic_encoder_callback, &encoderArg);
   346     if (rv != SECSuccess) {
   347         goto loser;
   348     }
   349     if (encoderArg.allocatedLen > derTemp.len+2) {
   350         void *dummy = PORT_Realloc(derTemp.data, derTemp.len+2);
   351 	if (dummy == NULL) {
   352 	    goto loser;
   353 	}
   354 	derTemp.data = dummy;
   355     }
   356     PORT_Memmove(&derTemp.data[2], &derTemp.data[0], derTemp.len);
   357     /* I couldn't figure out how to get the ASN1 encoder to implicitly
   358      * tag an implicitly tagged der blob.  So I'm putting in the outter-
   359      * most tag myself. -javi
   360      */
   361     derTemp.data[0] = (unsigned char)privKeyTemplate->kind;
   362     derTemp.data[1] = (unsigned char)derTemp.len;
   363     derTemp.len += 2;
   364     rv = SECITEM_CopyItem(poolp, &inCertReqMsg->derPOP, &derTemp);
   365     if (rv != SECSuccess) {
   366         goto loser;
   367     }
   368     PORT_Free(derTemp.data);
   369     PORT_ArenaUnmark(poolp, mark);
   370     return SECSuccess;
   371  loser:
   372     PORT_ArenaRelease(poolp, mark);
   373     if (derTemp.data) {
   374         PORT_Free(derTemp.data);
   375     }
   376     return SECFailure;
   377 }
   379 static const SEC_ASN1Template*
   380 crmf_get_template_for_privkey(CRMFPOPChoice inChoice) 
   381 {
   382     switch (inChoice) {
   383     case crmfKeyAgreement:
   384         return CRMFPOPOKeyAgreementTemplate;
   385     case crmfKeyEncipherment:
   386         return CRMFPOPOKeyEnciphermentTemplate;
   387     default:
   388         break;
   389     }
   390     return NULL;
   391 }
   393 static SECStatus
   394 crmf_add_privkey_thismessage(CRMFCertReqMsg *inCertReqMsg, SECItem *encPrivKey,
   395 			     CRMFPOPChoice inChoice)
   396 {
   397     PLArenaPool           *poolp;
   398     void                  *mark;
   399     CRMFPOPOPrivKey       *popoPrivKey;
   400     CRMFProofOfPossession *pop;
   401     SECStatus              rv;
   403     PORT_Assert(inCertReqMsg != NULL && encPrivKey != NULL);
   404     poolp = inCertReqMsg->poolp;
   405     mark = PORT_ArenaMark(poolp);
   406     pop = PORT_ArenaZNew(poolp, CRMFProofOfPossession);
   407     if (pop == NULL) {
   408         goto loser;
   409     }
   410     pop->popUsed = inChoice;
   411     /* popChoice is a union, so getting a pointer to one
   412      * field gives me a pointer to the other fields as
   413      * well.  This in essence points to both 
   414      * pop->popChoice.keyEncipherment and
   415      * pop->popChoice.keyAgreement
   416      */
   417     popoPrivKey = &pop->popChoice.keyEncipherment;
   419     rv = SECITEM_CopyItem(poolp, &(popoPrivKey->message.thisMessage),
   420 			  encPrivKey);
   421     if (rv != SECSuccess) {
   422         goto loser;
   423     }
   424     popoPrivKey->message.thisMessage.len <<= 3;
   425     popoPrivKey->messageChoice = crmfThisMessage;
   426     inCertReqMsg->pop = pop;
   427     rv = crmf_encode_popoprivkey(poolp, inCertReqMsg, popoPrivKey,
   428 				 crmf_get_template_for_privkey(inChoice));
   429     if (rv != SECSuccess) {
   430         goto loser;
   431     }
   432     PORT_ArenaUnmark(poolp, mark);
   433     return SECSuccess;
   435  loser:
   436     PORT_ArenaRelease(poolp, mark);
   437     return SECFailure;
   438 }
   440 static SECStatus
   441 crmf_add_privkey_dhmac(CRMFCertReqMsg *inCertReqMsg, SECItem *dhmac,
   442                              CRMFPOPChoice inChoice)
   443 {
   444     PLArenaPool           *poolp;
   445     void                  *mark;
   446     CRMFPOPOPrivKey       *popoPrivKey;
   447     CRMFProofOfPossession *pop;
   448     SECStatus              rv;
   450     PORT_Assert(inCertReqMsg != NULL && dhmac != NULL);
   451     poolp = inCertReqMsg->poolp;
   452     mark = PORT_ArenaMark(poolp);
   453     pop = PORT_ArenaZNew(poolp, CRMFProofOfPossession);
   454     if (pop == NULL) {
   455         goto loser;
   456     }
   457     pop->popUsed = inChoice;
   458     popoPrivKey = &pop->popChoice.keyAgreement;
   460     rv = SECITEM_CopyItem(poolp, &(popoPrivKey->message.dhMAC),
   461                           dhmac);
   462     if (rv != SECSuccess) {
   463         goto loser;
   464     }
   465     popoPrivKey->message.dhMAC.len <<= 3;
   466     popoPrivKey->messageChoice = crmfDHMAC;
   467     inCertReqMsg->pop = pop;
   468     rv = crmf_encode_popoprivkey(poolp, inCertReqMsg, popoPrivKey,
   469                                  crmf_get_template_for_privkey(inChoice));
   470     if (rv != SECSuccess) {
   471         goto loser;
   472     }
   473     PORT_ArenaUnmark(poolp, mark);
   474     return SECSuccess;
   476  loser:
   477     PORT_ArenaRelease(poolp, mark);
   478     return SECFailure;
   479 }
   481 static SECStatus
   482 crmf_add_privkey_subseqmessage(CRMFCertReqMsg        *inCertReqMsg,
   483 			       CRMFSubseqMessOptions  subsequentMessage,
   484 			       CRMFPOPChoice          inChoice)
   485 {
   486     void                  *mark;
   487     PLArenaPool           *poolp;
   488     CRMFProofOfPossession *pop;
   489     CRMFPOPOPrivKey       *popoPrivKey;
   490     SECStatus              rv;
   491     const SEC_ASN1Template *privKeyTemplate;
   493     if (subsequentMessage == crmfNoSubseqMess) {
   494         return SECFailure;
   495     }
   496     poolp = inCertReqMsg->poolp;
   497     mark = PORT_ArenaMark(poolp);
   498     pop = PORT_ArenaZNew(poolp, CRMFProofOfPossession);
   499     if (pop == NULL) {
   500         goto loser;
   501     }
   503     pop->popUsed = inChoice;
   504     /* 
   505      * We have a union, so a pointer to one member of the union
   506      * is also a member to another member of that same union.
   507      */
   508     popoPrivKey = &pop->popChoice.keyEncipherment;
   510     switch (subsequentMessage) {
   511     case crmfEncrCert:
   512         rv = crmf_encode_integer(poolp, 
   513 				 &(popoPrivKey->message.subsequentMessage),
   514 				 0);
   515 	break;
   516     case crmfChallengeResp:
   517         rv = crmf_encode_integer(poolp,
   518 				 &(popoPrivKey->message.subsequentMessage),
   519 				 1);
   520 	break;
   521     default:
   522         goto loser;
   523     }
   524     if (rv != SECSuccess) {
   525         goto loser;
   526     }
   527     popoPrivKey->messageChoice = crmfSubsequentMessage;
   528     privKeyTemplate = crmf_get_template_for_privkey(inChoice);
   529     inCertReqMsg->pop = pop;
   530     rv = crmf_encode_popoprivkey(poolp, inCertReqMsg, popoPrivKey,
   531 				 privKeyTemplate);
   533     if (rv != SECSuccess) {
   534         goto loser;
   535     }
   536     PORT_ArenaUnmark(poolp, mark);
   537     return SECSuccess;
   538  loser:
   539     PORT_ArenaRelease(poolp, mark);
   540     return SECFailure;
   541 }
   543 SECStatus 
   544 CRMF_CertReqMsgSetKeyEnciphermentPOP(CRMFCertReqMsg        *inCertReqMsg,
   545 				     CRMFPOPOPrivKeyChoice  inKeyChoice,
   546 				     CRMFSubseqMessOptions  subseqMess,
   547 				     SECItem               *encPrivKey)
   548 {
   549     SECStatus rv;
   551     PORT_Assert(inCertReqMsg != NULL && inCertReqMsg->pop == NULL);
   552     if (CRMF_CertReqMsgGetPOPType(inCertReqMsg) != crmfNoPOPChoice) {
   553         return SECFailure;
   554     }
   555     switch (inKeyChoice) {    
   556     case crmfThisMessage:
   557         rv = crmf_add_privkey_thismessage(inCertReqMsg, encPrivKey,
   558 					  crmfKeyEncipherment);
   559 	break;
   560     case crmfSubsequentMessage:
   561         rv = crmf_add_privkey_subseqmessage(inCertReqMsg, subseqMess, 
   562 					    crmfKeyEncipherment);
   563         break;
   564     case crmfDHMAC:
   565     default:
   566         rv = SECFailure;
   567     }
   568     return rv;
   569 }
   571 SECStatus 
   572 CRMF_CertReqMsgSetKeyAgreementPOP (CRMFCertReqMsg        *inCertReqMsg,
   573 				   CRMFPOPOPrivKeyChoice  inKeyChoice,
   574 				   CRMFSubseqMessOptions  subseqMess,
   575 				   SECItem               *encPrivKey)
   576 {
   577     SECStatus rv;
   579     PORT_Assert(inCertReqMsg != NULL && inCertReqMsg->pop == NULL);
   580     switch (inKeyChoice) {    
   581     case crmfThisMessage:
   582         rv = crmf_add_privkey_thismessage(inCertReqMsg, encPrivKey,
   583 					  crmfKeyAgreement);
   584 	break;
   585     case crmfSubsequentMessage:
   586         rv = crmf_add_privkey_subseqmessage(inCertReqMsg, subseqMess, 
   587 					    crmfKeyAgreement);
   588 	break;
   589     case crmfDHMAC:
   590         /* In this case encPrivKey should be the calculated dhMac
   591          * as specified in RFC 2511 */
   592         rv = crmf_add_privkey_dhmac(inCertReqMsg, encPrivKey,
   593                                     crmfKeyAgreement);
   594         break;
   595     default:
   596         rv = SECFailure;
   597     }
   598     return rv;
   599 }

mercurial