Skip to content

Commit

Permalink
Fix parsing/generation of (EC)DSA signatures from/to DER
Browse files Browse the repository at this point in the history
  • Loading branch information
ibauersachs committed May 19, 2019
1 parent dd477ca commit 8dcc8af
Show file tree
Hide file tree
Showing 3 changed files with 185 additions and 50 deletions.
167 changes: 118 additions & 49 deletions org/xbill/DNS/DNSSEC.java
Original file line number Diff line number Diff line change
Expand Up @@ -702,11 +702,17 @@ private static class ECKeyInfo {
int rlen = DSA_LEN;
if (r[0] < 0)
rlen++;
else
for(int i = 0; i < DSA_LEN - 1 && r[i] == 0 && r[i+1] >= 0; i++)
rlen--;

byte [] s = in.readByteArray(DSA_LEN);
int slen = DSA_LEN;
if (s[0] < 0)
slen++;
int slen = DSA_LEN;
if (s[0] < 0)
slen++;
else
for(int i = 0; i < DSA_LEN - 1 && s[i] == 0 && s[i+1] >= 0; i++)
slen--;

out.writeU8(ASN1_SEQ);
out.writeU8(rlen + slen + 4);
Expand All @@ -715,13 +721,19 @@ private static class ECKeyInfo {
out.writeU8(rlen);
if (rlen > DSA_LEN)
out.writeU8(0);
out.writeByteArray(r);
if (rlen >= DSA_LEN)
out.writeByteArray(r);
else
out.writeByteArray(r, DSA_LEN - rlen, rlen);

out.writeU8(ASN1_INT);
out.writeU8(slen);
if (slen > DSA_LEN)
out.writeU8(0);
out.writeByteArray(s);
if (slen >= DSA_LEN)
out.writeByteArray(s);
else
out.writeByteArray(s, DSA_LEN - slen, slen);

return out.toByteArray();
}
Expand All @@ -735,32 +747,48 @@ private static class ECKeyInfo {

int tmp = in.readU8();
if (tmp != ASN1_SEQ)
throw new IOException();
throw new IOException("Invalid ASN.1 data, expected " + ASN1_SEQ + " got " + tmp);
int seqlen = in.readU8();

tmp = in.readU8();
if (tmp != ASN1_INT)
throw new IOException();
throw new IOException("Invalid ASN.1 data, expected " + ASN1_INT + " got " + tmp);

// r must be of DSA_LEN or +1 if it has a leading zero for negative
// ASN.1 integers
int rlen = in.readU8();
if (rlen == DSA_LEN + 1) {
if (in.readU8() != 0)
throw new IOException();
} else if (rlen != DSA_LEN)
throw new IOException();
byte [] bytes = in.readByteArray(DSA_LEN);
out.writeByteArray(bytes);
if (rlen == DSA_LEN + 1 && in.readU8() == 0) {
--rlen;
} else if (rlen <= DSA_LEN) {
// pad with leading zeros, rfc2536#section-3
for (int i = 0; i < DSA_LEN - rlen; i++) {
out.writeU8(0);
}
} else {
throw new IOException("Invalid r-value in ASN.1 DER encoded DSA signature: " + rlen);
}

out.writeByteArray(in.readByteArray(rlen));

tmp = in.readU8();
if (tmp != ASN1_INT)
throw new IOException();
throw new IOException("Invalid ASN.1 data, expected " + ASN1_INT + " got " + tmp);

// s must be of DSA_LEN or +1 if it has a leading zero for negative
// ASN.1 integers
int slen = in.readU8();
if (slen == DSA_LEN + 1) {
if (in.readU8() != 0)
throw new IOException();
} else if (slen != DSA_LEN)
throw new IOException();
bytes = in.readByteArray(DSA_LEN);
out.writeByteArray(bytes);
if (slen == DSA_LEN + 1 && in.readU8() == 0) {
--slen;
} else if (slen <= DSA_LEN) {
// pad with leading zeros, rfc2536#section-3
for (int i = 0; i < DSA_LEN - slen; i++) {
out.writeU8(0);
}
} else {
throw new IOException("Invalid s-value in ASN.1 DER encoded DSA signature: " + slen);
}

out.writeByteArray(in.readByteArray(slen));

return out.toByteArray();
}
Expand Down Expand Up @@ -789,11 +817,17 @@ private static class ECKeyInfo {
int rlen = keyinfo.length;
if (r[0] < 0)
rlen++;
else
for(int i = 0; i < keyinfo.length - 1 && r[i] == 0 && r[i+1] >= 0; i++)
rlen--;

byte [] s = in.readByteArray(keyinfo.length);
int slen = keyinfo.length;
if (s[0] < 0)
slen++;
else
for(int i = 0; i < keyinfo.length - 1 && s[i] == 0 && s[i+1] >= 0; i++)
slen--;

out.writeU8(ASN1_SEQ);
out.writeU8(rlen + slen + 4);
Expand All @@ -802,13 +836,19 @@ private static class ECKeyInfo {
out.writeU8(rlen);
if (rlen > keyinfo.length)
out.writeU8(0);
out.writeByteArray(r);
if (rlen >= keyinfo.length)
out.writeByteArray(r);
else
out.writeByteArray(r, keyinfo.length - rlen, rlen);

out.writeU8(ASN1_INT);
out.writeU8(slen);
if (slen > keyinfo.length)
out.writeU8(0);
out.writeByteArray(s);
if (slen >= keyinfo.length)
out.writeByteArray(s);
else
out.writeByteArray(s, keyinfo.length - slen, slen);

return out.toByteArray();
}
Expand All @@ -820,32 +860,43 @@ private static class ECKeyInfo {

int tmp = in.readU8();
if (tmp != ASN1_SEQ)
throw new IOException();
throw new IOException("Invalid ASN.1 data, expected " + ASN1_SEQ + " got " + tmp);
int seqlen = in.readU8();

tmp = in.readU8();
if (tmp != ASN1_INT)
throw new IOException();
throw new IOException("Invalid ASN.1 data, expected " + ASN1_INT + " got " + tmp);

int rlen = in.readU8();
if (rlen == keyinfo.length + 1) {
if (in.readU8() != 0)
throw new IOException();
} else if (rlen != keyinfo.length)
throw new IOException();
byte[] bytes = in.readByteArray(keyinfo.length);
out.writeByteArray(bytes);
if (rlen == keyinfo.length + 1 && in.readU8() == 0) {
--rlen;
} else if (rlen <= keyinfo.length) {
// pad with leading zeros, rfc2536#section-3
for (int i = 0; i < keyinfo.length - rlen; i++) {
out.writeU8(0);
}
} else {
throw new IOException("Invalid r-value in ASN.1 DER encoded ECDSA signature: " + rlen);
}

out.writeByteArray(in.readByteArray(rlen));

tmp = in.readU8();
if (tmp != ASN1_INT)
throw new IOException();
throw new IOException("Invalid ASN.1 data, expected " + ASN1_INT + " got " + tmp);
int slen = in.readU8();
if (slen == keyinfo.length + 1) {
if (in.readU8() != 0)
throw new IOException();
} else if (slen != keyinfo.length)
throw new IOException();
bytes = in.readByteArray(keyinfo.length);
out.writeByteArray(bytes);
if (slen == keyinfo.length + 1 && in.readU8() == 0) {
--slen;
} else if (slen <= keyinfo.length) {
// pad with leading zeros, rfc2536#section-3
for (int i = 0; i < keyinfo.length - slen; i++) {
out.writeU8(0);
}
} else {
throw new IOException("Invalid s-value in ASN.1 DER encoded ECDSA signature: " + slen);
}

out.writeByteArray(in.readByteArray(slen));

return out.toByteArray();
}
Expand Down Expand Up @@ -920,16 +971,34 @@ private static class ECKeyInfo {
*/
public static void
verify(RRset rrset, RRSIGRecord rrsig, DNSKEYRecord key) throws DNSSECException
{
verify(rrset, rrsig, key, new Date());
}

/**
* Verify a DNSSEC signature.
* @param rrset The data to be verified.
* @param rrsig The RRSIG record containing the signature.
* @param key The DNSKEY record to verify the signature with.
* @param date The date against which the signature is verified.
* @throws UnsupportedAlgorithmException The algorithm is unknown
* @throws MalformedKeyException The key is malformed
* @throws KeyMismatchException The key and signature do not match
* @throws SignatureExpiredException The signature has expired
* @throws SignatureNotYetValidException The signature is not yet valid
* @throws SignatureVerificationException The signature does not verify.
* @throws DNSSECException Some other error occurred.
*/
public static void
verify(RRset rrset, RRSIGRecord rrsig, DNSKEYRecord key, Date date) throws DNSSECException
{
if (!matches(rrsig, key))
throw new KeyMismatchException(key, rrsig);

Date now = new Date();
if (now.compareTo(rrsig.getExpire()) > 0)
throw new SignatureExpiredException(rrsig.getExpire(), now);
if (now.compareTo(rrsig.getTimeSigned()) < 0)
throw new SignatureNotYetValidException(rrsig.getTimeSigned(),
now);
if (date.compareTo(rrsig.getExpire()) > 0)
throw new SignatureExpiredException(rrsig.getExpire(), date);
if (date.compareTo(rrsig.getTimeSigned()) < 0)
throw new SignatureNotYetValidException(rrsig.getTimeSigned(), date);

verify(key.getPublicKey(), rrsig.getAlgorithm(),
digestRRset(rrsig, rrset), rrsig.getSignature());
Expand Down Expand Up @@ -962,7 +1031,7 @@ private static class ECKeyInfo {
signature = DSASignaturetoDNS(signature, t);
}
catch (IOException e) {
throw new IllegalStateException();
throw new IllegalStateException(e);
}
} else if (pubkey instanceof ECPublicKey) {
try {
Expand All @@ -983,7 +1052,7 @@ private static class ECKeyInfo {
}
}
catch (IOException e) {
throw new IllegalStateException();
throw new IllegalStateException(e);
}
}

Expand Down
2 changes: 1 addition & 1 deletion pom.xml
Original file line number Diff line number Diff line change
Expand Up @@ -25,7 +25,7 @@
<connection>scm:git:https://github.com/dnsjava/dnsjava</connection>
<developerConnection>scm:git:git@github.com:dnsjava/dnsjava</developerConnection>
<url>https://github.com/dnsjava/dnsjava</url>
<tag>v2.1.8</tag>
<tag>HEAD</tag>
</scm>
<developers>
<developer>
Expand Down
66 changes: 66 additions & 0 deletions tests/org/xbill/DNS/DNSSECTest.java
Original file line number Diff line number Diff line change
@@ -0,0 +1,66 @@
// SPDX-License-Identifier: BSD-2-Clause
package org.xbill.DNS;

import java.io.IOException;
import java.util.Date;

import org.xbill.DNS.DNSSEC.DNSSECException;

import junit.framework.TestCase;

public class DNSSECTest extends TestCase
{
private TXTRecord txt = new TXTRecord(Name.root, DClass.IN, 3600, "test");

public void testECDSALeadingZeroUndersize() throws IOException, DNSSECException
{
DNSKEYRecord dnskey = (DNSKEYRecord) Record.fromString(Name.root, Type.DNSKEY, DClass.IN, 3600,
"256 3 13 HgcQzDrxDm641ASGyEF0MXrjDji4XDnWzjrY9VoIn5GfAvHpuqI2W8yihplAz6C/56Zxq1XbAHjLZATfhZFmaA==", Name.root);
RRSIGRecord rrsig = (RRSIGRecord) Record.fromString(Name.root, Type.RRSIG, DClass.IN, 3600,
"TXT 13 0 3600 19700101000003 19700101000000 46271 . dRwMEthIeGiucMcEcDmwixM8/LZcZ+W6lMM0KDSY5rwAGrm1j7tS/VU6xs+rpD5dSRmBYosinkWD6Jk3zRmyBQ==", Name.root);

RRset rrset = new RRset();
rrset.addRR(txt);
rrset.addRR(rrsig);
DNSSEC.verify(rrset, rrsig, dnskey, new Date(60));
}

public void testECDSALeadingZeroOversize() throws IOException, DNSSECException
{
DNSKEYRecord dnskey = (DNSKEYRecord) Record.fromString(Name.root, Type.DNSKEY, DClass.IN, 3600,
"256 3 13 OYt2tO1n75q/Wb6CglqPVrU22f02clZehWamgXc9ZGPhVMAerzPR9/bhf1XxtC3xAR9riVuGh9CEPVvmiNqukQ==", Name.root);
RRSIGRecord rrsig = (RRSIGRecord) Record.fromString(Name.root, Type.RRSIG, DClass.IN, 3600,
"TXT 13 0 3600 19700101000003 19700101000000 25719 . m6sD/b0ZbfBXsQruhq5dYTnHGaA+PRTL5Y1W36rMdnGBb7eOJRRzDS5Wk5hZlrS4RUKQ/tKMCn7lsl9fn4U2lw==", Name.root);

RRset rrset = new RRset();
rrset.addRR(txt);
rrset.addRR(rrsig);
DNSSEC.verify(rrset, rrsig, dnskey, new Date(60));
}

public void testDSALeadingZeroUndersize() throws DNSSECException, IOException
{
DNSKEYRecord dnskey = (DNSKEYRecord) Record.fromString(Name.root, Type.DNSKEY, DClass.IN, 3600,
"256 3 3 AJYu3cw2nLqOuyYO5rahJtk0bjjF/KaCzo4Syrom78z3EQ5SbbB4sF7ey80etKII864WF64B81uRpH5t9jQTxeEu0ImbzRMqzVDZkVG9xD7nN1kuF2eEcbJ6nPRO6RpJxRR9samq8kTwWkNNZIaTHS0UJxueNQMLcf1z2heQabMuKTVjDhwgYjVNDaIKbEFuUL55TKRAt3Xr7t5zCMLaujMvqNHOzCFEusXN5mXjJqAj8J0l4B4tbL7M4iIFZeXJDXGCEcsBbNrVAfFnlOO06B6dkB8L", Name.root);
RRSIGRecord rrsig = (RRSIGRecord) Record.fromString(Name.root, Type.RRSIG, DClass.IN, 3600l,
"TXT 3 0 3600 19700101000003 19700101000000 36714 . AAAycZeIdBGB7vjlFzd5+ZgV8IxGRLpLierdV1KO4SGIy707hKUXJRc=", Name.root);

RRset set = new RRset();
set.addRR(txt);
set.addRR(rrsig);
DNSSEC.verify(set, rrsig, dnskey, new Date(60));
}

public void testDSALeadingZeroOversize() throws DNSSECException, IOException
{
DNSKEYRecord dnskey = (DNSKEYRecord) Record.fromString(Name.root, Type.DNSKEY, DClass.IN, 3600,
"256 3 3 AJYu3cw2nLqOuyYO5rahJtk0bjjF/KaCzo4Syrom78z3EQ5SbbB4sF7ey80etKII864WF64B81uRpH5t9jQTxeEu0ImbzRMqzVDZkVG9xD7nN1kuF2eEcbJ6nPRO6RpJxRR9samq8kTwWkNNZIaTHS0UJxueNQMLcf1z2heQabMuKTVjDhwgYjVNDaIKbEFuUL55TKQflphJYUXcb2M3wKNGoXP7NufzhfVaDtiS44waWjC8IN98Ab+SPPfM4+xgTsgzWt8KvzL8hhqSW+4+5zjiQ6UG", Name.root);
RRSIGRecord rrsig = (RRSIGRecord) Record.fromString(Name.root, Type.RRSIG, DClass.IN, 3600l,
"TXT 3 0 3600 19700101000003 19700101000000 57407 . AIh8Bp0EFNszs3cB0gNatjWy8tBrgUAUe1gTHkVsm1pva1GYWOW/FbA=", Name.root);

RRset set = new RRset();
set.addRR(txt);
set.addRR(rrsig);
DNSSEC.verify(set, rrsig, dnskey, new Date(60));
}
}

0 comments on commit 8dcc8af

Please sign in to comment.