Repository: oasis-open/cti-python-stix2
Pull request #285 (2019-08-19 13:40:47)

WD 05 SCO


Patch:
diff --git a/docs/guide/datastore.ipynb b/docs/guide/datastore.ipynb
index 1c88cf1..1ea05ee 100644
--- a/docs/guide/datastore.ipynb
+++ b/docs/guide/datastore.ipynb
@@ -450,6 +450,14 @@
     "mem.source.filters.add([f1,f2])"
    ]
   },
+  {
+   "cell_type": "markdown",
+   "metadata": {},
+   "source": [
+    "##### Note: The `defanged` property is now always included (implicitly) for STIX 2.1 Cyber Observable Objects (SCOs)\n",
+    "This is important to remember if you are writing a filter that involves checking the `objects` property of a STIX 2.1 `ObservedData` object. If any of the objects associated with the `objects` property are STIX 2.1 SCOs, then your filter must include the `defanged` property. For an example, refer to `filters[14]` & `filters[15]` in stix2/test/v21/test_datastore_filters.py "
+   ]
+  },
   {
    "cell_type": "markdown",
    "metadata": {},
@@ -726,21 +734,21 @@
  ],
  "metadata": {
   "kernelspec": {
-   "display_name": "cti-python-stix2",
+   "display_name": "Python 3",
    "language": "python",
-   "name": "cti-python-stix2"
+   "name": "python3"
   },
   "language_info": {
    "codemirror_mode": {
     "name": "ipython",
-    "version": 2
+    "version": 3
    },
    "file_extension": ".py",
    "mimetype": "text/x-python",
    "name": "python",
    "nbconvert_exporter": "python",
-   "pygments_lexer": "ipython2",
-   "version": "2.7.12"
+   "pygments_lexer": "ipython3",
+   "version": "3.6.7"
   }
  },
  "nbformat": 4,
diff --git a/stix2/base.py b/stix2/base.py
index 3874721..dbe6461 100644
--- a/stix2/base.py
+++ b/stix2/base.py
@@ -3,10 +3,13 @@
 import collections
 import copy
 import datetime as dt
+import uuid
 
 import simplejson as json
 import six
 
