예제 #1
0
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
예제 #2
0
파일: test_utils.py 프로젝트: Delsan/coap
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
예제 #3
0
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
예제 #4
0
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)
예제 #5
0
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))