Closed Bug 1034854 Opened 10 years ago Closed 10 years ago

Add support for ECDSA to WebCrypto API

Categories

(Core :: DOM: Security, defect)

defect
Not set
normal

Tracking

()

RESOLVED FIXED
mozilla36
Tracking Status
firefox35 --- fixed
firefox36 --- fixed

People

(Reporter: rbarnes, Assigned: rbarnes)

References

(Blocks 1 open bug)

Details

(Keywords: dev-doc-needed)

Attachments

(1 file, 2 obsolete files)

      No description provided.
Assignee: nobody → rlb
Attached patch bug-1034854.patch (obsolete) — Splinter Review
This is going to want to wait for Bug 1037892 and Bug 1057161 to land, but I think it's mostly complete, modulo rebasing on top of those.
Status: NEW → ASSIGNED
Depends on: 1075686
Depends on: 1057161
Attached patch bug-1034854.1.patch (obsolete) — Splinter Review
This patch rebases the earlier WIP on top of Bug 1037892 and Bug 1057161.  The main interesting things are:
(1) the PublicKeyValid() method, which tests for a public key's validity by using the check added in Bug 1057161, and 
(2) the transformation of RsassaPkcs1Task to a more generic AsymmetricVerifyTask, adding ECDSA and simplifying the NSS verification calls.
Attachment #8484213 - Attachment is obsolete: true
Attachment #8499102 - Flags: review?(ttaubert)
Attachment #8499102 - Flags: review?(dkeeler)
Comment on attachment 8499102 [details] [diff] [review]
bug-1034854.1.patch

Review of attachment 8499102 [details] [diff] [review]:
-----------------------------------------------------------------

NSS-related things look good to me.

::: dom/crypto/CryptoKey.cpp
@@ +898,5 @@
> +  if (!slot.get()) {
> +    return false;
> +  }
> +
> +  CK_OBJECT_HANDLE id = PK11_ImportPublicKey(slot, aPubKey, PR_FALSE);

Maybe we should add a comment that this code assumes that if calling PK11_ImportPublicKey and checking for a valid handle succeeds, the key is valid.

::: dom/crypto/WebCryptoTask.cpp
@@ +64,5 @@
>    TA_SHA_384         = 17,
>    TA_SHA_512         = 18,
>    // Later additions
>    TA_AES_KW          = 19,
> +  TA_ECDSA           = 20

nit: include the trailing ',' so we don't have to touch this line if/when we add something else

