Hi Beck, hi Karsten.
First I'd like to make sure that I'm clear on what we're trying to do. The javadocs for VerifyDescriptors [1] says that it...
Verify server descriptors using the contained signing key. Verify that
- a contained fingerprint is actually a hash of the signing key and
- a router signature was created using the signing key.
Verify consensuses using the separate certs. Verify that
- the fingerprint in a cert is actually a hash of the identity key,
- a cert was signed using the identity key,
- a consensus was signed using the signing key from the cert.
Honestly I'm not yet sure what most of this means. The first #2 is simply checking that the descriptor content can be verified using the router-signature and signing-key, right? If so then this sounds like a good place to start since it's entirely self-contained within the descriptor and just involves implementation and testing of...
https://gitweb.torproject.org/stem.git/blob/HEAD:/stem/descriptor/server_des...
However, I need some suggestions for the choice of Python cryptography API, since I haven't used any before.
Nor have I. At present stem does not have any dependencies outside of python's builtin functions. If we need PyCrypto and it's the best choice then so be it, but be sure to wrap the imports in a try/catch so we only raise an ImportError when executing the function that requires the PyCrypto library.
Cheers! -Damian
[1] https://gitweb.torproject.org/metrics-tasks.git/blob/HEAD:/task-2768/VerifyD...
On 5/6/12 3:36 AM, Damian Johnson wrote:
First I'd like to make sure that I'm clear on what we're trying to do. The javadocs for VerifyDescriptors [1] says that it...
Verify server descriptors using the contained signing key. Verify that
- a contained fingerprint is actually a hash of the signing key and
- a router signature was created using the signing key.
Verify consensuses using the separate certs. Verify that
- the fingerprint in a cert is actually a hash of the identity key,
- a cert was signed using the identity key,
- a consensus was signed using the signing key from the cert.
Honestly I'm not yet sure what most of this means. The first #2 is simply checking that the descriptor content can be verified using the router-signature and signing-key, right?
Yup. But you also need the first #1, or metrics-db could modify server descriptors at will, put in its own key, and re-sign the descriptor with that key, and stem would think everything's valid.
If so then this sounds like a good place to start since it's entirely self-contained within the descriptor and just involves implementation and testing of...
https://gitweb.torproject.org/stem.git/blob/HEAD:/stem/descriptor/server_des...
Sounds good.
For other descriptors than server descriptors, you may want to have a similar method that accepts the signing key. In the case of certs and consensuses you'd have an is_valid() method for certs, a getter in the cert class to obtain the signing key, and an is_valid(signing_key) in the consensus class.
Best, Karsten
On Mon, May 7, 2012 at 5:13 PM, Karsten Loesing karsten@torproject.orgwrote:
On 5/6/12 3:36 AM, Damian Johnson wrote:
First I'd like to make sure that I'm clear on what we're trying to do. The javadocs for VerifyDescriptors [1] says that it...
Verify server descriptors using the contained signing key. Verify that
- a contained fingerprint is actually a hash of the signing key and
- a router signature was created using the signing key.
Verify consensuses using the separate certs. Verify that
- the fingerprint in a cert is actually a hash of the identity key,
- a cert was signed using the identity key,
- a consensus was signed using the signing key from the cert.
Honestly I'm not yet sure what most of this means. The first #2 is simply checking that the descriptor content can be verified using the router-signature and signing-key, right?
Yup. But you also need the first #1, or metrics-db could modify server descriptors at will, put in its own key, and re-sign the descriptor with that key, and stem would think everything's valid.
Then is it possible that metrics-db modifies the fingerprint at the same time so that the first #1 would pass? Any why would metrics-db try to do this at all?
Cheers, Beck
On 5/7/12 7:49 PM, Beck Chen wrote:
On Mon, May 7, 2012 at 5:13 PM, Karsten Loesing karsten@torproject.orgwrote:
On 5/6/12 3:36 AM, Damian Johnson wrote:
First I'd like to make sure that I'm clear on what we're trying to do. The javadocs for VerifyDescriptors [1] says that it...
Verify server descriptors using the contained signing key. Verify that
- a contained fingerprint is actually a hash of the signing key and
- a router signature was created using the signing key.
Verify consensuses using the separate certs. Verify that
- the fingerprint in a cert is actually a hash of the identity key,
- a cert was signed using the identity key,
- a consensus was signed using the signing key from the cert.
Honestly I'm not yet sure what most of this means. The first #2 is simply checking that the descriptor content can be verified using the router-signature and signing-key, right?
Yup. But you also need the first #1, or metrics-db could modify server descriptors at will, put in its own key, and re-sign the descriptor with that key, and stem would think everything's valid.
Then is it possible that metrics-db modifies the fingerprint at the same time so that the first #1 would pass?
Right, but when the fingerprint changes, we consider the descriptor as coming from a different relay. If metrics-db wanted to fake descriptors of a known relay and stem only checked #2, stem would be fooled here. With #1 it wouldn't.
Note that for most analyses you'd start with the consensus and only look at referenced descriptors by their SHA digest, which rules out modifications like the one discussed above. But if there's a method in the server descriptor class to check whether it is valid it should check #1 and #2.
Robert may have a more complete list of things that need to be checked in descriptors of all sorts. I just asked him in #2768 for such a list.
Any why would metrics-db try to do this at all?
Well, it wouldn't. But why trust it when you can check yourself?
Best, Karsten
I observed some inconsistency, if not errors, in the directory server specs [1]:
1. Outline Every authority has a very-secret, long-term "Authority Identity Key". This is stored encrypted and/or offline, and is used to sign "key certificate" documents. Every key certificate contains a medium-term (3-12 months) "authority signing key", that is used by the authority to sign other directory information.
2.1. Router descriptor format
"fingerprint" fingerprint NL [At most once] A fingerprint (a HASH_LEN-byte of asn1 encoded public key, encoded in hex, with a single space after every 4 characters) for this router's identity key. A descriptor is considered invalid (and MUST be rejected) if the fingerprint line does not match the public key.
"signing-key" NL a public key in PEM format [Exactly once] The OR's long-term identity key. It MUST be 1024 bits.
According to the outline, the long-term identity key should be different from the signing key, which changes every 3-12 months. Then why should the signing key become the identity key in the descriptor format, and fingerprint become the hash of the identity key?
Cheers, Beck
[1] https://gitweb.torproject.org/torspec.git?a=blob_plain;hb=HEAD;f=dir-spec.tx...
On 5/8/12, Beck Chen csybeck@gmail.com wrote:
According to the outline, the long-term identity key should be different from the signing key, which changes every 3-12 months. Then why should the signing key become the identity key in the descriptor format, and fingerprint become the hash of the identity key?
The ‘relay identity key’ is not the same as the ‘authority identity key’. The ‘relay identity key’ might also be different from the ‘directory signing key’; I'm not sure about that.
Descriptors contain and are signed with the ‘relay identity key’, and the fingerprint in a descriptor is the hash of the relay identity key.
Robert Ransom
On 5/8/12 4:47 AM, Robert Ransom wrote:
On 5/8/12, Beck Chen csybeck@gmail.com wrote:
According to the outline, the long-term identity key should be different from the signing key, which changes every 3-12 months. Then why should the signing key become the identity key in the descriptor format, and fingerprint become the hash of the identity key?
The ‘relay identity key’ is not the same as the ‘authority identity key’. The ‘relay identity key’ might also be different from the ‘directory signing key’; I'm not sure about that.
Relay identity key and directory signing key are indeed different.
Think of the authority identity key and directory signing key as a different layer on top of stuff that all relays do. When a relay becomes a directory authority, the operator creates an offline authority identity key and uses it to create an online directory signing key. When the directory signing key expires, which usually happens once per year, the operator creates a new one using the authority identity key and uploads it. The relay identity key and authority identity key usually stay the same for a long time.
For example, here are the long-term keys for gabelmoo (from src/or/config.c):
"gabelmoo orport=443 no-v2 " "v3ident=ED03BB616EB2F60BEC80151114BB25CEF515B226 " "212.112.245.170:80 F204 4413 DAC2 E02E 3D6B CF47 35A1 9BCA 1DE9 7281",
The ED03... part is the hash of the authority identity key, the F204... part is the relay identity. The directory signing key is not hard-coded. You can find all directory signing keys here:
https://metrics.torproject.org/data/certs.tar.bz2
Best, Karsten
Hi Karsten & Damian,
I've been trying to port function determineKeyHash() from VerifyDescriptors.java to stem, and it turned out to be more complicated than I thought due to my unfamiliarity to many cryptography terms and standards. I finally figured out a way to do it by using a light-weighted library called Python-RSA, and it solved my problem in no more than 5 lines of code. I realized that I should get better understanding of some basic stuff before I move on.
Here's the python code that verifies the key hash:
import rsa, hashlib pubkey = rsa.PublicKey.load_pkcs1(desc.signing_key) der_encoded = pubkey.save_pkcs1(format = "DER") hashlib.sha1(der_encoded).hexdigest() == desc.fingerprint.lower()
True
1. Specs says signing key is "a public key in PEM format" [1], but what standard does it use? I tried to use M2Crypto, a Python wrapper for OpenSSL, to import this key, but failed. Then I found out that OpenSSL uses X.509 for its public keys, thus M2Crypto only supports X.509 public keys. Then I looked at the Java code for determineKeyHash() and it suggests that the key uses PKCS standard. It seems that PKCS#1 is the final answer, but the javadoc page of PEMReader in BouncyCastle says it can read "OpenSSL PEM encoded streams containing X509 certificates, PKCS8 encoded keys and PKCS7 objects" [2]. So is it PKCS#1 or PKCS#8? And what's the difference?
2. Specs says fingerprint is "a HASH_LEN-byte of asn1 encoded public key, encoded in hex" [1]. But to me, it seems to be "a SHA1 digest of DER encoded public key, encoded in hex". Specifically, is it necessary to specify the length of fingerprint? And ASN1 is a standard with several possible encoding rules, do we always use DER?
I know that's a lot of questions...thanks!
Best, Beck
PS. Are you guys getting email updates from ticket #5810? I'm doubting that you two are not actually CCed since no one is replying... PSS. Do we need to keep Ravi in this conversation? PSSS. Should I wrap up my email to 78 words per line?
[1]. https://gitweb.torproject.org/torspec.git?a=blob_plain;hb=HEAD;f=dir-spec.tx... [2]. http://www.bouncycastle.org/docs/pkixdocs1.5on/org/bouncycastle/openssl/PEMR...
Hi Beck. Unfortunately at this point you know far more about descriptor crypto than me so I won't be much help. A quick search in apt for 'pkcs' only came up with one python result, PyKCS11 (http://www.bit4id.org/trac/pykcs11), which looks to be mostly related to smartcards. Hopefully Karsten will have some suggestions.
PS. Are you guys getting email updates from ticket #5810? I'm doubting that you two are not actually CCed since no one is replying...
Yes. Both Karsten and I are subscribed to tor-bugs@ so we see all trac updates.
PSS. Do we need to keep Ravi in this conversation?
Nope, everyone on this thread is on tor-dev@ already. If you aren't on this list yourself then I'd suggest it. :)
PSSS. Should I wrap up my email to 78 words per line?
Really doesn't matter. Some people care strongly about it, others don't. I'm in the later camp but do it anyway since I think it looks a little nicer and gmail defaults to 80-character wrap when you chose plaintext.
Cheers! -Damian
PS. Are you guys getting email updates from ticket #5810? I'm doubting that you two are not actually CCed since no one is replying...
Yes. Both Karsten and I are subscribed to tor-bugs@ so we see all trac updates.
Oh, and I should add that I didn't reply to your last post because it sounded like you were still investigating this and I didn't have any advice. If you want my input on something then please leave a question directed toward me on the ticket. ;)
It seems that PKCS#1 is the final answer, but
Quick question, if it is PKCS1 that we need then will the pycrypto package do the trick? It looks like PKCS1 support is pretty new for it, but the package itself is generally available by default. See the last comment on... https://bugs.launchpad.net/pycrypto/+bug/502291
Cheers! -Damian
Hi Karsten, hi Beck. Just pushed stem support for extrainfo descriptors (ye gods they have a lot of attributes)...
* Implementation https://gitweb.torproject.org/stem.git/blob/HEAD:/stem/descriptor/extrainfo_...
* Unit Tests https://gitweb.torproject.org/stem.git/blob/HEAD:/test/unit/descriptor/extra...
* Integ Tests https://gitweb.torproject.org/stem.git/blob/HEAD:/test/integ/descriptor/extr...
Barring distractions I plan to next implement network status entities, then start porting over Onionoo. That said, between going oncall for work and the upcoming release of Diablo 3 it might be a little while. :P
Cheers! -Damian
On 5/14/12 2:30 AM, Damian Johnson wrote:
Hi Karsten, hi Beck. Just pushed stem support for extrainfo descriptors (ye gods they have a lot of attributes)...
- Implementation
https://gitweb.torproject.org/stem.git/blob/HEAD:/stem/descriptor/extrainfo_...
- Unit Tests
https://gitweb.torproject.org/stem.git/blob/HEAD:/test/unit/descriptor/extra...
- Integ Tests
https://gitweb.torproject.org/stem.git/blob/HEAD:/test/integ/descriptor/extr...
Barring distractions I plan to next implement network status entities, then start porting over Onionoo. That said, between going oncall for work and the upcoming release of Diablo 3 it might be a little while. :P
Pretty cool!
Very much looking forward to the Python Onionoo! Let me know when/if you want to discuss the design there.
Best, Karsten
Hi Damian,
Quick question, if it is PKCS1 that we need then will the pycrypto package do the trick? It looks like PKCS1 support is pretty new for it, but the package itself is generally available by default.
PyCrypto should do the trick, as described in their API document [1]. But I encounter an error when trying to import the key:
from Crypto.PublicKey import RSA RSA.importKey(my_key)
ValueError: RSA key format is not supported
I asked this on Stack Overflow and someone provides an explanation [2]. It looks plausible, but I'm really confused about the tangled standards here.
Enjoy Diablo 3 :) I'll join you when I go back to USA.
Cheers, Beck
[1]. https://www.dlitz.net/software/pycrypto/api/current/Crypto.PublicKey.RSA-mod... [2]. http://stackoverflow.com/a/10574723/994146
Hi Beck,
I don't have good answers to your questions. To be honest, when I implemented the Java verification code for #2768, I looked for hints in an old Java version of Tor, rewrote that code, updated it for current BouncyCastle versions using their JavaDocs and examples, and tweaked everything until it finally worked. :)
On 5/13/12 9:04 AM, Beck Chen wrote:
- Specs says signing key is "a public key in PEM format" [1], but
what standard does it use? I tried to use M2Crypto, a Python wrapper for OpenSSL, to import this key, but failed. Then I found out that OpenSSL uses X.509 for its public keys, thus M2Crypto only supports X.509 public keys. Then I looked at the Java code for determineKeyHash() and it suggests that the key uses PKCS standard. It seems that PKCS#1 is the final answer, but the javadoc page of PEMReader in BouncyCastle says it can read "OpenSSL PEM encoded streams containing X509 certificates, PKCS8 encoded keys and PKCS7 objects" [2]. So is it PKCS#1 or PKCS#8? And what's the difference?
- Specs says fingerprint is "a HASH_LEN-byte of asn1 encoded public
key, encoded in hex" [1]. But to me, it seems to be "a SHA1 digest of DER encoded public key, encoded in hex". Specifically, is it necessary to specify the length of fingerprint? And ASN1 is a standard with several possible encoding rules, do we always use DER?
I'm cc'ing Nick who would have much better answers to your questions.
Thanks! Karsten
Hi Karsten,
I don't have good answers to your questions. To be honest, when I implemented the Java verification code for #2768, I looked for hints in an old Java version of Tor, rewrote that code, updated it for current BouncyCastle versions using their JavaDocs and examples, and tweaked everything until it finally worked. :)
That's exactly what I'm doing right now :)
I'm cc'ing Nick who would have much better answers to your questions.
Great. Thanks!
Best, Beck
On Mon, May 14, 2012 at 2:04 AM, Karsten Loesing karsten@torproject.org wrote:
Hi Beck,
I don't have good answers to your questions. To be honest, when I implemented the Java verification code for #2768, I looked for hints in an old Java version of Tor, rewrote that code, updated it for current BouncyCastle versions using their JavaDocs and examples, and tweaked everything until it finally worked. :)
On 5/13/12 9:04 AM, Beck Chen wrote:
- Specs says signing key is "a public key in PEM format" [1], but
what standard does it use? I tried to use M2Crypto, a Python wrapper for OpenSSL, to import this key, but failed. Then I found out that OpenSSL uses X.509 for its public keys, thus M2Crypto only supports X.509 public keys. Then I looked at the Java code for determineKeyHash() and it suggests that the key uses PKCS standard. It seems that PKCS#1 is the final answer, but the javadoc page of PEMReader in BouncyCastle says it can read "OpenSSL PEM encoded streams containing X509 certificates, PKCS8 encoded keys and PKCS7 objects" [2]. So is it PKCS#1 or PKCS#8?
In crypto_pk_write_to_string_impl, it's generated via PEM_write_bio_RSAPublicKey, so (according the manpage) that's a PKCS#1 RSAPublicKey structure. Let's hope the mangpage is right.
And what's the difference?
I'd have to read the standards to find out; if the standards don't make that clear, let me know and I can give it a try myself.
- Specs says fingerprint is "a HASH_LEN-byte of asn1 encoded public
key, encoded in hex" [1]. But to me, it seems to be "a SHA1 digest of DER encoded public key, encoded in hex". Specifically, is it necessary to specify the length of fingerprint?
That should indeed say say "a HASH_LEN-byte SHA1 digest of"....
"digest" is the part that's strictly necessary to specify, since I think we say that everything's SHA1 unless somebody says otherwise.
And ASN1 is a standard with several possible encoding rules, do we always use DER?
I believe so.
Hi Beck. Thanks for diving into the descriptor crypto. As you've found it's not an especially easy problem to solve. Just a reminder that if you aren't finding it interesting then there's several other stem related projects that you might find more to your liking.
Cheers! -Damian
Hi Damian,
I was actually on a short trip these days, and just arrived home. I'll restart working on it tomorrow, and I believe there is more I can do.
That said I might join Ravi's work on the general controller class a bit later after I've finished my work on the descriptor crypto.
Best, Beck
Hi Damian & Karsten,
I created ticket #5810 about this and CCed both of you: https://trac.torproject.org/projects/tor/ticket/5810 Just want to make sure you guys are notified.
Beck