diff --git a/TODO b/TODO
index 1283099a76828400e9f0a4dcfa9f2170564f4f11..107ccc028a2c4633551bb6070f75c5099a52de99 100644
--- a/TODO
+++ b/TODO
@@ -19,8 +19,8 @@
 === Decoder ===
 * Write functions not yet implemented
 * Add API for stream-decoding strings
-* Add API for random-accessing elements of a map
 * Add API for checking known tags and simple types
 * (unlikely) Add API for checking the pairing of a tag and the tagged type
 * Write tests for error conditions
 * Fuzzy-test the decoder
+* Add recursion limit to recursive functions (advance, map_find)
diff --git a/src/cbor.h b/src/cbor.h
index e37489516c265218c4f85b378df1b49fa4545d5d..75f844c04858702662418e82a3663dfffc4e407d 100644
--- a/src/cbor.h
+++ b/src/cbor.h
@@ -307,6 +307,8 @@ CBOR_INLINE_API CborError cbor_value_get_map_length(const CborValue *value, size
     return CborNoError;
 }
 
+CBOR_API CborError cbor_value_map_find_value(const CborValue *map, const char *string, CborValue *element);
+
 /* Floating point */
 CBOR_API CborError cbor_value_get_half_float(const CborValue *value, void *result);
 CBOR_INLINE_API CborError cbor_value_get_float(const CborValue *value, float *result)
diff --git a/src/cborparser.c b/src/cborparser.c
index ac44f80205034653016221e376f5bb32e44b681d..f5de1f73aa24fd83191ffc23d648b1eb627d2031 100644
--- a/src/cborparser.c
+++ b/src/cborparser.c
@@ -654,6 +654,61 @@ CborError cbor_value_text_string_equals(const CborValue *value, const char *stri
     return iterate_string_chunks(&copy, CONST_CAST(char *, string), &len, result, NULL, iterate_memcmp);
 }
 
