How is Item HMAC calculated?

lht
lht
Community Member

I am writing a program parsing OPVault files. I am stuck on getting Item HMAC validated. The OPVault Design doc briefly mentions it is computed over item elements and values. Since the element value type could be integer, string or base64 encoded binary. I am not clear what type should be used. I have so far tried various combinations and cannot reach the expected result. Some clarification about how to encode the values would be highly appreciated!

Python snippet of one of my attempts is pasted here:

    h = hmac.new(overview_mac_key, digestmod=hashlib.sha256)
    for k in sorted(item.keys()):
        if k == 'hmac':
            continue
        h.update(bytearray(k, 'utf8'))
        v = item[k]
        if k in ['k', 'd', 'o']:
            v = base64.b64decode(v)
        elif k in ['tx', 'created', 'updated']:
            v = v.to_bytes(4, byteorder='little')
        elif k == 'uuid':
            v = uuid.UUID(v).bytes
        else:
            v = bytearray(v, 'utf8')
        h.update(v)
        print("{}: {}".format(k, v))
    got = base64.b64encode(h.digest())
    expected = item['hmac']
    print('expected: {}\ngot: {}'.format(expected, got))
    assert(got == expected)

Full code for parsing item 0E8D7507A1C1449D8134787D638E849A from demo.opvault is at http://pastebin.com/5Cn5HDMP

Comments

  • AGAlumB
    AGAlumB
    1Password Alumni
    edited December 2016

    @lht: Thanks for reaching out. I'm sorry for the delay. I'm not an authority on this, so I've asked for help from a colleague. There's some info here in the knowledgebase regarding OPVault, but it sounds like you might not be using an Apple platform. If you're using CommonCrypto there's an example there under "Implementation". Either way we'll get back to you. :)

  • @lht i believe a bit of code can explain HMAC better than a 1000 words:

    foreach (var item in from KeyValuePair<string, JToken> item in Json
                            where item.Key != "hmac"
                            orderby item.Key
                            select item)
    {
        string value;
    
        if (item.Value.Type == JTokenType.Boolean)
        {
            value = (bool)item.Value ? "1" : "0";
        }
        else
        {
            value = (string)item.Value;
        }
    
        bytes.AddRange(item.Key.ToBytesFromUtf8());
        bytes.AddRange(value.ToBytesFromUtf8());
    }
    

    Hope that helps :)

  • lht
    lht
    Community Member

    @brenty, @SergeyTheAgile Surely that code explains everything! Fixed the boolean encoding and my code now works! How exciting! Thanks a lot!

  • AGAlumB
    AGAlumB
    1Password Alumni

    hehe I'm glad Sergey was able to share some holiday cheer code with you. :chuffed:

This discussion has been closed.