Notary

From MozillaWiki
Jump to: navigation, search

Notary

A XULRunner application for signing extensions.

Planned

  • Allow self-signed certificates. This could be for people who may choose not to use AMO. Though it doesn't really serve any practical purpose.
  • Create certificate requests? (I don't know what this is exactly. I only came across it. I have not been able to create one yet). I think this could be very useful if Mozilla can find an arrangement with a certificate authority.
  • I think this should be more of an application rather than a wizard. Maybe include information about a certificate and certificate authorities (this will probably be more/less the same you get when open up the certificate manager in firefox.)

Questions that get answered in time

  • How do we load certificates. From a file? From a website (can you do it from a website?)? I have only used self-signed certificates, and those usually come from files.
  • How should this be related to Mozilla products (Firefox/thunderbird). From my understanding, Firefox and Thunderbird have different certificate files, for different profiles. Should we just use Firefox's? Should this be for a particular profile? Maybe I am not understanding this correctly.

Hurdles

  • I am finding it difficult to get information on loading certificates. I think the function I am using is unable to do what I expect it to do. That is nsIX509CertDB doesn't actually handle databases (eg. cert8.db in firefox), but rather many individual certificates.
    While trying to find answers to whether nsIX509CertDB can handle the cert8.db file, I got the following response :

    “cesar: there are probably fewer than 3 people who might be able to answer your question w/o reading the sources. and they probably areds”

    While reading the source, it seems that *.db files aren't really accepted, but rather .crt and .cert (and more) files. So it looks like it only accepts individual files which it compiles into a database.
  • Learning about security and how it works around FF should give me a better understanding of how to better accomplish my goals. I am not a security expert, just paranoid :)
  • Even though NSS has several public functions, none of them seem to help me. And only a few of them are documented, and its out of date. I have to rely on what has been done (eg. using the command line tools) to figure out how it ought to be done.
  • This project seems like more work then it should be.

Timeline

Task Priority Status
create certificate requests High
  • This is required if people actually want certificates. The process is not very hard on the command line. Still need to figure out the appropriate extension of the request and the receiving file. I am going to make the request in ascii mode. No particular reason for this. Maybe I should allow it either way.
Password protect the certificate databases High
  • So it appears that there is a function in the NSS library that will do this. Which is great news. The problem is that it asks you to enter a new password in the command line, which is useless in a GUI.
  • I believe this is required. If you try and sign using a certificate in a database that is not password protected, you get an authentication error.
  • This is bloody work, because signtool doesn't do this. certutil does.
Recreate the signed archieve High
  • One file has to be the first file on the .xpi file
  • Should be able to use zipwriter, but it is going under a rewrite right now (see bug 379633)
List all known certificate authorities Done / Low Done. See notes. This was done first, even though it is low priority.
Sign a archieve Done / High
  • ok, it works. Leave me alone. The tutorial has yet to be finished. Every time I try to finish I cringe at the thought of what was done, tell myself "No one should have to do *that*.", and stop.
  • I have managed to get the META-INF folder and sub-files created. However, it is very messy and requires the NSS library to be built. I am going to make a small tutorial to guide some people through the process.
  • I have finally created an XPCOM object and used it from javascript. I know where the manifest function is. So it should be a matter of C&P, but where have I heard that before?
  • I am looking into signtool to see how it does it.
  • It looks like the only way I can do this is to make my own XPCOM objects in C++. This makes me sad (see below)
  • signtool doesn't seem to use NSS lib functions. Directly at least. It seems to be using its own functions to create the META-INF and subfiles. These translates into a lot more work for me.
  • Another lead was into how thunderbird signs emails and use that as a guide. So checking up on that
Import Certificates Medium
  • Pippki doesn't really do this. While it will show object signing certificates, it is asking for something else when you try to import it. I'm going to have to look into this more.
  • I am using pippki (what firefox uses to handle certificates) to do the work for me, since it has already been done.
  • For self-signed certificates
  • I think this is the intention of nsIX509CertDB
