View Javadoc
1   // NAME
2   //      $RCSfile: AsnEncoderv3.java,v $
3   // DESCRIPTION
4   //      [given below in javadoc format]
5   // DELTA
6   //      $Revision: 3.5 $
7   // CREATED
8   //      $Date: 2009/03/05 12:48:59 $
9   // COPYRIGHT
10  //      Westhawk Ltd
11  // TO DO
12  //
13  
14  /*
15   * Copyright (C) 1995, 1996 by West Consulting BV
16   *
17   * Permission to use, copy, modify, and distribute this software
18   * for any purpose and without fee is hereby granted, provided
19   * that the above copyright notices appear in all copies and that
20   * both the copyright notice and this permission notice appear in
21   * supporting documentation.
22   * This software is provided "as is" without express or implied
23   * warranty.
24   * author <a href="mailto:snmp@westhawk.co.uk">Tim Panton</a>
25   * original version by hargrave@dellgate.us.dell.com (Jordan Hargrave)
26   */
27  
28  /*
29   * Copyright (C) 1996 - 2006 by Westhawk Ltd
30   * <a href="www.westhawk.co.uk">www.westhawk.co.uk</a>
31   *
32   * Permission to use, copy, modify, and distribute this software
33   * for any purpose and without fee is hereby granted, provided
34   * that the above copyright notices appear in all copies and that
35   * both the copyright notice and this permission notice appear in
36   * supporting documentation.
37   * This software is provided "as is" without express or implied
38   * warranty.
39   * author <a href="mailto:snmp@westhawk.co.uk">Tim Panton</a>
40   */
41  
42  package uk.co.westhawk.snmp.stack;
43  
44  /*-
45   * ╱╲╱╲╱╲╱╲╱╲╱╲╱╲╱╲╱╲╱╲╱╲╱╲╱╲╱╲╱╲╱╲╱╲╱╲╱╲╱╲
46   * SNMP Java Client
47   * ჻჻჻჻჻჻
48   * Copyright 2023 MetricsHub, Westhawk
49   * ჻჻჻჻჻჻
50   * This program is free software: you can redistribute it and/or modify
51   * it under the terms of the GNU Lesser General Public License as
52   * published by the Free Software Foundation, either version 3 of the
53   * License, or (at your option) any later version.
54   *
55   * This program is distributed in the hope that it will be useful,
56   * but WITHOUT ANY WARRANTY; without even the implied warranty of
57   * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
58   * GNU General Lesser Public License for more details.
59   *
60   * You should have received a copy of the GNU General Lesser Public
61   * License along with this program.  If not, see
62   * <http://www.gnu.org/licenses/lgpl-3.0.html>.
63   * ╲╱╲╱╲╱╲╱╲╱╲╱╲╱╲╱╲╱╲╱╲╱╲╱╲╱╲╱╲╱╲╱╲╱╲╱╲╱╲╱
64   */
65  
66  import uk.co.westhawk.snmp.util.SnmpUtilities;
67  
68  import java.io.ByteArrayOutputStream;
69  import java.io.IOException;
70  import java.util.Enumeration;
71  
72  /**
73   * This class contains the v3 specific methods to encode a Pdu into bytes.
74   * We split the original class AsnEncoder into four classes.
75   *
76   * @since 4_14
77   * @author <a href="mailto:snmp@westhawk.co.uk">Tim Panton</a>
78   * @version $Revision: 3.5 $ $Date: 2009/03/05 12:48:59 $
79   */
80  class AsnEncoderv3 extends AsnEncoderBase {
81  	private static final String version_id = "@(#)$Id: AsnEncoderv3.java,v 3.5 2009/03/05 12:48:59 birgita Exp $ Copyright Westhawk Ltd";
82  
83  	/**
84  	 * Encode SNMPv3 packet into bytes.
85  	 * 
86  	 * @param context      The SNMP context
87  	 * @param contextMsgId The message ID
88  	 * @param node         The time window node
89  	 * @param msg_type     The message type
90  	 * @param pduId        The PDU ID
91  	 * @param errstat      The error status
92  	 * @param errind       The error index
93  	 * @param ve           The enumeration
94  	 * @return The encoded SNMPv3 packet
95  	 */
96  	byte[] EncodeSNMPv3(SnmpContextv3Basis context, int contextMsgId, TimeWindowNode node, byte msg_type, int pduId,
97  			int errstat, int errind, Enumeration ve) throws IOException, EncodingException {
98  
99  		// Prepare the encoded message output stream to be sent to the SNMP agent
100 		ByteArrayOutputStream encodedSnmpMessageOutputStream;
101 
102 		AsnSequence asnSequence = new AsnSequence();
103 
104 		// msgGlobalData = HeaderData
105 		AsnSequence asnHeaderData = new AsnSequence();
106 		asnHeaderData.add(new AsnInteger(contextMsgId));
107 		asnHeaderData.add(new AsnInteger(context.getMaxRecvSize()));
108 		asnHeaderData.add(new AsnOctets(getMessageFlags(context, msg_type)));
109 		asnHeaderData.add(new AsnInteger(context.USM_Security_Model));
110 
111 		// msgData = ScopedPdu (plaintext or encrypted)
112 		AsnSequence asnPlainScopedPdu = new AsnSequence();
113 		asnPlainScopedPdu.add(new AsnOctets(context.getContextEngineId()));
114 		asnPlainScopedPdu.add(new AsnOctets(context.getContextName()));
115 		// PDU sequence.
116 		AsnObject asnPduObject = EncodePdu(msg_type, pduId, errstat, errind, ve);
117 		asnPlainScopedPdu.add(asnPduObject);
118 
119 		// asnSecurityParameters
120 		if (AsnObject.debug > 10) {
121 			System.out.println("\nEncode USM: node " + node.toString());
122 		}
123 		AsnSequence asnSecurityObject = new AsnSequence();
124 		byte[] engineIdBytes = SnmpUtilities.toBytes(node.getSnmpEngineId());
125 		asnSecurityObject.add(new AsnOctets(engineIdBytes));
126 		asnSecurityObject.add(new AsnInteger(node.getSnmpEngineBoots()));
127 		asnSecurityObject.add(new AsnInteger(node.getSnmpEngineTime()));
128 		asnSecurityObject.add(new AsnOctets(context.getUserName()));
129 		AsnOctets fingerPrintOctets;
130 		int authenticationProtocol = context.getAuthenticationProtocol();
131 
132 		byte[] dummyFingerprint;
133 		if (context.isUseAuthentication()) {
134 			dummyFingerprint = SnmpUtilities.initFingerprint(authenticationProtocol);
135 			fingerPrintOctets = new AsnOctets(dummyFingerprint);
136 		} else {
137 			fingerPrintOctets = new AsnOctets("");
138 		}
139 		asnSecurityObject.add(fingerPrintOctets);
140 
141 		AsnOctets privacyAsnOctets;
142 		AsnOctets asnEncryptedScopedPdu = null;
143 
144 		if (context.isUsePrivacy()) {
145 			int privacyProtocol = context.getPrivacyProtocol();
146 			// Retrieves the localized privacy key from the derived privacy key
147 			byte[] privacyKey = context.generatePrivacyKey(node.getSnmpEngineId(), authenticationProtocol, privacyProtocol);
148 			byte[] salt = null;
149 			if (SnmpContextv3Face.AES_PRIVACY_PROTOCOLS.contains(privacyProtocol)) {
150 				salt = SnmpUtilities.getSaltAES();
151 			} else {
152 				salt = SnmpUtilities.getSaltDES(node.getSnmpEngineBoots());
153 			}
154 
155 			privacyAsnOctets = new AsnOctets(salt);
156 			encodedSnmpMessageOutputStream = new ByteArrayOutputStream();
157 			asnPlainScopedPdu.write(encodedSnmpMessageOutputStream);
158 
159 			byte[] plaintext = encodedSnmpMessageOutputStream.toByteArray();
160 			byte[] encryptedText = null;
161 			if (SnmpContextv3Face.AES_PRIVACY_PROTOCOLS.contains(privacyProtocol)) {
162 				encryptedText = SnmpUtilities.AESencrypt(plaintext, privacyKey, node.getSnmpEngineBoots(),
163 						node.getSnmpEngineTime(), salt);
164 			} else {
165 				encryptedText = SnmpUtilities.DESencrypt(plaintext, privacyKey, salt);
166 			}
167 
168 			asnEncryptedScopedPdu = new AsnOctets(encryptedText);
169 			if (AsnObject.debug > 10) {
170 				System.out.println("Encrypted body  with " + SnmpContextv3Face.PROTOCOL_NAMES[privacyProtocol]);
171 			}
172 		} else {
173 			privacyAsnOctets = new AsnOctets("");
174 		}
175 		asnSecurityObject.add(privacyAsnOctets);
176 
177 		ByteArrayOutputStream secOut = new ByteArrayOutputStream();
178 		asnSecurityObject.write(secOut);
179 		byte[] bytes = secOut.toByteArray();
180 		AsnOctets asnSecurityParameters = new AsnOctets(bytes);
181 
182 		asnSequence.add(new AsnInteger(SnmpConstants.SNMP_VERSION_3));
183 		asnSequence.add(asnHeaderData);
184 		asnSequence.add(asnSecurityParameters);
185 		if (context.isUsePrivacy()) {
186 			asnSequence.add(asnEncryptedScopedPdu);
187 		} else {
188 			asnSequence.add(asnPlainScopedPdu);
189 		}
190 
191 		if (AsnObject.debug > 10) {
192 			System.out.println("\n" + getClass().getName() + ".EncodeSNMPv3(): ");
193 		}
194 		// Write SNMP object
195 		encodedSnmpMessageOutputStream = new ByteArrayOutputStream();
196 		asnSequence.write(encodedSnmpMessageOutputStream);
197 
198 		int sz = encodedSnmpMessageOutputStream.size();
199 		if (sz > context.getMaxRecvSize()) {
200 			throw new EncodingException(
201 					"Packet size (" + sz + ") is > maximum size (" + context.getMaxRecvSize() + ")");
202 		}
203 		byte[] message = encodedSnmpMessageOutputStream.toByteArray();
204 
205 		// can only do this at after building the whole message
206 		if (context.isUseAuthentication()) {
207 			byte[] computedFingerprint = null;
208 
209 			// Calculate the fingerprint
210 			computedFingerprint = context.computeFingerprint(node.getSnmpEngineId(), authenticationProtocol,
211 					computedFingerprint, message);
212 
213 			int usmPos = asnSecurityParameters.getContentsPos();
214 			int fpPos = fingerPrintOctets.getContentsPos();
215 			fpPos += usmPos;
216 			if (AsnObject.debug > 10) {
217 				int fpLength = fingerPrintOctets.getContentsLength();
218 				String str = "Pos finger print = " + fpPos + ", len = " + fpLength;
219 				SnmpUtilities.dumpBytes(str, computedFingerprint);
220 			}
221 
222 			// Copy the fingerprint to the message
223 			SnmpUtilities.copyFingerprintToSnmpMessage(authenticationProtocol, computedFingerprint, message, fpPos);
224 
225 		}
226 		return message;
227 	}
228 
229 	private byte[] getMessageFlags(SnmpContextv3Basis context, byte messageType) throws EncodingException {
230 		byte authMask = (byte) (0x0);
231 		if (context.isUseAuthentication()) {
232 			authMask = (byte) (0x1);
233 		}
234 		byte privMask = (byte) (0x0);
235 		if (context.isUsePrivacy()) {
236 			if (context.isUseAuthentication()) {
237 				privMask = (byte) (0x2);
238 			} else {
239 				throw new EncodingException("Encryption without authentication is not allowed");
240 			}
241 		}
242 		byte reportMask = (byte) (0x0);
243 		if (context.isAuthoritative(messageType) == false) {
244 			reportMask = (byte) (0x4);
245 		}
246 		byte[] msgFlags = new byte[1];
247 		msgFlags[0] = (byte) (authMask | privMask | reportMask);
248 		return msgFlags;
249 	}
250 }