options {
edns-udp-size 1460;
};
This has the side-effect of causing TCP retries on large packets, which are often the DNSKEY responses. However, it also causes DNSSEC to work, so overall it's a good thing.
Friday, March 27, 2009
DNSSEC vs Firewall
Saturday, February 28, 2009
Ruby and OpenSSL
I recently had to do some DNSSEC-type (somewhat low-level) cryptography work, and found the seeming lack of Ruby OpenSSL documentation a big pain. I found numerous examples of how OpenSSL is commonly used with PEM-encoded keys, but precious little information on low-level key loading. To save others the trouble of having to dig up some of this, I've collected some short examples of how to do low-level RSA and DSA building from a lower level than most use.
This table summarizes the variables which need to be set to use an RSA public and private key.
RSA Keys
| Key Type | Item | Description |
|---|---|---|
| RSA Public | e | Public Exponent |
| RSA | n | Modulus |
| RSA Private | d | Private Exponent |
| RSA Private | p | Prime 1 |
| RSA Private | q | Prime 2 |
| RSA Private | dmq1 | Exponent 1 |
| RSA Private | dmp1 | Exponent 2 |
| RSA Private | iqmp | Coefficient |
Thus, in order to make a working RSA public key (so the method key.public_encrypt() or key.public_decrypt() work) you must set at least n and e. For a working private key, you would need to load all of the items. Exposing any of the items marked as "RSA Private" above will cause a key compromise.
RSA Example
In this example, a 128-bit RSA key is loaded from numerical values. In DNSSEC, the public key is stored in the DNSKEY record for the zones. Don't use these numbers for real crypto; the short key length is used only to make the numbers short enough to fit in the screen width. For real work, 1024 is probably a reasonable minimum length for short-lived uses, and 2048 for longer-term use.
require 'openssl'
#
# Build a RSA public key. We only need to load two things
# here in order to use the public key to use it to encrypt,
# sign, or verify.
#
pub = OpenSSL::PKey::RSA::new
pub.e = 65537
pub.n = 216457604585180710748301099018726389113
# At this point, this will work:
crypted = pub.public_encrypt("test")
#
# Build an RSA private key. For the private key to work, we need
# to load the entire key, private and public components. As we
# should have access to both, this is not really a problem.
#
prv = OpenSSL::PKey::RSA::new
prv.e = 65537
prv.d = 178210827022942698143906513631075003381
prv.n = 216457604585180710748301099018726389113
prv.p = 15294921647876231099
prv.q = 14152253249053866587
prv.dmp1 = 6806715058393856237
prv.dmq1 = 637679537428568107
prv.iqmp = 6672106206837437412
# Now we have a working private key.
puts prv.private_decrypt(crypted) # prints "test"
DSA keys
A DSA key is more or less the same, just with different variable names. It is also split into a public and private part, and the key can be loaded from individual components just as easily.
| Key Type | Item | Description |
|---|---|---|
| DSA Public | pub_key | Public Key |
| DSA | q | Prime 1 |
| DSA | p | Prime 2 |
| DSA | g | Multiplicative order modulo p is q |
| DSA Private | priv_key | Private Key |
DSA Example
Unfortunately, this example has some numbers which are too long to display nicely. I have used a trick to convert them from strings into integers so they will fit here. Normally you would not need to do this.
require 'openssl'
#
# Build an DSA private key. For the private key to work, we need
# to load the entire key, private and public components. As we
# should have access to both, this is not really a problem.
#
prv = OpenSSL::PKey::DSA::new
prv.pub_key = ("899167044393666062859565588228279347268072456516837337" +
"963353916587148226144760114643916732975837345856985656" +
"3340384802383806137452386519280693373122367959").to_i
prv.p = ("952649509730281181203079535805855260554748337655197352471196" +
"869232197576949258404031665397657842790773780623545384978542" +
"6685417827665656974405272756289291").to_i
prv.q = 903197981571669745498020976355730183999507610553
prv.g = ("535694721480531756072717909769318961974692885092552247120424" +
"749877864650255208980198391972633196543370921493242375015765" +
"755160911031468160738717891191998").to_i
prv.priv_key = 557886499717422048101097620625259920363848888840
# At this point, this will work:
signature = prv.sign(OpenSSL::Digest::DSS1.new, "test")
#
# Build a DSA public key. We only need to load two things
# here in order to use the public key to use it to encrypt,
# sign, or verify.
#
pub = OpenSSL::PKey::DSA::new
pub.pub_key = ("899167044393666062859565588228279347268072456516837337" +
"963353916587148226144760114643916732975837345856985656" +
"3340384802383806137452386519280693373122367959").to_i
pub.p = ("952649509730281181203079535805855260554748337655197352471196" +
"869232197576949258404031665397657842790773780623545384978542" +
"6685417827665656974405272756289291").to_i
pub.q = 903197981571669745498020976355730183999507610553
pub.g = ("535694721480531756072717909769318961974692885092552247120424" +
"749877864650255208980198391972633196543370921493242375015765" +
"755160911031468160738717891191998").to_i
# Now we have a working private key. Verify the signature
if pub.verify(OpenSSL::Digest::DSS1.new, signature, "test")
puts "Signature verified."
else
puts "Signature verification failed."
end
Thursday, February 26, 2009
Ruby Regular Expression Gotchas
I love Ruby. I love Ruby on Rails. Rarely have I found a language or a framework that just works.
However, you still have to know the finer details sometimes.
I recently made a model for a DNS zone. The name in the model is the "front part" of a fully qualified domain name. For instance, if zone.name = "foo" then I would write the name into my name server's configuration files as "foo.example.com."
Knowing that people were evil, I saw that if a user put a string in like "example.com. NS hackerz-will-someday-rule-the-earth.ru.\nfoo" I would happily write out two strings, one being rather bad.
Knowing how easy this sort of data validation is in Rails, I made my model look like:
class Zone < ActiveRecord::Base
validates_presence_of :name
validates_uniqueness_of :name
validates_format_of :name,
:with => /^[a-zA-Z0-9\-\_\.]+$/,
:message => "contains invalid characters."
end
Happy, I ran a few tests using my browser and found that I could not insert names with spaces, colons, tabs, etc. Then, several days later, I decided it was time to write tests for this.
require 'test_helper'
class ZoneTest < ActiveSupport::TestCase
def test_name_with_newline_fails
z = Zone.new(:name => "test\nzone")
assert !z.valid?
assert z.errors.on(:name)
end
def test_name_with_space_fails
z = Zone.new(:name => "test zone")
assert !z.valid?
assert z.errors.on(:name)
end
end
Imagine my surprise when test_name_with_space_fails() passed, and the one I was most worried about, test_name_with_newline_fails(), did not!
Not all regular expressions are alike
The problem is in what I thought ^ and $ actually matched. I thought these meant "match the beginning and ending of the string." However, it turns out it means "match the beginning and ending of each line contained in the string," where lines are divided by newlines. Ooops.
Changing ^ into \A and $ into \Z fixed this problem. Now I'm auditing all the code in this application to see if there are other problems like this.
This is just one thing to add to an ever-growing security checklist for my Rails work. It's also a very typical security hole: programmer error.
Wednesday, March 19, 2008
Javascript application framework 'extjs' and privacy
Out of the box, extjs version 2.0.2 leaks privacy information.
If you fail to change the value of Ext.BLANK_IMAGE_URL to something local, it will default to http://extjs.com/s.gif. At first this might not seem bad, but remember that every time this image is fetched the referring URL is sent to the extjs.com web server.
At worse, this is a minor information link. Depending on what you might place in your URL line, this could be a major issue.
I have posted a comment on the extjs forums, but so far the developers don't see the problem. They say it is well documented in their FAQ, and that it is documented in the API docs.
I would prefer they opt for a warning message saying "You did not set ..." rather than leaking information by default. I'll probably have to post a CERT on this one.