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 test_buf2int(logFixture, validint2buf): (val,_,buf) = validint2buf if not buf: return log.debug('val: {0}'.format(val)) log.debug('buf: {0}'.format(buf)) result = coapUtils.buf2int(buf) log.debug('result: {0}'.format(result)) assert result==val
def test_buf2int(logFixture, validint2buf): (val, _, buf) = validint2buf if not buf: return log.debug('val: {0}'.format(val)) log.debug('buf: {0}'.format(buf)) result = coapUtils.buf2int(buf) log.debug('result: {0}'.format(result)) assert result == val
def parseOption(message, previousOptionNumber): ''' \brief Extract an option from the beginning of a message. \param[in] message A list of bytes. \param[in] previousOptionNumber The option number from the previous option in the message; set to 0 if this is the first option. \return A tuple with the following elements: - element 0 is the option that was extracted. If no option was found (end of the options or end of the packet), None is returned. - element 1 is the message without the option. ''' log.debug('parseOption message={0} previousOptionNumber={1}'.format( u.formatBuf(message), previousOptionNumber, )) #==== detect end of packet if len(message) == 0: message = message[1:] return (None, message) #==== detect payload marker if message[0] == d.COAP_PAYLOAD_MARKER: message = message[1:] return (None, message) #==== parse option # header optionDelta = (message[0] >> 4) & 0x0f optionLength = (message[0] >> 0) & 0x0f message = message[1:] # optionDelta if optionDelta <= 12: pass elif optionDelta == 13: if len(message) < 1: raise e.messageFormatError( 'message too short, {0} bytes: no space for 1B optionDelta'. format(len(message))) optionDelta = u.buf2int(message[0:1]) + 13 message = message[1:] elif optionDelta == 14: if len(message) < 2: raise e.messageFormatError( 'message too short, {0} bytes: no space for 2B optionDelta'. format(len(message))) optionDelta = u.buf2int(message[0:2]) + 269 message = message[2:] else: raise e.messageFormatError( 'invalid optionDelta={0}'.format(optionDelta)) log.debug('optionDelta = {0}'.format(optionDelta)) # optionLength if optionLength <= 12: pass elif optionLength == 13: if len(message) < 1: raise e.messageFormatError( 'message too short, {0} bytes: no space for 1B optionLength'. format(len(message))) optionLength = u.buf2int(message[0:1]) + 13 message = message[1:] elif optionLength == 14: if len(message) < 2: raise e.messageFormatError( 'message too short, {0} bytes: no space for 2B optionLength'. format(len(message))) optionLength = u.buf2int(message[0:2]) + 269 message = message[2:] else: raise e.messageFormatError( 'invalid optionLength={0}'.format(optionLength)) log.debug('optionLength = {0}'.format(optionLength)) # optionValue if len(message) < optionLength: raise e.messageFormatError( 'message too short, {0} bytes: no space for optionValue'.format( len(message))) optionValue = message[:optionLength] message = message[optionLength:] log.debug('optionValue = {0}'.format(u.formatBuf(optionValue))) #===== create option optionNumber = previousOptionNumber + optionDelta log.debug('optionNumber = {0}'.format(optionNumber)) if optionNumber not in d.OPTION_NUM_ALL: raise e.messageFormatError( 'invalid option number {0} (0x{0:x})'.format(optionNumber)) if optionNumber == d.OPTION_NUM_URIHOST: option = UriHost(host=''.join([chr(b) for b in optionValue])) elif optionNumber == d.OPTION_NUM_URIPATH: option = UriPath(path=''.join([chr(b) for b in optionValue])) elif optionNumber == d.OPTION_NUM_CONTENTFORMAT: option = ContentFormat(cformat=optionValue) elif optionNumber == d.OPTION_NUM_BLOCK2: option = Block2(rawbytes=optionValue) elif optionNumber == d.OPTION_NUM_OBJECT_SECURITY: option = ObjectSecurity(payload=optionValue) elif optionNumber == d.OPTION_NUM_PROXYSCHEME: option = ProxyScheme(scheme=''.join([chr(b) for b in optionValue])) elif optionNumber == d.OPTION_NUM_STATELESSPROXY: option = StatelessProxy(value=optionValue) else: raise NotImplementedError( 'option {0} not implemented'.format(optionNumber)) return (option, message)
def unprotectMessage(context, version, code, options=[], ciphertext=[], partialIV=None): ''' \brief A function which verifies and decrypts the incoming CoAP message using OSCOAP. This function verifies the incoming CoAP message determined by input parameters according to draft-ietf-core-object-security-03. \param[in] Security context to use to verify+decrypt the outgoing message. \param[in] version CoAP version field of the incoming message. \param[in] code CoAP code field of the incoming message. \param[in] options A list of 'outer' options that are not encrypted. \param[in] ciphertext Ciphertext of the incoming CoAP message. \param[in] partialIV In case of request, partialIV corresponds to the one parsed from the message. In case of response, it corresponds to the appropriate partialIV used in request. Expected string of length given by the context algorithm. \return A tuple with the following elements: - element 0 is the list of inner (encrypted) CoAP options. - element 1 is the decrypted payload. ''' assert objectSecurityOptionLookUp(options) (optionsClassE, optionsClassI, optionsClassU) = _splitOptions(options) if optionsClassE: raise e.messageFormatError( 'invalid oscoap message. E-class option present in the outer message' ) if _isRequest(code): requestKid = context.recipientID if not context.replayWindowLookup(u.buf2int(u.str2buf(partialIV))): raise e.oscoapError('Replay protection failed') else: requestKid = context.senderID if isinstance(partialIV, str): partialIV = partialIV.encode(encoding='utf-8') #print(type(partialIV)) #print(partialIV) requestSeq = partialIV.lstrip(b'\0') aad = _constructAAD(version, code, u.buf2str(encodeOptions(optionsClassI)), context.aeadAlgorithm.value, requestKid, requestSeq) # construct nonce if _isRequest(code): # verifying request nonce = u.xorStrings(context.recipientIV, partialIV) else: # verifying response nonce = u.xorStrings(u.flipFirstBit(context.recipientIV), partialIV) try: plaintext = context.aeadAlgorithm.authenticateAndDecrypt( aad=aad, ciphertext=u.buf2str(ciphertext), key=context.recipientKey, nonce=nonce) except e.oscoapError: raise if _isRequest(code): context.replayWindowUpdate(u.buf2int(u.str2buf(partialIV))) # returns a tuple (innerOptions, payload) return decodeOptionsAndPayload(u.str2buf(plaintext))