+from stix2.canonicalization.Canonicalize import canonicalize
+
 from .exceptions import (
     AtLeastOnePropertyError, DependentPropertiesError, ExtraPropertiesError,
     ImmutableError, InvalidObjRefError, InvalidValueError,
@@ -112,10 +115,15 @@ class _STIXBase(collections.Mapping):
     def _check_at_least_one_property(self, list_of_properties=None):
         if not list_of_properties:
             list_of_properties = sorted(list(self.__class__._properties.keys()))
-            if 'type' in list_of_properties:
-                list_of_properties.remove('type')
+            if isinstance(self, _Observable):
+                props_to_remove = ["type", "id", "defanged", "spec_version"]
+            else:
+                props_to_remove = ["type"]
+
+            list_of_properties = [prop for prop in list_of_properties if prop not in props_to_remove]
         current_properties = self.properties_populated()
         list_of_properties_populated = set(list_of_properties).intersection(current_properties)
+
         if list_of_properties and (not list_of_properties_populated or list_of_properties_populated == set(['extensions'])):
             raise AtLeastOnePropertyError(self.__class__, list_of_properties)
 
@@ -300,9 +308,26 @@ class _Observable(_STIXBase):
         self.__allow_custom = kwargs.get('allow_custom', False)
         self._properties['extensions'].allow_custom = kwargs.get('allow_custom', False)
 
+        try:
+            # Since `spec_version` is optional, this is how we check for a 2.1 SCO
+            self._id_contributing_properties
+
+            if 'id' not in kwargs:
+                possible_id = self._generate_id(kwargs)
+                if possible_id is not None:
+                    kwargs['id'] = possible_id
+        except AttributeError:
+            # End up here if handling a 2.0 SCO, and don't need to do anything further
+            pass
+
         super(_Observable, self).__init__(**kwargs)
 
     def _check_ref(self, ref, prop, prop_name):
+        """
+        Only for checking `*_ref` or `*_refs` properties in spec_version 2.0
+        STIX Cyber Observables (SCOs)
+        """
+
         if '*' in self._STIXBase__valid_refs:
             return  # don't check if refs are valid
 
@@ -331,12 +356,52 @@ class _Observable(_STIXBase):
         if prop_name not in kwargs:
             return
 
+        from .properties import ObjectReferenceProperty
         if prop_name.endswith('_ref'):
-            ref = kwargs[prop_name]
-            self._check_ref(ref, prop, prop_name)
-        elif prop_name.endswith('_refs'):
-            for ref in kwargs[prop_name]:
+            if isinstance(prop, ObjectReferenceProperty):
+                ref = kwargs[prop_name]
                 self._check_ref(ref, prop, prop_name)
+        elif prop_name.endswith('_refs'):
+            if isinstance(prop.contained, ObjectReferenceProperty):
+                for ref in kwargs[prop_name]:
+                    self._check_ref(ref, prop, prop_name)
+
+    def _generate_id(self, kwargs):
+        required_prefix = self._type + "--"
+        namespace = uuid.UUID("00abedb4-aa42-466c-9c01-fed23315a9b7")
+
+        properties_to_use = self._id_contributing_properties
+        if properties_to_use:
+            streamlined_obj_vals = []
+            if "hashes" in kwargs and "hashes" in properties_to_use:
+                possible_hash = _choose_one_hash(kwargs["hashes"])
+                if possible_hash:
+                    streamlined_obj_vals.append(possible_hash)
+            for key in properties_to_use:
+                if key != "hashes" and key in kwargs:
+                    if isinstance(kwargs[key], dict) or isinstance(kwargs[key], _STIXBase):
+                        temp_deep_copy = copy.deepcopy(dict(kwargs[key]))
+                        _recursive_stix_to_dict(temp_deep_copy)
+                        streamlined_obj_vals.append(temp_deep_copy)
+                    elif isinstance(kwargs[key], list) and isinstance(kwargs[key][0], _STIXBase):
+                        for obj in kwargs[key]:
+                            temp_deep_copy = copy.deepcopy(dict(obj))
+                            _recursive_stix_to_dict(temp_deep_copy)
+                            streamlined_obj_vals.append(temp_deep_copy)
+                    else:
+                        streamlined_obj_vals.append(kwargs[key])
+
+            if streamlined_obj_vals:
+                data = canonicalize(streamlined_obj_vals, utf8=False)
+
+                # try/except here to enable python 2 compatibility
+                try:
+                    return required_prefix + six.text_type(uuid.uuid5(namespace, data))
+                except UnicodeDecodeError:
+                    return required_prefix + six.text_type(uuid.uuid5(namespace, six.binary_type(data)))
+
+        # We return None if there are no values specified for any of the id-contributing-properties
+        return None
 
 
 class _Extension(_STIXBase):
@@ -346,6 +411,34 @@ class _Extension(_STIXBase):
         self._check_at_least_one_property()
 
 
+def _choose_one_hash(hash_dict):
+    if "MD5" in hash_dict:
+        return {"MD5": hash_dict["MD5"]}
+    elif "SHA-1" in hash_dict:
+        return {"SHA-1": hash_dict["SHA-1"]}
+    elif "SHA-256" in hash_dict:
+        return {"SHA-256": hash_dict["SHA-256"]}
+    elif "SHA-512" in hash_dict:
+        return {"SHA-512": hash_dict["SHA-512"]}
+    else:
+        k = next(iter(hash_dict), None)
+        if k is not None:
+            return {k: hash_dict[k]}
+
+
 def _cls_init(cls, obj, kwargs):
     if getattr(cls, '__init__', object.__init__) is not object.__init__:
         cls.__init__(obj, **kwargs)
+
+
+def _recursive_stix_to_dict(input_dict):
+    for key in input_dict:
+        if isinstance(input_dict[key], dict):
+            _recursive_stix_to_dict(input_dict[key])
+        elif isinstance(input_dict[key], _STIXBase):
+            input_dict[key] = dict(input_dict[key])
+
+            # There may stil be nested _STIXBase objects
+            _recursive_stix_to_dict(input_dict[key])
+        else:
+            return
diff --git a/stix2/canonicalization/Canonicalize.py b/stix2/canonicalization/Canonicalize.py
new file mode 100644
index 0000000..78145be
--- /dev/null
+++ b/stix2/canonicalization/Canonicalize.py
@@ -0,0 +1,512 @@
+##############################################################################
+#                                                                            #
+#  Copyright 2006-2019 WebPKI.org (http://webpki.org).                       #
+#                                                                            #
+#  Licensed under the Apache License, Version 2.0 (the "License");           #
+#  you may not use this file except in compliance with the License.          #
+#  You may obtain a copy of the License at                                   #
+#                                                                            #
+#      https://www.apache.org/licenses/LICENSE-2.0                           #
+#                                                                            #
+#  Unless required by applicable law or agreed to in writing, software       #
+#  distributed under the License is distributed on an "AS IS" BASIS,         #
+#  WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.  #
+#  See the License for the specific language governing permissions and       #
+#  limitations under the License.                                            #
+#                                                                            #
+##############################################################################
+
+#################################################
+# JCS compatible JSON serializer for Python 3.x #
+#################################################
+
+# This file has been modified to be compatible with Python 2.x as well
+
+import re
+
+import six
+
+from stix2.canonicalization.NumberToJson import convert2Es6Format
+
+try:
+    from _json import encode_basestring_ascii as c_encode_basestring_ascii
+except ImportError:
+    c_encode_basestring_ascii = None
+try:
+    from _json import encode_basestring as c_encode_basestring
+except ImportError:
+    c_encode_basestring = None
+try:
+    from _json import make_encoder as c_make_encoder
+except ImportError:
+    c_make_encoder = None
+
+ESCAPE = re.compile(r'[\x00-\x1f\\"\b\f\n\r\t]')
+ESCAPE_ASCII = re.compile(r'([\\"]|[^\ -~])')
+HAS_UTF8 = re.compile(b'[\x80-\xff]')
+ESCAPE_DCT = {
+    '\\': '\\\\',
+    '"': '\\"',
+    '\b': '\\b',
+    '\f': '\\f',
+    '\n': '\\n',
+    '\r': '\\r',
+    '\t': '\\t',
+}
+for i in range(0x20):
+    ESCAPE_DCT.setdefault(chr(i), '\\u{0:04x}'.format(i))
+
+INFINITY = float('inf')
+
+
+def py_encode_basestring(s):
+    """Return a JSON representation of a Python string
+
+    """
+    def replace(match):
+        return ESCAPE_DCT[match.group(0)]
+    return '"' + ESCAPE.sub(replace, s) + '"'
+
+
+encode_basestring = (c_encode_basestring or py_encode_basestring)
+
+
+def py_encode_basestring_ascii(s):
+    """Return an ASCII-only JSON representation of a Python string
+
+    """
+    def replace(match):
+        s = match.group(0)
+        try:
+            return ESCAPE_DCT[s]
+        except KeyError:
+            n = ord(s)
+            if n < 0x10000:
+                return '\\u{0:04x}'.format(n)
+            else:
+                # surrogate pair
+                n -= 0x10000
+                s1 = 0xd800 | ((n >> 10) & 0x3ff)
+                s2 = 0xdc00 | (n & 0x3ff)
+                return '\\u{0:04x}\\u{1:04x}'.format(s1, s2)
+    return '"' + ESCAPE_ASCII.sub(replace, s) + '"'
+
+
+encode_basestring_ascii = (
+    c_encode_basestring_ascii or py_encode_basestring_ascii
+)
+
+
+class JSONEncoder(object):
+    """Extensible JSON <http://json.org> encoder for Python data structures.
+
+    Supports the following objects and types by default:
+
+    +-------------------+---------------+
+    | Python            | JSON          |
+    +===================+===============+
+    | dict              | object        |
+    +-------------------+---------------+
+    | list, tuple       | array         |
+    +-------------------+---------------+
+    | str               | string        |
+    +-------------------+---------------+
+    | int, float        | number        |
+    +-------------------+---------------+
+    | True              | true          |
+    +-------------------+---------------+
+    | False             | false         |
+    +-------------------+---------------+
+    | None              | null          |
+    +-------------------+---------------+
+
+    To extend this to recognize other objects, subclass and implement a
+    ``.default()`` method with another method that returns a serializable
+    object for ``o`` if possible, otherwise it should call the superclass
+    implementation (to raise ``TypeError``).
+
+    """
+    item_separator = ', '
+    key_separator = ': '
+
+    def __init__(
+        self, skipkeys=False, ensure_ascii=False,
+        check_circular=True, allow_nan=True, sort_keys=True,
+        indent=None, separators=(',', ':'), default=None,
+    ):
+        """Constructor for JSONEncoder, with sensible defaults.
+
+        If skipkeys is false, then it is a TypeError to attempt
+        encoding of keys that are not str, int, float or None.  If
+        skipkeys is True, such items are simply skipped.
+
+        If ensure_ascii is true, the output is guaranteed to be str
+        objects with all incoming non-ASCII characters escaped.  If
+        ensure_ascii is false, the output can contain non-ASCII characters.
+
+        If check_circular is true, then lists, dicts, and custom encoded
+        objects will be checked for circular references during encoding to
+        prevent an infinite recursion (which would cause an OverflowError).
+        Otherwise, no such check takes place.
+
+        If allow_nan is true, then NaN, Infinity, and -Infinity will be
+        encoded as such.  This behavior is not JSON specification compliant,
+        but is consistent with most JavaScript based encoders and decoders.
+        Otherwise, it will be a ValueError to encode such floats.
+
+        If sort_keys is true, then the output of dictionaries will be
+        sorted by key; this is useful for regression tests to ensure
+        that JSON serializations can be compared on a day-to-day basis.
+
+        If indent is a non-negative integer, then JSON array
+        elements and object members will be pretty-printed with that
+        indent level.  An indent level of 0 will only insert newlines.
+        None is the most compact representation.
+
+        If specified, separators should be an (item_separator, key_separator)
+        tuple.  The default is (', ', ': ') if *indent* is ``None`` and
+        (',', ': ') otherwise.  To get the most compact JSON representation,
+        you should specify (',', ':') to eliminate whitespace.
+
+        If specified, default is a function that gets called for objects
+        that can't otherwise be serialized.  It should return a JSON encodable
+        version of the object or raise a ``TypeError``.
+
+        """
+
+        self.skipkeys = skipkeys
+        self.ensure_ascii = ensure_ascii
+        self.check_circular = check_circular
+        self.allow_nan = allow_nan
+        self.sort_keys = sort_keys
+        self.indent = indent
+        if separators is not None:
+            self.item_separator, self.key_separator = separators
+        elif indent is not None:
+            self.item_separator = ','
+        if default is not None:
+            self.default = default
+
+    def default(self, o):
+        """Implement this method in a subclass such that it returns
+        a serializable object for ``o``, or calls the base implementation
+        (to raise a ``TypeError``).
+
+        For example, to support arbitrary iterators, you could
+        implement default like this::
+
+            def default(self, o):
+                try:
+                    iterable = iter(o)
+                except TypeError:
+                    pass
+                else:
+                    return list(iterable)
+                # Let the base class default method raise the TypeError
+                return JSONEncoder.default(self, o)
+
+        """
+        raise TypeError(
+            "Object of type '%s' is not JSON serializable" %
+            o.__class__.__name__,
+        )
+
+    def encode(self, o):
+        """Return a JSON string representation of a Python data structure.
+
+        >>> from json.encoder import JSONEncoder
+        >>> JSONEncoder().encode({"foo": ["bar", "baz"]})
+        '{"foo": ["bar", "baz"]}'
+
+        """
+        # This is for extremely simple cases and benchmarks.
+        if isinstance(o, str):
+            if self.ensure_ascii:
+                return encode_basestring_ascii(o)
+            else:
+                return encode_basestring(o)
+        # This doesn't pass the iterator directly to ''.join() because the
+        # exceptions aren't as detailed.  The list call should be roughly
+        # equivalent to the PySequence_Fast that ''.join() would do.
+        chunks = self.iterencode(o, _one_shot=False)
+        if not isinstance(chunks, (list, tuple)):
+            chunks = list(chunks)
+        return ''.join(chunks)
+
+    def iterencode(self, o, _one_shot=False):
+        """Encode the given object and yield each string
+        representation as available.
+
+        For example::
+
+            for chunk in JSONEncoder().iterencode(bigobject):
+                mysocket.write(chunk)
+
+        """
+        if self.check_circular:
+            markers = {}
+        else:
+            markers = None
+        if self.ensure_ascii:
+            _encoder = encode_basestring_ascii
+        else:
+            _encoder = encode_basestring
+
+        def floatstr(
+            o, allow_nan=self.allow_nan,
+            _repr=float.__repr__, _inf=INFINITY, _neginf=-INFINITY,
+        ):
+            # Check for specials.  Note that this type of test is processor
+            # and/or platform-specific, so do tests which don't depend on the
+            # internals.
+
+            if o != o:
+                text = 'NaN'
+            elif o == _inf:
+                text = 'Infinity'
+            elif o == _neginf:
+                text = '-Infinity'
+            else:
+                return _repr(o)
+
+            if not allow_nan:
+                raise ValueError(
+                    "Out of range float values are not JSON compliant: " +
+                    repr(o),
+                )
+
+            return text
+
+        if (
+            _one_shot and c_make_encoder is not None
+            and self.indent is None
+        ):
+            _iterencode = c_make_encoder(
+                markers, self.default, _encoder, self.indent,
+                self.key_separator, self.item_separator, self.sort_keys,
+                self.skipkeys, self.allow_nan,
+            )
+        else:
+            _iterencode = _make_iterencode(
+                markers, self.default, _encoder, self.indent, floatstr,
+                self.key_separator, self.item_separator, self.sort_keys,
+                self.skipkeys, _one_shot,
+            )
+        return _iterencode(o, 0)
+
+
+def _make_iterencode(
+    markers, _default, _encoder, _indent, _floatstr,
+        _key_separator, _item_separator, _sort_keys, _skipkeys, _one_shot,
+        # HACK: hand-optimized bytecode; turn globals into locals
+        ValueError=ValueError,
+        dict=dict,
+        float=float,
+        id=id,
+        int=int,
+        isinstance=isinstance,
+        list=list,
+        str=str,
+        tuple=tuple,
+        _intstr=int.__str__,
+):
+
+    if _indent is not None and not isinstance(_indent, str):
+        _indent = ' ' * _indent
+
+    def _iterencode_list(lst, _current_indent_level):
+        if not lst:
+            yield '[]'
+            return
+        if markers is not None:
+            markerid = id(lst)
+            if markerid in markers:
+                raise ValueError("Circular reference detected")
+            markers[markerid] = lst
+        buf = '['
+        if _indent is not None:
+            _current_indent_level += 1
+            newline_indent = '\n' + _indent * _current_indent_level
+            separator = _item_separator + newline_indent
+            buf += newline_indent
+        else:
+            newline_indent = None
+            separator = _item_separator
+        first = True
+        for value in lst:
+            if first:
+                first = False
+            else:
+                buf = separator
+            if isinstance(value, str):
+                yield buf + _encoder(value)
+            elif value is None:
+                yield buf + 'null'
+            elif value is True:
+                yield buf + 'true'
+            elif value is False:
+                yield buf + 'false'
+            elif isinstance(value, int):
+                # Subclasses of int/float may override __str__, but we still
+                # want to encode them as integers/floats in JSON. One example
+                # within the standard library is IntEnum.
+                yield buf + convert2Es6Format(value)
+            elif isinstance(value, float):
+                # see comment above for int
+                yield buf + convert2Es6Format(value)
+            else:
+                yield buf
+                if isinstance(value, (list, tuple)):
+                    chunks = _iterencode_list(value, _current_indent_level)
+                elif isinstance(value, dict):
+                    chunks = _iterencode_dict(value, _current_indent_level)
+                else:
+                    chunks = _iterencode(value, _current_indent_level)
+                # Below line commented-out for python2 compatibility
+                # yield from chunks
+                for chunk in chunks:
+                    yield chunk
+        if newline_indent is not None:
+            _current_indent_level -= 1
+            yield '\n' + _indent * _current_indent_level
+        yield ']'
+        if markers is not None:
+            del markers[markerid]
+
+    def _iterencode_dict(dct, _current_indent_level):
+        if not dct:
+            yield '{}'
+            return
+        if markers is not None:
+            markerid = id(dct)
+            if markerid in markers:
+                raise ValueError("Circular reference detected")
+            markers[markerid] = dct
+        yield '{'
+        if _indent is not None:
+            _current_indent_level += 1
+            newline_indent = '\n' + _indent * _current_indent_level
+            item_separator = _item_separator + newline_indent
+            yield newline_indent
+        else:
+            newline_indent = None
+            item_separator = _item_separator
+        first = True
+        if _sort_keys:
+            items = sorted(dct.items(), key=lambda kv: kv[0].encode('utf-16_be'))
+        else:
+            items = dct.items()
+        for key, value in items:
+            # Replaced isinstance(key, str) with below to enable simultaneous python 2 & 3 compatibility
+            if isinstance(key, six.string_types) or isinstance(key, six.binary_type):
+                pass
+            # JavaScript is weakly typed for these, so it makes sense to
+            # also allow them.  Many encoders seem to do something like this.
+            elif isinstance(key, float):
+                # see comment for int/float in _make_iterencode
+                key = convert2Es6Format(key)
+            elif key is True:
+                key = 'true'
+            elif key is False:
+                key = 'false'
+            elif key is None:
+                key = 'null'
+            elif isinstance(key, int):
+                # see comment for int/float in _make_iterencode
+                key = convert2Es6Format(key)
+            elif _skipkeys:
+                continue
+            else:
+                raise TypeError("key " + repr(key) + " is not a string")
+            if first:
+                first = False
+            else:
+                yield item_separator
+            yield _encoder(key)
+            yield _key_separator
+            if isinstance(value, str):
+                yield _encoder(value)
+            elif value is None:
+                yield 'null'
+            elif value is True:
+                yield 'true'
+            elif value is False:
+                yield 'false'
+            elif isinstance(value, int):
+                # see comment for int/float in _make_iterencode
+                yield convert2Es6Format(value)
+            elif isinstance(value, float):
+                # see comment for int/float in _make_iterencode
+                yield convert2Es6Format(value)
+            else:
+                if isinstance(value, (list, tuple)):
+                    chunks = _iterencode_list(value, _current_indent_level)
+                elif isinstance(value, dict):
+                    chunks = _iterencode_dict(value, _current_indent_level)
+                else:
+                    chunks = _iterencode(value, _current_indent_level)
+                # Below line commented-out for python2 compatibility
+                # yield from chunks
+                for chunk in chunks:
+                    yield chunk
+        if newline_indent is not None:
+            _current_indent_level -= 1
+            yield '\n' + _indent * _current_indent_level
+        yield '}'
+        if markers is not None:
+            del markers[markerid]
+
+    def _iterencode(o, _current_indent_level):
+        # Replaced isinstance(o, str) with below to enable simultaneous python 2 & 3 compatibility
+        if isinstance(o, six.string_types) or isinstance(o, six.binary_type):
+            yield _encoder(o)
+        elif o is None:
+            yield 'null'
+        elif o is True:
+            yield 'true'
+        elif o is False:
+            yield 'false'
+        elif isinstance(o, int):
+            # see comment for int/float in _make_iterencode
+            yield convert2Es6Format(o)
+        elif isinstance(o, float):
+            # see comment for int/float in _make_iterencode
+            yield convert2Es6Format(o)
+        elif isinstance(o, (list, tuple)):
+            # Below line commented-out for python2 compatibility
+            # yield from _iterencode_list(o, _current_indent_level)
+            for thing in _iterencode_list(o, _current_indent_level):
+                yield thing
+        elif isinstance(o, dict):
+            # Below line commented-out for python2 compatibility
+            # yield from _iterencode_dict(o, _current_indent_level)
+            for thing in _iterencode_dict(o, _current_indent_level):
+                yield thing
+        else:
+            if markers is not None:
+                markerid = id(o)
+                if markerid in markers:
+                    raise ValueError("Circular reference detected")
+                markers[markerid] = o
+            o = _default(o)
+            # Below line commented-out for python2 compatibility
+            # yield from _iterencode(o, _current_indent_level)
+            for thing in _iterencode(o, _current_indent_level):
+                yield thing
+            if markers is not None:
+                del markers[markerid]
+    return _iterencode
+
+
+def canonicalize(obj, utf8=True):
+    textVal = JSONEncoder(sort_keys=True).encode(obj)
+    if utf8:
+        return textVal.encode()
+    return textVal
+
+
+def serialize(obj, utf8=True):
+    textVal = JSONEncoder(sort_keys=False).encode(obj)
+    if utf8:
+        return textVal.encode()
+    return textVal
diff --git a/stix2/canonicalization/NumberToJson.py b/stix2/canonicalization/NumberToJson.py
new file mode 100644
index 0000000..cea54d0
--- /dev/null
+++ b/stix2/canonicalization/NumberToJson.py
@@ -0,0 +1,95 @@
+##############################################################################
+#                                                                            #
+#  Copyright 2006-2019 WebPKI.org (http://webpki.org).                       #
+#                                                                            #
+#  Licensed under the Apache License, Version 2.0 (the "License");           #
+#  you may not use this file except in compliance with the License.          #
+#  You may obtain a copy of the License at                                   #
+#                                                                            #
+#      https://www.apache.org/licenses/LICENSE-2.0                           #
+#                                                                            #
+#  Unless required by applicable law or agreed to in writing, software       #
+#  distributed under the License is distributed on an "AS IS" BASIS,         #
+#  WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.  #
+#  See the License for the specific language governing permissions and       #
+#  limitations under the License.                                            #
+#                                                                            #
+##############################################################################
+
+
+##################################################################
+# Convert a Python double/float into an ES6/V8 compatible string #
+##################################################################
+def convert2Es6Format(value):
+    # Convert double/float to str using the native Python formatter
+    fvalue = float(value)
+
+    # Zero is a special case.  The following line takes "-0" case as well
+    if fvalue == 0:
+        return '0'
+
+    # The rest of the algorithm works on the textual representation only
+    pyDouble = str(fvalue)
+
+    # The following line catches the "inf" and "nan" values returned by str(fvalue)
+    if pyDouble.find('n') >= 0:
+        raise ValueError("Invalid JSON number: " + pyDouble)
+
+    # Save sign separately, it doesn't have any role in the algorithm
+    pySign = ''
+    if pyDouble.find('-') == 0:
+        pySign = '-'
+        pyDouble = pyDouble[1:]
+
+    # Now we should only have valid non-zero values
+    pyExpStr = ''
+    pyExpVal = 0
+    q = pyDouble.find('e')
+    if q > 0:
+        # Grab the exponent and remove it from the number
+        pyExpStr = pyDouble[q:]
+        if pyExpStr[2:3] == '0':
+            # Supress leading zero on exponents
+            pyExpStr = pyExpStr[:2] + pyExpStr[3:]
+        pyDouble = pyDouble[0:q]
+        pyExpVal = int(pyExpStr[1:])
+
+    # Split number in pyFirst + pyDot + pyLast
+    pyFirst = pyDouble
+    pyDot = ''
+    pyLast = ''
+    q = pyDouble.find('.')
+    if q > 0:
+        pyDot = '.'
+        pyFirst = pyDouble[:q]
+        pyLast = pyDouble[q + 1:]
+
+    # Now the string is split into: pySign + pyFirst + pyDot + pyLast + pyExpStr
+    if pyLast == '0':
+        # Always remove trailing .0
+        pyDot = ''
+        pyLast = ''
+
+    if pyExpVal > 0 and pyExpVal < 21:
+        # Integers are shown as is with up to 21 digits
+        pyFirst += pyLast
+        pyLast = ''
+        pyDot = ''
+        pyExpStr = ''
+        q = pyExpVal - len(pyFirst)
+        while q >= 0:
+            q -= 1
+            pyFirst += '0'
+    elif pyExpVal < 0 and pyExpVal > -7:
+        # Small numbers are shown as 0.etc with e-6 as lower limit
+        pyLast = pyFirst + pyLast
+        pyFirst = '0'
+        pyDot = '.'
+        pyExpStr = ''
+        q = pyExpVal
+        while q < -1:
+            q += 1
+            pyLast = '0' + pyLast
+
+    # The resulting sub-strings are concatenated
+    return pySign + pyFirst + pyDot + pyLast + pyExpStr
diff --git a/stix2/canonicalization/__init__.py b/stix2/canonicalization/__init__.py
new file mode 100644
index 0000000..e69de29
diff --git a/stix2/properties.py b/stix2/properties.py
index 1095156..c956a08 100644
--- a/stix2/properties.py
+++ b/stix2/properties.py
@@ -15,7 +15,10 @@ import stix2
 
 from .base import _Observable, _STIXBase
 from .core import STIX2_OBJ_MAPS, parse, parse_observable
-from .exceptions import CustomContentError, DictionaryKeyError
+from .exceptions import (
+    CustomContentError, DictionaryKeyError, MissingPropertiesError,
+    MutuallyExclusivePropertiesError,
+)
 from .utils import _get_dict, get_class_hierarchy_names, parse_into_datetime
 
 ERROR_INVALID_ID = (
@@ -417,12 +420,28 @@ class HexProperty(Property):
 
 class ReferenceProperty(Property):
 
-    def __init__(self, type=None, spec_version=stix2.DEFAULT_VERSION, **kwargs):
+    def __init__(self, valid_types=None, invalid_types=None, spec_version=stix2.DEFAULT_VERSION, **kwargs):
         """
         references sometimes must be to a specific object type
         """
-        self.required_prefix = type + "--" if type else None
         self.spec_version = spec_version
+
+        # These checks need to be done prior to the STIX object finishing construction
+        # and thus we can't use base.py's _check_mutually_exclusive_properties()
+        # in the typical location of _check_object_constraints() in sdo.py
+        if valid_types and invalid_types:
+            raise MutuallyExclusivePropertiesError(self.__class__, ['invalid_types', 'valid_types'])
+        elif valid_types is None and invalid_types is None:
+            raise MissingPropertiesError(self.__class__, ['invalid_types', 'valid_types'])
+
+        if valid_types and type(valid_types) is not list:
+            valid_types = [valid_types]
+        elif invalid_types and type(invalid_types) is not list:
+            invalid_types = [invalid_types]
+
+        self.valid_types = valid_types
+        self.invalid_types = invalid_types
+
         super(ReferenceProperty, self).__init__(**kwargs)
 
     def clean(self, value):
@@ -430,7 +449,27 @@ class ReferenceProperty(Property):
             value = value.id
         value = str(value)
 
-        _validate_id(value, self.spec_version, self.required_prefix)
+        possible_prefix = value[:value.index('--') + 2]
+
+        if self.valid_types:
+            if self.valid_types == ["only_SDO"]:
+                self.valid_types = STIX2_OBJ_MAPS['v21']['objects'].keys()
+            elif self.valid_types == ["only_SCO"]:
+                self.valid_types = STIX2_OBJ_MAPS['v21']['observables'].keys()
+            elif self.valid_types == ["only_SCO_&_SRO"]:
+                self.valid_types = list(STIX2_OBJ_MAPS['v21']['observables'].keys()) + ['relationship', 'sighting']
+
+            if possible_prefix[:-2] in self.valid_types:
+                required_prefix = possible_prefix
+            else:
+                raise ValueError("The type-specifying prefix '%s' for this property is not valid" % (possible_prefix))
+        elif self.invalid_types:
+            if possible_prefix[:-2] not in self.invalid_types:
+                required_prefix = possible_prefix
+            else:
+                raise ValueError("An invalid type-specifying prefix '%s' was specified for this property" % (possible_prefix, value))
+
+        _validate_id(value, self.spec_version, required_prefix)
 
         return value
 
diff --git a/stix2/v20/common.py b/stix2/v20/common.py
index c2dedfb..377d992 100644
--- a/stix2/v20/common.py
+++ b/stix2/v20/common.py
@@ -49,7 +49,7 @@ class GranularMarking(_STIXBase):
     """
 
     _properties = OrderedDict([
-        ('marking_ref', ReferenceProperty(required=True, spec_version='2.0', type='marking-definition')),
+        ('marking_ref', ReferenceProperty(valid_types='marking-definition', spec_version='2.0', required=True)),
         ('selectors', ListProperty(SelectorProperty, required=True)),
     ])
 
@@ -105,10 +105,10 @@ class MarkingDefinition(_STIXBase, _MarkingsMixin):
     _properties = OrderedDict([
         ('type', TypeProperty(_type)),
         ('id', IDProperty(_type, spec_version='2.0')),
-        ('created_by_ref', ReferenceProperty(type='identity', spec_version='2.0')),
+        ('created_by_ref', ReferenceProperty(valid_types='identity', spec_version='2.0')),
         ('created', TimestampProperty(default=lambda: NOW)),
         ('external_references', ListProperty(ExternalReference)),
-        ('object_marking_refs', ListProperty(ReferenceProperty(type='marking-definition', spec_version='2.0'))),
+        ('object_marking_refs', ListProperty(ReferenceProperty(valid_types='marking-definition', spec_version='2.0'))),
         ('granular_markings', ListProperty(GranularMarking)),
         ('definition_type', StringProperty(required=True)),
         ('definition', MarkingProperty(required=True)),
diff --git a/stix2/v20/sdo.py b/stix2/v20/sdo.py
index 3fd3a84..19c53d8 100644
--- a/stix2/v20/sdo.py
+++ b/stix2/v20/sdo.py
@@ -23,7 +23,7 @@ class AttackPattern(STIXDomainObject):
     _properties = OrderedDict([
         ('type', TypeProperty(_type)),
         ('id', IDProperty(_type, spec_version='2.0')),
-        ('created_by_ref', ReferenceProperty(type='identity', spec_version='2.0')),
+        ('created_by_ref', ReferenceProperty(valid_types='identity', spec_version='2.0')),
         ('created', TimestampProperty(default=lambda: NOW, precision='millisecond')),
         ('modified', TimestampProperty(default=lambda: NOW, precision='millisecond')),
         ('name', StringProperty(required=True)),
@@ -32,7 +32,7 @@ class AttackPattern(STIXDomainObject):
         ('revoked', BooleanProperty(default=lambda: False)),
         ('labels', ListProperty(StringProperty)),
         ('external_references', ListProperty(ExternalReference)),
-        ('object_marking_refs', ListProperty(ReferenceProperty(type='marking-definition', spec_version='2.0'))),
+        ('object_marking_refs', ListProperty(ReferenceProperty(valid_types='marking-definition', spec_version='2.0'))),
         ('granular_markings', ListProperty(GranularMarking)),
     ])
 
@@ -46,7 +46,7 @@ class Campaign(STIXDomainObject):
     _properties = OrderedDict([
         ('type', TypeProperty(_type)),
         ('id', IDProperty(_type, spec_version='2.0')),
-        ('created_by_ref', ReferenceProperty(type='identity', spec_version='2.0')),
+        ('created_by_ref', ReferenceProperty(valid_types='identity', spec_version='2.0')),
         ('created', TimestampProperty(default=lambda: NOW, precision='millisecond')),
         ('modified', TimestampProperty(default=lambda: NOW, precision='millisecond')),
         ('name', StringProperty(required=True)),
@@ -58,7 +58,7 @@ class Campaign(STIXDomainObject):
         ('revoked', BooleanProperty(default=lambda: False)),
         ('labels', ListProperty(StringProperty)),
         ('external_references', ListProperty(ExternalReference)),
-        ('object_marking_refs', ListProperty(ReferenceProperty(type='marking-definition', spec_version='2.0'))),
+        ('object_marking_refs', ListProperty(ReferenceProperty(valid_types='marking-definition', spec_version='2.0'))),
         ('granular_markings', ListProperty(GranularMarking)),
     ])
 
@@ -72,7 +72,7 @@ class CourseOfAction(STIXDomainObject):
     _properties = OrderedDict([
         ('type', TypeProperty(_type)),
         ('id', IDProperty(_type, spec_version='2.0')),
-        ('created_by_ref', ReferenceProperty(type='identity', spec_version='2.0')),
+        ('created_by_ref', ReferenceProperty(valid_types='identity', spec_version='2.0')),
         ('created', TimestampProperty(default=lambda: NOW, precision='millisecond')),
         ('modified', TimestampProperty(default=lambda: NOW, precision='millisecond')),
         ('name', StringProperty(required=True)),
@@ -80,7 +80,7 @@ class CourseOfAction(STIXDomainObject):
         ('revoked', BooleanProperty(default=lambda: False)),
         ('labels', ListProperty(StringProperty)),
         ('external_references', ListProperty(ExternalReference)),
-        ('object_marking_refs', ListProperty(ReferenceProperty(type='marking-definition', spec_version='2.0'))),
+        ('object_marking_refs', ListProperty(ReferenceProperty(valid_types='marking-definition', spec_version='2.0'))),
         ('granular_markings', ListProperty(GranularMarking)),
     ])
 
@@ -94,7 +94,7 @@ class Identity(STIXDomainObject):
     _properties = OrderedDict([
         ('type', TypeProperty(_type)),
         ('id', IDProperty(_type, spec_version='2.0')),
-        ('created_by_ref', ReferenceProperty(type='identity', spec_version='2.0')),
+        ('created_by_ref', ReferenceProperty(valid_types='identity', spec_version='2.0')),
         ('created', TimestampProperty(default=lambda: NOW, precision='millisecond')),
         ('modified', TimestampProperty(default=lambda: NOW, precision='millisecond')),
         ('name', StringProperty(required=True)),
@@ -105,7 +105,7 @@ class Identity(STIXDomainObject):
         ('revoked', BooleanProperty(default=lambda: False)),
         ('labels', ListProperty(StringProperty)),
         ('external_references', ListProperty(ExternalReference)),
-        ('object_marking_refs', ListProperty(ReferenceProperty(type='marking-definition', spec_version='2.0'))),
+        ('object_marking_refs', ListProperty(ReferenceProperty(valid_types='marking-definition', spec_version='2.0'))),
         ('granular_markings', ListProperty(GranularMarking)),
     ])
 
@@ -119,7 +119,7 @@ class Indicator(STIXDomainObject):
     _properties = OrderedDict([
         ('type', TypeProperty(_type)),
         ('id', IDProperty(_type, spec_version='2.0')),
-        ('created_by_ref', ReferenceProperty(type='identity', spec_version='2.0')),
+        ('created_by_ref', ReferenceProperty(valid_types='identity', spec_version='2.0')),
         ('created', TimestampProperty(default=lambda: NOW, precision='millisecond')),
         ('modified', TimestampProperty(default=lambda: NOW, precision='millisecond')),
         ('name', StringProperty()),
@@ -131,7 +131,7 @@ class Indicator(STIXDomainObject):
         ('revoked', BooleanProperty(default=lambda: False)),
         ('labels', ListProperty(StringProperty, required=True)),
         ('external_references', ListProperty(ExternalReference)),
-        ('object_marking_refs', ListProperty(ReferenceProperty(type='marking-definition', spec_version='2.0'))),
+        ('object_marking_refs', ListProperty(ReferenceProperty(valid_types='marking-definition', spec_version='2.0'))),
         ('granular_markings', ListProperty(GranularMarking)),
     ])
 
@@ -145,7 +145,7 @@ class IntrusionSet(STIXDomainObject):
     _properties = OrderedDict([
         ('type', TypeProperty(_type)),
         ('id', IDProperty(_type, spec_version='2.0')),
-        ('created_by_ref', ReferenceProperty(type='identity', spec_version='2.0')),
+        ('created_by_ref', ReferenceProperty(valid_types='identity', spec_version='2.0')),
         ('created', TimestampProperty(default=lambda: NOW, precision='millisecond')),
         ('modified', TimestampProperty(default=lambda: NOW, precision='millisecond')),
         ('name', StringProperty(required=True)),
@@ -160,7 +160,7 @@ class IntrusionSet(STIXDomainObject):
         ('revoked', BooleanProperty(default=lambda: False)),
         ('labels', ListProperty(StringProperty)),
         ('external_references', ListProperty(ExternalReference)),
-        ('object_marking_refs', ListProperty(ReferenceProperty(type='marking-definition', spec_version='2.0'))),
+        ('object_marking_refs', ListProperty(ReferenceProperty(valid_types='marking-definition', spec_version='2.0'))),
         ('granular_markings', ListProperty(GranularMarking)),
     ])
 
@@ -174,7 +174,7 @@ class Malware(STIXDomainObject):
     _properties = OrderedDict([
         ('type', TypeProperty(_type)),
         ('id', IDProperty(_type, spec_version='2.0')),
-        ('created_by_ref', ReferenceProperty(type='identity', spec_version='2.0')),
+        ('created_by_ref', ReferenceProperty(valid_types='identity', spec_version='2.0')),
         ('created', TimestampProperty(default=lambda: NOW, precision='millisecond')),
         ('modified', TimestampProperty(default=lambda: NOW, precision='millisecond')),
         ('name', StringProperty(required=True)),
@@ -183,7 +183,7 @@ class Malware(STIXDomainObject):
         ('revoked', BooleanProperty(default=lambda: False)),
         ('labels', ListProperty(StringProperty, required=True)),
         ('external_references', ListProperty(ExternalReference)),
-        ('object_marking_refs', ListProperty(ReferenceProperty(type='marking-definition', spec_version='2.0'))),
+        ('object_marking_refs', ListProperty(ReferenceProperty(valid_types='marking-definition', spec_version='2.0'))),
         ('granular_markings', ListProperty(GranularMarking)),
     ])
 
@@ -197,7 +197,7 @@ class ObservedData(STIXDomainObject):
     _properties = OrderedDict([
         ('type', TypeProperty(_type)),
         ('id', IDProperty(_type, spec_version='2.0')),
-        ('created_by_ref', ReferenceProperty(type='identity', spec_version='2.0')),
+        ('created_by_ref', ReferenceProperty(valid_types='identity', spec_version='2.0')),
         ('created', TimestampProperty(default=lambda: NOW, precision='millisecond')),
         ('modified', TimestampProperty(default=lambda: NOW, precision='millisecond')),
         ('first_observed', TimestampProperty(required=True)),
@@ -207,7 +207,7 @@ class ObservedData(STIXDomainObject):
         ('revoked', BooleanProperty(default=lambda: False)),
         ('labels', ListProperty(StringProperty)),
         ('external_references', ListProperty(ExternalReference)),
-        ('object_marking_refs', ListProperty(ReferenceProperty(type='marking-definition', spec_version='2.0'))),
+        ('object_marking_refs', ListProperty(ReferenceProperty(valid_types='marking-definition', spec_version='2.0'))),
         ('granular_markings', ListProperty(GranularMarking)),
     ])
 
@@ -227,17 +227,17 @@ class Report(STIXDomainObject):
     _properties = OrderedDict([
         ('type', TypeProperty(_type)),
         ('id', IDProperty(_type, spec_version='2.0')),
-        ('created_by_ref', ReferenceProperty(type='identity', spec_version='2.0')),
+        ('created_by_ref', ReferenceProperty(valid_types='identity', spec_version='2.0')),
         ('created', TimestampProperty(default=lambda: NOW, precision='millisecond')),
         ('modified', TimestampProperty(default=lambda: NOW, precision='millisecond')),
         ('name', StringProperty(required=True)),
         ('description', StringProperty()),
         ('published', TimestampProperty(required=True)),
-        ('object_refs', ListProperty(ReferenceProperty(spec_version='2.0'), required=True)),
+        ('object_refs', ListProperty(ReferenceProperty(invalid_types=[""], spec_version='2.0'), required=True)),
         ('revoked', BooleanProperty(default=lambda: False)),
         ('labels', ListProperty(StringProperty, required=True)),
         ('external_references', ListProperty(ExternalReference)),
-        ('object_marking_refs', ListProperty(ReferenceProperty(type='marking-definition', spec_version='2.0'))),
+        ('object_marking_refs', ListProperty(ReferenceProperty(valid_types='marking-definition', spec_version='2.0'))),
         ('granular_markings', ListProperty(GranularMarking)),
     ])
 
@@ -251,7 +251,7 @@ class ThreatActor(STIXDomainObject):
     _properties = OrderedDict([
         ('type', TypeProperty(_type)),
         ('id', IDProperty(_type, spec_version='2.0')),
-        ('created_by_ref', ReferenceProperty(type='identity', spec_version='2.0')),
+        ('created_by_ref', ReferenceProperty(valid_types='identity', spec_version='2.0')),
         ('created', TimestampProperty(default=lambda: NOW, precision='millisecond')),
         ('modified', TimestampProperty(default=lambda: NOW, precision='millisecond')),
         ('name', StringProperty(required=True)),
@@ -267,7 +267,7 @@ class ThreatActor(STIXDomainObject):
         ('revoked', BooleanProperty(default=lambda: False)),
         ('labels', ListProperty(StringProperty, required=True)),
         ('external_references', ListProperty(ExternalReference)),
-        ('object_marking_refs', ListProperty(ReferenceProperty(type='marking-definition', spec_version='2.0'))),
+        ('object_marking_refs', ListProperty(ReferenceProperty(valid_types='marking-definition', spec_version='2.0'))),
         ('granular_markings', ListProperty(GranularMarking)),
     ])
 
@@ -281,7 +281,7 @@ class Tool(STIXDomainObject):
     _properties = OrderedDict([
         ('type', TypeProperty(_type)),
         ('id', IDProperty(_type, spec_version='2.0')),
-        ('created_by_ref', ReferenceProperty(type='identity', spec_version='2.0')),
+        ('created_by_ref', ReferenceProperty(valid_types='identity', spec_version='2.0')),
         ('created', TimestampProperty(default=lambda: NOW, precision='millisecond')),
         ('modified', TimestampProperty(default=lambda: NOW, precision='millisecond')),
         ('name', StringProperty(required=True)),
@@ -291,7 +291,7 @@ class Tool(STIXDomainObject):
         ('revoked', BooleanProperty(default=lambda: False)),
         ('labels', ListProperty(StringProperty, required=True)),
         ('external_references', ListProperty(ExternalReference)),
-        ('object_marking_refs', ListProperty(ReferenceProperty(type='marking-definition', spec_version='2.0'))),
+        ('object_marking_refs', ListProperty(ReferenceProperty(valid_types='marking-definition', spec_version='2.0'))),
         ('granular_markings', ListProperty(GranularMarking)),
     ])
 
@@ -305,7 +305,7 @@ class Vulnerability(STIXDomainObject):
     _properties = OrderedDict([
         ('type', TypeProperty(_type)),
         ('id', IDProperty(_type, spec_version='2.0')),
-        ('created_by_ref', ReferenceProperty(type='identity', spec_version='2.0')),
+        ('created_by_ref', ReferenceProperty(valid_types='identity', spec_version='2.0')),
         ('created', TimestampProperty(default=lambda: NOW, precision='millisecond')),
         ('modified', TimestampProperty(default=lambda: NOW, precision='millisecond')),
         ('name', StringProperty(required=True)),
@@ -313,7 +313,7 @@ class Vulnerability(STIXDomainObject):
         ('revoked', BooleanProperty(default=lambda: False)),
         ('labels', ListProperty(StringProperty)),
         ('external_references', ListProperty(ExternalReference)),
-        ('object_marking_refs', ListProperty(ReferenceProperty(type='marking-definition', spec_version='2.0'))),
+        ('object_marking_refs', ListProperty(ReferenceProperty(valid_types='marking-definition', spec_version='2.0'))),
         ('granular_markings', ListProperty(GranularMarking)),
     ])
 
@@ -352,7 +352,7 @@ def CustomObject(type='x-custom-type', properties=None):
             [
                 ('type', TypeProperty(type)),
                 ('id', IDProperty(type, spec_version='2.0')),
-                ('created_by_ref', ReferenceProperty(type='identity', spec_version='2.0')),
+                ('created_by_ref', ReferenceProperty(valid_types='identity', spec_version='2.0')),
                 ('created', TimestampProperty(default=lambda: NOW, precision='millisecond')),
                 ('modified', TimestampProperty(default=lambda: NOW, precision='millisecond')),
             ],
@@ -361,7 +361,7 @@ def CustomObject(type='x-custom-type', properties=None):
                 ('revoked', BooleanProperty(default=lambda: False)),
                 ('labels', ListProperty(StringProperty)),
                 ('external_references', ListProperty(ExternalReference)),
-                ('object_marking_refs', ListProperty(ReferenceProperty(type='marking-definition', spec_version='2.0'))),
+                ('object_marking_refs', ListProperty(ReferenceProperty(valid_types='marking-definition', spec_version='2.0'))),
                 ('granular_markings', ListProperty(GranularMarking)),
             ],
             sorted([x for x in properties if x[0].startswith('x_')], key=lambda x: x[0]),
diff --git a/stix2/v20/sro.py b/stix2/v20/sro.py
index f450003..3f561b8 100644
--- a/stix2/v20/sro.py
+++ b/stix2/v20/sro.py
@@ -16,21 +16,23 @@ class Relationship(STIXRelationshipObject):
     `the STIX 2.0 specification <http://docs.oasis-open.org/cti/stix/v2.0/cs01/part2-stix-objects/stix-v2.0-cs01-part2-stix-objects.html#_Toc496714340>`__.
     """
 
+    _invalid_source_target_types = ['bundle', 'language-content', 'marking-definition', 'relationship', 'sighting']
+
     _type = 'relationship'
     _properties = OrderedDict([
         ('type', TypeProperty(_type)),
         ('id', IDProperty(_type, spec_version='2.0')),
-        ('created_by_ref', ReferenceProperty(type='identity', spec_version='2.0')),
+        ('created_by_ref', ReferenceProperty(valid_types='identity', spec_version='2.0')),
         ('created', TimestampProperty(default=lambda: NOW, precision='millisecond')),
         ('modified', TimestampProperty(default=lambda: NOW, precision='millisecond')),
         ('relationship_type', StringProperty(required=True)),
         ('description', StringProperty()),
-        ('source_ref', ReferenceProperty(spec_version='2.0', required=True)),
-        ('target_ref', ReferenceProperty(spec_version='2.0', required=True)),
+        ('source_ref', ReferenceProperty(invalid_types=_invalid_source_target_types, spec_version='2.0', required=True)),
+        ('target_ref', ReferenceProperty(invalid_types=_invalid_source_target_types, spec_version='2.0', required=True)),
         ('revoked', BooleanProperty(default=lambda: False)),
         ('labels', ListProperty(StringProperty)),
         ('external_references', ListProperty(ExternalReference)),
-        ('object_marking_refs', ListProperty(ReferenceProperty(type='marking-definition', spec_version='2.0'))),
+        ('object_marking_refs', ListProperty(ReferenceProperty(valid_types='marking-definition', spec_version='2.0'))),
         ('granular_markings', ListProperty(GranularMarking)),
     ])
 
@@ -59,20 +61,20 @@ class Sighting(STIXRelationshipObject):
     _properties = OrderedDict([
         ('type', TypeProperty(_type)),
         ('id', IDProperty(_type, spec_version='2.0')),
-        ('created_by_ref', ReferenceProperty(type='identity', spec_version='2.0')),
+        ('created_by_ref', ReferenceProperty(valid_types='identity', spec_version='2.0')),
         ('created', TimestampProperty(default=lambda: NOW, precision='millisecond')),
         ('modified', TimestampProperty(default=lambda: NOW, precision='millisecond')),
         ('first_seen', TimestampProperty()),
         ('last_seen', TimestampProperty()),
         ('count', IntegerProperty(min=0, max=999999999)),
-        ('sighting_of_ref', ReferenceProperty(spec_version='2.0', required=True)),
-        ('observed_data_refs', ListProperty(ReferenceProperty(type='observed-data', spec_version='2.0'))),
-        ('where_sighted_refs', ListProperty(ReferenceProperty(type='identity', spec_version='2.0'))),
+        ('sighting_of_ref', ReferenceProperty(valid_types="only_SDO", spec_version='2.0', required=True)),
+        ('observed_data_refs', ListProperty(ReferenceProperty(valid_types='observed-data', spec_version='2.0'))),
+        ('where_sighted_refs', ListProperty(ReferenceProperty(valid_types='identity', spec_version='2.0'))),
         ('summary', BooleanProperty(default=lambda: False)),
         ('revoked', BooleanProperty(default=lambda: False)),
         ('labels', ListProperty(StringProperty)),
         ('external_references', ListProperty(ExternalReference)),
-        ('object_marking_refs', ListProperty(ReferenceProperty(type='marking-definition', spec_version='2.0'))),
+        ('object_marking_refs', ListProperty(ReferenceProperty(valid_types='marking-definition', spec_version='2.0'))),
         ('granular_markings', ListProperty(GranularMarking)),
     ])
 
diff --git a/stix2/v21/common.py b/stix2/v21/common.py
index 70be5dc..44d3675 100644
--- a/stix2/v21/common.py
+++ b/stix2/v21/common.py
@@ -54,7 +54,7 @@ class GranularMarking(_STIXBase):
 
     _properties = OrderedDict([
         ('lang', StringProperty()),
-        ('marking_ref', ReferenceProperty(type='marking-definition', spec_version='2.1')),
+        ('marking_ref', ReferenceProperty(valid_types='marking-definition', spec_version='2.1')),
         ('selectors', ListProperty(SelectorProperty, required=True)),
     ])
 
@@ -74,10 +74,10 @@ class LanguageContent(_STIXBase):
         ('type', TypeProperty(_type)),
         ('spec_version', StringProperty(fixed='2.1')),
         ('id', IDProperty(_type, spec_version='2.1')),
-        ('created_by_ref', ReferenceProperty(type='identity', spec_version='2.1')),
+        ('created_by_ref', ReferenceProperty(valid_types='identity', spec_version='2.1')),
         ('created', TimestampProperty(default=lambda: NOW, precision='millisecond')),
         ('modified', TimestampProperty(default=lambda: NOW, precision='millisecond')),
-        ('object_ref', ReferenceProperty(spec_version='2.1', required=True)),
+        ('object_ref', ReferenceProperty(invalid_types=[""], spec_version='2.1', required=True)),
         # TODO: 'object_modified' it MUST be an exact match for the modified time of the STIX Object (SRO or SDO) being referenced.
         ('object_modified', TimestampProperty(precision='millisecond')),
         # TODO: 'contents' https://docs.google.com/document/d/1ShNq4c3e1CkfANmD9O--mdZ5H0O_GLnjN28a_yrEaco/edit#heading=h.cfz5hcantmvx
@@ -86,7 +86,7 @@ class LanguageContent(_STIXBase):
         ('labels', ListProperty(StringProperty)),
         ('confidence', IntegerProperty()),
         ('external_references', ListProperty(ExternalReference)),
-        ('object_marking_refs', ListProperty(ReferenceProperty(type='marking-definition', spec_version='2.1'))),
+        ('object_marking_refs', ListProperty(ReferenceProperty(valid_types='marking-definition', spec_version='2.1'))),
         ('granular_markings', ListProperty(GranularMarking)),
     ])
 
