def require_cryptography(obj): """ Raise exception if cryptography module is not available. Call this function in constructors. """ if not CRYPTOGRAPHY_AVAILABLE: raise UaError( "Can't use {0}, cryptography module is not installed".format( obj.__class__.__name__))
def _check_incoming_chunk(self, chunk): assert isinstance( chunk, MessageChunk), "Expected chunk, got: {}".format(chunk) if chunk.MessageHeader.MessageType != MessageType.SecureOpen: if chunk.MessageHeader.ChannelId != self.channel.SecurityToken.ChannelId: raise UaError("Wrong channel id {}, expected {}".format( chunk.MessageHeader.ChannelId, self.channel.SecurityToken.ChannelId)) if chunk.SecurityHeader.TokenId != self.channel.SecurityToken.TokenId: if chunk.SecurityHeader.TokenId not in self._old_tokens: raise UaError("Wrong token id {}, expected {}".format( chunk.SecurityHeader.TokenId, self.channel.SecurityToken.TokenId)) else: # Do some cleanup, spec says we can remove old tokens when new one are used idx = self._old_tokens.index(chunk.SecurityHeader.TokenId) if idx != 0: self._old_tokens = self._old_tokens[idx:] if self._incoming_parts: if self._incoming_parts[ 0].SequenceHeader.RequestId != chunk.SequenceHeader.RequestId: raise UaError("Wrong request id {}, expected {}".format( chunk.SequenceHeader.RequestId, self._incoming_parts[0].SequenceHeader.RequestId)) # sequence number must be incremented or wrapped num = chunk.SequenceHeader.SequenceNumber if self._peer_sequence_number is not None: if num != self._peer_sequence_number + 1: wrap = (1 << 32) - 1024 if num < 1024 and self._peer_sequence_number >= wrap: # specs Part 6, 6.7.2 logger.debug("Sequence number wrapped: %d -> %d", self._peer_sequence_number, num) else: raise UaError( "Wrong sequence {} -> {} (server bug or replay attack)" .format(self._peer_sequence_number, num)) self._peer_sequence_number = num
def encrypt_asymmetric(pubkey, data, policy_uri): """ Encrypt data with pubkey using an asymmetric algorithm. The algorithm is selected by policy_uri. Returns a tuple (encrypted_data, algorithm_uri) """ for cls in [SecurityPolicyBasic256Sha256, SecurityPolicyBasic256, SecurityPolicyBasic128Rsa15]: if policy_uri == cls.URI: return (cls.encrypt_asymmetric(pubkey, data), cls.AsymmetricEncryptionURI) if not policy_uri or policy_uri == POLICY_NONE_URI: return (data, '') raise UaError("Unsupported security policy `{0}`".format(policy_uri))
def receive_from_socket(self, socket): """ Convert binary stream to OPC UA TCP message (see OPC UA specs Part 6, 7.1: Hello, Acknowledge or ErrorMessage), or a Message object, or None (if intermediate chunk is received) """ logger.debug("Waiting for header") header = Header.from_string(socket) logger.info("received header: %s", header) body = socket.read(header.body_size) if len(body) != header.body_size: raise UaError("{} bytes expected, {} available".format( header.body_size, len(body))) return self.receive_from_header_and_body(header, utils.Buffer(body))
def __init__(self, security_policy, body=b'', msg_type=MessageType.SecureMessage, chunk_type=ChunkType.Single): self.MessageHeader = Header(msg_type, chunk_type) if msg_type in (MessageType.SecureMessage, MessageType.SecureClose): self.SecurityHeader = SymmetricAlgorithmHeader() elif msg_type == MessageType.SecureOpen: self.SecurityHeader = AsymmetricAlgorithmHeader() else: raise UaError("Unsupported message type: {}".format(msg_type)) self.SequenceHeader = SequenceHeader() self.Body = body self._security_policy = security_policy
def _receive(self, msg): self._check_incoming_chunk(msg) self._incoming_parts.append(msg) if msg.MessageHeader.ChunkType == ChunkType.Intermediate: return None if msg.MessageHeader.ChunkType == ChunkType.Abort: err = ErrorMessage.from_binary(utils.Buffer(msg.Body)) logger.warning("Message %s aborted: %s", msg, err) # specs Part 6, 6.7.3 say that aborted message shall be ignored # and SecureChannel should not be closed self._incoming_parts = [] return None elif msg.MessageHeader.ChunkType == ChunkType.Single: message = Message(self._incoming_parts) self._incoming_parts = [] return message else: raise UaError("Unsupported chunk type: {}".format(msg))