Blog about this; promote it High This project is worthless without promotion
  • I started a blog to keep that of this as well as other projects
Differentiate between CA certificates and signing certificates Done / Low
  • pippki does this already.
  • There is a function that checks for it.

More to come later.

Notes

This is stuff that I write down because it took me many wasted hours to get it.

Regarding importing/loading Certificates into XULRunner

File What is this
secmod.db PKCS #11 module information (I think this is hardware related. Hardware sucks, so forget this.)
key3.db keys database (whatever this means)
cert8.db certificates (.crt files?), This is a Berkley DB file according to the file command
  • Unlike the name suggest, nsIX509CertDB.importCertsFromFile() is not for importing Certs from a Database file. It is rather for importing a single CA certificate (stupid plural) from a file. XPCShell will crash and burn even if you are loading a valid certificate. XULRunner will not however. Important note to keep in mind while developing.
  • It is also good to note that cert8.db, key3.db, and secmod.db do not exist until you use the function for the first time. XULRunner 1.8.1.3 does not create these files when creating a profile. You can copy over *.db certificate files from a firefox directory instead.
  • The latest trunk version at the time of writing (1.9a I guess. 2007-05-30) do create *.db files. However, despite being 64/128/128 KB in size, there is nothing in them. So when you first load notary, there is 0 rows. I created a test certificate and put it in, and that seemed to load it. You cannot copy over certificates from firefox. I do not know how to fix this.
  • Importing certificates using nsIX509CertDB.importCertsFromFile() saves automatically.

Regarding Certificates in trees

I thought I could get away with empty <tree></tree> and loading it similar to how the browser does it. But this needs more work.

The process goes something like this :

  1. Cache your certificates using nsscertcache
  2. Create an nsCertTree and loadCertsFromCache (In my situation, I passed nsIX509Cert.CA_CERT for Certificate Authorities)
  3. Take the XUL tree object, grab treeBoxObject.view, and set it to your nsCertTree that you created above.

This, at minimum requires tree, treecol, and an empty treechildren. It will fill up with 100+ rows of Certificate authorities. Which is correct, but there all blank.

Once again, mxr to the rescue. It seems that your treecol need very specific ID's for this to work, which isn't documented as far as I know. These are :

ID What is this
certcol Certificate Name
tokencol Security Device

I think those are the only two. So there should be two cols, with those ids. The tree will show two types of certificates

  1. public keys from Certificate Authorities
  2. certificates used for signing

I think it would be nice to differentiate between the two. I don't think there is an automated way to differentiate them yet.

ZIP files

The zipreader is too basic for what we need. Thankfully, someone took the initiative and wrote a zipwriter, which I am hoping to use. You need the latest version of XULRunner (trunk build, or the next release which is 1.9 to the best of my knowledge) to build it, not 1.8.*; good luck!

XPCOM and NSS

XPCOM has some limitations right now in regards to security and nss. While I have been able to display and retrieve CA certificates, that has only been a small part of the process, and perhaps the easiest part as well. The problem I will be facing is signing an xpi, in which there are no XPCOM components to create the zigbert.sf or sign the extension.

There are a few possible workarounds, starting with most desirable :

  1. Use PSM
  2. Do some C++ to work with NSS' API (can I do this?) and build on top of PSM
  3. use nsIProcess to call each tool

The last one being probably the least amount of work, but where's the fun in that? PSM documentation also scattered. Some parts seem to be in XULPlanet.

Public Key Encryption

Maybe this doesn't belong here, but whatever. Public key encryption, as I found out today can work two ways. The method I learned was how users send encrypted information to websites. It turns out that public key encryption can work the other way too. The private key can be used to encrypt the data (which I knew as well), and the public key can be used to decrypt. This is how certificates work. The CA uses their private key to encrypt the data and openly releases their public key. The browser uses the public key to decrypt. Thankfully I came across a very.. thorough explanation of public key cryptography.