@@ +997,5 @@
> +        case CKM_SHA384:
> +          mOidTag = SEC_OID_PKCS1_SHA384_WITH_RSA_ENCRYPTION; break;
> +        case CKM_SHA512:
> +          mOidTag = SEC_OID_PKCS1_SHA512_WITH_RSA_ENCRYPTION; break;
> +        default: {

are the braces necessary here?

@@ +1037,5 @@
> +        case CKM_SHA384:
> +          mOidTag = SEC_OID_ANSIX962_ECDSA_SHA384_SIGNATURE; break;
> +        case CKM_SHA512:
> +          mOidTag = SEC_OID_ANSIX962_ECDSA_SHA512_SIGNATURE; break;
> +        default: {

same with the braces

@@ +1069,5 @@
>    virtual nsresult DoCrypto() MOZ_OVERRIDE
>    {
>      nsresult rv;
>      if (mSign) {
>        ScopedSECItem signature((SECItem*) PORT_Alloc(sizeof(SECItem)));

we should probably null-check signature here

@@ +1081,5 @@
> +
> +      if (mEcdsa) {
> +        // DER-decode the signature
> +        int signatureLength = PK11_SignatureLen(mPrivKey);
> +        ScopedSECItem rawSignature(DSAU_DecodeDerSigToLen(signature.get(),

Don't forget to add this and the other DSAU_ function to config/external/nss/nss.def

@@ +1102,5 @@
> +        if (!rawSignature.get()) {
> +          return NS_ERROR_DOM_UNKNOWN_ERR;
> +        }
> +
> +        signature = (SECItem*) PORT_Alloc(sizeof(SECItem));

null-check signature here

::: dom/crypto/test/test_WebCrypto_ECDSA.html
@@ +51,5 @@
> +);
> +
> +// -----------------------------------------------------------------------------
> +TestArray.addTest(
> +  "ECDSA JWK import and verify a known-good signature",

Would it be worthwhile to also test rejecting a known-tampered signature?

@@ +55,5 @@
> +  "ECDSA JWK import and verify a known-good signature",
> +  function() {
> +    var that = this;
> +    var alg = { name: "ECDSA", namedCurve: "P-256", hash: "SHA-256" };
> +    

nit: trailing whitespace

::: dom/webidl/SubtleCrypto.webidl
@@ +89,5 @@
>  
> +dictionary EcdsaParams : Algorithm {
> +  required AlgorithmIdentifier hash;
> +};
> +

nit: unnecessary extra blank line
Attachment #8499102 - Flags: review?(dkeeler) → review+
Comment on attachment 8499102 [details] [diff] [review]
bug-1034854.1.patch

Review of attachment 8499102 [details] [diff] [review]:
-----------------------------------------------------------------

::: dom/crypto/CryptoKey.cpp
@@ +820,5 @@
>      if (!arena) {
>        return nullptr;
>      }
>  
> +    ScopedSECKEYPublicKey key(PORT_ArenaZNew(arena, SECKEYPublicKey));

That seems magic, I assume this is only used to free key->pkcs11Slot after calling PublicKeyValid()? If PublicKeyValid() succeeds then key->pkcs11Slot always != NULL. So I think we'd only need this if PK11_DestroyObject() fails? It doesn't look like key->pkcs11Slot is set to NULL when PK11_DestroyObject() succeeds? Would we double-free in that case?

How about copying the key param to a ScopedSECKEYPublicKey only in PublicKeyValid()? So we wouldn't confuse readers here and would free pkcs11Slot a little closer to where it's allocated?

Maybe I'm getting things completely wrong here but in either case maybe add a comment as to why we're using a scoped public key here when allocating in a scoped arena?
Attachment #8499102 - Flags: review?(ttaubert) → review+
(In reply to Tim Taubert [:ttaubert] from comment #4)
> Comment on attachment 8499102 [details] [diff] [review]
> bug-1034854.1.patch
> 
> Review of attachment 8499102 [details] [diff] [review]:
> -----------------------------------------------------------------
> 
> ::: dom/crypto/CryptoKey.cpp
> @@ +820,5 @@
> >      if (!arena) {
> >        return nullptr;
> >      }
> >  
> > +    ScopedSECKEYPublicKey key(PORT_ArenaZNew(arena, SECKEYPublicKey));
> 
> That seems magic, I assume this is only used to free key->pkcs11Slot after
> calling PublicKeyValid()? If PublicKeyValid() succeeds then key->pkcs11Slot
> always != NULL. So I think we'd only need this if PK11_DestroyObject()
> fails? It doesn't look like key->pkcs11Slot is set to NULL when
> PK11_DestroyObject() succeeds? Would we double-free in that case?
> 
> How about copying the key param to a ScopedSECKEYPublicKey only in
> PublicKeyValid()? So we wouldn't confuse readers here and would free
> pkcs11Slot a little closer to where it's allocated?
> 
> Maybe I'm getting things completely wrong here but in either case maybe add
> a comment as to why we're using a scoped public key here when allocating in
> a scoped arena?

I think this was just a result of me generally switching unscoped pointers to scoped ones.  But that seems unnecessary in this case because the pointer is pointing to arena-managed memory, and the arena is scoped.  I'll revert it to unscoped.
bz: Could you please review the WebIDL so that the build scripts will let me land this?

Happy try run: 
https://treeherder.mozilla.org/ui/#/jobs?repo=try&revision=b76a1f13c8ab
Attachment #8499102 - Attachment is obsolete: true
Attachment #8504520 - Flags: review?(bzbarsky)
Comment on attachment 8504520 [details] [diff] [review]
bug-1034854.2.patch r=ttaubert,dkeeler,bz

r=me on the IDL bits
Attachment #8504520 - Flags: review?(bzbarsky) → review+
Attachment #8504520 - Attachment description: bug-1034854.2.patch → bug-1034854.2.patch r=ttaubert,dkeeler,bz
Keywords: checkin-needed
https://hg.mozilla.org/mozilla-central/rev/c044fae78ad8
Status: ASSIGNED → RESOLVED
Closed: 10 years ago
Resolution: --- → FIXED
Target Milestone: --- → mozilla36
Comment on attachment 8504520 [details] [diff] [review]
bug-1034854.2.patch r=ttaubert,dkeeler,bz

Approval Request Comment
[Feature/regressing bug #]: WebCrypto (865789)
[User impact if declined]: ECDSA will be unavailable in WebCrypto
[Describe test coverage new/current, TBPL]: Mochitests in patch
[Risks and why]: Low risk, only affects WebCrypto
[String/UUID change made/needed]: None
Attachment #8504520 - Flags: approval-mozilla-aurora?
Attachment #8504520 - Flags: approval-mozilla-aurora? → approval-mozilla-aurora+
Depends on: 1158296
You need to log in before you can comment on or make changes to this bug.