def POST(self, options=[], payload=[]): link_layer_keyset = [self.networkKeyIndex, u.buf2str(self.networkKey)] configuration = {} configuration[ cojpDefines.COJP_PARAMETERS_LABELS_LLKEYSET] = link_layer_keyset configuration_serialized = cbor.dumps(configuration) respPayload = [ord(b) for b in configuration_serialized] objectSecurity = oscoap.objectSecurityOptionLookUp(options) if objectSecurity: # we need to add the pledge to a list of joined nodes, if not present already eui64 = u.buf2str(objectSecurity.kid[:-1]) found = False for node in self.joinedNodes: if node['eui64'] == eui64: found = True break if not found: self.joinedNodes += [{ 'eui64': eui64, # remove last prepended byte 'context': objectSecurity.context }] # return the Join Response regardless of whether it is a first or Nth join attempt return (d.COAP_RC_2_04_CHANGED, [], respPayload) else: return (d.COAP_RC_4_01_UNAUTHORIZED, [], [])
def _transmit(self, uri, confirmable, code, options=[], payload=[]): assert code in d.METHOD_ALL if code in [d.METHOD_GET, d.METHOD_DELETE]: assert payload == [] assert type(uri) == str (host, destPort, uriOptions) = coapUri.uri2options(uri) destIp = socket.getaddrinfo(host, destPort)[0][4][0] (securityContext, sequenceNumber) = oscoap.getRequestSecurityParams( oscoap.objectSecurityOptionLookUp(options)) with self.transmittersLock: self._cleanupTransmitter() messageId = self._getMessageID(destIp, destPort) token = self._getToken(destIp, destPort) newTransmitter = coapTransmitter.coapTransmitter( sendFunc=self.socketUdp.sendUdp, srcIp=self.ipAddress, srcPort=self.udpPort, destIp=destIp, destPort=destPort, confirmable=confirmable, messageId=messageId, code=code, token=token, options=uriOptions + options, payload=payload, securityContext=securityContext, requestSeq=sequenceNumber, ackTimeout=self.ackTimeout, respTimeout=self.respTimeout, maxRetransmit=self.maxRetransmit) key = (destIp, destPort, token, messageId) assert key not in self.transmitters.keys() self.transmitters[key] = newTransmitter response = newTransmitter.transmit() if securityContext: try: (innerOptions, plaintext) = oscoap.unprotectMessage( securityContext, version=response['version'], code=response['code'], options=response['options'], ciphertext=response['ciphertext'], partialIV=sequenceNumber, ) response['options'] = response['options'] + innerOptions response['payload'] = plaintext except e.oscoapError: raise return response
def parseMessage(message): returnVal = {} # header if len(message) < 4: raise e.messageFormatError( 'message too short, {0} bytes: no space for header'.format( len(message))) returnVal['version'] = (message[0] >> 6) & 0x03 if returnVal['version'] != d.COAP_VERSION: raise e.messageFormatError('invalid CoAP version {0}'.format( returnVal['version'])) returnVal['type'] = (message[0] >> 4) & 0x03 if returnVal['type'] not in d.TYPE_ALL: raise e.messageFormatError('invalid message type {0}'.format( returnVal['type'])) TKL = message[0] & 0x0f if TKL > 8: raise e.messageFormatError('TKL too large {0}'.format(TKL)) returnVal['code'] = message[1] returnVal['messageId'] = u.buf2int(message[2:4]) message = message[4:] # token if len(message) < TKL: raise e.messageFormatError( 'message too short, {0} bytes: no space for token'.format( len(message))) if TKL: returnVal['token'] = u.buf2int(message[:TKL]) message = message[TKL:] else: returnVal['token'] = None # outer options and payload/ciphertext (returnVal['options'], payload) = decodeOptionsAndPayload(message) # if object security option is present decode the value in order to be able to decrypt the message objectSecurity = oscoap.objectSecurityOptionLookUp(returnVal['options']) if objectSecurity: oscoapDict = oscoap.parseObjectSecurity( objectSecurity.getPayloadBytes(), payload) objectSecurity.setKid(oscoapDict['kid']) returnVal.update(oscoapDict) else: returnVal['payload'] = payload log.debug('parsed message: {0}'.format(returnVal)) return returnVal
def GET(self, options=[]): respCode = d.COAP_RC_2_05_CONTENT respOptions = [] k1 = {} k1[coseDefines.KEY_LABEL_KTY] = coseDefines.KEY_VALUE_SYMMETRIC k1[coseDefines.KEY_LABEL_KID] = u.buf2str(self.networkKeyIndex) k1[coseDefines.KEY_LABEL_K] = u.buf2str(self.networkKey) join_response = [[k1]] join_response_serialized = cbor.dumps(join_response) respPayload = [ord(b) for b in join_response_serialized] objectSecurity = oscoap.objectSecurityOptionLookUp(options) assert objectSecurity self.joinedNodes += [{ 'eui64': u.buf2str(objectSecurity.kid[:8]), # remove last prepended byte 'context': objectSecurity.context }] return (respCode, respOptions, respPayload)
def _receive(self, timestamp, sender, rawbytes): # all UDP packets are received here output = [] output += ['\n{0} _receive message:'.format(self.name)] output += ['- timestamp: {0}'.format(timestamp)] output += ['- sender: {0}'.format(sender)] output += ['- bytes: {0}'.format(u.formatBuf(rawbytes))] output = '\n'.join(output) log.debug(output) srcIp = sender[0] srcIp = u.trimAddress(srcIp) srcPort = sender[1] # parse messages try: message = m.parseMessage(rawbytes) options = message['options'] except e.messageFormatError as err: log.warning('malformed message {0}: {1}'.format( u.formatBuf(rawbytes), str(err))) return # dispatch message try: if message['code'] in d.METHOD_ALL: # this is meant for a resource (request) #==== decrypt message if encrypted innerOptions = [] foundContext = None requestPartialIV = None if 'ciphertext' in message.keys(): # retrieve security context # before decrypting we don't know what resource this request is meant for # so we take the first binding with the correct context (recipientID) blindContext = self._securityContextLookup( u.buf2str(message['kid'])) if not blindContext: if self.secContextHandler: appContext = self.secContextHandler( u.buf2str(message['kid'])) if not appContext: raise e.coapRcUnauthorized( 'Security context not found.') else: raise e.coapRcUnauthorized( 'Security context not found.') foundContext = blindContext if blindContext != None else appContext requestPartialIV = u.zeroPadString( u.buf2str(message['partialIV']), foundContext.getIVLength()) # decrypt the message try: (innerOptions, plaintext) = oscoap.unprotectMessage( foundContext, version=message['version'], code=message['code'], options=message['options'], ciphertext=message['ciphertext'], partialIV=requestPartialIV) except e.oscoapError as err: raise e.coapRcBadRequest( 'OSCOAP unprotect failed: {0}'.format(str(err))) payload = plaintext else: # message not encrypted payload = message['payload'] options += innerOptions #==== find right resource # retrieve path path = coapUri.options2path(options) log.debug('path="{0}"'.format(path)) # find resource that matches this path resource = None with self.resourceLock: for r in self.resources: if r.matchesPath(path): resource = r break log.debug('resource={0}'.format(resource)) if not resource: raise e.coapRcNotFound() #==== check if appropriate security context was used for the resource (context, authorizedMethods) = resource.getSecurityBinding() if context is not None: if context != foundContext: raise e.coapRcUnauthorized( 'Unauthorized security context for the given resource' ) objectSecurity = oscoap.objectSecurityOptionLookUp(options) if objectSecurity: objectSecurity.setContext(foundContext) #==== get a response # call the right resource's method try: if message[ 'code'] == d.METHOD_GET and d.METHOD_GET in authorizedMethods: (respCode, respOptions, respPayload) = resource.GET(options=options) elif message[ 'code'] == d.METHOD_POST and d.METHOD_POST in authorizedMethods: (respCode, respOptions, respPayload) = resource.POST(options=options, payload=payload) elif message[ 'code'] == d.METHOD_PUT and d.METHOD_PUT in authorizedMethods: (respCode, respOptions, respPayload) = resource.PUT(options=options, payload=payload) elif message[ 'code'] == d.METHOD_DELETE and d.METHOD_DELETE in authorizedMethods: (respCode, respOptions, respPayload) = resource.DELETE(options=options) elif message['code'] not in d.METHOD_ALL: raise SystemError('unexpected code {0}'.format( message['code'])) else: raise e.coapRcUnauthorized( 'Unauthorized method for the given resource') except Exception as err: if isinstance(err, e.coapRc): raise else: raise e.coapRcInternalServerError() #==== send back response # determine type of response packet if message['type'] == d.TYPE_CON: responseType = d.TYPE_ACK elif message['type'] == d.TYPE_NON: responseType = d.TYPE_NON else: raise SystemError('unexpected type {0}'.format( message['type'])) # if resource is protected with a security context, add Object-Security option if foundContext: # verify that the Object-Security option was not set by the resource handler assert not any( isinstance(option, o.ObjectSecurity) for option in respOptions) objectSecurity = o.ObjectSecurity(context=foundContext) respOptions += [objectSecurity] # if Stateless-Proxy option was present in the request echo it for option in options: if isinstance(option, o.StatelessProxy): respOptions += [option] break # build response packets and pass partialIV from the request for OSCOAP's processing response = m.buildMessage(msgtype=responseType, token=message['token'], code=respCode, messageId=message['messageId'], options=respOptions, payload=respPayload, securityContext=foundContext, partialIV=requestPartialIV) # send self.socketUdp.sendUdp( destIp=srcIp, destPort=srcPort, msg=response, ) elif message['code'] in d.COAP_RC_ALL: # this is meant for a transmitter (response) # find transmitter msgkey = (srcIp, srcPort, message['token'], message['messageId']) found = False with self.transmittersLock: self._cleanupTransmitter() for (k, v) in self.transmitters.items(): # try matching if (msgkey[0] == k[0] and msgkey[1] == k[1] and (msgkey[2] == k[2] or msgkey[3] == k[3])): found = True v.receiveMessage(timestamp, srcIp, srcPort, message) break if found == False: raise e.coapRcBadRequest( 'could not find transmitter corresponding to {0}, transmitters are {1}' .format( msgkey, ','.join( [str(k) for k in self.transmitters.keys()]))) else: raise NotImplementedError() except e.coapRc as err: # log log.warning(err) # determine type of response packet if message['type'] == d.TYPE_CON: responseType = d.TYPE_ACK elif message['type'] == d.TYPE_NON: responseType = d.TYPE_NON else: raise SystemError('unexpected type {0}'.format( message['type'])) # if Stateless-Proxy option was present in the request echo it errorOptions = [] for option in options: if isinstance(option, o.StatelessProxy): errorOptions += [option] break # build response packets response = m.buildMessage( msgtype=responseType, token=message['token'], code=err.rc, messageId=message['messageId'], options=errorOptions, ) # send self.socketUdp.sendUdp( destIp=srcIp, destPort=srcPort, msg=response, ) except Exception as err: log.critical(traceback.format_exc())