The Hello World of XPCOM

There are a few good resources, and this is not to be a replacement for an actual complete guide. Rather, this is to complement the guide, and provide extra information that the guide..lacks. I found Mark Finkle's blog post to almost be what I wanted. But decided to add my own bits.

I assume :

  1. You know what XPCOM is
  2. You have experience in Javascript/C++
  3. You know how to access XPCOM objects from Javascript

I am new to XPCOM, and I no doubt misunderstand something and communicating the wrong information to others.

In order to write a C++ XPCOM component and use it in javascript, you must have at least three things (yay redundancy) :

  1. The interface - This is how javascript sees your code. Javascript has no knowledge of the implementation, it just knows what that implementation needs.
  2. The implementation - the code that does what you want
  3. A factory that creates the object.

Lets start with the interface. The interface is not written in C++ as one might expect. It written in some obscure language known as IDL. Here is an example of my idl file :

iTest.idl

     1 #include "nsISupports.idl"
     2 
     3 [scriptable, uuid(7696adf6-147f-11dc-8314-0800200c9a66)]
     4 interface iTest : nsISupports {
     5     void hello(out ACString world);
     6 };

As you can see, it looks like C++ syntactically, but don't be fooled. You can't overload functions using IDL, and there are a few other limitations as well. But let me break it down line-by-line.

Line 1 : we're including nsISupports, which the interface implements. All objects must implement nsISupports as far as I know. It handles reference counting, and a ton of other garbage. You'll notice that the outs a string rather than as a return value. This was a novice mistake. It is easier just to return a string.

Line 3 : I guess scriptable means we can use it from a scripting language (in this case Javascript). If you look at xulplanet's XPCOM reference, you sometimes see functions with [noscript]. A uuid is a unique identifier. On most *nix machines, you can get it from uuidgen command.

Line 4 : Here we name the class, and tell it what other interfaces it implements.

