Bare Uniform Resources (URs) allow for the encoding of a variety of information, including a variety of crypto-data. It was originally Blockchain Commons’ suggested methodology to encode CBOR data such as seeds and private keys in a self-describing way.
However, after the release of URs, Blockchain Commons developed the Gordian Envelope format. This “Smart Document” system describes how to use CBOR in a more comprehensive way to encode data so that it’s not only self-describing, but also privacy-focused. It also allows for much more data to be stored together due to its ability to model more complex structures. Envelope is now Blockchain Commons’ preferred methodology for data storage.
UR nonetheless remains relevant: they’re a way to encode binary CBOR in a hardened, easily transmissible way using plain text and checksums. Since Envelopes are CBOR-based, they can be encoded for transmission using URs (and therefore Animated QRs, adding all of the original advantages of URs to the more robust and extensive Envelope data format.
Why Use URs for Envelopes?
Gordian Envelopes can be kept in memory or stored as a binary stream. However, there are many situations when you might want to store or transmit them, and in these situations, the UR format offers all of its normal benefits:
- Improves Self-Description. The content within a Gordian Envelope is all self-described by CBOR tags. But how do you know that big blob of data is a Gordian Envelope at all? That’s where
ur:envelope
comes in. - Improves Interoperability. As a standard URI, a UR can instantly be understood by many different applications.
- Offers Error Checking. The checksum digits at the end of a UR can identify if an Envelope has been corrupted.
- Allows Printing. URs can be printed out and stored. Though Envelope URs can be very long and therefore a huge pain to reenter, doing so might allow the recovery of otherwise lost data.
- Supports Animated QRs. URs are built to be efficiently encoded as QRs. As a result, UR-encoded Envelopes can be stored as QRs or even Animated QRs for larger data. This allows the easy transmission of Envelopes across airgaps. It also allows for much easier recovery from printed content, even for larger Envelopes, as Animated QRs can be printed out using multiple frames.
None of these advantages are unique to Envelopes: they’re all general advantages of the UR form that can be extended to Envelopes stored as URs.
Envelopes: [envelope]
Envelopes allow for the encoding of semantic triples, as shown in the following example generated by the bc-envelope-cli-rust tool:
$ ENVELOPE=`envelope subject type string alice | envelope assertion add pred-obj string knows string bob`
The envelope-cli
actually encodes its data as a UR by default, because it’s such a useful format for data storage of this type.
$ echo $ENVELOPE
ur:envelope/lftpsoihhsjziniaihoytpsoihjejtjlktjktpsoiaidjlidzmvllokt
The envelope-cli
tool also supports the output of data in Envelope format, which clearly shows what’s being stored:
$ envelope format $ENVELOPE
"alice" [
"knows": "bob"
]
Internally, the data is actually stored as CBOR using the Envelope IETF spec . As of this writing, the following CDDL description details how an Envelope is encoded. (See the spec to ensure it’s up to date!)
envelope = #6.200(envelope-content)
envelope-content =
leaf /
elided /
node /
assertion /
wrapped
leaf = #6.201(any)
elided = sha256-digest
sha256-digest = bytes .size 32
node = [subject, + assertion-element]
subject = envelope-content
assertion-element = assertion / elided-assertion
elided-assertion = elided ; MUST represent an assertion.
assertion = { predicate => object }
predicate = envelope-content
object = envelope-content
wrapped = envelope
You can examine the original CBOR for a ur:envelope
encoding using a few CBOR-related tools. Remember that each UR is divided into a self-description (ur:envelope/
) and a set of minimal Bytewords that encode the CBOR and then provide a checksum (lftpsoihhsjziniaihoytpsoihjejtjlktjktpsoiaidjlidzmvllokt
). To convert the UR back to CBOR, you must first convert the minimal bytewords back to hex.
Blockchain Commons’ bytewords-cli
provides the easiest way to do so:
$ bytewords -i minimal -o hex lftpsoihhsjziniaihoytpsoihjejtjlktjktpsoiaidjlidzmvllokt
82d8c965616c696365a1d8c9656b6e6f7773d8c963626f62
You can then read that hex using an CBOR tool.
cbor2diag provides one way to do so:
$ cbor2diag -x 82d8c965616c696365a1d8c9656b6e6f7773d8c963626f62
[201("alice"), {201("knows"): 201("bob")}]
The cbor.me website breaks down the CBOR even further:
82 # array(2)
D8 C9 # tag(201)
65 # text(5)
616C696365 # "alice"
A1 # map(1)
D8 C9 # tag(201)
65 # text(5)
6B6E6F7773 # "knows"
D8 C9 # tag(201)
63 # text(3)
626F62 # "bob"
As shown, this Envelope is an array of a subject (“alice”) and an assertion, with the latter being a map that contains the predicate “knows” and and the object “bob”. Each individual element of the Envelope is tagged 201
to mark it as an Envelope leaf, all in accordance with the IETF spec.
Crypto Secrets in Envelopes
Our original UR documents describe holding a variety of crypto-secrets in bare URs, including keys, SSKR shares, and PSBTs. Today we believe that most of these could be better stored as Envelopes, with the Envelope then encoded as a UR.
Our URs for Keys page therefore offers an example of a Seed Envelope:
$ envelope format ur:envelope/lntpsogdhkwzdtfthptokigtvwnnjsqzcxknsktdoyadcsspoybdtpsokseceheyetdpidinjycxguihihiecxgdkpidjziniacxghihjkjycxhfihiajyjljpcxdehkinjtjnjtcxfwjzkpihdtcxendyeeideseoiyeyoyaatpsokkadwdghisinjkcxinjkcxjyisihcxeheyetdpidinjycxdeeyeecxktjljpiedtcxjkihihiecxkpjkihiecxhsjkcxhscxjokpidjziniacxjpihiojpihjkjkinjljtcxjyihjkjycxkoihiajyjljpcxiyjljpcxgogmjkdwcxfljljpieinhsjtcxguihihiecxghjljljzdwcxhsjtiecxjkihihiejyjljljzdpiajzindmbkbkghisinjkcxjkihihiecxhsjtiecxhsjzjzcxjeihkkjkcxioihjtihjphsjyihiecxiyjpjljncxinjycxjkisjlkpjziecxidihcxiajljtjkinieihjpihiecxkpjtjkihiakpjpihcxhsjkcxjyisinjkcxjeihkkcxjnhsjyihjpinhsjzcxinjkcxidihinjtiocxjkishsjpihiecxjokpidjziniajzkkcxhsjkcxhscxjyihjkjycxkoihiajyjljpdmbkbkfpjzjkjlcxjejtjlktjtcxhsjkcxfygdfpgscxdpcxvolansfyhsjpjecxgdkpjpjojzihcxfpjskphscxgsjlkoihvolantcxhsjkcxhsjtcxjljziecxkoihjpjkinjljtcxjliycxgsiniyihfdhsjkiscxishsiecxjyishsjycxiajljzjljpdmbkbkfwinjyiajlinjtcxgthsjkjyihjpcxgrihkkcxfginjtioihjpjojpinjtjyftcxendyeeideseoiyeybkfejyisihjpihkpjncxfpiaiajlkpjtjycxcndycxfpieiejpihjkjkcxhpjndleeeedidlendyvolanldldyvolanldldydldyhlftcxdyksececemeceyeciedyeeeoeneceniheseoetecfyetfefgfpfpesfxieeehseohsihfpiefedyehehenetbkoycfadzttpsotantjyoeadjsktjkisdeiajljkiniojtihjpdefzdydtdtaolytantjlonaxhdclaohldlmdrtlacxhnfpptplfyltwelafsnezslyndhllnvdimmwlpylkbwzjltbdmenaahdcxlejtimcnrlbtdemdoereyaqzprkpndbdgwfzflqdbzkohgzobycxcnvabaosbglfamtantjooeadlocsdyykaeykaeykaoykaocyhngrmuwzattantjooyadlslraewkadwklawkaycynewncnlboybetpsosezofptpbtlnlyjzkefmjejldeny
Bytes(16) [
'isA': 'Seed'
'508': 40308({1: "wsh(cosigner(@0))", 2: [40303({3: h'025d2f95c080206041a9ae4487ed803d9ffa819b5d86e76a9485f77ef26fd62e36', 4: h'8a6e6a23b70d2895a2b5f8b4b2759b0b4f4047b3157657fb112023e60ea71282', 6: 40304({1: [48, true, 0, true, 0, true, 2, true], 2: 1615565810}), 7: 40304({1: [[0, false, 1, false], [], false]}), 8: 2683380607})]})
'date': 2021-02-24T09:19:01Z
'name': "128-bit Seed Public Test Vector (Yinmn Blue) 604b93f2"
'note': "This is the 128-bit (24 word) seed used as a public regression test vector for URs, Gordian Seed Tool, and seedtool-cli.\n\nThis seed and all keys generated from it should be considered unsecure as this key material is being shared publicly as a test vector.\n\nAlso known as DPAL - “Dark Purple Aqua Love” as an old version of LifeHash had that color.\n\nBitcoin Master Key Fingerprint: 604b93f2\nEthereum Account #0 Address [m/44'/60’/0’/0/0]: 0x557525d043656e9385D8EFAA9Cd4a3aeAdE01168\n"
]
SSKRs offer a slightly more complex situation, as Gordian Envelopes offer a whole new paradigm, where an SSKR share becomes a lock for an entire Envelope.
We suspect PSBTs are the most likely to stick around as bare URs, but there are considerable advantages to store them in Envelopes, especially the ability to use GSTP to improve the security of communications.
Integrating Envelope URs Into Your Code
You can incorporate URs into your own code using the libraries of your choice:
You’re most likely to use higher level Envelope libraries.
Envelope Libraries
Language | Repo | Contributor | Status |
---|---|---|---|
Rust | bc-envelope-rust | Blockchain Commons | |
Swift | BCSwiftEnvelope | Blockchain Commons |
However UR libraries are also available if there’s a use case where you need to encode an Envelope yourself:
UR Libraries
Language | Repo | Contributor | Status |
---|---|---|---|
C++ | bc-ur | Blockchain Commons | |
Java | bc-ur-java | Bitmark | |
Java | Hummingbird | Craig Raw | |
Python | foundation-ur-py | Foundation | |
Rust | bc-ur-rust | Blockchain Commons | |
Rust | ur-rust | Dominik Spicher | |
Swift | URKit + URUI | Blockchain Commons | |
TypeScript | bc-ur for TS | xardass |
Conclusion
UR is a great format for storing a variety of data types that have been encoded as CBOR. The ultimate question is: what data do you put in there? At this point, Blockchain Commons suggests using UR to transmit and save data that has been stored as a Gordian Envelope. This allows you to maintain all of the advantages of URs, such as QR integration, self-description, printed storage, and error checking, and also receive the advantages of Envelopes, such as metadata inclusion, privacy-focused elision, and the use of extensions such as GSTP.
They also fit together entirely naturally, as Envelope is a CBOR data format and UR is a way to encode that CBOR as ASCII.