@@ -145,10 +145,10 @@ class MarkingDefinition(_STIXBase, _MarkingsMixin):
         ('type', TypeProperty(_type)),
         ('spec_version', StringProperty(fixed='2.1')),
         ('id', IDProperty(_type)),
-        ('created_by_ref', ReferenceProperty(type='identity', spec_version='2.1')),
+        ('created_by_ref', ReferenceProperty(valid_types='identity', spec_version='2.1')),
         ('created', TimestampProperty(default=lambda: NOW)),
         ('external_references', ListProperty(ExternalReference)),
-        ('object_marking_refs', ListProperty(ReferenceProperty(type='marking-definition', spec_version='2.1'))),
+        ('object_marking_refs', ListProperty(ReferenceProperty(valid_types='marking-definition', spec_version='2.1'))),
         ('granular_markings', ListProperty(GranularMarking)),
         ('definition_type', StringProperty(required=True)),
         ('definition', MarkingProperty(required=True)),
diff --git a/stix2/v21/observables.py b/stix2/v21/observables.py
index f383899..9fffeed 100644
--- a/stix2/v21/observables.py
+++ b/stix2/v21/observables.py
@@ -7,16 +7,21 @@ Observable and do not have a ``_type`` attribute.
 
 from collections import OrderedDict
 import itertools
+import warnings
 
 from ..base import _Extension, _Observable, _STIXBase
 from ..custom import _custom_extension_builder, _custom_observable_builder
-from ..exceptions import AtLeastOnePropertyError, DependentPropertiesError
+from ..exceptions import (
+    AtLeastOnePropertyError, DependentPropertiesError, STIXDeprecationWarning,
+)
 from ..properties import (
     BinaryProperty, BooleanProperty, CallableValues, DictionaryProperty,
     EmbeddedObjectProperty, EnumProperty, ExtensionsProperty, FloatProperty,
-    HashesProperty, HexProperty, IntegerProperty, ListProperty,
-    ObjectReferenceProperty, StringProperty, TimestampProperty, TypeProperty,
+    HashesProperty, HexProperty, IDProperty, IntegerProperty, ListProperty,
+    ObjectReferenceProperty, ReferenceProperty, StringProperty,
+    TimestampProperty, TypeProperty,
 )
+from .common import GranularMarking
 
 
 class Artifact(_Observable):
@@ -28,6 +33,7 @@ class Artifact(_Observable):
     _type = 'artifact'
     _properties = OrderedDict([
         ('type', TypeProperty(_type)),
+        ('id', IDProperty(_type, spec_version='2.1')),
         ('mime_type', StringProperty()),
         ('payload_bin', BinaryProperty()),
         ('url', StringProperty()),
@@ -35,11 +41,16 @@ class Artifact(_Observable):
         ('encryption_algorithm', StringProperty()),
         ('decryption_key', StringProperty()),
         ('extensions', ExtensionsProperty(spec_version='2.1', enclosing_type=_type)),
+        ('spec_version', StringProperty(fixed='2.1')),
+        ('object_marking_refs', ListProperty(ReferenceProperty(valid_types='marking-definition', spec_version='2.1'))),
+        ('granular_markings', ListProperty(GranularMarking)),
+        ('defanged', BooleanProperty(default=lambda: False)),
     ])
+    _id_contributing_properties = ["hashes", "payload_bin"]
 
     def _check_object_constraints(self):
         super(Artifact, self)._check_object_constraints()
-        self._check_mutually_exclusive_properties(['payload_bin', 'url'])
+        self._check_mutually_exclusive_properties(['payload_bin', 'url'], at_least_one=False)
         self._check_properties_dependency(['hashes'], ['url'])
 
 