+/**
+ * Attempts to find the value in map \a map that corresponds to the text string
+ * entry \a string. If the item is found, it is stored in \a result. If no item
+ * is found matching the key, then \a result will contain an element of type
+ * \ref CborInvalidType.
+ *
+ * \note This function may be expensive to execute.
+ */
+CborError cbor_value_map_find_value(const CborValue *map, const char *string, CborValue *element)
+{
+    assert(cbor_value_is_map(map));
+    size_t len = strlen(string);
+    CborError err = cbor_value_enter_container(map, element);
+    if (err)
+        goto error;
+
+    while (!cbor_value_at_end(element)) {
+        // find the non-tag so we can compare
+        err = cbor_value_skip_tag(element);
+        if (err)
+            goto error;
+        if (cbor_value_is_text_string(element)) {
+            bool equals;
+            size_t dummyLen = len;
+            err = iterate_string_chunks(element, CONST_CAST(char *, string), &dummyLen,
+                                        &equals, element, iterate_memcmp);
+            if (err)
+                goto error;
+            if (equals)
+                return preparse_value(element);
+        } else {
+            // skip this key
+            err = cbor_value_advance(element);
+            if (err)
+                goto error;
+        }
+
+        // skip this value
+        err = cbor_value_skip_tag(element);
+        if (err)
+            goto error;
+        err = cbor_value_advance(element);
+        if (err)
+            goto error;
+    }
+
+    // not found
+    element->type = CborInvalidType;
+    return CborNoError;
+
+error:
+    element->type = CborInvalidType;
+    return err;
+}
+
 /**
  * Extracts a half-precision floating point from \a value and stores it in \a
  * result.
diff --git a/tests/parser/tst_parser.cpp b/tests/parser/tst_parser.cpp
index 8a981a60100eafca96e03db97edd7d978639eac0..eca3342db66310568edc1a51cf9f24d86f19b45e 100644
--- a/tests/parser/tst_parser.cpp
+++ b/tests/parser/tst_parser.cpp
@@ -66,6 +66,8 @@ private slots:
     void stringLength();
     void stringCompare_data();
     void stringCompare();
+    void mapFind_data();
+    void mapFind();
 };
 
 char toHexUpper(unsigned n)
@@ -1002,5 +1004,96 @@ void tst_Parser::stringCompare()
     compareOneString("\xc1\xc2" + data, string, expected);
 }
 
+void tst_Parser::mapFind_data()
+{
+    // Rules:
+    //  we are searching for string "needle"
+    //  if present, the value should be the string "haystack" (with tag 42)
+
+    QTest::addColumn<QByteArray>("data");
+    QTest::addColumn<bool>("expected");
+
+    QTest::newRow("emptymap") << raw("\xa0") << false;
+    QTest::newRow("_emptymap") << raw("\xbf\xff") << false;
+
+    // maps not containing our items
+    QTest::newRow("absent-unsigned-unsigned") << raw("\xa1\0\0") << false;
+    QTest::newRow("absent-taggedunsigned-unsigned") << raw("\xa1\xc0\0\0") << false;
+    QTest::newRow("absent-unsigned-taggedunsigned") << raw("\xa1\0\xc0\0") << false;
+    QTest::newRow("absent-taggedunsigned-taggedunsigned") << raw("\xa1\xc0\0\xc0\0") << false;
+    QTest::newRow("absent-string-unsigned") << raw("\xa1\x68haystack\0") << false;
+    QTest::newRow("absent-taggedstring-unsigned") << raw("\xa1\xc0\x68haystack\0") << false;
+    QTest::newRow("absent-string-taggedunsigned") << raw("\xa1\x68haystack\xc0\0") << false;
+    QTest::newRow("absent-taggedstring-taggedunsigned") << raw("\xa1\xc0\x68haystack\xc0\0") << false;
+    QTest::newRow("absent-string-string") << raw("\xa1\x68haystack\x66needle") << false;
+    QTest::newRow("absent-string-taggedstring") << raw("\xa1\x68haystack\xc0\x66needle") << false;
+    QTest::newRow("absent-taggedstring-string") << raw("\xa1\xc0\x68haystack\x66needle") << false;
+    QTest::newRow("absent-string-taggedstring") << raw("\xa1\xc0\x68haystack\xc0\x66needle") << false;
+
+    QTest::newRow("absent-string-emptyarray") << raw("\xa1\x68haystack\x80") << false;
+    QTest::newRow("absent-string-_emptyarray") << raw("\xa1\x68haystack\x9f\xff") << false;
+    QTest::newRow("absent-string-array1") << raw("\xa1\x68haystack\x81\0") << false;
+    QTest::newRow("absent-string-array2") << raw("\xa1\x68haystack\x85\0\1\2\3\4") << false;
+    QTest::newRow("absent-string-array3") << raw("\xa1\x68haystack\x85\x63one\x63two\x65three\x64""four\x64""five") << false;
+
+    QTest::newRow("absent-string-emptymap") << raw("\xa1\x68haystack\xa0") << false;
+    QTest::newRow("absent-string-_emptymap") << raw("\xa1\x68haystack\xbf\xff") << false;
+    QTest::newRow("absent-string-map") << raw("\xa1\x68haystack\xa1\x68haystack\x66needle") << false;
+    QTest::newRow("absent-string-map2") << raw("\xa1\x68haystack\xa1\x68haystack\x66needle\61z\62yx") << false;
+
+    // maps containing our items
+    QTest::newRow("alone") << raw("\xa1\x66needle\xd8\x2a\x68haystack") << true;
+    QTest::newRow("tagged") << raw("\xa1\xc1\x66needle\xd8\x2a\x68haystack") << true;
+    QTest::newRow("doubletagged") << raw("\xa1\xc1\xc2\x66needle\xd8\x2a\x68haystack") << true;
+    QTest::newRow("chunked") << raw("\xa1\x7f\x66needle\xff\xd8\x2a\x68haystack") << true;
+    QTest::newRow("chunked*2") << raw("\xa1\x7f\x60\x66needle\xff\xd8\x2a\x68haystack") << true;
+    QTest::newRow("chunked*2bis") << raw("\xa1\x7f\x66needle\x60\xff\xd8\x2a\x68haystack") << true;
+    QTest::newRow("chunked*3") << raw("\xa1\x7f\x62ne\x62""ed\x62le\xff\xd8\x2a\x68haystack") << true;
+    QTest::newRow("chunked*8") << raw("\xa1\x7f\x61n\x61""e\x60\x61""e\x61""d\x60\x62le\x60\xff\xd8\x2a\x68haystack") << true;
+
+    QTest::newRow("1before") << raw("\xa2\x68haystack\x66needle\x66needle\xd8\x2a\x68haystack") << true;
+    QTest::newRow("tagged-1before") << raw("\xa2\xc1\x68haystack\x66needle\xc1\x66needle\xd8\x2a\x68haystack") << true;
+    QTest::newRow("doubletagged-1before2") << raw("\xa2\xc1\xc2\x68haystack\x66needle\xc1\xc2\x66needle\xd8\x2a\x68haystack") << true;
+
+    QTest::newRow("arraybefore") << raw("\xa2\x61z\x80\x66needle\xd8\x2a\x68haystack") << true;
+    QTest::newRow("nestedarraybefore") << raw("\xa2\x61z\x81\x81\0\x66needle\xd8\x2a\x68haystack") << true;
+    QTest::newRow("arrayarraybefore") << raw("\xa2\x82\1\2\x80\x66needle\xd8\x2a\x68haystack") << true;
+
+    QTest::newRow("mapbefore") << raw("\xa2\x61z\xa0\x66needle\xd8\x2a\x68haystack") << true;
+    QTest::newRow("nestedmapbefore") << raw("\xa2\x61z\xa1\0\x81\0\x66needle\xd8\x2a\x68haystack") << true;
+    QTest::newRow("mapmapbefore") << raw("\xa2\xa1\1\2\xa0\x66needle\xd8\x2a\x68haystack") << true;
+}
+
+void tst_Parser::mapFind()
+{
+    QFETCH(QByteArray, data);
+    QFETCH(bool, expected);
+
+    CborParser parser;
+    CborValue value;
+    CborError err = cbor_parser_init(data.constData(), data.length(), 0, &parser, &value);
+    QVERIFY2(!err, QByteArray("Got error \"") + cbor_error_string(err) + "\"");
+
+    CborValue element;
+    err = cbor_value_map_find_value(&value, "needle", &element);
+    QVERIFY2(!err, QByteArray("Got error \"") + cbor_error_string(err) + "\"");
+
+    if (expected) {
+        QCOMPARE(int(element.type), int(CborTagType));
+
+        CborTag tag;
+        err = cbor_value_get_tag(&element, &tag);
+        QVERIFY2(!err, QByteArray("Got error \"") + cbor_error_string(err) + "\"");
+        QCOMPARE(int(tag), 42);
+
+        bool equals;
+        err = cbor_value_text_string_equals(&element, "haystack", &equals);
+        QVERIFY2(!err, QByteArray("Got error \"") + cbor_error_string(err) + "\"");
+        QVERIFY(equals);
+    } else {
+        QCOMPARE(int(element.type), int(CborInvalidType));
+    }
+}
+
 QTEST_MAIN(tst_Parser)
 #include "tst_parser.moc"