Line 5 : My function. It takes in an object from Javascript (but doesn't use it. I heard you cannot use out like inout) and changes it. The out keyword tells the IDL compiler to change it to a non-const reference. You can see that I didn't return a string, but rather passed in one. Its strange to explain, maybe it would be more clear as we progress.

Now we need to compile using xpidl. This is found in your xulrunner/dist/bin directory. You'll also need to include the directory which contains nsISupports, that is in xulrunner/dist/sdk/include. We need to create two files, a header file (.h) which is used in our code, and a typelib file (.xpt) that is used by XULRunner.

Here is the somewhat the form it takes

     xpidl -m header -w -v -I path/to/include iTest.idl
     xpidl -m typelib -w -v -I path/to/include iTest.idl

That will create iTest.xpt and iTest.h

Wow this is going to be long.

Ok. The implementation. Now you need to define a class that implements the iTest interface. I will put the factory and the implementation in one file, mainly for simplicity.

     1 #include "iTest.h"
     2 #include "nsIGenericFactory.h"
     3 #include "nsStringAPI.h"
     4 #include "nsEmbedString.h"

Line 1 : The IDL header in .h form

Line 2 : We need a factory to handle the creation of the object. This header file gives us access to a macro that we need

Line 3 : This allows us to take in Strings. We should use this rather than char *.

Line 4 : This isn't needed, ignore this.

     6 #define BOO_CID \
     7     { 0x4842ae27, 0x4361, 0x4549, \
     8     { 0x97, 0x23, 0xb4, 0xf6, 0xb1, 0x47, 0x6a, 0xc9 } }

Line 6-8 : This is the CID of our class. I don't know why I put it here. I only use it in one place. The CID is a universally unique identifier (UUID).

    10 class Boo : public iTest {
    11 public :
    12     Boo();
    13     NS_DECL_ISUPPORTS
    14 
    15     NS_IMETHOD Hello(nsACString & world);
    16 };

Our class definition, this should be obvious. We implement our interface. The only thing that isn't obvious is line 13 and 15.

Line 13 : This macro defines (I may be wrong here), functionality of nsISupports. For example, reference counter and addref() method.

Line 14 : All methods in the declaration should have a return value of NS_IMETHOD. See the iTest.h file for a sample implementation. We also use nsACString, which is an abstract class. You want to avoid concrete implementations when passing variables.

    18 Boo::Boo() {
    19     NS_INIT_ISUPPORTS();
    20 }

Line 19 : This isn't needed anymore

    23 NS_IMETHODIMP Boo::Hello(nsACString & world) {
    24     world.Append("Hello");
    25     return NS_OK;
    26 }

Line 23 : The declaration uses NS_IMETHOD, and the implementation uses NS_IMETHODIMP. This is just a rule, don't ask.

Line 24 : We append "Hello" to the parameter. I am told that we should use .Assign() rather than .Append() because .Append() relies on an existing value (remember its an out parameter).

Line 25 : We need to return whether we were successful or not.

Line 37-46 : Commented out

    47 NS_IMPL_ISUPPORTS2(Boo, nsISupports, iTest);

Line 47 : Bascially, it tells XPCOM what classes class "Boo" supports. You'll notice that its NS_IMPL_ISUPPORTSn, where n is the number of interfaces you implement. If you don't include this, and you try to .createInstance() in your javascript, you will likely hit an expection.

    48 NS_GENERIC_FACTORY_CONSTRUCTOR(Boo)

Line 48 : Magically creates a constructor object. It will make slightly more sense below.

    50 static const nsModuleComponentInfo components[] =
    51 {
    52     { "Boo module",
    53       BOO_CID,
    54       "@cesar.org/boo;1",
    55       BooConstructor
    56     }
    57 };

I'm not quite sure how to explain this because I'm not sure what its for.

Line 52 : "Boo module" is just a name as far as I can tell. I haven't seen it used.

Line 53 : BOO_CID is the contract ID. This is the only placed used as far as I know.

Line 54 : "@cesar.org/boo;1" is the CID. This is what we use in Components.classes["@cesar.org/boo;1"]

Line 55 : BooConstructor is magic. It is created by the NS_GENERIC_FACTORY_CONSTRUCTOR

    59 NS_IMPL_NSGETMODULE(haheRobbleRobble, components)

Line 59 : Turns our hard work into a module named... "haheRobbleRobble". Don't ask

Now you need to compile this cpp file (lets call it hello.cpp) into a shared library. For windows/mac, your on your own. In linux, your in luck!

     g++ -fPIC -c -g hello.cpp -o hello.o -I path/to/include 

creates an object file in a format that can be put into a shared library.

     g++ -shared -o libhello.so hello.o [ -z defs -W1 ]

Actually creates the shared library (I heard the library must start with lib). Its also a good idea to include -z defs and -W1, since they can save you a ton of trouble if you forgot to link something. Although, when I did it, I got a ton of undefined references :)

Ok, we have an xpt and a .so file. Now we just need to have XULRunner find these and it will load them up so we can start using them. I am using the trunk version of XULRunner, your results may vary.

First copy the library to your app/components directory. If you don't have it, create it. Then copy the .xpt file to your xulrunner/components directory. I have heard that it should go in app/components as well, but I found that it wouldn't register.

Now either delete your application profile ~/.myapp, or increment the BuildID in your application.ini file. Either method should start loading components (in our case, haheRobbleRobble). A neat way to find out if your component/interface exist is to do the following in your javascript code

     for (i in Components.[classes|interfaces]) { dump(i + "\n"); }

Which prints a long list of items. Pipe to less to find your component/interface.

Now you can access your XPCOM object like this

     var x = new Object();
     Boo.Hello(x);
     alert(x.value);

Thanks to Christian Biesinger for his comments and input.

Feedback

I appreciate any comments/suggestions/criticisms. But please post anything in the discussion page.