@@ -52,11 +63,17 @@ class AutonomousSystem(_Observable):
     _type = 'autonomous-system'
     _properties = OrderedDict([
         ('type', TypeProperty(_type)),
+        ('id', IDProperty(_type, spec_version='2.1')),
         ('number', IntegerProperty(required=True)),
         ('name', StringProperty()),
         ('rir', StringProperty()),
         ('extensions', ExtensionsProperty(spec_version='2.1', enclosing_type=_type)),
+        ('spec_version', StringProperty(fixed='2.1')),
+        ('object_marking_refs', ListProperty(ReferenceProperty(valid_types='marking-definition', spec_version='2.1'))),
+        ('granular_markings', ListProperty(GranularMarking)),
+        ('defanged', BooleanProperty(default=lambda: False)),
     ])
+    _id_contributing_properties = ["number"]
 
 
 class Directory(_Observable):
@@ -68,15 +85,21 @@ class Directory(_Observable):
     _type = 'directory'
     _properties = OrderedDict([
         ('type', TypeProperty(_type)),
+        ('id', IDProperty(_type, spec_version='2.1')),
         ('path', StringProperty(required=True)),
         ('path_enc', StringProperty()),
         # these are not the created/modified timestamps of the object itself
-        ('created', TimestampProperty()),
-        ('modified', TimestampProperty()),
-        ('accessed', TimestampProperty()),
-        ('contains_refs', ListProperty(ObjectReferenceProperty(valid_types=['file', 'directory']))),
+        ('ctime', TimestampProperty()),
+        ('mtime', TimestampProperty()),
+        ('atime', TimestampProperty()),
+        ('contains_refs', ListProperty(ReferenceProperty(valid_types=['file', 'directory'], spec_version='2.1'))),
         ('extensions', ExtensionsProperty(spec_version='2.1', enclosing_type=_type)),
+        ('spec_version', StringProperty(fixed='2.1')),
+        ('object_marking_refs', ListProperty(ReferenceProperty(valid_types='marking-definition', spec_version='2.1'))),
+        ('granular_markings', ListProperty(GranularMarking)),
+        ('defanged', BooleanProperty(default=lambda: False)),
     ])
+    _id_contributing_properties = ["path"]
 
 
 class DomainName(_Observable):
@@ -88,10 +111,24 @@ class DomainName(_Observable):
     _type = 'domain-name'
     _properties = OrderedDict([
         ('type', TypeProperty(_type)),
+        ('id', IDProperty(_type, spec_version='2.1')),
         ('value', StringProperty(required=True)),
-        ('resolves_to_refs', ListProperty(ObjectReferenceProperty(valid_types=['ipv4-addr', 'ipv6-addr', 'domain-name']))),
+        ('resolves_to_refs', ListProperty(ReferenceProperty(valid_types=['ipv4-addr', 'ipv6-addr', 'domain-name'], spec_version='2.1'))),
         ('extensions', ExtensionsProperty(spec_version='2.1', enclosing_type=_type)),
+        ('spec_version', StringProperty(fixed='2.1')),
+        ('object_marking_refs', ListProperty(ReferenceProperty(valid_types='marking-definition', spec_version='2.1'))),
+        ('granular_markings', ListProperty(GranularMarking)),
+        ('defanged', BooleanProperty(default=lambda: False)),
     ])
+    _id_contributing_properties = ["value"]
+
+    def _check_object_constraints(self):
+        if self.get('resolves_to_refs'):
+            warnings.warn(
+                "The 'resolves_to_refs' property of domain-name is deprecated in "
+                "STIX 2.1. Use the 'resolves-to' relationship type instead",
+                STIXDeprecationWarning,
+            )
 
 
 class EmailAddress(_Observable):
@@ -103,11 +140,17 @@ class EmailAddress(_Observable):
     _type = 'email-addr'
     _properties = OrderedDict([
         ('type', TypeProperty(_type)),
+        ('id', IDProperty(_type, spec_version='2.1')),
         ('value', StringProperty(required=True)),
         ('display_name', StringProperty()),
-        ('belongs_to_ref', ObjectReferenceProperty(valid_types='user-account')),
+        ('belongs_to_ref', ReferenceProperty(valid_types='user-account', spec_version='2.1')),
         ('extensions', ExtensionsProperty(spec_version='2.1', enclosing_type=_type)),
+        ('spec_version', StringProperty(fixed='2.1')),
+        ('object_marking_refs', ListProperty(ReferenceProperty(valid_types='marking-definition', spec_version='2.1'))),
+        ('granular_markings', ListProperty(GranularMarking)),
+        ('defanged', BooleanProperty(default=lambda: False)),
     ])
+    _id_contributing_properties = ["value"]
 
 
 class EmailMIMEComponent(_STIXBase):
@@ -137,22 +180,29 @@ class EmailMessage(_Observable):
     _type = 'email-message'
     _properties = OrderedDict([
         ('type', TypeProperty(_type)),
+        ('id', IDProperty(_type, spec_version='2.1')),
         ('is_multipart', BooleanProperty(required=True)),
         ('date', TimestampProperty()),
         ('content_type', StringProperty()),
-        ('from_ref', ObjectReferenceProperty(valid_types='email-addr')),
-        ('sender_ref', ObjectReferenceProperty(valid_types='email-addr')),
-        ('to_refs', ListProperty(ObjectReferenceProperty(valid_types='email-addr'))),
-        ('cc_refs', ListProperty(ObjectReferenceProperty(valid_types='email-addr'))),
-        ('bcc_refs', ListProperty(ObjectReferenceProperty(valid_types='email-addr'))),
+        ('from_ref', ReferenceProperty(valid_types='email-addr', spec_version='2.1')),
+        ('sender_ref', ReferenceProperty(valid_types='email-addr', spec_version='2.1')),
+        ('to_refs', ListProperty(ReferenceProperty(valid_types='email-addr', spec_version='2.1'))),
+        ('cc_refs', ListProperty(ReferenceProperty(valid_types='email-addr', spec_version='2.1'))),
+        ('bcc_refs', ListProperty(ReferenceProperty(valid_types='email-addr', spec_version='2.1'))),
+        ('message_id', StringProperty()),
         ('subject', StringProperty()),
         ('received_lines', ListProperty(StringProperty)),
         ('additional_header_fields', DictionaryProperty(spec_version='2.1')),
         ('body', StringProperty()),
         ('body_multipart', ListProperty(EmbeddedObjectProperty(type=EmailMIMEComponent))),
-        ('raw_email_ref', ObjectReferenceProperty(valid_types='artifact')),
+        ('raw_email_ref', ReferenceProperty(valid_types='artifact', spec_version='2.1')),
         ('extensions', ExtensionsProperty(spec_version='2.1', enclosing_type=_type)),
+        ('spec_version', StringProperty(fixed='2.1')),
+        ('object_marking_refs', ListProperty(ReferenceProperty(valid_types='marking-definition', spec_version='2.1'))),
+        ('granular_markings', ListProperty(GranularMarking)),
+        ('defanged', BooleanProperty(default=lambda: False)),
     ])
+    _id_contributing_properties = ["from_ref", "subject", "body"]
 
     def _check_object_constraints(self):
         super(EmailMessage, self)._check_object_constraints()
@@ -170,7 +220,7 @@ class ArchiveExt(_Extension):
 
     _type = 'archive-ext'
     _properties = OrderedDict([
-        ('contains_refs', ListProperty(ObjectReferenceProperty(valid_types='file'), required=True)),
+        ('contains_refs', ListProperty(ObjectReferenceProperty(valid_types=['file', 'directory']), required=True)),
         ('comment', StringProperty()),
     ])
 
@@ -323,6 +373,7 @@ class File(_Observable):
     _type = 'file'
     _properties = OrderedDict([
         ('type', TypeProperty(_type)),
+        ('id', IDProperty(_type, spec_version='2.1')),
         ('hashes', HashesProperty(spec_version='2.1')),
         ('size', IntegerProperty(min=0)),
         ('name', StringProperty()),
@@ -330,14 +381,19 @@ class File(_Observable):
         ('magic_number_hex', HexProperty()),
         ('mime_type', StringProperty()),
         # these are not the created/modified timestamps of the object itself
-        ('created', TimestampProperty()),
-        ('modified', TimestampProperty()),
-        ('accessed', TimestampProperty()),
-        ('parent_directory_ref', ObjectReferenceProperty(valid_types='directory')),
-        ('contains_refs', ListProperty(ObjectReferenceProperty)),
-        ('content_ref', ObjectReferenceProperty(valid_types='artifact')),
+        ('ctime', TimestampProperty()),
+        ('mtime', TimestampProperty()),
+        ('atime', TimestampProperty()),
+        ('parent_directory_ref', ReferenceProperty(valid_types='directory', spec_version='2.1')),
+        ('contains_refs', ListProperty(ReferenceProperty(invalid_types="", spec_version='2.1'))),
+        ('content_ref', ReferenceProperty(valid_types='artifact', spec_version='2.1')),
         ('extensions', ExtensionsProperty(spec_version='2.1', enclosing_type=_type)),
+        ('spec_version', StringProperty(fixed='2.1')),
+        ('object_marking_refs', ListProperty(ReferenceProperty(valid_types='marking-definition', spec_version='2.1'))),
+        ('granular_markings', ListProperty(GranularMarking)),
+        ('defanged', BooleanProperty(default=lambda: False)),
     ])
+    _id_contributing_properties = ["hashes", "name", "extensions"]
 
     def _check_object_constraints(self):
         super(File, self)._check_object_constraints()
@@ -353,11 +409,32 @@ class IPv4Address(_Observable):
     _type = 'ipv4-addr'
     _properties = OrderedDict([
         ('type', TypeProperty(_type)),
+        ('id', IDProperty(_type, spec_version='2.1')),
         ('value', StringProperty(required=True)),
-        ('resolves_to_refs', ListProperty(ObjectReferenceProperty(valid_types='mac-addr'))),
-        ('belongs_to_refs', ListProperty(ObjectReferenceProperty(valid_types='autonomous-system'))),
+        ('resolves_to_refs', ListProperty(ReferenceProperty(valid_types='mac-addr', spec_version='2.1'))),
+        ('belongs_to_refs', ListProperty(ReferenceProperty(valid_types='autonomous-system', spec_version='2.1'))),
         ('extensions', ExtensionsProperty(spec_version='2.1', enclosing_type=_type)),
+        ('spec_version', StringProperty(fixed='2.1')),
+        ('object_marking_refs', ListProperty(ReferenceProperty(valid_types='marking-definition', spec_version='2.1'))),
+        ('granular_markings', ListProperty(GranularMarking)),
+        ('defanged', BooleanProperty(default=lambda: False)),
     ])
+    _id_contributing_properties = ["value"]
+
+    def _check_object_constraints(self):
+        if self.get('resolves_to_refs'):
+            warnings.warn(
+                "The 'resolves_to_refs' property of ipv4-addr is deprecated in "
+                "STIX 2.1. Use the 'resolves-to' relationship type instead",
+                STIXDeprecationWarning,
+            )
+
+        if self.get('belongs_to_refs'):
+            warnings.warn(
+                "The 'belongs_to_refs' property of ipv4-addr is deprecated in "
+                "STIX 2.1. Use the 'belongs-to' relationship type instead",
+                STIXDeprecationWarning,
+            )
 
 
 class IPv6Address(_Observable):
@@ -369,11 +446,32 @@ class IPv6Address(_Observable):
     _type = 'ipv6-addr'
     _properties = OrderedDict([
         ('type', TypeProperty(_type)),
+        ('id', IDProperty(_type, spec_version='2.1')),
         ('value', StringProperty(required=True)),
-        ('resolves_to_refs', ListProperty(ObjectReferenceProperty(valid_types='mac-addr'))),
-        ('belongs_to_refs', ListProperty(ObjectReferenceProperty(valid_types='autonomous-system'))),
+        ('resolves_to_refs', ListProperty(ReferenceProperty(valid_types='mac-addr', spec_version='2.1'))),
+        ('belongs_to_refs', ListProperty(ReferenceProperty(valid_types='autonomous-system', spec_version='2.1'))),
         ('extensions', ExtensionsProperty(spec_version='2.1', enclosing_type=_type)),
+        ('spec_version', StringProperty(fixed='2.1')),
+        ('object_marking_refs', ListProperty(ReferenceProperty(valid_types='marking-definition', spec_version='2.1'))),
+        ('granular_markings', ListProperty(GranularMarking)),
+        ('defanged', BooleanProperty(default=lambda: False)),
     ])
+    _id_contributing_properties = ["value"]
+
+    def _check_object_constraints(self):
+        if self.get('resolves_to_refs'):
+            warnings.warn(
+                "The 'resolves_to_refs' property of ipv6-addr is deprecated in "
+                "STIX 2.1. Use the 'resolves-to' relationship type instead",
+                STIXDeprecationWarning,
+            )
+
+        if self.get('belongs_to_refs'):
+            warnings.warn(
+                "The 'belongs_to_refs' property of ipv6-addr is deprecated in "
+                "STIX 2.1. Use the 'belongs-to' relationship type instead",
+                STIXDeprecationWarning,
+            )
 
 
 class MACAddress(_Observable):
@@ -385,9 +483,15 @@ class MACAddress(_Observable):
     _type = 'mac-addr'
     _properties = OrderedDict([
         ('type', TypeProperty(_type)),
+        ('id', IDProperty(_type, spec_version='2.1')),
         ('value', StringProperty(required=True)),
         ('extensions', ExtensionsProperty(spec_version='2.1', enclosing_type=_type)),
+        ('spec_version', StringProperty(fixed='2.1')),
+        ('object_marking_refs', ListProperty(ReferenceProperty(valid_types='marking-definition', spec_version='2.1'))),
+        ('granular_markings', ListProperty(GranularMarking)),
+        ('defanged', BooleanProperty(default=lambda: False)),
     ])
+    _id_contributing_properties = ["value"]
 
 
 class Mutex(_Observable):
@@ -399,9 +503,15 @@ class Mutex(_Observable):
     _type = 'mutex'
     _properties = OrderedDict([
         ('type', TypeProperty(_type)),
+        ('id', IDProperty(_type, spec_version='2.1')),
         ('name', StringProperty(required=True)),
         ('extensions', ExtensionsProperty(spec_version='2.1', enclosing_type=_type)),
+        ('spec_version', StringProperty(fixed='2.1')),
+        ('object_marking_refs', ListProperty(ReferenceProperty(valid_types='marking-definition', spec_version='2.1'))),
+        ('granular_markings', ListProperty(GranularMarking)),
+        ('defanged', BooleanProperty(default=lambda: False)),
     ])
+    _id_contributing_properties = ["name"]
 
 
 class HTTPRequestExt(_Extension):
@@ -505,11 +615,12 @@ class NetworkTraffic(_Observable):
     _type = 'network-traffic'
     _properties = OrderedDict([
         ('type', TypeProperty(_type)),
+        ('id', IDProperty(_type, spec_version='2.1')),
         ('start', TimestampProperty()),
         ('end', TimestampProperty()),
         ('is_active', BooleanProperty()),
-        ('src_ref', ObjectReferenceProperty(valid_types=['ipv4-addr', 'ipv6-addr', 'mac-addr', 'domain-name'])),
-        ('dst_ref', ObjectReferenceProperty(valid_types=['ipv4-addr', 'ipv6-addr', 'mac-addr', 'domain-name'])),
+        ('src_ref', ReferenceProperty(valid_types=['ipv4-addr', 'ipv6-addr', 'mac-addr', 'domain-name'], spec_version='2.1')),
+        ('dst_ref', ReferenceProperty(valid_types=['ipv4-addr', 'ipv6-addr', 'mac-addr', 'domain-name'], spec_version='2.1')),
         ('src_port', IntegerProperty(min=0, max=65535)),
         ('dst_port', IntegerProperty(min=0, max=65535)),
         ('protocols', ListProperty(StringProperty, required=True)),
@@ -518,12 +629,17 @@ class NetworkTraffic(_Observable):
         ('src_packets', IntegerProperty(min=0)),
         ('dst_packets', IntegerProperty(min=0)),
         ('ipfix', DictionaryProperty(spec_version='2.1')),
-        ('src_payload_ref', ObjectReferenceProperty(valid_types='artifact')),
-        ('dst_payload_ref', ObjectReferenceProperty(valid_types='artifact')),
-        ('encapsulates_refs', ListProperty(ObjectReferenceProperty(valid_types='network-traffic'))),
-        ('encapsulates_by_ref', ObjectReferenceProperty(valid_types='network-traffic')),
+        ('src_payload_ref', ReferenceProperty(valid_types='artifact', spec_version='2.1')),
+        ('dst_payload_ref', ReferenceProperty(valid_types='artifact', spec_version='2.1')),
+        ('encapsulates_refs', ListProperty(ReferenceProperty(valid_types='network-traffic', spec_version='2.1'))),
+        ('encapsulated_by_ref', ReferenceProperty(valid_types='network-traffic', spec_version='2.1')),
         ('extensions', ExtensionsProperty(spec_version='2.1', enclosing_type=_type)),
+        ('spec_version', StringProperty(fixed='2.1')),
+        ('object_marking_refs', ListProperty(ReferenceProperty(valid_types='marking-definition', spec_version='2.1'))),
+        ('granular_markings', ListProperty(GranularMarking)),
+        ('defanged', BooleanProperty(default=lambda: False)),
     ])
+    _id_contributing_properties = ["start", "src_ref", "dst_ref", "src_port", "dst_port", "protocols"]
 
     def _check_object_constraints(self):
         super(NetworkTraffic, self)._check_object_constraints()
@@ -624,20 +740,26 @@ class Process(_Observable):
     _type = 'process'
     _properties = OrderedDict([
         ('type', TypeProperty(_type)),
+        ('id', IDProperty(_type, spec_version='2.1')),
         ('is_hidden', BooleanProperty()),
         ('pid', IntegerProperty()),
         # this is not the created timestamps of the object itself
-        ('created', TimestampProperty()),
+        ('created_time', TimestampProperty()),
         ('cwd', StringProperty()),
         ('command_line', StringProperty()),
         ('environment_variables', DictionaryProperty(spec_version='2.1')),
-        ('opened_connection_refs', ListProperty(ObjectReferenceProperty(valid_types='network-traffic'))),
-        ('creator_user_ref', ObjectReferenceProperty(valid_types='user-account')),
-        ('image_ref', ObjectReferenceProperty(valid_types='file')),
-        ('parent_ref', ObjectReferenceProperty(valid_types='process')),
-        ('child_refs', ListProperty(ObjectReferenceProperty('process'))),
+        ('opened_connection_refs', ListProperty(ReferenceProperty(valid_types='network-traffic', spec_version='2.1'))),
+        ('creator_user_ref', ReferenceProperty(valid_types='user-account', spec_version='2.1')),
+        ('image_ref', ReferenceProperty(valid_types='file', spec_version='2.1')),
+        ('parent_ref', ReferenceProperty(valid_types='process', spec_version='2.1')),
+        ('child_refs', ListProperty(ReferenceProperty(valid_types='process', spec_version='2.1'))),
         ('extensions', ExtensionsProperty(spec_version='2.1', enclosing_type=_type)),
+        ('spec_version', StringProperty(fixed='2.1')),
+        ('object_marking_refs', ListProperty(ReferenceProperty(valid_types='marking-definition', spec_version='2.1'))),
+        ('granular_markings', ListProperty(GranularMarking)),
+        ('defanged', BooleanProperty(default=lambda: False)),
     ])
+    _id_contributing_properties = []
 
     def _check_object_constraints(self):
         # no need to check windows-service-ext, since it has a required property
