1// Copyright (c) 1999-2010 Brian Wellington (bwelling@xbill.org)
2
3package org.xbill.DNS;
4
5import java.io.*;
6import java.math.*;
7import java.security.*;
8import java.security.interfaces.*;
9import java.security.spec.*;
10import java.util.*;
11
12/**
13 * Constants and methods relating to DNSSEC.
14 *
15 * DNSSEC provides authentication for DNS information.
16 * @see RRSIGRecord
17 * @see DNSKEYRecord
18 * @see RRset
19 *
20 * @author Brian Wellington
21 */
22
23public class DNSSEC {
24
25public static class Algorithm {
26	private Algorithm() {}
27
28	/** RSA/MD5 public key (deprecated) */
29	public static final int RSAMD5 = 1;
30
31	/** Diffie Hellman key */
32	public static final int DH = 2;
33
34	/** DSA public key */
35	public static final int DSA = 3;
36
37	/** RSA/SHA1 public key */
38	public static final int RSASHA1 = 5;
39
40	/** DSA/SHA1, NSEC3-aware public key */
41	public static final int DSA_NSEC3_SHA1 = 6;
42
43	/** RSA/SHA1, NSEC3-aware public key */
44	public static final int RSA_NSEC3_SHA1 = 7;
45
46	/** RSA/SHA256 public key */
47	public static final int RSASHA256 = 8;
48
49	/** RSA/SHA512 public key */
50	public static final int RSASHA512 = 10;
51
52	/** ECDSA Curve P-256 with SHA-256 public key **/
53	public static final int ECDSAP256SHA256 = 13;
54
55	/** ECDSA Curve P-384 with SHA-384 public key **/
56	public static final int ECDSAP384SHA384 = 14;
57
58	/** Indirect keys; the actual key is elsewhere. */
59	public static final int INDIRECT = 252;
60
61	/** Private algorithm, specified by domain name */
62	public static final int PRIVATEDNS = 253;
63
64	/** Private algorithm, specified by OID */
65	public static final int PRIVATEOID = 254;
66
67	private static Mnemonic algs = new Mnemonic("DNSSEC algorithm",
68						    Mnemonic.CASE_UPPER);
69
70	static {
71		algs.setMaximum(0xFF);
72		algs.setNumericAllowed(true);
73
74		algs.add(RSAMD5, "RSAMD5");
75		algs.add(DH, "DH");
76		algs.add(DSA, "DSA");
77		algs.add(RSASHA1, "RSASHA1");
78		algs.add(DSA_NSEC3_SHA1, "DSA-NSEC3-SHA1");
79		algs.add(RSA_NSEC3_SHA1, "RSA-NSEC3-SHA1");
80		algs.add(RSASHA256, "RSASHA256");
81		algs.add(RSASHA512, "RSASHA512");
82		algs.add(ECDSAP256SHA256, "ECDSAP256SHA256");
83		algs.add(ECDSAP384SHA384, "ECDSAP384SHA384");
84		algs.add(INDIRECT, "INDIRECT");
85		algs.add(PRIVATEDNS, "PRIVATEDNS");
86		algs.add(PRIVATEOID, "PRIVATEOID");
87	}
88
89	/**
90	 * Converts an algorithm into its textual representation
91	 */
92	public static String
93	string(int alg) {
94		return algs.getText(alg);
95	}
96
97	/**
98	 * Converts a textual representation of an algorithm into its numeric
99	 * code.  Integers in the range 0..255 are also accepted.
100	 * @param s The textual representation of the algorithm
101	 * @return The algorithm code, or -1 on error.
102	 */
103	public static int
104	value(String s) {
105		return algs.getValue(s);
106	}
107}
108
109private
110DNSSEC() { }
111
112private static void
113digestSIG(DNSOutput out, SIGBase sig) {
114	out.writeU16(sig.getTypeCovered());
115	out.writeU8(sig.getAlgorithm());
116	out.writeU8(sig.getLabels());
117	out.writeU32(sig.getOrigTTL());
118	out.writeU32(sig.getExpire().getTime() / 1000);
119	out.writeU32(sig.getTimeSigned().getTime() / 1000);
120	out.writeU16(sig.getFootprint());
121	sig.getSigner().toWireCanonical(out);
122}
123
124/**
125 * Creates a byte array containing the concatenation of the fields of the
126 * SIG record and the RRsets to be signed/verified.  This does not perform
127 * a cryptographic digest.
128 * @param rrsig The RRSIG record used to sign/verify the rrset.
129 * @param rrset The data to be signed/verified.
130 * @return The data to be cryptographically signed or verified.
131 */
132public static byte []
133digestRRset(RRSIGRecord rrsig, RRset rrset) {
134	DNSOutput out = new DNSOutput();
135	digestSIG(out, rrsig);
136
137	int size = rrset.size();
138	Record [] records = new Record[size];
139
140	Iterator it = rrset.rrs();
141	Name name = rrset.getName();
142	Name wild = null;
143	int sigLabels = rrsig.getLabels() + 1; // Add the root label back.
144	if (name.labels() > sigLabels)
145		wild = name.wild(name.labels() - sigLabels);
146	while (it.hasNext())
147		records[--size] = (Record) it.next();
148	Arrays.sort(records);
149
150	DNSOutput header = new DNSOutput();
151	if (wild != null)
152		wild.toWireCanonical(header);
153	else
154		name.toWireCanonical(header);
155	header.writeU16(rrset.getType());
156	header.writeU16(rrset.getDClass());
157	header.writeU32(rrsig.getOrigTTL());
158	for (int i = 0; i < records.length; i++) {
159		out.writeByteArray(header.toByteArray());
160		int lengthPosition = out.current();
161		out.writeU16(0);
162		out.writeByteArray(records[i].rdataToWireCanonical());
163		int rrlength = out.current() - lengthPosition - 2;
164		out.save();
165		out.jump(lengthPosition);
166		out.writeU16(rrlength);
167		out.restore();
168	}
169	return out.toByteArray();
170}
171
172/**
173 * Creates a byte array containing the concatenation of the fields of the
174 * SIG(0) record and the message to be signed.  This does not perform
175 * a cryptographic digest.
176 * @param sig The SIG record used to sign the rrset.
177 * @param msg The message to be signed.
178 * @param previous If this is a response, the signature from the query.
179 * @return The data to be cryptographically signed.
180 */
181public static byte []
182digestMessage(SIGRecord sig, Message msg, byte [] previous) {
183	DNSOutput out = new DNSOutput();
184	digestSIG(out, sig);
185
186	if (previous != null)
187		out.writeByteArray(previous);
188
189	msg.toWire(out);
190	return out.toByteArray();
191}
192
193/**
194 * A DNSSEC exception.
195 */
196public static class DNSSECException extends Exception {
197	DNSSECException(String s) {
198		super(s);
199	}
200}
201
202/**
203 * An algorithm is unsupported by this DNSSEC implementation.
204 */
205public static class UnsupportedAlgorithmException extends DNSSECException {
206	UnsupportedAlgorithmException(int alg) {
207		super("Unsupported algorithm: " + alg);
208	}
209}
210
211/**
212 * The cryptographic data in a DNSSEC key is malformed.
213 */
214public static class MalformedKeyException extends DNSSECException {
215	MalformedKeyException(KEYBase rec) {
216		super("Invalid key data: " + rec.rdataToString());
217	}
218}
219
220/**
221 * A DNSSEC verification failed because fields in the DNSKEY and RRSIG records
222 * do not match.
223 */
224public static class KeyMismatchException extends DNSSECException {
225	private KEYBase key;
226	private SIGBase sig;
227
228	KeyMismatchException(KEYBase key, SIGBase sig) {
229		super("key " +
230		      key.getName() + "/" +
231		      DNSSEC.Algorithm.string(key.getAlgorithm()) + "/" +
232		      key.getFootprint() + " " +
233		      "does not match signature " +
234		      sig.getSigner() + "/" +
235		      DNSSEC.Algorithm.string(sig.getAlgorithm()) + "/" +
236		      sig.getFootprint());
237	}
238}
239
240/**
241 * A DNSSEC verification failed because the signature has expired.
242 */
243public static class SignatureExpiredException extends DNSSECException {
244	private Date when, now;
245
246	SignatureExpiredException(Date when, Date now) {
247		super("signature expired");
248		this.when = when;
249		this.now = now;
250	}
251
252	/**
253	 * @return When the signature expired
254	 */
255	public Date
256	getExpiration() {
257		return when;
258	}
259
260	/**
261	 * @return When the verification was attempted
262	 */
263	public Date
264	getVerifyTime() {
265		return now;
266	}
267}
268
269/**
270 * A DNSSEC verification failed because the signature has not yet become valid.
271 */
272public static class SignatureNotYetValidException extends DNSSECException {
273	private Date when, now;
274
275	SignatureNotYetValidException(Date when, Date now) {
276		super("signature is not yet valid");
277		this.when = when;
278		this.now = now;
279	}
280
281	/**
282	 * @return When the signature will become valid
283	 */
284	public Date
285	getExpiration() {
286		return when;
287	}
288
289	/**
290	 * @return When the verification was attempted
291	 */
292	public Date
293	getVerifyTime() {
294		return now;
295	}
296}
297
298/**
299 * A DNSSEC verification failed because the cryptographic signature
300 * verification failed.
301 */
302public static class SignatureVerificationException extends DNSSECException {
303	SignatureVerificationException() {
304		super("signature verification failed");
305	}
306}
307
308/**
309 * The key data provided is inconsistent.
310 */
311public static class IncompatibleKeyException extends IllegalArgumentException {
312	IncompatibleKeyException() {
313		super("incompatible keys");
314	}
315}
316
317private static int
318BigIntegerLength(BigInteger i) {
319	return (i.bitLength() + 7) / 8;
320}
321
322private static BigInteger
323readBigInteger(DNSInput in, int len) throws IOException {
324	byte [] b = in.readByteArray(len);
325	return new BigInteger(1, b);
326}
327
328private static BigInteger
329readBigInteger(DNSInput in) {
330	byte [] b = in.readByteArray();
331	return new BigInteger(1, b);
332}
333
334private static void
335writeBigInteger(DNSOutput out, BigInteger val) {
336	byte [] b = val.toByteArray();
337	if (b[0] == 0)
338		out.writeByteArray(b, 1, b.length - 1);
339	else
340		out.writeByteArray(b);
341}
342
343private static PublicKey
344toRSAPublicKey(KEYBase r) throws IOException, GeneralSecurityException {
345	DNSInput in = new DNSInput(r.getKey());
346	int exponentLength = in.readU8();
347	if (exponentLength == 0)
348		exponentLength = in.readU16();
349	BigInteger exponent = readBigInteger(in, exponentLength);
350	BigInteger modulus = readBigInteger(in);
351
352	KeyFactory factory = KeyFactory.getInstance("RSA");
353	return factory.generatePublic(new RSAPublicKeySpec(modulus, exponent));
354}
355
356private static PublicKey
357toDSAPublicKey(KEYBase r) throws IOException, GeneralSecurityException,
358	MalformedKeyException
359{
360	DNSInput in = new DNSInput(r.getKey());
361
362	int t = in.readU8();
363	if (t > 8)
364		throw new MalformedKeyException(r);
365
366	BigInteger q = readBigInteger(in, 20);
367	BigInteger p = readBigInteger(in, 64 + t*8);
368	BigInteger g = readBigInteger(in, 64 + t*8);
369	BigInteger y = readBigInteger(in, 64 + t*8);
370
371	KeyFactory factory = KeyFactory.getInstance("DSA");
372	return factory.generatePublic(new DSAPublicKeySpec(y, p, q, g));
373}
374
375private static class ECKeyInfo {
376	int length;
377	public BigInteger p, a, b, gx, gy, n;
378	EllipticCurve curve;
379	ECParameterSpec spec;
380
381	ECKeyInfo(int length, String p_str, String a_str, String b_str,
382		  String gx_str, String gy_str, String n_str)
383	{
384		this.length = length;
385		p = new BigInteger(p_str, 16);
386		a = new BigInteger(a_str, 16);
387		b = new BigInteger(b_str, 16);
388		gx = new BigInteger(gx_str, 16);
389		gy = new BigInteger(gy_str, 16);
390		n = new BigInteger(n_str, 16);
391		curve = new EllipticCurve(new ECFieldFp(p), a, b);
392		spec = new ECParameterSpec(curve, new ECPoint(gx, gy), n, 1);
393	}
394}
395
396// RFC 5114 Section 2.6
397private static final ECKeyInfo ECDSA_P256 = new ECKeyInfo(32,
398	"FFFFFFFF00000001000000000000000000000000FFFFFFFFFFFFFFFFFFFFFFFF",
399	"FFFFFFFF00000001000000000000000000000000FFFFFFFFFFFFFFFFFFFFFFFC",
400	"5AC635D8AA3A93E7B3EBBD55769886BC651D06B0CC53B0F63BCE3C3E27D2604B",
401	"6B17D1F2E12C4247F8BCE6E563A440F277037D812DEB33A0F4A13945D898C296",
402	"4FE342E2FE1A7F9B8EE7EB4A7C0F9E162BCE33576B315ECECBB6406837BF51F5",
403	"FFFFFFFF00000000FFFFFFFFFFFFFFFFBCE6FAADA7179E84F3B9CAC2FC632551");
404
405// RFC 5114 Section 2.7
406private static final ECKeyInfo ECDSA_P384 = new ECKeyInfo(48,
407	"FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFEFFFFFFFF0000000000000000FFFFFFFF",
408	"FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFEFFFFFFFF0000000000000000FFFFFFFC",
409	"B3312FA7E23EE7E4988E056BE3F82D19181D9C6EFE8141120314088F5013875AC656398D8A2ED19D2A85C8EDD3EC2AEF",
410	"AA87CA22BE8B05378EB1C71EF320AD746E1D3B628BA79B9859F741E082542A385502F25DBF55296C3A545E3872760AB7",
411	"3617DE4A96262C6F5D9E98BF9292DC29F8F41DBD289A147CE9DA3113B5F0B8C00A60B1CE1D7E819D7A431D7C90EA0E5F",
412	"FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFC7634D81F4372DDF581A0DB248B0A77AECEC196ACCC52973");
413
414private static PublicKey
415toECDSAPublicKey(KEYBase r, ECKeyInfo keyinfo) throws IOException,
416	GeneralSecurityException, MalformedKeyException
417{
418	DNSInput in = new DNSInput(r.getKey());
419
420	// RFC 6605 Section 4
421	BigInteger x = readBigInteger(in, keyinfo.length);
422	BigInteger y = readBigInteger(in, keyinfo.length);
423	ECPoint q = new ECPoint(x, y);
424
425	KeyFactory factory = KeyFactory.getInstance("EC");
426	return factory.generatePublic(new ECPublicKeySpec(q, keyinfo.spec));
427}
428
429/** Converts a KEY/DNSKEY record into a PublicKey */
430static PublicKey
431toPublicKey(KEYBase r) throws DNSSECException {
432	int alg = r.getAlgorithm();
433	try {
434		switch (alg) {
435		case Algorithm.RSAMD5:
436		case Algorithm.RSASHA1:
437		case Algorithm.RSA_NSEC3_SHA1:
438		case Algorithm.RSASHA256:
439		case Algorithm.RSASHA512:
440			return toRSAPublicKey(r);
441		case Algorithm.DSA:
442		case Algorithm.DSA_NSEC3_SHA1:
443			return toDSAPublicKey(r);
444		case Algorithm.ECDSAP256SHA256:
445			return toECDSAPublicKey(r, ECDSA_P256);
446		case Algorithm.ECDSAP384SHA384:
447			return toECDSAPublicKey(r, ECDSA_P384);
448		default:
449			throw new UnsupportedAlgorithmException(alg);
450		}
451	}
452	catch (IOException e) {
453		throw new MalformedKeyException(r);
454	}
455	catch (GeneralSecurityException e) {
456		throw new DNSSECException(e.toString());
457	}
458}
459
460private static byte []
461fromRSAPublicKey(RSAPublicKey key) {
462	DNSOutput out = new DNSOutput();
463	BigInteger exponent = key.getPublicExponent();
464	BigInteger modulus = key.getModulus();
465	int exponentLength = BigIntegerLength(exponent);
466
467	if (exponentLength < 256)
468		out.writeU8(exponentLength);
469	else {
470		out.writeU8(0);
471		out.writeU16(exponentLength);
472	}
473	writeBigInteger(out, exponent);
474	writeBigInteger(out, modulus);
475
476	return out.toByteArray();
477}
478
479private static byte []
480fromDSAPublicKey(DSAPublicKey key) {
481	DNSOutput out = new DNSOutput();
482	BigInteger q = key.getParams().getQ();
483	BigInteger p = key.getParams().getP();
484	BigInteger g = key.getParams().getG();
485	BigInteger y = key.getY();
486	int t = (p.toByteArray().length - 64) / 8;
487
488	out.writeU8(t);
489	writeBigInteger(out, q);
490	writeBigInteger(out, p);
491	writeBigInteger(out, g);
492	writeBigInteger(out, y);
493
494	return out.toByteArray();
495}
496
497private static byte []
498fromECDSAPublicKey(ECPublicKey key) {
499	DNSOutput out = new DNSOutput();
500
501	BigInteger x = key.getW().getAffineX();
502	BigInteger y = key.getW().getAffineY();
503
504	writeBigInteger(out, x);
505	writeBigInteger(out, y);
506
507	return out.toByteArray();
508}
509
510/** Builds a DNSKEY record from a PublicKey */
511static byte []
512fromPublicKey(PublicKey key, int alg) throws DNSSECException
513{
514
515	switch (alg) {
516	case Algorithm.RSAMD5:
517	case Algorithm.RSASHA1:
518	case Algorithm.RSA_NSEC3_SHA1:
519	case Algorithm.RSASHA256:
520	case Algorithm.RSASHA512:
521		if (! (key instanceof RSAPublicKey))
522			throw new IncompatibleKeyException();
523		return fromRSAPublicKey((RSAPublicKey) key);
524	case Algorithm.DSA:
525	case Algorithm.DSA_NSEC3_SHA1:
526		if (! (key instanceof DSAPublicKey))
527			throw new IncompatibleKeyException();
528		return fromDSAPublicKey((DSAPublicKey) key);
529	case Algorithm.ECDSAP256SHA256:
530	case Algorithm.ECDSAP384SHA384:
531		if (! (key instanceof ECPublicKey))
532			throw new IncompatibleKeyException();
533		return fromECDSAPublicKey((ECPublicKey) key);
534	default:
535		throw new UnsupportedAlgorithmException(alg);
536	}
537}
538
539/**
540 * Convert an algorithm number to the corresponding JCA string.
541 * @param alg The algorithm number.
542 * @throws UnsupportedAlgorithmException The algorithm is unknown.
543 */
544public static String
545algString(int alg) throws UnsupportedAlgorithmException {
546	switch (alg) {
547	case Algorithm.RSAMD5:
548		return "MD5withRSA";
549	case Algorithm.DSA:
550	case Algorithm.DSA_NSEC3_SHA1:
551		return "SHA1withDSA";
552	case Algorithm.RSASHA1:
553	case Algorithm.RSA_NSEC3_SHA1:
554		return "SHA1withRSA";
555	case Algorithm.RSASHA256:
556		return "SHA256withRSA";
557	case Algorithm.RSASHA512:
558		return "SHA512withRSA";
559	case Algorithm.ECDSAP256SHA256:
560		return "SHA256withECDSA";
561	case Algorithm.ECDSAP384SHA384:
562		return "SHA384withECDSA";
563	default:
564		throw new UnsupportedAlgorithmException(alg);
565	}
566}
567
568private static final int ASN1_SEQ = 0x30;
569private static final int ASN1_INT = 0x2;
570
571private static final int DSA_LEN = 20;
572
573private static byte []
574DSASignaturefromDNS(byte [] dns) throws DNSSECException, IOException {
575	if (dns.length != 1 + DSA_LEN * 2)
576		throw new SignatureVerificationException();
577
578	DNSInput in = new DNSInput(dns);
579	DNSOutput out = new DNSOutput();
580
581	int t = in.readU8();
582
583	byte [] r = in.readByteArray(DSA_LEN);
584	int rlen = DSA_LEN;
585	if (r[0] < 0)
586		rlen++;
587
588	byte [] s = in.readByteArray(DSA_LEN);
589        int slen = DSA_LEN;
590        if (s[0] < 0)
591                slen++;
592
593	out.writeU8(ASN1_SEQ);
594	out.writeU8(rlen + slen + 4);
595
596	out.writeU8(ASN1_INT);
597	out.writeU8(rlen);
598	if (rlen > DSA_LEN)
599		out.writeU8(0);
600	out.writeByteArray(r);
601
602	out.writeU8(ASN1_INT);
603	out.writeU8(slen);
604	if (slen > DSA_LEN)
605		out.writeU8(0);
606	out.writeByteArray(s);
607
608	return out.toByteArray();
609}
610
611private static byte []
612DSASignaturetoDNS(byte [] signature, int t) throws IOException {
613	DNSInput in = new DNSInput(signature);
614	DNSOutput out = new DNSOutput();
615
616	out.writeU8(t);
617
618	int tmp = in.readU8();
619	if (tmp != ASN1_SEQ)
620		throw new IOException();
621	int seqlen = in.readU8();
622
623	tmp = in.readU8();
624	if (tmp != ASN1_INT)
625		throw new IOException();
626	int rlen = in.readU8();
627	if (rlen == DSA_LEN + 1) {
628		if (in.readU8() != 0)
629			throw new IOException();
630	} else if (rlen != DSA_LEN)
631		throw new IOException();
632	byte [] bytes = in.readByteArray(DSA_LEN);
633	out.writeByteArray(bytes);
634
635	tmp = in.readU8();
636	if (tmp != ASN1_INT)
637		throw new IOException();
638	int slen = in.readU8();
639	if (slen == DSA_LEN + 1) {
640		if (in.readU8() != 0)
641			throw new IOException();
642	} else if (slen != DSA_LEN)
643		throw new IOException();
644	bytes = in.readByteArray(DSA_LEN);
645	out.writeByteArray(bytes);
646
647	return out.toByteArray();
648}
649
650private static byte []
651ECDSASignaturefromDNS(byte [] signature, ECKeyInfo keyinfo)
652	throws DNSSECException, IOException
653{
654	if (signature.length != keyinfo.length * 2)
655		throw new SignatureVerificationException();
656
657	DNSInput in = new DNSInput(signature);
658	DNSOutput out = new DNSOutput();
659
660	byte [] r = in.readByteArray(keyinfo.length);
661	int rlen = keyinfo.length;
662	if (r[0] < 0)
663		rlen++;
664
665	byte [] s = in.readByteArray(keyinfo.length);
666	int slen = keyinfo.length;
667	if (s[0] < 0)
668		slen++;
669
670	out.writeU8(ASN1_SEQ);
671	out.writeU8(rlen + slen + 4);
672
673	out.writeU8(ASN1_INT);
674	out.writeU8(rlen);
675	if (rlen > keyinfo.length)
676		out.writeU8(0);
677	out.writeByteArray(r);
678
679	out.writeU8(ASN1_INT);
680	out.writeU8(slen);
681	if (slen > keyinfo.length)
682		out.writeU8(0);
683	out.writeByteArray(s);
684
685	return out.toByteArray();
686}
687
688private static byte []
689ECDSASignaturetoDNS(byte [] signature, ECKeyInfo keyinfo) throws IOException {
690	DNSInput in = new DNSInput(signature);
691	DNSOutput out = new DNSOutput();
692
693	int tmp = in.readU8();
694	if (tmp != ASN1_SEQ)
695		throw new IOException();
696	int seqlen = in.readU8();
697
698	tmp = in.readU8();
699	if (tmp != ASN1_INT)
700		throw new IOException();
701	int rlen = in.readU8();
702	if (rlen == keyinfo.length + 1) {
703		if (in.readU8() != 0)
704			throw new IOException();
705	} else if (rlen != keyinfo.length)
706		throw new IOException();
707	byte[] bytes = in.readByteArray(keyinfo.length);
708	out.writeByteArray(bytes);
709
710	tmp = in.readU8();
711	if (tmp != ASN1_INT)
712		throw new IOException();
713	int slen = in.readU8();
714	if (slen == keyinfo.length + 1) {
715		if (in.readU8() != 0)
716			throw new IOException();
717	} else if (slen != keyinfo.length)
718		throw new IOException();
719	bytes = in.readByteArray(keyinfo.length);
720	out.writeByteArray(bytes);
721
722	return out.toByteArray();
723}
724
725private static void
726verify(PublicKey key, int alg, byte [] data, byte [] signature)
727throws DNSSECException
728{
729	if (key instanceof DSAPublicKey) {
730		try {
731			signature = DSASignaturefromDNS(signature);
732		}
733		catch (IOException e) {
734			throw new IllegalStateException();
735		}
736	} else if (key instanceof ECPublicKey) {
737		try {
738			switch (alg) {
739			case Algorithm.ECDSAP256SHA256:
740				signature = ECDSASignaturefromDNS(signature,
741								  ECDSA_P256);
742				break;
743			case Algorithm.ECDSAP384SHA384:
744				signature = ECDSASignaturefromDNS(signature,
745								  ECDSA_P384);
746				break;
747			default:
748				throw new UnsupportedAlgorithmException(alg);
749			}
750		}
751		catch (IOException e) {
752			throw new IllegalStateException();
753		}
754	}
755
756	try {
757		Signature s = Signature.getInstance(algString(alg));
758		s.initVerify(key);
759		s.update(data);
760		if (!s.verify(signature))
761			throw new SignatureVerificationException();
762	}
763	catch (GeneralSecurityException e) {
764		throw new DNSSECException(e.toString());
765	}
766}
767
768private static boolean
769matches(SIGBase sig, KEYBase key)
770{
771	return (key.getAlgorithm() == sig.getAlgorithm() &&
772		key.getFootprint() == sig.getFootprint() &&
773		key.getName().equals(sig.getSigner()));
774}
775
776/**
777 * Verify a DNSSEC signature.
778 * @param rrset The data to be verified.
779 * @param rrsig The RRSIG record containing the signature.
780 * @param key The DNSKEY record to verify the signature with.
781 * @throws UnsupportedAlgorithmException The algorithm is unknown
782 * @throws MalformedKeyException The key is malformed
783 * @throws KeyMismatchException The key and signature do not match
784 * @throws SignatureExpiredException The signature has expired
785 * @throws SignatureNotYetValidException The signature is not yet valid
786 * @throws SignatureVerificationException The signature does not verify.
787 * @throws DNSSECException Some other error occurred.
788 */
789public static void
790verify(RRset rrset, RRSIGRecord rrsig, DNSKEYRecord key) throws DNSSECException
791{
792	if (!matches(rrsig, key))
793		throw new KeyMismatchException(key, rrsig);
794
795	Date now = new Date();
796	if (now.compareTo(rrsig.getExpire()) > 0)
797		throw new SignatureExpiredException(rrsig.getExpire(), now);
798	if (now.compareTo(rrsig.getTimeSigned()) < 0)
799		throw new SignatureNotYetValidException(rrsig.getTimeSigned(),
800							now);
801
802	verify(key.getPublicKey(), rrsig.getAlgorithm(),
803	       digestRRset(rrsig, rrset), rrsig.getSignature());
804}
805
806private static byte []
807sign(PrivateKey privkey, PublicKey pubkey, int alg, byte [] data,
808     String provider) throws DNSSECException
809{
810	byte [] signature;
811	try {
812		Signature s;
813		if (provider != null)
814			s = Signature.getInstance(algString(alg), provider);
815		else
816			s = Signature.getInstance(algString(alg));
817		s.initSign(privkey);
818		s.update(data);
819		signature = s.sign();
820	}
821	catch (GeneralSecurityException e) {
822		throw new DNSSECException(e.toString());
823	}
824
825	if (pubkey instanceof DSAPublicKey) {
826		try {
827			DSAPublicKey dsa = (DSAPublicKey) pubkey;
828			BigInteger P = dsa.getParams().getP();
829			int t = (BigIntegerLength(P) - 64) / 8;
830			signature = DSASignaturetoDNS(signature, t);
831		}
832		catch (IOException e) {
833			throw new IllegalStateException();
834		}
835	} else if (pubkey instanceof ECPublicKey) {
836		try {
837			switch (alg) {
838			case Algorithm.ECDSAP256SHA256:
839				signature = ECDSASignaturetoDNS(signature,
840								ECDSA_P256);
841				break;
842			case Algorithm.ECDSAP384SHA384:
843				signature = ECDSASignaturetoDNS(signature,
844								ECDSA_P384);
845				break;
846			default:
847				throw new UnsupportedAlgorithmException(alg);
848			}
849		}
850		catch (IOException e) {
851			throw new IllegalStateException();
852		}
853	}
854
855	return signature;
856}
857static void
858checkAlgorithm(PrivateKey key, int alg) throws UnsupportedAlgorithmException
859{
860	switch (alg) {
861	case Algorithm.RSAMD5:
862	case Algorithm.RSASHA1:
863	case Algorithm.RSA_NSEC3_SHA1:
864	case Algorithm.RSASHA256:
865	case Algorithm.RSASHA512:
866		if (! (key instanceof RSAPrivateKey))
867			throw new IncompatibleKeyException();
868		break;
869	case Algorithm.DSA:
870	case Algorithm.DSA_NSEC3_SHA1:
871		if (! (key instanceof DSAPrivateKey))
872			throw new IncompatibleKeyException();
873		break;
874	case Algorithm.ECDSAP256SHA256:
875	case Algorithm.ECDSAP384SHA384:
876		if (! (key instanceof ECPrivateKey))
877			throw new IncompatibleKeyException();
878		break;
879	default:
880		throw new UnsupportedAlgorithmException(alg);
881	}
882}
883
884/**
885 * Generate a DNSSEC signature.  key and privateKey must refer to the
886 * same underlying cryptographic key.
887 * @param rrset The data to be signed
888 * @param key The DNSKEY record to use as part of signing
889 * @param privkey The PrivateKey to use when signing
890 * @param inception The time at which the signatures should become valid
891 * @param expiration The time at which the signatures should expire
892 * @throws UnsupportedAlgorithmException The algorithm is unknown
893 * @throws MalformedKeyException The key is malformed
894 * @throws DNSSECException Some other error occurred.
895 * @return The generated signature
896 */
897public static RRSIGRecord
898sign(RRset rrset, DNSKEYRecord key, PrivateKey privkey,
899     Date inception, Date expiration) throws DNSSECException
900{
901	return sign(rrset, key, privkey, inception, expiration, null);
902}
903
904/**
905 * Generate a DNSSEC signature.  key and privateKey must refer to the
906 * same underlying cryptographic key.
907 * @param rrset The data to be signed
908 * @param key The DNSKEY record to use as part of signing
909 * @param privkey The PrivateKey to use when signing
910 * @param inception The time at which the signatures should become valid
911 * @param expiration The time at which the signatures should expire
912 * @param provider The name of the JCA provider.  If non-null, it will be
913 * passed to JCA getInstance() methods.
914 * @throws UnsupportedAlgorithmException The algorithm is unknown
915 * @throws MalformedKeyException The key is malformed
916 * @throws DNSSECException Some other error occurred.
917 * @return The generated signature
918 */
919public static RRSIGRecord
920sign(RRset rrset, DNSKEYRecord key, PrivateKey privkey,
921     Date inception, Date expiration, String provider) throws DNSSECException
922{
923	int alg = key.getAlgorithm();
924	checkAlgorithm(privkey, alg);
925
926	RRSIGRecord rrsig = new RRSIGRecord(rrset.getName(), rrset.getDClass(),
927					    rrset.getTTL(), rrset.getType(),
928					    alg, rrset.getTTL(),
929					    expiration, inception,
930					    key.getFootprint(),
931					    key.getName(), null);
932
933	rrsig.setSignature(sign(privkey, key.getPublicKey(), alg,
934				digestRRset(rrsig, rrset), provider));
935	return rrsig;
936}
937
938static SIGRecord
939signMessage(Message message, SIGRecord previous, KEYRecord key,
940	    PrivateKey privkey, Date inception, Date expiration)
941	throws DNSSECException
942{
943	int alg = key.getAlgorithm();
944	checkAlgorithm(privkey, alg);
945
946	SIGRecord sig = new SIGRecord(Name.root, DClass.ANY, 0, 0,
947					    alg, 0, expiration, inception,
948					    key.getFootprint(),
949					    key.getName(), null);
950	DNSOutput out = new DNSOutput();
951	digestSIG(out, sig);
952	if (previous != null)
953		out.writeByteArray(previous.getSignature());
954	message.toWire(out);
955
956	sig.setSignature(sign(privkey, key.getPublicKey(),
957			      alg, out.toByteArray(), null));
958	return sig;
959}
960
961static void
962verifyMessage(Message message, byte [] bytes, SIGRecord sig, SIGRecord previous,
963	      KEYRecord key) throws DNSSECException
964{
965	if (!matches(sig, key))
966		throw new KeyMismatchException(key, sig);
967
968	Date now = new Date();
969
970	if (now.compareTo(sig.getExpire()) > 0)
971		throw new SignatureExpiredException(sig.getExpire(), now);
972	if (now.compareTo(sig.getTimeSigned()) < 0)
973		throw new SignatureNotYetValidException(sig.getTimeSigned(),
974							now);
975
976	DNSOutput out = new DNSOutput();
977	digestSIG(out, sig);
978	if (previous != null)
979		out.writeByteArray(previous.getSignature());
980
981	Header header = (Header) message.getHeader().clone();
982	header.decCount(Section.ADDITIONAL);
983	out.writeByteArray(header.toWire());
984
985	out.writeByteArray(bytes, Header.LENGTH,
986			   message.sig0start - Header.LENGTH);
987
988	verify(key.getPublicKey(), sig.getAlgorithm(),
989	       out.toByteArray(), sig.getSignature());
990}
991
992/**
993 * Generate the digest value for a DS key
994 * @param key Which is covered by the DS record
995 * @param digestid The type of digest
996 * @return The digest value as an array of bytes
997 */
998static byte []
999generateDSDigest(DNSKEYRecord key, int digestid)
1000{
1001	MessageDigest digest;
1002	try {
1003		switch (digestid) {
1004		case DSRecord.Digest.SHA1:
1005			digest = MessageDigest.getInstance("sha-1");
1006			break;
1007		case DSRecord.Digest.SHA256:
1008			digest = MessageDigest.getInstance("sha-256");
1009			break;
1010		case DSRecord.Digest.SHA384:
1011			digest = MessageDigest.getInstance("sha-384");
1012			break;
1013		default:
1014			throw new IllegalArgumentException(
1015					"unknown DS digest type " + digestid);
1016		}
1017	}
1018	catch (NoSuchAlgorithmException e) {
1019		throw new IllegalStateException("no message digest support");
1020	}
1021	digest.update(key.getName().toWire());
1022	digest.update(key.rdataToWireCanonical());
1023	return digest.digest();
1024}
1025
1026}
1027