Notary
Contents
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 |
|
Password protect the certificate databases | High |
|
Recreate the signed archieve | High |
|
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 |
|
Import Certificates | Medium |
|
Blog about this; promote it | High | This project is worthless without promotion
|
Differentiate between CA certificates and signing certificates | Done / Low |
|
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 :
- Cache your certificates using nsscertcache
- Create an nsCertTree and loadCertsFromCache (In my situation, I passed nsIX509Cert.CA_CERT for Certificate Authorities)
- 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
- public keys from Certificate Authorities
- 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 :
- Use PSM
- Do some C++ to work with NSS' API (can I do this?) and build on top of PSM
- 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 :
- You know what XPCOM is
- You have experience in Javascript/C++
- 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) :
- The interface - This is how javascript sees your code. Javascript has no knowledge of the implementation, it just knows what that implementation needs.
- The implementation - the code that does what you want
- 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.