@@ -663,13 +785,19 @@ class Software(_Observable):
     _type = 'software'
     _properties = OrderedDict([
         ('type', TypeProperty(_type)),
+        ('id', IDProperty(_type, spec_version='2.1')),
         ('name', StringProperty(required=True)),
         ('cpe', StringProperty()),
         ('languages', ListProperty(StringProperty)),
         ('vendor', StringProperty()),
         ('version', StringProperty()),
         ('extensions', ExtensionsProperty(spec_version='2.1', enclosing_type=_type)),
+        ('spec_version', StringProperty(fixed='2.1')),
+        ('object_marking_refs', ListProperty(ReferenceProperty(valid_types='marking-definition', spec_version='2.1'))),
+        ('granular_markings', ListProperty(GranularMarking)),
+        ('defanged', BooleanProperty(default=lambda: False)),
     ])
+    _id_contributing_properties = ["name", "cpe", "vendor", "version"]
 
 
 class URL(_Observable):
@@ -681,9 +809,15 @@ class URL(_Observable):
     _type = 'url'
     _properties = OrderedDict([
         ('type', TypeProperty(_type)),
+        ('id', IDProperty(_type, spec_version='2.1')),
         ('value', StringProperty(required=True)),
         ('extensions', ExtensionsProperty(spec_version='2.1', enclosing_type=_type)),
+        ('spec_version', StringProperty(fixed='2.1')),
+        ('object_marking_refs', ListProperty(ReferenceProperty(valid_types='marking-definition', spec_version='2.1'))),
+        ('granular_markings', ListProperty(GranularMarking)),
+        ('defanged', BooleanProperty(default=lambda: False)),
     ])
+    _id_contributing_properties = ["value"]
 
 
 class UNIXAccountExt(_Extension):
@@ -710,6 +844,7 @@ class UserAccount(_Observable):
     _type = 'user-account'
     _properties = OrderedDict([
         ('type', TypeProperty(_type)),
+        ('id', IDProperty(_type, spec_version='2.1')),
         ('user_id', StringProperty()),
         ('credential', StringProperty()),
         ('account_login', StringProperty()),
@@ -725,7 +860,12 @@ class UserAccount(_Observable):
         ('account_first_login', TimestampProperty()),
         ('account_last_login', TimestampProperty()),
         ('extensions', ExtensionsProperty(spec_version='2.1', enclosing_type=_type)),
+        ('spec_version', StringProperty(fixed='2.1')),
+        ('object_marking_refs', ListProperty(ReferenceProperty(valid_types='marking-definition', spec_version='2.1'))),
+        ('granular_markings', ListProperty(GranularMarking)),
+        ('defanged', BooleanProperty(default=lambda: False)),
     ])
+    _id_contributing_properties = ["account_type", "user_id", "account_login"]
 
 
 class WindowsRegistryValueType(_STIXBase):
@@ -767,14 +907,20 @@ class WindowsRegistryKey(_Observable):
     _type = 'windows-registry-key'
     _properties = OrderedDict([
         ('type', TypeProperty(_type)),
+        ('id', IDProperty(_type, spec_version='2.1')),
         ('key', StringProperty()),
         ('values', ListProperty(EmbeddedObjectProperty(type=WindowsRegistryValueType))),
         # this is not the modified timestamps of the object itself
-        ('modified', TimestampProperty()),
-        ('creator_user_ref', ObjectReferenceProperty(valid_types='user-account')),
+        ('modified_time', TimestampProperty()),
+        ('creator_user_ref', ReferenceProperty(valid_types='user-account', spec_version='2.1')),
         ('number_of_subkeys', IntegerProperty()),
         ('extensions', ExtensionsProperty(spec_version='2.1', enclosing_type=_type)),
+        ('spec_version', StringProperty(fixed='2.1')),
+        ('object_marking_refs', ListProperty(ReferenceProperty(valid_types='marking-definition', spec_version='2.1'))),
+        ('granular_markings', ListProperty(GranularMarking)),
+        ('defanged', BooleanProperty(default=lambda: False)),
     ])
+    _id_contributing_properties = ["key", "values"]
 
     @property
     def values(self):
@@ -818,6 +964,7 @@ class X509Certificate(_Observable):
     _type = 'x509-certificate'
     _properties = OrderedDict([
         ('type', TypeProperty(_type)),
+        ('id', IDProperty(_type, spec_version='2.1')),
         ('is_self_signed', BooleanProperty()),
         ('hashes', HashesProperty(spec_version='2.1')),
         ('version', StringProperty()),
@@ -832,7 +979,12 @@ class X509Certificate(_Observable):
         ('subject_public_key_exponent', IntegerProperty()),
         ('x509_v3_extensions', EmbeddedObjectProperty(type=X509V3ExtenstionsType)),
         ('extensions', ExtensionsProperty(spec_version='2.1', enclosing_type=_type)),
+        ('spec_version', StringProperty(fixed='2.1')),
+        ('object_marking_refs', ListProperty(ReferenceProperty(valid_types='marking-definition', spec_version='2.1'))),
+        ('granular_markings', ListProperty(GranularMarking)),
+        ('defanged', BooleanProperty(default=lambda: False)),
     ])
+    _id_contributing_properties = ["hashes", "serial_number"]
 
 
 def CustomObservable(type='x-custom-observable', properties=None):
diff --git a/stix2/v21/sdo.py b/stix2/v21/sdo.py
index 662007c..7cd7891 100644
--- a/stix2/v21/sdo.py
+++ b/stix2/v21/sdo.py
@@ -30,7 +30,7 @@ class AttackPattern(STIXDomainObject):
         ('type', TypeProperty(_type)),
         ('spec_version', StringProperty(fixed='2.1')),
         ('id', IDProperty(_type, spec_version='2.1')),
-        ('created_by_ref', ReferenceProperty(type='identity', spec_version='2.1')),
+        ('created_by_ref', ReferenceProperty(valid_types='identity', spec_version='2.1')),
         ('created', TimestampProperty(default=lambda: NOW, precision='millisecond')),
         ('modified', TimestampProperty(default=lambda: NOW, precision='millisecond')),
         ('name', StringProperty(required=True)),
@@ -42,7 +42,7 @@ class AttackPattern(STIXDomainObject):
         ('confidence', IntegerProperty()),
         ('lang', StringProperty()),
         ('external_references', ListProperty(ExternalReference)),
-        ('object_marking_refs', ListProperty(ReferenceProperty(type='marking-definition', spec_version='2.1'))),
+        ('object_marking_refs', ListProperty(ReferenceProperty(valid_types='marking-definition', spec_version='2.1'))),
         ('granular_markings', ListProperty(GranularMarking)),
     ])
 
@@ -58,7 +58,7 @@ class Campaign(STIXDomainObject):
         ('type', TypeProperty(_type)),
         ('spec_version', StringProperty(fixed='2.1')),
         ('id', IDProperty(_type, spec_version='2.1')),
-        ('created_by_ref', ReferenceProperty(type='identity', spec_version='2.1')),
+        ('created_by_ref', ReferenceProperty(valid_types='identity', spec_version='2.1')),
         ('created', TimestampProperty(default=lambda: NOW, precision='millisecond')),
         ('modified', TimestampProperty(default=lambda: NOW, precision='millisecond')),
         ('name', StringProperty(required=True)),
@@ -72,7 +72,7 @@ class Campaign(STIXDomainObject):
         ('confidence', IntegerProperty()),
         ('lang', StringProperty()),
         ('external_references', ListProperty(ExternalReference)),
-        ('object_marking_refs', ListProperty(ReferenceProperty(type='marking-definition', spec_version='2.1'))),
+        ('object_marking_refs', ListProperty(ReferenceProperty(valid_types='marking-definition', spec_version='2.1'))),
         ('granular_markings', ListProperty(GranularMarking)),
     ])
 
@@ -98,7 +98,7 @@ class CourseOfAction(STIXDomainObject):
         ('type', TypeProperty(_type)),
         ('spec_version', StringProperty(fixed='2.1')),
         ('id', IDProperty(_type, spec_version='2.1')),
-        ('created_by_ref', ReferenceProperty(type='identity', spec_version='2.1')),
+        ('created_by_ref', ReferenceProperty(valid_types='identity', spec_version='2.1')),
         ('created', TimestampProperty(default=lambda: NOW, precision='millisecond')),
         ('modified', TimestampProperty(default=lambda: NOW, precision='millisecond')),
         ('name', StringProperty(required=True)),
@@ -112,7 +112,7 @@ class CourseOfAction(STIXDomainObject):
         ('confidence', IntegerProperty()),
         ('lang', StringProperty()),
         ('external_references', ListProperty(ExternalReference)),
-        ('object_marking_refs', ListProperty(ReferenceProperty(type='marking-definition', spec_version='2.1'))),
+        ('object_marking_refs', ListProperty(ReferenceProperty(valid_types='marking-definition', spec_version='2.1'))),
         ('granular_markings', ListProperty(GranularMarking)),
     ])
 
@@ -138,18 +138,18 @@ class Grouping(STIXDomainObject):
         ('id', IDProperty(_type, spec_version='2.1')),
         ('created', TimestampProperty(default=lambda: NOW, precision='millisecond')),
         ('modified', TimestampProperty(default=lambda: NOW, precision='millisecond')),
-        ('created_by_ref', ReferenceProperty(type='identity', spec_version='2.1')),
+        ('created_by_ref', ReferenceProperty(valid_types='identity', spec_version='2.1')),
         ('revoked', BooleanProperty(default=lambda: False)),
         ('labels', ListProperty(StringProperty)),
         ('confidence', IntegerProperty()),
         ('lang', StringProperty()),
         ('external_references', ListProperty(ExternalReference)),
-        ('object_marking_refs', ListProperty(ReferenceProperty(type='marking-definition', spec_version='2.1'))),
+        ('object_marking_refs', ListProperty(ReferenceProperty(valid_types='marking-definition', spec_version='2.1'))),
         ('granular_markings', ListProperty(GranularMarking)),
         ('name', StringProperty()),
         ('description', StringProperty()),
         ('context', StringProperty(required=True)),
-        ('object_refs', ListProperty(ReferenceProperty, required=True)),
+        ('object_refs', ListProperty(ReferenceProperty(invalid_types=[""], spec_version='2.1'), required=True)),
     ])
 
 
@@ -164,7 +164,7 @@ class Identity(STIXDomainObject):
         ('type', TypeProperty(_type)),
         ('spec_version', StringProperty(fixed='2.1')),
         ('id', IDProperty(_type, spec_version='2.1')),
-        ('created_by_ref', ReferenceProperty(type='identity', spec_version='2.1')),
+        ('created_by_ref', ReferenceProperty(valid_types='identity', spec_version='2.1')),
         ('created', TimestampProperty(default=lambda: NOW, precision='millisecond')),
         ('modified', TimestampProperty(default=lambda: NOW, precision='millisecond')),
         ('name', StringProperty(required=True)),
@@ -178,7 +178,7 @@ class Identity(STIXDomainObject):
         ('confidence', IntegerProperty()),
         ('lang', StringProperty()),
         ('external_references', ListProperty(ExternalReference)),
-        ('object_marking_refs', ListProperty(ReferenceProperty(type='marking-definition', spec_version='2.1'))),
+        ('object_marking_refs', ListProperty(ReferenceProperty(valid_types='marking-definition', spec_version='2.1'))),
         ('granular_markings', ListProperty(GranularMarking)),
     ])
 
@@ -194,7 +194,7 @@ class Indicator(STIXDomainObject):
         ('type', TypeProperty(_type)),
         ('spec_version', StringProperty(fixed='2.1')),
         ('id', IDProperty(_type, spec_version='2.1')),
-        ('created_by_ref', ReferenceProperty(type='identity', spec_version='2.1')),
+        ('created_by_ref', ReferenceProperty(valid_types='identity', spec_version='2.1')),
         ('created', TimestampProperty(default=lambda: NOW, precision='millisecond')),
         ('modified', TimestampProperty(default=lambda: NOW, precision='millisecond')),
         ('name', StringProperty()),
@@ -211,7 +211,7 @@ class Indicator(STIXDomainObject):
         ('confidence', IntegerProperty()),
         ('lang', StringProperty()),
         ('external_references', ListProperty(ExternalReference)),
-        ('object_marking_refs', ListProperty(ReferenceProperty(type='marking-definition', spec_version='2.1'))),
+        ('object_marking_refs', ListProperty(ReferenceProperty(valid_types='marking-definition', spec_version='2.1'))),
         ('granular_markings', ListProperty(GranularMarking)),
     ])
 
@@ -237,7 +237,7 @@ class Infrastructure(STIXDomainObject):
         ('type', TypeProperty(_type)),
         ('spec_version', StringProperty(fixed='2.1')),
         ('id', IDProperty(_type, spec_version='2.1')),
-        ('created_by_ref', ReferenceProperty(type='identity', spec_version='2.1')),
+        ('created_by_ref', ReferenceProperty(valid_types='identity', spec_version='2.1')),
         ('created', TimestampProperty(default=lambda: NOW, precision='millisecond')),
         ('modified', TimestampProperty(default=lambda: NOW, precision='millisecond')),
         ('revoked', BooleanProperty(default=lambda: False)),
@@ -245,7 +245,7 @@ class Infrastructure(STIXDomainObject):
         ('confidence', IntegerProperty()),
         ('lang', StringProperty()),
         ('external_references', ListProperty(ExternalReference)),
-        ('object_marking_refs', ListProperty(ReferenceProperty(type='marking-definition', spec_version='2.1'))),
+        ('object_marking_refs', ListProperty(ReferenceProperty(valid_types='marking-definition', spec_version='2.1'))),
         ('granular_markings', ListProperty(GranularMarking)),
         ('name', StringProperty(required=True)),
         ('description', StringProperty()),
@@ -278,7 +278,7 @@ class IntrusionSet(STIXDomainObject):
         ('type', TypeProperty(_type)),
         ('spec_version', StringProperty(fixed='2.1')),
         ('id', IDProperty(_type, spec_version='2.1')),
-        ('created_by_ref', ReferenceProperty(type='identity', spec_version='2.1')),
+        ('created_by_ref', ReferenceProperty(valid_types='identity', spec_version='2.1')),
         ('created', TimestampProperty(default=lambda: NOW, precision='millisecond')),
         ('modified', TimestampProperty(default=lambda: NOW, precision='millisecond')),
         ('name', StringProperty(required=True)),
@@ -295,7 +295,7 @@ class IntrusionSet(STIXDomainObject):
         ('confidence', IntegerProperty()),
         ('lang', StringProperty()),
         ('external_references', ListProperty(ExternalReference)),
-        ('object_marking_refs', ListProperty(ReferenceProperty(type='marking-definition', spec_version='2.1'))),
+        ('object_marking_refs', ListProperty(ReferenceProperty(valid_types='marking-definition', spec_version='2.1'))),
         ('granular_markings', ListProperty(GranularMarking)),
     ])
 
@@ -321,7 +321,7 @@ class Location(STIXDomainObject):
         ('type', TypeProperty(_type)),
         ('spec_version', StringProperty(fixed='2.1')),
         ('id', IDProperty(_type, spec_version='2.1')),
-        ('created_by_ref', ReferenceProperty(type='identity', spec_version='2.1')),
+        ('created_by_ref', ReferenceProperty(valid_types='identity', spec_version='2.1')),
         ('created', TimestampProperty(default=lambda: NOW, precision='millisecond')),
         ('modified', TimestampProperty(default=lambda: NOW, precision='millisecond')),
         ('name', StringProperty()),
@@ -340,7 +340,7 @@ class Location(STIXDomainObject):
         ('confidence', IntegerProperty()),
         ('lang', StringProperty()),
         ('external_references', ListProperty(ExternalReference)),
-        ('object_marking_refs', ListProperty(ReferenceProperty(type='marking-definition', spec_version='2.1'))),
+        ('object_marking_refs', ListProperty(ReferenceProperty(valid_types='marking-definition', spec_version='2.1'))),
         ('granular_markings', ListProperty(GranularMarking)),
     ])
 
@@ -429,7 +429,7 @@ class Malware(STIXDomainObject):
         ('type', TypeProperty(_type)),
         ('spec_version', StringProperty(fixed='2.1')),
         ('id', IDProperty(_type, spec_version='2.1')),
-        ('created_by_ref', ReferenceProperty(type='identity', spec_version='2.1')),
+        ('created_by_ref', ReferenceProperty(valid_types='identity', spec_version='2.1')),
         ('created', TimestampProperty(default=lambda: NOW, precision='millisecond')),
         ('modified', TimestampProperty(default=lambda: NOW, precision='millisecond')),
         ('name', StringProperty()),
@@ -444,13 +444,13 @@ class Malware(STIXDomainObject):
         ('architecture_execution_envs', ListProperty(StringProperty)),
         ('implementation_languages', ListProperty(StringProperty)),
         ('capabilities', ListProperty(StringProperty)),
-        ('sample_refs', ListProperty(ReferenceProperty(spec_version='2.1'))),
+        ('sample_refs', ListProperty(ReferenceProperty(valid_types=['artifact', 'file'], spec_version='2.1'))),
         ('revoked', BooleanProperty(default=lambda: False)),
         ('labels', ListProperty(StringProperty)),
         ('confidence', IntegerProperty()),
         ('lang', StringProperty()),
         ('external_references', ListProperty(ExternalReference)),
-        ('object_marking_refs', ListProperty(ReferenceProperty(type='marking-definition', spec_version='2.1'))),
+        ('object_marking_refs', ListProperty(ReferenceProperty(valid_types='marking-definition', spec_version='2.1'))),
         ('granular_markings', ListProperty(GranularMarking)),
     ])
 
@@ -484,19 +484,19 @@ class MalwareAnalysis(STIXDomainObject):
         ('id', IDProperty(_type, spec_version='2.1')),
         ('created', TimestampProperty(default=lambda: NOW, precision='millisecond')),
         ('modified', TimestampProperty(default=lambda: NOW, precision='millisecond')),
-        ('created_by_ref', ReferenceProperty(type='identity', spec_version='2.1')),
+        ('created_by_ref', ReferenceProperty(valid_types='identity', spec_version='2.1')),
         ('revoked', BooleanProperty(default=lambda: False)),
         ('labels', ListProperty(StringProperty)),
         ('confidence', IntegerProperty()),
         ('lang', StringProperty()),
         ('external_references', ListProperty(ExternalReference)),
-        ('object_marking_refs', ListProperty(ReferenceProperty(type='marking-definition', spec_version='2.1'))),
+        ('object_marking_refs', ListProperty(ReferenceProperty(valid_types='marking-definition', spec_version='2.1'))),
         ('granular_markings', ListProperty(GranularMarking)),
         ('product', StringProperty(required=True)),
         ('version', StringProperty()),
-        ('host_vm_ref', ReferenceProperty(type='software', spec_version='2.1')),
-        ('operating_system_ref', ReferenceProperty(type='software', spec_version='2.1')),
-        ('installed_software_refs', ListProperty(ReferenceProperty(type='software', spec_version='2.1'))),
+        ('host_vm_ref', ReferenceProperty(valid_types='software', spec_version='2.1')),
+        ('operating_system_ref', ReferenceProperty(valid_types='software', spec_version='2.1')),
+        ('installed_software_refs', ListProperty(ReferenceProperty(valid_types='software', spec_version='2.1'))),
         ('configuration_version', StringProperty()),
         ('modules', ListProperty(StringProperty)),
         ('analysis_engine_version', StringProperty()),
@@ -505,7 +505,7 @@ class MalwareAnalysis(STIXDomainObject):
         ('analysis_started', TimestampProperty()),
         ('analysis_ended', TimestampProperty()),
         ('av_result', StringProperty()),
-        ('analysis_sco_refs', ListProperty(ReferenceProperty(spec_version='2.1'))),
+        ('analysis_sco_refs', ListProperty(ReferenceProperty(valid_types="only_SCO", spec_version='2.1'))),
     ])
 
     def _check_object_constraints(self):
@@ -525,19 +525,19 @@ class Note(STIXDomainObject):
         ('type', TypeProperty(_type)),
         ('spec_version', StringProperty(fixed='2.1')),
         ('id', IDProperty(_type, spec_version='2.1')),
-        ('created_by_ref', ReferenceProperty(type='identity', spec_version='2.1')),
+        ('created_by_ref', ReferenceProperty(valid_types='identity', spec_version='2.1')),
         ('created', TimestampProperty(default=lambda: NOW, precision='millisecond')),
         ('modified', TimestampProperty(default=lambda: NOW, precision='millisecond')),
         ('abstract', StringProperty()),
         ('content', StringProperty(required=True)),
         ('authors', ListProperty(StringProperty)),
-        ('object_refs', ListProperty(ReferenceProperty, required=True)),
+        ('object_refs', ListProperty(ReferenceProperty(invalid_types=[""], spec_version='2.1'), required=True)),
         ('revoked', BooleanProperty(default=lambda: False)),
         ('labels', ListProperty(StringProperty)),
         ('confidence', IntegerProperty()),
         ('lang', StringProperty()),
         ('external_references', ListProperty(ExternalReference)),
-        ('object_marking_refs', ListProperty(ReferenceProperty(type='marking-definition', spec_version='2.1'))),
+        ('object_marking_refs', ListProperty(ReferenceProperty(valid_types='marking-definition', spec_version='2.1'))),
         ('granular_markings', ListProperty(GranularMarking)),
     ])
 
@@ -553,20 +553,20 @@ class ObservedData(STIXDomainObject):
         ('type', TypeProperty(_type)),
         ('spec_version', StringProperty(fixed='2.1')),
         ('id', IDProperty(_type, spec_version='2.1')),
-        ('created_by_ref', ReferenceProperty(type='identity', spec_version='2.1')),
+        ('created_by_ref', ReferenceProperty(valid_types='identity', spec_version='2.1')),
         ('created', TimestampProperty(default=lambda: NOW, precision='millisecond')),
         ('modified', TimestampProperty(default=lambda: NOW, precision='millisecond')),
         ('first_observed', TimestampProperty(required=True)),
         ('last_observed', TimestampProperty(required=True)),
         ('number_observed', IntegerProperty(min=1, max=999999999, required=True)),
         ('objects', ObservableProperty(spec_version='2.1')),
-        ('object_refs', ListProperty(ReferenceProperty(spec_version="2.1"))),
+        ('object_refs', ListProperty(ReferenceProperty(valid_types="only_SCO_&_SRO", spec_version="2.1"))),
         ('revoked', BooleanProperty(default=lambda: False)),
         ('labels', ListProperty(StringProperty)),
         ('confidence', IntegerProperty()),
         ('lang', StringProperty()),
         ('external_references', ListProperty(ExternalReference)),
-        ('object_marking_refs', ListProperty(ReferenceProperty(type='marking-definition', spec_version='2.1'))),
+        ('object_marking_refs', ListProperty(ReferenceProperty(valid_types='marking-definition', spec_version='2.1'))),
         ('granular_markings', ListProperty(GranularMarking)),
     ])
 
@@ -609,7 +609,7 @@ class Opinion(STIXDomainObject):
         ('type', TypeProperty(_type)),
         ('spec_version', StringProperty(fixed='2.1')),
         ('id', IDProperty(_type, spec_version='2.1')),
-        ('created_by_ref', ReferenceProperty(type='identity', spec_version='2.1')),
+        ('created_by_ref', ReferenceProperty(valid_types='identity', spec_version='2.1')),
         ('created', TimestampProperty(default=lambda: NOW, precision='millisecond')),
         ('modified', TimestampProperty(default=lambda: NOW, precision='millisecond')),
         ('explanation', StringProperty()),
@@ -625,13 +625,13 @@ class Opinion(STIXDomainObject):
                 ], required=True,
             ),
         ),
-        ('object_refs', ListProperty(ReferenceProperty, required=True)),
+        ('object_refs', ListProperty(ReferenceProperty(invalid_types=[""], spec_version='2.1'), required=True)),
         ('revoked', BooleanProperty(default=lambda: False)),
         ('labels', ListProperty(StringProperty)),
         ('confidence', IntegerProperty()),
         ('lang', StringProperty()),
         ('external_references', ListProperty(ExternalReference)),
-        ('object_marking_refs', ListProperty(ReferenceProperty(type='marking-definition', spec_version='2.1'))),
+        ('object_marking_refs', ListProperty(ReferenceProperty(valid_types='marking-definition', spec_version='2.1'))),
         ('granular_markings', ListProperty(GranularMarking)),
     ])
 
@@ -647,20 +647,20 @@ class Report(STIXDomainObject):
         ('type', TypeProperty(_type)),
         ('spec_version', StringProperty(fixed='2.1')),
         ('id', IDProperty(_type, spec_version='2.1')),
-        ('created_by_ref', ReferenceProperty(type='identity', spec_version='2.1')),
+        ('created_by_ref', ReferenceProperty(valid_types='identity', spec_version='2.1')),
         ('created', TimestampProperty(default=lambda: NOW, precision='millisecond')),
         ('modified', TimestampProperty(default=lambda: NOW, precision='millisecond')),
         ('name', StringProperty(required=True)),
         ('description', StringProperty()),
         ('report_types', ListProperty(StringProperty, required=True)),
         ('published', TimestampProperty(required=True)),
-        ('object_refs', ListProperty(ReferenceProperty, required=True)),
+        ('object_refs', ListProperty(ReferenceProperty(invalid_types=[""], spec_version='2.1'), required=True)),
         ('revoked', BooleanProperty(default=lambda: False)),
         ('labels', ListProperty(StringProperty)),
         ('confidence', IntegerProperty()),
         ('lang', StringProperty()),
         ('external_references', ListProperty(ExternalReference)),
-        ('object_marking_refs', ListProperty(ReferenceProperty(type='marking-definition', spec_version='2.1'))),
+        ('object_marking_refs', ListProperty(ReferenceProperty(valid_types='marking-definition', spec_version='2.1'))),
         ('granular_markings', ListProperty(GranularMarking)),
     ])
 
@@ -676,7 +676,7 @@ class ThreatActor(STIXDomainObject):
         ('type', TypeProperty(_type)),
         ('spec_version', StringProperty(fixed='2.1')),
         ('id', IDProperty(_type, spec_version='2.1')),
-        ('created_by_ref', ReferenceProperty(type='identity', spec_version='2.1')),
+        ('created_by_ref', ReferenceProperty(valid_types='identity', spec_version='2.1')),
         ('created', TimestampProperty(default=lambda: NOW, precision='millisecond')),
         ('modified', TimestampProperty(default=lambda: NOW, precision='millisecond')),
         ('name', StringProperty(required=True)),
@@ -697,7 +697,7 @@ class ThreatActor(STIXDomainObject):
         ('confidence', IntegerProperty()),
         ('lang', StringProperty()),
         ('external_references', ListProperty(ExternalReference)),
-        ('object_marking_refs', ListProperty(ReferenceProperty(type='marking-definition', spec_version='2.1'))),
+        ('object_marking_refs', ListProperty(ReferenceProperty(valid_types='marking-definition', spec_version='2.1'))),
         ('granular_markings', ListProperty(GranularMarking)),
     ])
 
@@ -723,7 +723,7 @@ class Tool(STIXDomainObject):
         ('type', TypeProperty(_type)),
         ('spec_version', StringProperty(fixed='2.1')),
         ('id', IDProperty(_type, spec_version='2.1')),
-        ('created_by_ref', ReferenceProperty(type='identity', spec_version='2.1')),
+        ('created_by_ref', ReferenceProperty(valid_types='identity', spec_version='2.1')),
         ('created', TimestampProperty(default=lambda: NOW, precision='millisecond')),
         ('modified', TimestampProperty(default=lambda: NOW, precision='millisecond')),
         ('name', StringProperty(required=True)),
@@ -737,7 +737,7 @@ class Tool(STIXDomainObject):
         ('confidence', IntegerProperty()),
         ('lang', StringProperty()),
         ('external_references', ListProperty(ExternalReference)),
-        ('object_marking_refs', ListProperty(ReferenceProperty(type='marking-definition', spec_version='2.1'))),
+        ('object_marking_refs', ListProperty(ReferenceProperty(valid_types='marking-definition', spec_version='2.1'))),
         ('granular_markings', ListProperty(GranularMarking)),
     ])
 
@@ -753,7 +753,7 @@ class Vulnerability(STIXDomainObject):
         ('type', TypeProperty(_type)),
         ('spec_version', StringProperty(fixed='2.1')),
         ('id', IDProperty(_type, spec_version='2.1')),
-        ('created_by_ref', ReferenceProperty(type='identity', spec_version='2.1')),
+        ('created_by_ref', ReferenceProperty(valid_types='identity', spec_version='2.1')),
         ('created', TimestampProperty(default=lambda: NOW, precision='millisecond')),
         ('modified', TimestampProperty(default=lambda: NOW, precision='millisecond')),
         ('name', StringProperty(required=True)),
@@ -763,7 +763,7 @@ class Vulnerability(STIXDomainObject):
         ('confidence', IntegerProperty()),
         ('lang', StringProperty()),
         ('external_references', ListProperty(ExternalReference)),
-        ('object_marking_refs', ListProperty(ReferenceProperty(type='marking-definition', spec_version='2.1'))),
+        ('object_marking_refs', ListProperty(ReferenceProperty(valid_types='marking-definition', spec_version='2.1'))),
         ('granular_markings', ListProperty(GranularMarking)),
     ])
 
@@ -803,7 +803,7 @@ def CustomObject(type='x-custom-type', properties=None):
                 ('type', TypeProperty(type)),
                 ('spec_version', StringProperty(fixed='2.1')),
                 ('id', IDProperty(type, spec_version='2.1')),
-                ('created_by_ref', ReferenceProperty(type='identity', spec_version='2.1')),
+                ('created_by_ref', ReferenceProperty(valid_types='identity', spec_version='2.1')),
                 ('created', TimestampProperty(default=lambda: NOW, precision='millisecond')),
                 ('modified', TimestampProperty(default=lambda: NOW, precision='millisecond')),
             ],
@@ -814,7 +814,7 @@ def CustomObject(type='x-custom-type', properties=None):
                 ('confidence', IntegerProperty()),
                 ('lang', StringProperty()),
                 ('external_references', ListProperty(ExternalReference)),
-                ('object_marking_refs', ListProperty(ReferenceProperty(type='marking-definition', spec_version='2.1'))),
+                ('object_marking_refs', ListProperty(ReferenceProperty(valid_types='marking-definition', spec_version='2.1'))),
                 ('granular_markings', ListProperty(GranularMarking)),
             ],
             sorted([x for x in properties if x[0].startswith('x_')], key=lambda x: x[0]),
diff --git a/stix2/v21/sro.py b/stix2/v21/sro.py
index 149094e..57c7719 100644
--- a/stix2/v21/sro.py
+++ b/stix2/v21/sro.py
@@ -17,18 +17,20 @@ class Relationship(STIXRelationshipObject):
     `the STIX 2.1 specification <link here>`__.
     """
 
+    _invalid_source_target_types = ['bundle', 'language-content', 'marking-definition', 'relationship', 'sighting']
+
     _type = 'relationship'
     _properties = OrderedDict([
         ('type', TypeProperty(_type)),
         ('spec_version', StringProperty(fixed='2.1')),
         ('id', IDProperty(_type, spec_version='2.1')),
-        ('created_by_ref', ReferenceProperty(type='identity', spec_version='2.1')),
+        ('created_by_ref', ReferenceProperty(valid_types='identity', spec_version='2.1')),
         ('created', TimestampProperty(default=lambda: NOW, precision='millisecond')),
         ('modified', TimestampProperty(default=lambda: NOW, precision='millisecond')),
         ('relationship_type', StringProperty(required=True)),
         ('description', StringProperty()),
-        ('source_ref', ReferenceProperty(spec_version='2.1', required=True)),
-        ('target_ref', ReferenceProperty(spec_version='2.1', required=True)),
+        ('source_ref', ReferenceProperty(invalid_types=_invalid_source_target_types, spec_version='2.1', required=True)),
+        ('target_ref', ReferenceProperty(invalid_types=_invalid_source_target_types, spec_version='2.1', required=True)),
         ('start_time', TimestampProperty()),
         ('stop_time', TimestampProperty()),
         ('revoked', BooleanProperty(default=lambda: False)),
@@ -36,7 +38,7 @@ class Relationship(STIXRelationshipObject):
         ('confidence', IntegerProperty()),
         ('lang', StringProperty()),
         ('external_references', ListProperty(ExternalReference)),
-        ('object_marking_refs', ListProperty(ReferenceProperty(type='marking-definition', spec_version='2.1'))),
+        ('object_marking_refs', ListProperty(ReferenceProperty(valid_types='marking-definition', spec_version='2.1'))),
         ('granular_markings', ListProperty(GranularMarking)),
     ])
 
@@ -77,23 +79,23 @@ class Sighting(STIXRelationshipObject):
         ('type', TypeProperty(_type)),
         ('spec_version', StringProperty(fixed='2.1')),
         ('id', IDProperty(_type, spec_version='2.1')),
-        ('created_by_ref', ReferenceProperty(type='identity', spec_version='2.1')),
+        ('created_by_ref', ReferenceProperty(valid_types='identity', spec_version='2.1')),
         ('created', TimestampProperty(default=lambda: NOW, precision='millisecond')),
         ('modified', TimestampProperty(default=lambda: NOW, precision='millisecond')),
         ('description', StringProperty()),
         ('first_seen', TimestampProperty()),
         ('last_seen', TimestampProperty()),
         ('count', IntegerProperty(min=0, max=999999999)),
-        ('sighting_of_ref', ReferenceProperty(spec_version='2.1', required=True)),
-        ('observed_data_refs', ListProperty(ReferenceProperty(type='observed-data', spec_version='2.1'))),
-        ('where_sighted_refs', ListProperty(ReferenceProperty(type='identity', spec_version='2.1'))),
+        ('sighting_of_ref', ReferenceProperty(valid_types="only_SDO", spec_version='2.1', required=True)),
+        ('observed_data_refs', ListProperty(ReferenceProperty(valid_types='observed-data', spec_version='2.1'))),
+        ('where_sighted_refs', ListProperty(ReferenceProperty(valid_types='identity', spec_version='2.1'))),
         ('summary', BooleanProperty()),
         ('revoked', BooleanProperty(default=lambda: False)),
         ('labels', ListProperty(StringProperty)),
         ('confidence', IntegerProperty()),
         ('lang', StringProperty()),
         ('external_references', ListProperty(ExternalReference)),
-        ('object_marking_refs', ListProperty(ReferenceProperty(type='marking-definition', spec_version='2.1'))),
+        ('object_marking_refs', ListProperty(ReferenceProperty(valid_types='marking-definition', spec_version='2.1'))),
         ('granular_markings', ListProperty(GranularMarking)),
     ])
 


Test patch:
diff --git a/stix2/test/v20/test_properties.py b/stix2/test/v20/test_properties.py
index 9952eac..f71d829 100644
--- a/stix2/test/v20/test_properties.py
+++ b/stix2/test/v20/test_properties.py
@@ -276,7 +276,7 @@ def test_boolean_property_invalid(value):
 
 
 def test_reference_property():
-    ref_prop = ReferenceProperty(spec_version="2.0")
+    ref_prop = ReferenceProperty(valid_types="my-type", spec_version="2.0")
 
     assert ref_prop.clean("my-type--00000000-0000-4000-8000-000000000000")
     with pytest.raises(ValueError):
@@ -288,7 +288,7 @@ def test_reference_property():
 
 
 def test_reference_property_specific_type():
-    ref_prop = ReferenceProperty("my-type", spec_version="2.0")
+    ref_prop = ReferenceProperty(valid_types="my-type", spec_version="2.0")
 
     with pytest.raises(ValueError):
         ref_prop.clean("not-my-type--8a8e8758-f92c-4058-ba38-f061cd42a0cf")
diff --git a/stix2/test/v20/test_utils.py b/stix2/test/v20/test_utils.py
index ee011c1..0433fd5 100644
--- a/stix2/test/v20/test_utils.py
+++ b/stix2/test/v20/test_utils.py
@@ -144,8 +144,8 @@ def test_deduplicate(stix_objs1):
                         "type": "network-traffic",
                         "src_ref": "1",
                         "protocols": [
-                          "tcp",
-                          "http",
+                            "tcp",
+                            "http",
                         ],
                         "extensions": {
                             "http-request-ext": {
diff --git a/stix2/test/v21/test_core.py b/stix2/test/v21/test_core.py
index 25348cd..2018395 100644
--- a/stix2/test/v21/test_core.py
+++ b/stix2/test/v21/test_core.py
@@ -127,7 +127,7 @@ def test_register_observable_with_default_version():
             "1": {
                 "type": "directory",
                 "path": "/usr/home",
-                "contains_refs": ["0"],
+                "contains_refs": ["file--420bc087-8b53-5ae9-8210-20d27d5e96c8"],
             },
         },
     )
@@ -165,7 +165,7 @@ def test_register_observable_extension_with_default_version():
             "1": {
                 "type": "directory",
                 "path": "/usr/home",
-                "contains_refs": ["0"],
+                "contains_refs": ["file--420bc087-8b53-5ae9-8210-20d27d5e96c8"],
             },
         },
     )
diff --git a/stix2/test/v21/test_datastore_filters.py b/stix2/test/v21/test_datastore_filters.py
index b96aa4d..b7b41a0 100644
--- a/stix2/test/v21/test_datastore_filters.py
+++ b/stix2/test/v21/test_datastore_filters.py
@@ -86,7 +86,10 @@ stix_objs = [
         "objects": {
             "0": {
                 "type": "file",
+                "spec_version": "2.1",
+                "id": "file--42a7175a-42cc-508f-8fa7-23b330aff876",
                 "name": "HAL 9000.exe",
+                "defanged": False,
             },
         },
 
@@ -109,8 +112,14 @@ filters = [
     Filter("object_marking_refs", "=", "marking-definition--613f2e26-0000-4000-8000-b8e91df99dc9"),
     Filter("granular_markings.selectors", "in", "description"),
     Filter("external_references.source_name", "=", "CVE"),
-    Filter("objects", "=", {"0": {"type": "file", "name": "HAL 9000.exe"}}),
-    Filter("objects", "contains", {"type": "file", "name": "HAL 9000.exe"}),
+    Filter(
+        "objects", "=",
+        {"0": {"type": "file", "id": "file--42a7175a-42cc-508f-8fa7-23b330aff876", "name": "HAL 9000.exe", "spec_version": "2.1", "defanged": False}},
+    ),
+    Filter(
+        "objects", "contains",
+        {"type": "file", "id": "file--42a7175a-42cc-508f-8fa7-23b330aff876", "name": "HAL 9000.exe", "spec_version": "2.1", "defanged": False},
+    ),
     Filter("labels", "contains", "heartbleed"),
 ]
 
diff --git a/stix2/test/v21/test_observed_data.py b/stix2/test/v21/test_observed_data.py
index c2999f8..0074bf7 100644
--- a/stix2/test/v21/test_observed_data.py
+++ b/stix2/test/v21/test_observed_data.py
@@ -1,5 +1,6 @@
 import datetime as dt
 import re
+import uuid
 
 import pytest
 import pytz
@@ -25,6 +26,8 @@ EXPECTED = """{
     "objects": {
         "0": {
             "type": "file",
+            "id": "file--5956efbb-a7b0-566d-a7f9-a202eb05c70f",
+            "spec_version": "2.1",
             "name": "foo.exe"
         }
     }
@@ -42,13 +45,19 @@ def test_observed_data_example():
         number_observed=50,
         objects={
             "0": {
-                "name": "foo.exe",
                 "type": "file",
+                "id": "file--5956efbb-a7b0-566d-a7f9-a202eb05c70f",
+                "name": "foo.exe",
             },
         },
     )
 
-    assert str(observed_data) == EXPECTED
+    assert observed_data.id == "observed-data--b67d30ff-02ac-498a-92f9-32f845f448cf"
+    assert observed_data.created_by_ref == "identity--311b2d2d-f010-4473-83ec-1edf84858f4c"
+    assert observed_data.created == observed_data.modified == dt.datetime(2016, 4, 6, 19, 58, 16, tzinfo=pytz.utc)
+    assert observed_data.first_observed == observed_data.last_observed == dt.datetime(2015, 12, 21, 19, 00, 00, tzinfo=pytz.utc)
+    assert observed_data.number_observed == 50
+    assert observed_data.objects['0'] == stix2.v21.File(name="foo.exe")
 
 
 EXPECTED_WITH_REF = """{
@@ -64,13 +73,17 @@ EXPECTED_WITH_REF = """{
     "objects": {
         "0": {
             "type": "file",
+            "id": "file--5956efbb-a7b0-566d-a7f9-a202eb05c70f",
+            "spec_version": "2.1",
             "name": "foo.exe"
         },
         "1": {
             "type": "directory",
+            "id": "directory--536a61a4-0934-516b-9aad-fcbb75e0583a",
+            "spec_version": "2.1",
             "path": "/usr/home",
             "contains_refs": [
-                "0"
+                "file--5956efbb-a7b0-566d-a7f9-a202eb05c70f"
             ]
         }
     }
@@ -88,18 +101,25 @@ def test_observed_data_example_with_refs():
         number_observed=50,
         objects={
             "0": {
-                "name": "foo.exe",
                 "type": "file",
+                "id": "file--5956efbb-a7b0-566d-a7f9-a202eb05c70f",
+                "name": "foo.exe",
             },
             "1": {
                 "type": "directory",
+                "id": "directory--536a61a4-0934-516b-9aad-fcbb75e0583a",
                 "path": "/usr/home",
-                "contains_refs": ["0"],
+                "contains_refs": ["file--5956efbb-a7b0-566d-a7f9-a202eb05c70f"],
             },
         },
     )
-
-    assert str(observed_data) == EXPECTED_WITH_REF
+    assert observed_data.id == "observed-data--b67d30ff-02ac-498a-92f9-32f845f448cf"
+    assert observed_data.created_by_ref == "identity--311b2d2d-f010-4473-83ec-1edf84858f4c"
+    assert observed_data.created == observed_data.modified == dt.datetime(2016, 4, 6, 19, 58, 16, tzinfo=pytz.utc)
+    assert observed_data.first_observed == observed_data.last_observed == dt.datetime(2015, 12, 21, 19, 00, 00, tzinfo=pytz.utc)
+    assert observed_data.number_observed == 50
+    assert observed_data.objects['0'] == stix2.v21.File(name="foo.exe")
+    assert observed_data.objects['1'] == stix2.v21.Directory(path="/usr/home", contains_refs=["file--5956efbb-a7b0-566d-a7f9-a202eb05c70f"])
 
 
 EXPECTED_OBJECT_REFS = """{
@@ -113,9 +133,9 @@ EXPECTED_OBJECT_REFS = """{
     "last_observed": "2015-12-21T19:00:00Z",
     "number_observed": 50,
     "object_refs": [
-        "foo--758bf2c0-a6f1-56d1-872e-6b727467739a",
-        "bar--d97ed5c4-3f33-46d9-b25b-c3d7b94d1457",
-        "baz--eca0b3ba-8d76-11e9-a1fd-34415dabec0c"
+        "file--758bf2c0-a6f1-56d1-872e-6b727467739a",
+        "url--d97ed5c4-3f33-46d9-b25b-c3d7b94d1457",
+        "mutex--eca0b3ba-8d76-11e9-a1fd-34415dabec0c"
     ]
 }"""
 
@@ -130,9 +150,9 @@ def test_observed_data_example_with_object_refs():
         last_observed="2015-12-21T19:00:00Z",
         number_observed=50,
         object_refs=[
-            "foo--758bf2c0-a6f1-56d1-872e-6b727467739a",
-            "bar--d97ed5c4-3f33-46d9-b25b-c3d7b94d1457",
-            "baz--eca0b3ba-8d76-11e9-a1fd-34415dabec0c",
+            "file--758bf2c0-a6f1-56d1-872e-6b727467739a",
+            "url--d97ed5c4-3f33-46d9-b25b-c3d7b94d1457",
+            "mutex--eca0b3ba-8d76-11e9-a1fd-34415dabec0c",
         ],
     )
 
@@ -156,9 +176,9 @@ def test_observed_data_object_constraint():
                 },
             },
             object_refs=[
-                "foo--758bf2c0-a6f1-56d1-872e-6b727467739a",
-                "bar--d97ed5c4-3f33-46d9-b25b-c3d7b94d1457",
-                "baz--eca0b3ba-8d76-11e9-a1fd-34415dabec0c",
+                "file--758bf2c0-a6f1-56d1-872e-6b727467739a",
+                "url--d97ed5c4-3f33-46d9-b25b-c3d7b94d1457",
+                "mutex--eca0b3ba-8d76-11e9-a1fd-34415dabec0c",
             ],
         )
 
@@ -176,19 +196,20 @@ def test_observed_data_example_with_bad_refs():
             objects={
                 "0": {
                     "type": "file",
+                    "id": "file--5956efbb-a7b0-566d-a7f9-a202eb05c70f",
                     "name": "foo.exe",
                 },
                 "1": {
                     "type": "directory",
                     "path": "/usr/home",
-                    "contains_refs": ["2"],
+                    "contains_refs": ["monkey--5956efbb-a7b0-566d-a7f9-a202eb05c70f"],
                 },
             },
         )
 
-    assert excinfo.value.cls == stix2.v21.ObservedData
-    assert excinfo.value.prop_name == "objects"
-    assert excinfo.value.reason == "Invalid object reference for 'Directory:contains_refs': '2' is not a valid object in local scope"
+    assert excinfo.value.cls == stix2.v21.Directory
+    assert excinfo.value.prop_name == "contains_refs"
+    assert "The type-specifying prefix 'monkey--' for this property is not valid" in excinfo.value.reason
 
 
 def test_observed_data_example_with_non_dictionary():
@@ -244,6 +265,7 @@ def test_observed_data_example_with_empty_dictionary():
                 "0": {
                     "name": "foo.exe",
                     "type": "file",
+                    "id": "file--5956efbb-a7b0-566d-a7f9-a202eb05c70f",
                 },
             },
         },
@@ -342,17 +364,21 @@ def test_parse_autonomous_system_valid(data):
         "type": "email-addr",
         "value": "john@example.com",
         "display_name": "John Doe",
-        "belongs_to_ref": "0"
+        "belongs_to_ref": "user-account--fc07c1af-6b11-41f8-97a4-47920d866a91"
     }""",
     ],
 )
 def test_parse_email_address(data):
-    odata = stix2.parse_observable(data, {"0": "user-account"}, version='2.1')
+    odata = stix2.parse_observable(data, version='2.1')
     assert odata.type == "email-addr"
 
-    odata_str = re.compile('"belongs_to_ref": "0"', re.DOTALL).sub('"belongs_to_ref": "3"', data)
-    with pytest.raises(stix2.exceptions.InvalidObjRefError):
-        stix2.parse_observable(odata_str, {"0": "user-account"}, version='2.1')
+    odata_str = re.compile(
+        '"belongs_to_ref": "user-account--fc07c1af-6b11-41f8-97a4-47920d866a91"', re.DOTALL,
+    ).sub(
+        '"belongs_to_ref": "mutex--9be6365f-b89c-48c0-9340-6953f6595718"', data,
+    )
+    with pytest.raises(stix2.exceptions.InvalidValueError):
+        stix2.parse_observable(odata_str, version='2.1')
 
 
 @pytest.mark.parametrize(
@@ -363,12 +389,12 @@ def test_parse_email_address(data):
         "is_multipart": true,
         "content_type": "multipart/mixed",
         "date": "2016-06-19T14:20:40.000Z",
-        "from_ref": "1",
+        "from_ref": "email-addr--d4ef7e1f-086d-5ff4-bce4-312ddc3eae76",
         "to_refs": [
-          "2"
+          "email-addr--8b0eb924-208c-5efd-80e5-84e2d610e54b"
         ],
         "cc_refs": [
-          "3"
+          "email-addr--1766f860-5cf3-5697-8789-35f1242663d5"
         ],
         "subject": "Check out this picture of a cat!",
         "additional_header_fields": {
@@ -385,12 +411,12 @@ def test_parse_email_address(data):
           {
             "content_type": "image/png",
             "content_disposition": "attachment; filename=\\"tabby.png\\"",
-            "body_raw_ref": "4"
+            "body_raw_ref": "artifact--80b04ad8-db52-464b-a85a-a44a5f3a60c5"
           },
           {
             "content_type": "application/zip",
             "content_disposition": "attachment; filename=\\"tabby_pics.zip\\"",
-            "body_raw_ref": "5"
+            "body_raw_ref": "file--e63474fc-b386-5630-a003-1b555e22f99b"
           }
         ]
     }
@@ -398,15 +424,7 @@ def test_parse_email_address(data):
     ],
 )
 def test_parse_email_message(data):
-    valid_refs = {
-        "0": "email-message",
-        "1": "email-addr",
-        "2": "email-addr",
-        "3": "email-addr",
-        "4": "artifact",
-        "5": "file",
-    }
-    odata = stix2.parse_observable(data, valid_refs, version='2.1')
+    odata = stix2.parse_observable(data, version='2.1')
     assert odata.type == "email-message"
     assert odata.body_multipart[0].content_disposition == "inline"
 
@@ -416,8 +434,8 @@ def test_parse_email_message(data):
         """
     {
          "type": "email-message",
-         "from_ref": "0",
-         "to_refs": ["1"],
+         "from_ref": "email-addr--d4ef7e1f-086d-5ff4-bce4-312ddc3eae76",
+         "to_refs": ["email-addr--8b0eb924-208c-5efd-80e5-84e2d610e54b"],
          "is_multipart": true,
          "date": "1997-11-21T15:55:06.000Z",
          "subject": "Saying Hello",
@@ -427,12 +445,8 @@ def test_parse_email_message(data):
     ],
 )
 def test_parse_email_message_not_multipart(data):
-    valid_refs = {
-        "0": "email-addr",
-        "1": "email-addr",
-    }
     with pytest.raises(stix2.exceptions.DependentPropertiesError) as excinfo:
-        stix2.parse_observable(data, valid_refs, version='2.1')
+        stix2.parse_observable(data, version='2.1')
 
     assert excinfo.value.cls == stix2.v21.EmailMessage
     assert excinfo.value.dependencies == [("is_multipart", "body")]
@@ -442,35 +456,38 @@ def test_parse_email_message_not_multipart(data):
     "data", [
         """"0": {
             "type": "file",
+            "id": "file--ecd47d73-15e4-5250-afda-ef8897b22340",
             "hashes": {
                 "SHA-256": "ceafbfd424be2ca4a5f0402cae090dda2fb0526cf521b60b60077c0f622b285a"
             }
         },
         "1": {
             "type": "file",
+            "id": "file--65f2873d-38c2-56b4-bfa5-e3ef21e8a3c3",
             "hashes": {
-                "SHA-256": "19c549ec2628b989382f6b280cbd7bb836a0b461332c0fe53511ce7d584b89d3"
+                "SHA-1": "6e71b3cac15d32fe2d36c270887df9479c25c640"
             }
         },
         "2": {
             "type": "file",
+            "id": "file--ef2d6dca-ec7d-5ab7-8dd9-ec9c0dee0eac",
             "hashes": {
-                "SHA-256": "0969de02ecf8a5f003e3f6d063d848c8a193aada092623f8ce408c15bcb5f038"
+                "SHA-512": "b7e98c78c24fb4c2c7b175e90474b21eae0ccf1b5ea4708b4e0f2d2940004419edc7161c18a1e71b2565df099ba017bcaa67a248e2989b6268ce078b88f2e210"
             }
         },
         "3": {
             "type": "file",
             "name": "foo.zip",
             "hashes": {
-                "SHA-256": "35a01331e9ad96f751278b891b6ea09699806faedfa237d40513d92ad1b7100f"
+                "SHA3-256": "35a01331e9ad96f751278b891b6ea09699806faedfa237d40513d92ad1b7100f"
             },
             "mime_type": "application/zip",
             "extensions": {
                 "archive-ext": {
                     "contains_refs": [
-                        "0",
-                        "1",
-                        "2"
+                        "file--ecd47d73-15e4-5250-afda-ef8897b22340",
+                        "file--65f2873d-38c2-56b4-bfa5-e3ef21e8a3c3",
+                        "file--ef2d6dca-ec7d-5ab7-8dd9-ec9c0dee0eac"
                     ]
                 }
             }
@@ -481,7 +498,11 @@ def test_parse_file_archive(data):
     odata_str = OBJECTS_REGEX.sub('"objects": { %s }' % data, EXPECTED)
     odata = stix2.parse(odata_str, version="2.1")
     assert all(x in odata.objects["3"].extensions['archive-ext'].contains_refs
-               for x in ["0", "1", "2"])
+               for x in [
+                   "file--ecd47d73-15e4-5250-afda-ef8897b22340",
+                   "file--65f2873d-38c2-56b4-bfa5-e3ef21e8a3c3",
+                   "file--ef2d6dca-ec7d-5ab7-8dd9-ec9c0dee0eac",
+               ])
 
 
 @pytest.mark.parametrize(
@@ -492,12 +513,12 @@ def test_parse_file_archive(data):
         "is_multipart": true,
         "content_type": "multipart/mixed",
         "date": "2016-06-19T14:20:40.000Z",
-        "from_ref": "1",
+        "from_ref": "email-addr--d4ef7e1f-086d-5ff4-bce4-312ddc3eae76",
         "to_refs": [
-          "2"
+          "email-addr--8b0eb924-208c-5efd-80e5-84e2d610e54b"
         ],
         "cc_refs": [
-          "3"
+          "email-addr--1766f860-5cf3-5697-8789-35f1242663d5"
         ],
         "subject": "Check out this picture of a cat!",
         "additional_header_fields": {
@@ -518,7 +539,7 @@ def test_parse_file_archive(data):
           {
             "content_type": "application/zip",
             "content_disposition": "attachment; filename=\\"tabby_pics.zip\\"",
-            "body_raw_ref": "5"
+            "body_raw_ref": "file--e63474fc-b386-5630-a003-1b555e22f99b"
           }
         ]
     }
@@ -526,16 +547,8 @@ def test_parse_file_archive(data):
     ],
 )
 def test_parse_email_message_with_at_least_one_error(data):
-    valid_refs = {
-        "0": "email-message",
-        "1": "email-addr",
-        "2": "email-addr",
-        "3": "email-addr",
-        "4": "artifact",
-        "5": "file",
-    }
     with pytest.raises(stix2.exceptions.InvalidValueError) as excinfo:
-        stix2.parse_observable(data, valid_refs, version='2.1')
+        stix2.parse_observable(data, version='2.1')
 
     assert excinfo.value.cls == stix2.v21.EmailMessage
     assert "At least one of the" in str(excinfo.value)
@@ -547,8 +560,8 @@ def test_parse_email_message_with_at_least_one_error(data):
         """
     {
         "type": "network-traffic",
-        "src_ref": "0",
-        "dst_ref": "1",
+        "src_ref": "ipv4-addr--e535b017-cc1c-566b-a3e2-f69f92ed9c4c",
+        "dst_ref": "ipv4-addr--78327430-9ad9-5632-ae3d-8e2fce8f5483",
         "protocols": [
           "tcp"
         ]
@@ -558,13 +571,12 @@ def test_parse_email_message_with_at_least_one_error(data):
 )
 def test_parse_basic_tcp_traffic(data):
     odata = stix2.parse_observable(
-        data, {"0": "ipv4-addr", "1": "ipv4-addr"},
-        version='2.1',
+        data, version='2.1',
     )
 
     assert odata.type == "network-traffic"
-    assert odata.src_ref == "0"
-    assert odata.dst_ref == "1"
+    assert odata.src_ref == "ipv4-addr--e535b017-cc1c-566b-a3e2-f69f92ed9c4c"
+    assert odata.dst_ref == "ipv4-addr--78327430-9ad9-5632-ae3d-8e2fce8f5483"
     assert odata.protocols == ["tcp"]
 
 
@@ -582,7 +594,7 @@ def test_parse_basic_tcp_traffic(data):
         "src_byte_count": 35779,
         "dst_byte_count": 935750,
         "encapsulates_refs": [
-          "4"
+          "network-traffic--016914c3-b680-5df2-81c4-bb9ccf8dc8b0"
         ]
   }
     """,
@@ -590,7 +602,7 @@ def test_parse_basic_tcp_traffic(data):
 )
 def test_parse_basic_tcp_traffic_with_error(data):
     with pytest.raises(stix2.exceptions.AtLeastOnePropertyError) as excinfo:
-        stix2.parse_observable(data, {"4": "network-traffic"}, version='2.1')
+        stix2.parse_observable(data, version='2.1')
 
     assert excinfo.value.cls == stix2.v21.NetworkTraffic
     assert excinfo.value.properties == ["dst_ref", "src_ref"]
@@ -636,16 +648,18 @@ def test_observed_data_with_process_example():
         objects={
             "0": {
                 "type": "file",
+                "id": "file--0d16c8d3-c177-5f5d-a022-b1bdac329bea",
                 "hashes": {
                     "SHA-256": "35a01331e9ad96f751278b891b6ea09699806faedfa237d40513d92ad1b7100f",
                 },
             },
             "1": {
                 "type": "process",
+                "id": "process--f6c4a02c-23e1-4a6d-a0d7-d862e893817a",
                 "pid": 1221,
-                "created": "2016-01-20T14:11:25.55Z",
+                "created_time": "2016-01-20T14:11:25.55Z",
                 "command_line": "./gedit-bin --new-window",
-                "image_ref": "0",
+                "image_ref": "file--0d16c8d3-c177-5f5d-a022-b1bdac329bea",
             },
         },
     )
@@ -689,31 +703,33 @@ def test_artifact_mutual_exclusion_error():
 
 
 def test_directory_example():
-    dir = stix2.v21.Directory(
-        _valid_refs={"1": "file"},
+    f = stix2.v21.File(
+        name="penguin.exe",
+    )
+
+    dir1 = stix2.v21.Directory(
         path='/usr/lib',
-        created="2015-12-21T19:00:00Z",
-        modified="2015-12-24T19:00:00Z",
-        accessed="2015-12-21T20:00:00Z",
-        contains_refs=["1"],
+        ctime="2015-12-21T19:00:00Z",
+        mtime="2015-12-24T19:00:00Z",
+        atime="2015-12-21T20:00:00Z",
+        contains_refs=[str(f.id)],
     )
 
-    assert dir.path == '/usr/lib'
-    assert dir.created == dt.datetime(2015, 12, 21, 19, 0, 0, tzinfo=pytz.utc)
-    assert dir.modified == dt.datetime(2015, 12, 24, 19, 0, 0, tzinfo=pytz.utc)
-    assert dir.accessed == dt.datetime(2015, 12, 21, 20, 0, 0, tzinfo=pytz.utc)
-    assert dir.contains_refs == ["1"]
+    assert dir1.path == '/usr/lib'
+    assert dir1.ctime == dt.datetime(2015, 12, 21, 19, 0, 0, tzinfo=pytz.utc)
+    assert dir1.mtime == dt.datetime(2015, 12, 24, 19, 0, 0, tzinfo=pytz.utc)
+    assert dir1.atime == dt.datetime(2015, 12, 21, 20, 0, 0, tzinfo=pytz.utc)
+    assert dir1.contains_refs == ["file--9d050a3b-72cd-5b57-bf18-024e74e1e5eb"]
 
 
 def test_directory_example_ref_error():
-    with pytest.raises(stix2.exceptions.InvalidObjRefError) as excinfo:
+    with pytest.raises(stix2.exceptions.InvalidValueError) as excinfo:
         stix2.v21.Directory(
-            _valid_refs=[],
             path='/usr/lib',
-            created="2015-12-21T19:00:00Z",
-            modified="2015-12-24T19:00:00Z",
-            accessed="2015-12-21T20:00:00Z",
-            contains_refs=["1"],
+            ctime="2015-12-21T19:00:00Z",
+            mtime="2015-12-24T19:00:00Z",
+            atime="2015-12-21T20:00:00Z",
+            contains_refs=["domain-name--02af94ea-7e38-5718-87c3-5cc023e3d49d"],
         )
 
     assert excinfo.value.cls == stix2.v21.Directory
@@ -721,22 +737,24 @@ def test_directory_example_ref_error():
 
 
 def test_domain_name_example():
-    dn = stix2.v21.DomainName(
-        _valid_refs={"1": 'domain-name'},
+    dn1 = stix2.v21.DomainName(
+        value="mitre.org",
+    )
+
+    dn2 = stix2.v21.DomainName(
         value="example.com",
-        resolves_to_refs=["1"],
+        resolves_to_refs=[str(dn1.id)],
     )
 
-    assert dn.value == "example.com"
-    assert dn.resolves_to_refs == ["1"]
+    assert dn2.value == "example.com"
+    assert dn2.resolves_to_refs == ["domain-name--02af94ea-7e38-5718-87c3-5cc023e3d49d"]
 
 
 def test_domain_name_example_invalid_ref_type():
-    with pytest.raises(stix2.exceptions.InvalidObjRefError) as excinfo:
+    with pytest.raises(stix2.exceptions.InvalidValueError) as excinfo:
         stix2.v21.DomainName(
-            _valid_refs={"1": "file"},
             value="example.com",
-            resolves_to_refs=["1"],
+            resolves_to_refs=["file--44a431e6-764b-5556-a3f5-bf655930a581"],
         )
 
     assert excinfo.value.cls == stix2.v21.DomainName
@@ -752,9 +770,9 @@ def test_file_example():
         size=100,
         magic_number_hex="1C",
         mime_type="application/msword",
-        created="2016-12-21T19:00:00Z",
-        modified="2016-12-24T19:00:00Z",
-        accessed="2016-12-21T20:00:00Z",
+        ctime="2016-12-21T19:00:00Z",
+        mtime="2016-12-24T19:00:00Z",
+        atime="2016-12-21T20:00:00Z",
     )
 
     assert f.name == "qwerty.dll"
@@ -762,9 +780,9 @@ def test_file_example():
     assert f.magic_number_hex == "1C"
     assert f.hashes["SHA-256"] == "ceafbfd424be2ca4a5f0402cae090dda2fb0526cf521b60b60077c0f622b285a"
     assert f.mime_type == "application/msword"
-    assert f.created == dt.datetime(2016, 12, 21, 19, 0, 0, tzinfo=pytz.utc)
-    assert f.modified == dt.datetime(2016, 12, 24, 19, 0, 0, tzinfo=pytz.utc)
-    assert f.accessed == dt.datetime(2016, 12, 21, 20, 0, 0, tzinfo=pytz.utc)
+    assert f.ctime == dt.datetime(2016, 12, 21, 19, 0, 0, tzinfo=pytz.utc)
+    assert f.mtime == dt.datetime(2016, 12, 24, 19, 0, 0, tzinfo=pytz.utc)
+    assert f.atime == dt.datetime(2016, 12, 21, 20, 0, 0, tzinfo=pytz.utc)
 
 
 def test_file_example_with_NTFSExt():
@@ -878,6 +896,7 @@ RASTER_IMAGE_EXT = """{
 "objects": {
   "0": {
     "type": "file",
+    "id": "file--44a431e6-764b-5556-a3f5-bf655930a581",
     "name": "picture.jpg",
     "hashes": {
       "SHA-256": "35a01331e9ad96f751278b891b6ea09699806faedfa237d40513d92ad1b7100f"
@@ -989,18 +1008,17 @@ def test_file_example_encryption_error():
     assert "At least one of the (hashes, name)" in str(excinfo.value)
 
 
-def test_ip4_address_example():
+def test_ipv4_address_example():
     ip4 = stix2.v21.IPv4Address(
-        _valid_refs={"4": "mac-addr", "5": "mac-addr"},
         value="198.51.100.3",
-        resolves_to_refs=["4", "5"],
+        resolves_to_refs=["mac-addr--a85820f7-d9b7-567a-a3a6-dedc34139342", "mac-addr--9a59b496-fdeb-510f-97b5-7137210bc699"],
     )
 
     assert ip4.value == "198.51.100.3"
-    assert ip4.resolves_to_refs == ["4", "5"]
+    assert ip4.resolves_to_refs == ["mac-addr--a85820f7-d9b7-567a-a3a6-dedc34139342", "mac-addr--9a59b496-fdeb-510f-97b5-7137210bc699"]
 
 
-def test_ip4_address_valid_refs():
+def test_ipv4_address_valid_refs():
     mac1 = stix2.v21.MACAddress(
         value="a1:b2:c3:d4:e5:f6",
     )
@@ -1009,22 +1027,21 @@ def test_ip4_address_valid_refs():
     )
 
     ip4 = stix2.v21.IPv4Address(
-        _valid_refs={"1": mac1, "2": mac2},
         value="177.60.40.7",
-        resolves_to_refs=["1", "2"],
+        resolves_to_refs=[str(mac1.id), str(mac2.id)],
     )
 
     assert ip4.value == "177.60.40.7"
-    assert ip4.resolves_to_refs == ["1", "2"]
+    assert ip4.resolves_to_refs == ["mac-addr--a85820f7-d9b7-567a-a3a6-dedc34139342", "mac-addr--9a59b496-fdeb-510f-97b5-7137210bc699"]
 
 
-def test_ip4_address_example_cidr():
+def test_ipv4_address_example_cidr():
     ip4 = stix2.v21.IPv4Address(value="198.51.100.0/24")
 
     assert ip4.value == "198.51.100.0/24"
 
 
-def test_ip6_address_example():
+def test_ipv6_address_example():
     ip6 = stix2.v21.IPv6Address(value="2001:0db8:85a3:0000:0000:8a2e:0370:7334")
 
     assert ip6.value == "2001:0db8:85a3:0000:0000:8a2e:0370:7334"
@@ -1038,14 +1055,13 @@ def test_mac_address_example():
 
 def test_network_traffic_example():
     nt = stix2.v21.NetworkTraffic(
-        _valid_refs={"0": "ipv4-addr", "1": "ipv4-addr"},
-        protocols="tcp",
-        src_ref="0",
-        dst_ref="1",
+        protocols=["tcp"],
+        src_ref="ipv4-addr--29a591d9-533a-5ecd-a5a1-cadee4411e88",
+        dst_ref="ipv4-addr--6d39dd0b-1f74-5faf-8d76-d8762c2a57cb",
     )
     assert nt.protocols == ["tcp"]
-    assert nt.src_ref == "0"
-    assert nt.dst_ref == "1"
+    assert nt.src_ref == "ipv4-addr--29a591d9-533a-5ecd-a5a1-cadee4411e88"
+    assert nt.dst_ref == "ipv4-addr--6d39dd0b-1f74-5faf-8d76-d8762c2a57cb"
 
 
 def test_network_traffic_http_request_example():
@@ -1060,9 +1076,8 @@ def test_network_traffic_http_request_example():
         },
     )
     nt = stix2.v21.NetworkTraffic(
-        _valid_refs={"0": "ipv4-addr"},
-        protocols="tcp",
-        src_ref="0",
+        protocols=["tcp"],
+        src_ref="ipv4-addr--29a591d9-533a-5ecd-a5a1-cadee4411e88",
         extensions={'http-request-ext': h},
     )
     assert nt.extensions['http-request-ext'].request_method == "get"
@@ -1076,9 +1091,8 @@ def test_network_traffic_http_request_example():
 def test_network_traffic_icmp_example():
     h = stix2.v21.ICMPExt(icmp_type_hex="08", icmp_code_hex="00")
     nt = stix2.v21.NetworkTraffic(
-        _valid_refs={"0": "ipv4-addr"},
-        protocols="tcp",
-        src_ref="0",
+        protocols=["tcp"],
+        src_ref="ipv4-addr--29a591d9-533a-5ecd-a5a1-cadee4411e88",
         extensions={'icmp-ext': h},
     )
     assert nt.extensions['icmp-ext'].icmp_type_hex == "08"
@@ -1093,9 +1107,8 @@ def test_network_traffic_socket_example():
         socket_type="SOCK_STREAM",
     )
     nt = stix2.v21.NetworkTraffic(
-        _valid_refs={"0": "ipv4-addr"},
-        protocols="tcp",
-        src_ref="0",
+        protocols=["tcp"],
+        src_ref="ipv4-addr--29a591d9-533a-5ecd-a5a1-cadee4411e88",
         extensions={'socket-ext': h},
     )
     assert nt.extensions['socket-ext'].is_listening
@@ -1107,9 +1120,8 @@ def test_network_traffic_socket_example():
 def test_network_traffic_tcp_example():
     h = stix2.v21.TCPExt(src_flags_hex="00000002")
     nt = stix2.v21.NetworkTraffic(
-        _valid_refs={"0": "ipv4-addr"},
-        protocols="tcp",
-        src_ref="0",
+        protocols=["tcp"],
+        src_ref="ipv4-addr--29a591d9-533a-5ecd-a5a1-cadee4411e88",
         extensions={'tcp-ext': h},
     )
     assert nt.extensions['tcp-ext'].src_flags_hex == "00000002"
@@ -1123,11 +1135,10 @@ def test_mutex_example():
 
 def test_process_example():
     p = stix2.v21.Process(
-        _valid_refs={"0": "file"},
         pid=1221,
-        created="2016-01-20T14:11:25.55Z",
+        created_time="2016-01-20T14:11:25.55Z",
         command_line="./gedit-bin --new-window",
-        image_ref="0",
+        image_ref="file--ea587d87-5ed2-5625-a9ac-01fd64161fd8",
     )
 
     assert p.command_line == "./gedit-bin --new-window"
@@ -1139,7 +1150,7 @@ def test_process_example_empty_error():
 
     assert excinfo.value.cls == stix2.v21.Process
     properties_of_process = list(stix2.v21.Process._properties.keys())
-    properties_of_process.remove("type")
+    properties_of_process = [prop for prop in properties_of_process if prop not in ["type", "id", "defanged", "spec_version"]]
     assert excinfo.value.properties == sorted(properties_of_process)
     msg = "At least one of the ({1}) properties for {0} must be populated."
     msg = msg.format(
@@ -1363,18 +1374,20 @@ def test_new_version_with_related_objects():
         objects={
             'src_ip': {
                 'type': 'ipv4-addr',
+                'id': 'ipv4-addr--2b94bc65-17d4-54f6-9ffe-7d103551bb9f',
                 'value': '127.0.0.1/32',
             },
             'domain': {
                 'type': 'domain-name',
+                'id': 'domain-name--220a2699-5ebf-5b57-bf02-424964bb19c0',
                 'value': 'example.com',
-                'resolves_to_refs': ['src_ip'],
+                'resolves_to_refs': ['ipv4-addr--2b94bc65-17d4-54f6-9ffe-7d103551bb9f'],
             },
         },
     )
     new_version = data.new_version(last_observed="2017-12-12T12:00:00Z")
     assert new_version.last_observed.year == 2017
-    assert new_version.objects['domain'].resolves_to_refs[0] == 'src_ip'
+    assert new_version.objects['domain'].resolves_to_refs[0] == 'ipv4-addr--2b94bc65-17d4-54f6-9ffe-7d103551bb9f'
 
 
 def test_objects_deprecation():
@@ -1391,3 +1404,125 @@ def test_objects_deprecation():
                 },
             },
         )
+
+
+def test_deterministic_id_same_extra_prop_vals():
+    email_addr_1 = stix2.v21.EmailAddress(
+        value="john@example.com",
+        display_name="Johnny Doe",
+    )
+
+    email_addr_2 = stix2.v21.EmailAddress(
+        value="john@example.com",
+        display_name="Johnny Doe",
+    )
+
+    assert email_addr_1.id == email_addr_2.id
+
+    uuid_obj_1 = uuid.UUID(email_addr_1.id[-36:])
+    assert uuid_obj_1.variant == uuid.RFC_4122
+    assert uuid_obj_1.version == 5
+
+    uuid_obj_2 = uuid.UUID(email_addr_2.id[-36:])
+    assert uuid_obj_2.variant == uuid.RFC_4122
+    assert uuid_obj_2.version == 5
+
+
+def test_deterministic_id_diff_extra_prop_vals():
+    email_addr_1 = stix2.v21.EmailAddress(
+        value="john@example.com",
+        display_name="Johnny Doe",
+    )
+
+    email_addr_2 = stix2.v21.EmailAddress(
+        value="john@example.com",
+        display_name="Janey Doe",
+    )
+
+    assert email_addr_1.id == email_addr_2.id
+
+    uuid_obj_1 = uuid.UUID(email_addr_1.id[-36:])
+    assert uuid_obj_1.variant == uuid.RFC_4122
+    assert uuid_obj_1.version == 5
+
+    uuid_obj_2 = uuid.UUID(email_addr_2.id[-36:])
+    assert uuid_obj_2.variant == uuid.RFC_4122
+    assert uuid_obj_2.version == 5
+
+
+def test_deterministic_id_diff_contributing_prop_vals():
+    email_addr_1 = stix2.v21.EmailAddress(
+        value="john@example.com",
+        display_name="Johnny Doe",
+    )
+
+    email_addr_2 = stix2.v21.EmailAddress(
+        value="jane@example.com",
+        display_name="Janey Doe",
+    )
+
+    assert email_addr_1.id != email_addr_2.id
+
+    uuid_obj_1 = uuid.UUID(email_addr_1.id[-36:])
+    assert uuid_obj_1.variant == uuid.RFC_4122
+    assert uuid_obj_1.version == 5
+
+    uuid_obj_2 = uuid.UUID(email_addr_2.id[-36:])
+    assert uuid_obj_2.variant == uuid.RFC_4122
+    assert uuid_obj_2.version == 5
+
+
+def test_deterministic_id_no_contributing_props():
+    email_msg_1 = stix2.v21.EmailMessage(
+        is_multipart=False,
+    )
+
+    email_msg_2 = stix2.v21.EmailMessage(
+        is_multipart=False,
+    )
+
+    assert email_msg_1.id != email_msg_2.id
+
+    uuid_obj_1 = uuid.UUID(email_msg_1.id[-36:])
+    assert uuid_obj_1.variant == uuid.RFC_4122
+    assert uuid_obj_1.version == 4
+
+    uuid_obj_2 = uuid.UUID(email_msg_2.id[-36:])
+    assert uuid_obj_2.variant == uuid.RFC_4122
+    assert uuid_obj_2.version == 4
+
+
+def test_ipv4_resolves_to_refs_deprecation():
+    with pytest.warns(stix2.exceptions.STIXDeprecationWarning):
+
+        stix2.v21.IPv4Address(
+            value="26.09.19.70",
+            resolves_to_refs=["mac-addr--08900593-0265-52fc-93c0-5b4a942f5887"],
+        )
+
+
+def test_ipv4_belongs_to_refs_deprecation():
+    with pytest.warns(stix2.exceptions.STIXDeprecationWarning):
+
+        stix2.v21.IPv4Address(
+            value="21.12.19.64",
+            belongs_to_refs=["autonomous-system--52e0a49d-d683-5801-a7b8-145765a1e116"],
+        )
+
+
+def test_ipv6_resolves_to_refs_deprecation():
+    with pytest.warns(stix2.exceptions.STIXDeprecationWarning):
+
+        stix2.v21.IPv6Address(
+            value="2001:0db8:85a3:0000:0000:8a2e:0370:7334",
+            resolves_to_refs=["mac-addr--08900593-0265-52fc-93c0-5b4a942f5887"],
+        )
+
+
+def test_ipv6_belongs_to_refs_deprecation():
+    with pytest.warns(stix2.exceptions.STIXDeprecationWarning):
+
+        stix2.v21.IPv6Address(
+            value="2001:0db8:85a3:0000:0000:8a2e:0370:7334",
+            belongs_to_refs=["autonomous-system--52e0a49d-d683-5801-a7b8-145765a1e116"],
+        )
diff --git a/stix2/test/v21/test_properties.py b/stix2/test/v21/test_properties.py
index fde13d3..1fb3cc4 100644
--- a/stix2/test/v21/test_properties.py
+++ b/stix2/test/v21/test_properties.py
@@ -271,7 +271,7 @@ def test_boolean_property_invalid(value):
 
 
 def test_reference_property():
-    ref_prop = ReferenceProperty(spec_version="2.1")
+    ref_prop = ReferenceProperty(valid_types="my-type", spec_version="2.1")
 
     assert ref_prop.clean("my-type--00000000-0000-4000-8000-000000000000")
     with pytest.raises(ValueError):
@@ -283,7 +283,7 @@ def test_reference_property():
 
 
 def test_reference_property_specific_type():
-    ref_prop = ReferenceProperty("my-type", spec_version="2.1")
+    ref_prop = ReferenceProperty(valid_types="my-type", spec_version="2.1")
 
     with pytest.raises(ValueError):
         ref_prop.clean("not-my-type--8a8e8758-f92c-4058-ba38-f061cd42a0cf")
diff --git a/stix2/test/v21/test_utils.py b/stix2/test/v21/test_utils.py
index dec3294..5cf88e4 100644
--- a/stix2/test/v21/test_utils.py
+++ b/stix2/test/v21/test_utils.py
@@ -135,14 +135,16 @@ def test_deduplicate(stix_objs1):
                     "0": {
                         "name": "foo.exe",
                         "type": "file",
+                        "id": "file--5956efbb-a7b0-566d-a7f9-a202eb05c70f",
                     },
                     "1": {
                         "type": "ipv4-addr",
                         "value": "198.51.100.3",
+                        "id": "ipv4-addr--1f8f4d63-9f33-5353-a3e3-e1b84c83a7b5",
                     },
                     "2": {
                         "type": "network-traffic",
-                        "src_ref": "1",
+                        "src_ref": "ipv4-addr--1f8f4d63-9f33-5353-a3e3-e1b84c83a7b5",
                         "protocols": [
                           "tcp",
                           "http",
@@ -161,7 +163,7 @@ def test_deduplicate(stix_objs1):
                         },
                     },
                 },
-            ), ('1', {"type": "ipv4-addr", "value": "198.51.100.3"}), 1,
+            ), ('1', {"type": "ipv4-addr", "value": "198.51.100.3", "id": "ipv4-addr--1f8f4d63-9f33-5353-a3e3-e1b84c83a7b5"}), 1,
         ),
         (
             {