From cbfff058cda5193050ed72ce863cdf846acbed8b Mon Sep 17 00:00:00 2001 From: Benjamin Bengfort Date: Thu, 31 Mar 2022 16:19:06 -0500 Subject: [PATCH] Advanced Envelope Handlers - MVP (#84) --- .github/workflows/build.yml | 6 +- docs/content/reference/glossary.en.md | 35 +- docs/content/secure-envelopes/_index.en.md | 112 +++ docs/static/img/secure_envelopes.png | Bin 0 -> 98301 bytes pkg/ivms101/testdata/identity_payload.json | 158 ++++ pkg/trisa/api/v1beta1/api.pb.go | 2 +- pkg/trisa/api/v1beta1/errors.go | 8 +- pkg/trisa/api/v1beta1/errors.pb.go | 111 +-- pkg/trisa/api/v1beta1/errors_test.go | 40 +- pkg/trisa/crypto/crypto.go | 12 + pkg/trisa/crypto/rsaoeap/rsaoeap.go | 18 + pkg/trisa/envelope/envelope.go | 756 ++++++++++++++++++ pkg/trisa/envelope/envelope_test.go | 587 ++++++++++++++ pkg/trisa/envelope/errors.go | 21 + pkg/trisa/envelope/options.go | 94 +++ pkg/trisa/envelope/state.go | 25 + pkg/trisa/envelope/state_test.go | 34 + .../envelope/testdata/error_envelope.json | 18 + pkg/trisa/envelope/testdata/payload.json | 243 ++++++ .../envelope/testdata/payload/identity.json | 162 ++++ .../envelope/testdata/payload/pending.json | 20 + .../testdata/payload/transaction.json | 11 + .../envelope/testdata/pending_payload.json | 252 ++++++ .../envelope/testdata/sealed_envelope.json | 13 + pkg/trisa/envelope/testdata/sealing_key.pem | 52 ++ .../envelope/testdata/unsealed_envelope.json | 13 + pkg/trisa/handler/handler.go | 1 - proto/trisa/api/v1beta1/errors.proto | 3 +- 28 files changed, 2742 insertions(+), 65 deletions(-) create mode 100644 docs/content/secure-envelopes/_index.en.md create mode 100644 docs/static/img/secure_envelopes.png create mode 100644 pkg/ivms101/testdata/identity_payload.json create mode 100644 pkg/trisa/envelope/envelope.go create mode 100644 pkg/trisa/envelope/envelope_test.go create mode 100644 pkg/trisa/envelope/errors.go create mode 100644 pkg/trisa/envelope/options.go create mode 100644 pkg/trisa/envelope/state.go create mode 100644 pkg/trisa/envelope/state_test.go create mode 100644 pkg/trisa/envelope/testdata/error_envelope.json create mode 100644 pkg/trisa/envelope/testdata/payload.json create mode 100644 pkg/trisa/envelope/testdata/payload/identity.json create mode 100644 pkg/trisa/envelope/testdata/payload/pending.json create mode 100644 pkg/trisa/envelope/testdata/payload/transaction.json create mode 100644 pkg/trisa/envelope/testdata/pending_payload.json create mode 100644 pkg/trisa/envelope/testdata/sealed_envelope.json create mode 100644 pkg/trisa/envelope/testdata/sealing_key.pem create mode 100644 pkg/trisa/envelope/testdata/unsealed_envelope.json diff --git a/.github/workflows/build.yml b/.github/workflows/build.yml index e086008..3ca72ea 100644 --- a/.github/workflows/build.yml +++ b/.github/workflows/build.yml @@ -19,7 +19,7 @@ jobs: go-version: 1.16 - name: Install Staticcheck - run: go install honnef.co/go/tools/cmd/staticcheck@latest + run: go install honnef.co/go/tools/cmd/staticcheck@v0.2.2 - name: Checkout Code uses: actions/checkout@v2 @@ -47,7 +47,7 @@ jobs: - name: Install Dependencies run: | go version - go get -u github.com/kevinburke/go-bindata/... + go install github.com/kevinburke/go-bindata/go-bindata@v3.23.0 go install google.golang.org/protobuf/cmd/protoc-gen-go@v1.26 go install google.golang.org/grpc/cmd/protoc-gen-go-grpc@v1.1 @@ -77,7 +77,7 @@ jobs: - name: Install Dependencies run: | go version - go get -u github.com/kevinburke/go-bindata/... + go install github.com/kevinburke/go-bindata/go-bindata@v3.23.0 go install google.golang.org/protobuf/cmd/protoc-gen-go@v1.26 go install google.golang.org/grpc/cmd/protoc-gen-go-grpc@v1.1 diff --git a/docs/content/reference/glossary.en.md b/docs/content/reference/glossary.en.md index 380d885..02a9a21 100644 --- a/docs/content/reference/glossary.en.md +++ b/docs/content/reference/glossary.en.md @@ -1,13 +1,44 @@ --- title: Glossary date: 2021-06-14T15:59:09-05:00 -lastmod: 2021-06-14T15:59:09-05:00 +lastmod: 2022-03-31T16:10:07-05:00 description: "TRISA Glossary and Terminology" weight: 20 --- +## General Terminology + - **Originator**: The initiator of a blockchain transaction and therefore also the initiator of a TRISA transfer. The "originator" can refer to the originating VASP, the originating customer of the VASP or both. - **Beneficiary**: The recipient of a blockchain transaction and therefore also the recipient of a TRISA transfer. The "beneficiary" can refer to the beneficiary VASP, the beneficiary customer of the VASP or both. -- **Local vs Remote VASP**: A reference to the source of peer-to-peer traffic in an information exchange. The "local VASP" usually refers to the service you are running, while the "remote VASP" usually refers to some other VASP in the TRISA network. Local vs. Remote is often used to condition originator vs. beneficiary. If you are initiating the transaction then the local VASP is the originator and the remote VASP is the beneficiary. If you are receiving a transaction then the local VASP is the beneficiary and the remote VASP is the originator. \ No newline at end of file +- **Local vs Remote VASP**: A reference to the source of peer-to-peer traffic in an information exchange. The "local VASP" usually refers to the service you are running, while the "remote VASP" usually refers to some other VASP in the TRISA network. Local vs. Remote is often used interchangeably with originator vs. beneficiary. If you are initiating the transaction then the local VASP is the originator and the remote VASP is the beneficiary. If you are receiving a transaction then the local VASP is the beneficiary and the remote VASP is the originator. + +- **Travel Rule**: Record-keeping rules for transfers between financial institutions that allow law enforcement agencies to prevent illicit finance (e.g. money laundering or the financing of terrorism). + +- **VASP**: Virtual Asset Service Provider. A legal entity (usually a business) that manages and transfers virtual assets and are required by the Travel Rule to conduct information exchanges. Compliance exchanges in the TRISA network are between VASPs. + + +## Cryptographic Terminology + +- **mTLS**: Mutual Transport Layer Security is an encryption protocol that authenticates both the client and the server in a network connection and encrypts communications between the parties so that data cannot be read in flight. mTLS is an extention of TLS (formerly SSL) that requires both sides of a network connection to have a certificate that establishes their identity and which can be used to encrypt packets sent on the channel. + +- **Symmetric Cryptography**: Both encryption and decryption of data are performed using a single secret key that must be shared by the sender and the recipient. Shared secrets introduce the problem of how to share the secret key, however symmetric cryptographic algorithms are usually faster and better for bulk encryption of larger amounts of data. Generally, secrets are shared by asymmetric-key encryption and data encrypted using symmetric encryption. + +- **Secret Key Cryptography**: See _symmetric cryptography_. + +- **Public Key Cryptography**: A cryptographic method that uses a pair of related keys. Each pair consists of a _public key_, which can be shared with others, and a _private key_, which must not be shared with anyone but the owner. In practice, data can be encrypted with a public key but only decrypted with the private key. + +- **Asymmetric Cryptography**: See _public key cryptography_. + +- **Digital Signature**: A mathematical method that produces a _signature_ of data, e.g. some other piece of data that summarizes or describes the original data, usually via a hashing method. If the original data changes, its digital signature will change, therefore digital signatures are generally used as proof that the original data has not been tampered with, particularly if the signature has been generated cryptographically. For example, if a certificate has a signature that is signed with the private key of the certificate authority, anyone with the CA's public key can verify the signature of the certificate, ensuring it was the certificate produced by the CA. + +- **HMAC**: Hash Message Authentication Code. A secure method of producing a digital signature for data that uses a cryptographic hash function and a secret key. HMACs are used to verify that data has not been modified or changed, see also _digital signature_. + +- **Certificate**: Usually a reference to an X.509 certificate, a standard format for public key infrastructure. An X.509 certificate is a digital document that securely associates cryptographic key pairs with identities. Certificates are signed by a certificate authority to guarantee their provenance and contain subject information and other metadata concerning , the keypair, and its usage. Generally a reference to a certificate refers to the public key -- the part of the certificate that is shared for authentication purposes. However when certificates are issued, they are issued as a public/private key pair. + +- **Identity Certificate**: a TRISA specific term that refers to the certificate issued by the TRISA CA to a VASP entity that they should use to connect to other VASPs in the TRISA network via mTLS. + +- **Sealing Certificate (or keys)**: a TRISA specific term that refers to a key pair that is used to seal secure envelopes in the TRISA protocol. Sealing keys may be certificates issued by the TRISA CA, or they may be keys generated by the VASP and exchanged during the TRISA protocol. + +- **Certificate Authority**: an entity that issues and revokes certificates and whose public keys are used to establish trust in the identities its issued certificates provide. Certificate authorities usually control cryptographic hardware and the "root of trust" -- a digital key pair that is used to generate and sign intermediate and leaf certificates for public key infrastructure. \ No newline at end of file diff --git a/docs/content/secure-envelopes/_index.en.md b/docs/content/secure-envelopes/_index.en.md new file mode 100644 index 0000000..7c3331e --- /dev/null +++ b/docs/content/secure-envelopes/_index.en.md @@ -0,0 +1,112 @@ +--- +title: Secure Envelopes +date: 2022-03-03T15:36:09-06:00 +lastmod: 2022-03-03T15:36:09-06:00 +description: "Working with Secure Envelopes and cryptography in the TRISA protocol." +weight: 21 +--- + +![Secure Envelopes](/img/secure_envelopes.png) + + +The primary data structure for a TRISA exchange is the `SecureEnvelope` -- a wrapper for compliance payload data that facilitates peer-to-peer trust in compliance information exchanges. The design of TRISA secure envelopes had the following requirements: + +1. **Privacy**: only the recipient of the secure envelope should be able to open the secure envelope, even outside of the context of an RPC that is secured by transport layer encryption. +2. **Non-repudiation**: the secure envelope should cryptographically guarantee that the compliance payload is valid and has not been modified or tampered with since the original exchange. +3. **Long-term data storage**: the secure envelope should be encrypted at rest and can be "erased" by deleting its associated private keys when the compliance statute is over (usually 5-7 years). +4. **Security**: secure envelopes should prevent statistical attacks on the encrypted payload data while using public-key cryptography for ease of key management. + +These requirements are essential to understanding how to successfully engage with TRISA peers, therefore understanding the `SecureEnvelope` is the first step to being able to implement the TRISA protocol. + +## Working with Secure Envelopes + +There are two basic workflows for secure envelopes: creating and sealing an envelope to send to a counterparty, or unsealing and parsing a received secure envelope. + +### Creating a Secure Envelope + +**Prerequisites**: + +1. You should have constructed an appropriate TRISA `Payload` that contains an `identity` (an IVMS 101 `IdentityPayload`), a `transaction` (a TRISA generic transaction) and a `sent_at` timestamp (RFC-3339 formatted). +2. You should have the _public sealing key_ of the receipient. You can obtain this key either via the `KeyExchange` RPC or by requesting the key from the [directory service]({{< ref "/gds" >}}). + +**Steps**: + +1. Create a new envelope with a uuid4 envelope ID and the current timestamp +2. Marshal the `Payload` protocol buffers into an array of bytes. +3. Generate an encryption key and encrypt the payload bytes. +4. Generate an hmac secret and sign the encrypted payload +5. Use the public sealing key of the recipient to encrypt the encryption key and hmac secret. +6. Mark the envelope as sealed and populate all required metadata. + +### Opening a Secure Envelope + +1. Use the `public_key_signature` to identify the private key required to decrypt the encryption key and hmac secret. +2. Use your _private sealing key_ to decrypt the encryption key and HMAC secret. +3. Use the hmac secret and hmac algorithm to verify the encrypted payload has not been tampered with by ensuring the hmac you generate is identical to the hmac on the envelope. +4. Use the encryption key and encryption algorithm to decrypt the payload. +5. Unmarshal the payload into a TRISA `Payload` object. +6. Unmarshal the `identity` and `transaction` payloads and verify that you can parse them into data structures you can use for your compliance workflow. + +### Envelope States + +As you can see from the above workflows, envelopes can be in one of three states: + +1. **Sealed**: the encryption key and hmac secret on the envelope are encrypted with the public key of the recipient. Only the recipient can open the envelope in this state. +2. **Unsealed**: the encryption key and hmac secret are in the clear and can be used to decrypt the payload and verify the HMAC. +3. **Clear**: the payload has been decrypted and can be unmarshaled into a TRISA `Payload` protocol buffer. + +Generally speaking, when working with secure envelopes, "sealing" an envelope moves it through the following states: + +``` +Payload --> Clear --> Unsealed --> Sealed +``` + +Conversely opening an envelope moves it through the following states: + +``` +Sealed --> Unsealed --> Clear --> Payload +``` + +Maintaining envelopes in these various states can be useful to different applications. For example, an unsealed or clear envelope can be used to move data inside of an application while maintaining associated TRISA metdata. A sealed envelope can be used for long-term storage and with proper key-management, ensure erasure of the envelope simply by deleting the keys. + +## The Anatomy of a Secure Envelope + +A `SecureEnvelope` contains envelope metadata, cryptographic metadata, and an encrypted payload and HMAC signature. There are two types of complete envelope: + +1. An error envelope containing complete envelope metadata and a TRISA error. This envelope is sent as a rejection or transfer control message back to the sender from the recipient. +2. A payload envelope that has complete envelope and cryptographic metadata, as well as an encrypted payload and HMAC signature, but without an error. Payload envelopes can be either "sealed" or "unsealed" as described above. + +### Envelope Metadata + +| Field | Definition | +|--------------|-----------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------| +| `id` | also referred to as the "envelope id" - this is a unique ID generated by the originator and must be identical on all envelopes referring to the same virtual asset transaction. The originator usually generates this ID as a [UUID4](https://en.wikipedia.org/wiki/Universally_unique_identifier#Version_4_(random)). | +| `timestamp` | a nanosecond resolution RFC-3339 formatted timestamp (e.g. [`RFC3339Nano`](https://pkg.go.dev/time#RFC3339Nano)) that is used to order messages with the same ID. | +| `error` | a TRISA-specific error that is intended to help facilitate compliance exchanges. TRISA [error codes](https://github.com/trisacrypto/trisa/blob/main/proto/trisa/api/v1beta1/errors.proto) are used to reject compliance data or to request a fix and retry in a follow-on secure envelope. | + +### Cryptographic Metadata + +| Field | Definition | +|------------------------|------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------| +| `encryption_key` | the key used to encrypt the compliance payload. Keys are generated by the originator using a crypto random method. To prevent statistical attacks, a new key is generated for every transfer (e.g. at the same time as the envelope ID). If the envelope is sealed the `encryption_key` is encrypted using the public sealing key of the recipient. | +| `encryption_algorithm` | a string that describes the algorithm used to encrypt the compliance payload. This string should provide enough information for the recipient to be able to decrypt the payload. The default in the reference implementation is `"AES256-GCM"` which describes the use of [ AES Galois Counter Mode ]( https://datatracker.ietf.org/doc/html/rfc5288 ) with a 32 byte key. | +| `hmac_secret` | the secret used to calculate the HMAC signature. This secret is generated by the originator using a crypto random method, and is generated for every transfer. If the envelope is sealed then the `hmac_secret` is encrypted using the public sealing key of the recipient. | +| `hmac_algorithm` | a string that describes the algorithm used to compute the HMAC signature. The default in the reference implementation is `"HMAC-SHA256"` which describes the use of the [ HMAC ]( https://en.wikipedia.org/wiki/HMAC ) algorithm with a [ SHA-256 ]( https://en.wikipedia.org/wiki/SHA-2 ) secure hashing function. | +| `sealed` | a boolean that describes the state of the envelope. If true, this means that the `encryption_key` and `hmac_secret` have been encrypted using the public sealing key of the recipient. | +| `public_key_signature` | the signature of the public key used to seal the envelope, a helper for the recipient to identify the private key required to unseal the envelope. | + +### Payload + +| Field | Definition | +|-----------|-----------------------------------------------------------------------------------------------| +| `payload` | the ` Payload ` protocol buffer marshaled to bytes and encrypted using the ` encryption_key`. | +| `hmac` | the HMAC signature computed from the encrypted payload bytes and the ` hmac_secret`. | + +The `Payload` protocol buffer has the following fields: + +| Field | Definition | +|---------------|---------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------| +| `identity` | a protobuf [ any ]( https://developers.google.com/protocol-buffers/docs/proto3#any ) that contains the compliance identity information of the originator and the beneficiary. Although this can be any message type, it should be an [ IVMS101 Identity Payload ]( https://intervasp.org ). | +| `transaction` | a protobuf [ any ]( https://developers.google.com/protocol-buffers/docs/proto3#any ) that contains information used to identify the associated transaction on the blockchain or to send control flow messages and handling-specific instructions. Use one of the TRISA defined [generic transaction data structures](https://github.com/trisacrypto/trisa/blob/main/proto/trisa/data/generic/v1beta1/transaction.proto). | +| `sent_at` | The RFC-3339 formatted timestamp that the originator sent the first compliance message to the beneficiary. This timestamp is part of the compliance payload for non-repudiation purposes. | +| `received_at` | The RFC-3339 formatted timestamp when the beneficiary accepted the compliance message and returned the completed payload to the originator. This timestamp is part of the compliance payload for non-repudiation purposes. | \ No newline at end of file diff --git a/docs/static/img/secure_envelopes.png b/docs/static/img/secure_envelopes.png new file mode 100644 index 0000000000000000000000000000000000000000..fd73849f423d944197fe1eb3cce86f15fafe4b2f GIT binary patch literal 98301 zcmeEuRajeH*KPY&C{Un9OR=_CakpZ{3GNo8cyWi|6(|J?6n7|I+zA?5in|6vaEAmB zZfDc?`@jD=x99rgf{Q%ad#yFsnsbgh=2(QPsmS5sJjc0n=MEl7URvYMorg+y?);36_q!sSb_Hb zRR6wYsW0#Mj3(9Z>}qer=X8FN_in#FZKNy~7yLs?DB{Vt*KCBFJN0Ktq^VPCAu6-l zvVcxd2WzMz1MA-x%tu@&pKp@oqduo^o>#Mj2FAEu%DNs;-+^M7KQdhwlmD z-~M{xYfP+aRWL)E(^$JN+F-#PELhQ278>lSU1Hv)!!u1yR;AaoM3l-fV~3lUF&`eI zTIKma!*Cu84W{d$ofSu#(yI1N5=%I|dSu7x$vx0h)6{5}n(tJ)jdWL<@EzFQ&1hU7 zdLpr|ySHPyV`A0z~pNe?5A?;1O8t}E1Jl_{i z9utI%*BukwK6mF%S0azzUd~zBXiL#!8WBvU;kATEHWQQb;ms9qpdv|A;9P z{cG8OVq8&e_x|lecNR|fzihoLf zp|a^d*o*U=XI%Jpc^`tXBrm8D~U4RhxM?TanZw_z9;t7iiBJlVT~mi}3a{0I8u2C*-?o?Pe? z*T-#`C7LzwCg64%aw9gv*_v)=`lD+zHr8j&+tlT!5pMH40$g%W<^H;m4rWjLND#!G z`UpMq(}>S5aop>`$9kkKJ0(5#Pvq|advt(3z8^dM?BBtE{XWlKOWTaY{gTu%O(Nx~ zAUj1b$D+^c6|?shuj8jvIhfE=o#xPI9eBeuJxU1u%wm7(1Eza5@uAyL)!^SP`f<^% zbZwmJQFxE?<-S-Gb0pMe$+d}r81Y9ge($v@7cwg>uqNc5z_ipy3J23gnI_Vtj!&(f zNWGzOJU6AR?h*cwD5}M{3-)dh)vxvU>HN3LyYma1MV|2_@#wnD{fau#{xYOvon3z& z6qN&l#8oAs;&M2I;y0945cMPUa>njGBgu}4sl}Lne)!=V?3Rz|n(g7;)rVr9ES*>P z#&QOYlY@p_ZeKern%4UAit!j7hq5m^{bEF7jja8(cD#fRVOf~tIz}LEqnn+E@MkP1 z8BEsnL^sjKaef*c*|DeQO>3?FWe;~;e||jCLweYMYe~n9>HBHFo>OW+jomKdY(vxB z4sd7TzDIx$hS5=0eUrZF-P&Wfubj9Y)!q&sy`7W=z!8tj35|UB^k1KP zd%4&#G1P}|Xgj2GRw~LG#tN@`jqadqPt6fo#ehJ&*H0xdLknncB4bH-XYXPe^>LPpq@9$ zw@8@noF_3`d`IW;@4RPr)425lo-s8Wtfz3pH@H1)6sk@1R|bjAIhnozb1%g;_do62 z`z&*O>$3$V;)`(+J!N%1uZ>ur&+EL-NN<{>-(*tB+XGowY>a91g+aKM0IB}rb`F<)gB~*vE)Y1IQ z_o6o;Z<|l!4fzHgMxOQ6*Ik1ZW-pwVoiUzw4avSaiIr zH<<0Iy6Mxp!=R1~;6G~lx|{xb(qzqlPMCUK7cnRxxaJJb}5x2nXe7$sBG4|IAamJ?um|} zni~$%v_~*uqtkTt#WnRyYKlKuJq+y6#KkSIn8Z4)gC7#aqybi`I5n?U^YdI_hxtFt zj5Q)^)O#&9Y-?QIi`#)^CB(a^9H2_s@S6DF1G^tWrsrk(Y_ef>gMvMo%z4uLu9xw< z7DW)2@vrpsra^l0-DwFWle|wlv2<*##?dX(bYB zpP5^K;R-&wDgtwfG?yM-e%>2$$-Z8`tFXciC2r`O5mSOsRtQb#ts`w-%NCC{0*U}{;b zi9rM*AlKYk`KBYFy#2{88}R2IPl9{-jIt(gKM+YwM#Nbm>w%#WJF(MXUK<+RuG`&s|tdE(_A1uR^HEzF97?ZLs#(>siat099au z{%d30Z(KmcZ>YXzMwZcArA;lIFtC#D`C3i}oecPP?JP#?4E>$N9ZIqucS2RV`ql1d ziG)`LihyI}oUtRO=0T%Bs^5EnWa)+3`Ecu5f~|lv^DW(99(}Mbcyd`p(`dP@o@f@- zsOJ^rl$y7{63L_IOq{v=^W;acr8RyW5aalS<2OuZs$gG_v`vi_(SqqR>6-kn9O)as z4;=X)UpR-Y#9USx91Mogz#XH$4#W>7 z52c&jD<3fv*!}G7kp7K~=+b!Kw{tgr=~17F}6?JcT( zlYTF$q8o~5Y`n#5{+)&>`Fw)ym<5zpIM~UTZ!SO7WD1FMny1Xjug2xtz*pwYb3HVQ^c8C9or8M1_v}=|7Y;v!aG(h=s+P;W0 z-N@-F1+6EzV(|04l=16p+yE!UvPr7j){>{izegc`5vj5N3Dx|eNs;S%IVM7hRHm$M z;yK*nt-ZVzcOC%gtQ)zr16@JqM@GyG0r$(^MW+tf#1x}K!y?tY)`*;$AKWXwqQ-;O z!eu1PKY&BOx%Whq>9>vSW&5*>qxKas_hwhN-ir_{#Ivn5onB6UArZ4JcY zdTLMEQcadn5RyU>$gtMKDY(87Q3%7@bXAbAw+VbF>bXCFuJUobQiGTHOly^K_k5qY zIlT!5u*!1|Vvhc*ecudv281Nh;Notu$6wHY=Y!rjlEFbZgp6e!soG#+-l)jsY9y!r zE4_o7v^ZU3#Y6nE1NhUZO+HcfXN-+pQ|HFP5Zvu$vcZp=9Y$cc4uV_iTRM z1P&8(HWcfsv^%d-^OMD}Ct5W3b?U9gcmxoX9HLkakT`P`;3=W%soEBy7i`?4RQ&N}5gc7E&pvu*j&?z0V(sHosZqQqXprH^Ov}8^D8>w6pXSfM=#FZXVdi;27VZO)q0S{AY(O>A{TK(0RUCmk}po zqi>C$%ZfToeJdtAa7&LMXy^~q<`9sdBa@D+s5cnNrr#X<&!~a3POq)9PTROtn9j-g zp+kmN{}lC|)B*|nfmO>hQ0ozdG2?dlg;d!VpA!d`AJ+{=Bu#qFGlsKIt+u<6T8pN@ z_@ps|X9f>uqGF`d(RCpM*@p>2*a#~wFFW6XK&(^_4*ozjU$Ef!<1w{gmJWe4RIiCM zV|{{^@kC~f%Y){d${6x&w~H$JoB8cW{tcB3TMndZPT){=WDnWN->6=!@(GMCG5ajtLY=%TZYsjpihMzNj+hyq?X}r)GkK+^u8`R+C zKylL78HhMdQH=Dgy~c4mY0&>k4R*iH)$TO6bv0$mFH{^Qlm4i!Zil32nk_Ci$FXLx z`g(B3^%YN4>4%GvH%<{#ndOaNlQWqQ;<;MIHUg#94?tJE`%jOXsmF|o?$xPnol!Mj zckcU6Fj_BtfT8CQTKiE{I2|+r6WFR2g>AgZhx=xBnSXcFd5KF`;&tcvF~?e|3Eb|bYkO{Qf{#(5)#od*i@yq-KTbdqKNumIN9k^+S>tLgy!5zF{ z`Mo3-BktJNN!>$&O1n>lO)0d)?U6U!<;zXKPROk$O-(rPtxird0C!qq4k#}@x#?Tz zDXKV4t7ZASV8Q)8w&Lm58fXA0@C9W5K%2b-GMPs_=Vsa{Hbu^QI^uP7OV}Jq6_f-H zgB(a;!SQII+TYWr`V-63x~}s$3e0_KOKjH?%iwt}x8~tTHr28W4%s(gVQ<)TOw7KC z9kA(miqk<>4H=mxxgE0orLkRmS*Qdl9+tn*5bjUArQ>@KW`@Rs?1r$FH;^nmhrVzz zn8)vh9(=YvU;b7qF3^K&*{_^K`Jp%OSsrurTdR+Nn#{oReqrTrD?M*YbNYAp8(AY+ zUFwxZ)n1EzpS-4a_fy@XYR&+?A3P~F`n%3wq2_9~h-AkOc{e86mesy9(nP`mx1N#l zyH)$V91C-*bG8&2p!utH!R*+yn0+nZe}1QE{d~ujIdDP+CA@!Uoy_wGsCpYET*m}BAU$O6v4iFvVOUrL`cCh<-er!b@s#@C4*XX4$&t#aMhIOY zQ~9};sX_MEq~FVz^JE83iWx}j_k zT4n25TN;K~jx-8x`7MbuzPt4Z3m&lC@e7mP zXXHmwGu2S;o=^O^rfyf|%fqAJ)E&=uqmS6ItBgCSC#$;fShqyNVDq$cr{J(=e z4V?vB6L|BW-hUSG9 zIsS~}?#8WXT3^I6AuPtCf|y)DZ?LtOWs{5`g|gwjyw8ej6tVX&7IGqeaC)t|!_k0o z$LSvujoheO6JQb_2Gz^wLQxvm9O^{*l=2_wY?n~9QcE3wq(+7Q0L=5L>(L`SVG+RF zb;~g~FKqJWJ9TqTC9lcRX`&*(m&V%VWY?*To*wR`ac;%~h3_qOV-Itxoa)T3-iG4e z4|E8ZI$}h+UXty*mCkk$(E;5J;Ip9C=++h#NJ{bKo8d~rkp!~^i;&UO% zC4YS7wDz@(KW2$WxV#8$(lv9gRHGqja3my3)H*wd<>a{OOAz+$cy}ljpejkuXdd$N z&BZv3uTc!w2pa{!ZHF7(D>oWl(dh3ILVLIJ%+}8*8#o6v-xLUxG14pb-~USKJxeQx zgQ)yo&JB`}GF(W1TcC$Dz*8N(q*hI%>+Z}k1`+sLw4})#4F3qosAU7j`jseqOaEmu ziwtEmfQT?pQNhKXKo_F6PM8oTNzX)g8O*3msQuDc8lKzNWbhb+#l(GzcA4U6Sn zoL+S3`SfS&BX%tHUF^RY264poWzm{dY zXh{}j@SItXr9l9ud?UxteSVt}DoXOUy0Z9J7Jm@4<|=UI@z-ch2uK%>kKv7G zRP&@SZ0l_Qe!~Wv)zP(c{5E*8!D}La85E&Ca1L>(VMw!cFb@s_`AN3@+gHJVfE{}} zZbZl+jrk0PC$x$vaaD=L7<(VZy+2%5RNTB*oAL>4_dW|2MM_c2%j3l1pH>}CZKRKoQJ z74B5O+x)T>;#M$n6IT7Y*$a#ONLy<}ONx)}qOh)Z@S7-GtromKgnv9?R8AN^Jf(~i zF)j_;%1!I2K>n5oyQr-r5|GPi@&W@cEP~XxTBvpdpkR!T;H@UxhxCc}MjzcDU>p z2E&-qG79^C|!t zEjmVxKl5I*V@72z(ejr5g`YeHW^(6_3gvo%Tsk{KZ@P!D050 zzhLEJ=UbaPT6F5!1xz}zDPA)1$p5l#R*b{-_bJ{4kEcKl8eud)+L+h)G3=OkGJJ2}p?VU2)jJC&zzRY4!Cn>LlIeKI;q@jk}htMfgw)&=3Ayn%K?M2Nd z2ET8oq*5f}wKf2J(<`YgVYe|YAE*U-r{?}Ne}msbJD1wDXC#<^t$yWw9{^NXeDH+X zok8aXj`}yx_O~CjuCw)c+Y#2a+r-U`x{UjAHMq{^>iAQ2J=OXk1LIj6Q>djX`q4C zX%N|K&6xzkwN z>|&ztuSm3QTa-h3Y_1%n7YMSlZ?3b7L~M#(&`D$Qj-?RMMYkO^dtnyL9Y* z9O;WphG&xwk02q|3^Y|0&u_SA$qvdfg+^f0HiTrXV{P%r2v0WWVP+y}k>ImA3#8V* z73$*O<~AcWl(+@KghH?*-bb9pP(S7PJ)auE4r-k^zxGe9VzGTly6rFnA0D07P@hy% zW-FCbN*L#>(Xh~^^;fUY%y*bqj1}0;(jwJ_P7e29`eSzY9DO`x9?%c4(nXc*QnHdS zKg>%=x1qYY1N(IJyHfW;Qm~4jao)_lD|IDN3 zmuUMu$;>o2rc`}+qH!>d<6{qEn&k_`ib&Q}%CRO%b~!}9YIHM3x>{Y~Ftv zNqIp5PY{$lAODSx553_PN*(dM%BL|uUii%BP|zuGIIPM;mkSiR8N5kO_mEA zGjXgKy5@z8vNZz(^_;N~nc~eehrEC$=}hMdnogRw4>!rA8w&dI(#4wW+`^!*?zZ21GA-zx30 z2eTVuInYn%m3fhW%sv{C+^hlhjKRDBuvfR*0-*v@qm4LBtFR^QQcT3!)Zd>C8J(Hszss zti2A;n@$#gy*UmalDP%Ex8#^yB&0)K}WKJ9V%)i2u+23UV2 z0X6aBFN9$ok-|E zOvrSJ8))a7NSVH>^{wEI%{*-zQ>9&O#AfBBgH|IwEn8j7b(JEwj@RhiyP?*;-4;bI zSptjQ2Z5?3yJKFzSt!bVSLDY+5EEMSGv}+GhIn|=6ulHS8jg6gtrhJ`etRVJCjfC# z`twI?NpXJb7tax7$X9{tqRpsc=7IL!?qr!Y1)?Z3Vn+GsK)e*cy`tx;72WACEk{h_B*PU5!?ca&_8Qo>BDH%?27 zd2sThN(MQ6lGvi^0TRZcE!>Ta0g2}%_Q_q>UoG;8#}KEzUPKbC}n~z07E#uC4R>xkMq_aQ(&KsP_iw zlbe)I4(!ANat^MG>WoCH`tkg*uv*dZqmoQ%JKw(>@S)7LcSIUCO$Y4l&m|2U^J}lx zCN`HJT{VfXoRFGfH$&5~0_Xc~L!JufCT3+|{%o}I)!g)To7OU6fqiaQjc)c<@bC9A z0MD@3+qe>awDzD=ta{4V%?;c}*IfNk4524=5}+fg`w*|u(dT6ZSVuO{80;}64eDCh zHa6d;G18C~7-Y{kEurBzE)2P9x-CtAb+rQ8kp#KXOmkC6UiYhV;;TH5$Xgr#F)TRa zP}B-WyQErwSBLf~C7~S(b5{8twon==nY#fZoS9ULTrUNkWb7w)8oB0v8igaN9VWw> z-()bh?u89TlkG+J=kM0voL_y+4eiuTId$_xT$@B10D>L5iJKx-qA>rLFGZN+TxX|d z77F3s0aWry?s$X@e~tBo<2&bO3eii`rRaz}YJDf{?UJ0e<8ylN31wa`=of0E#xX?h}tue%Mo=n(ax+o_IG6f08>#dIu#y~u3NMr(^? z7zU(<@txX5>96<_FDDmUYq#5T`~~nz8hVsz&j|TN>%1S5OzdXm!%3%&O(dMi0wr1z z{OM%`1;eF6o0!R^Ro=qb`=GAVpCaJ_P5%55vp=tl?+(_Kxj1Q zNaA4-NJXYaztzIa2sh6OsmM`Ymi=@LceD5LXKH-|&vuI*6sl9gBIAl8Fvw3r?x;@p=Ts_L*0`j@H%0iZRY8oJ8NI1?WwH%ols|+8nB0-F zp+Iff=@>T<&)|2|lZlf!{0C{Bbak$E;|Rk`YR$c;jMELuF2ZK(KapOmr}7M!#-ZSDYh_2UV6eTNe|QN!!F{r=5#eddi*i2mV(P?fMmBr&~wr(7av zMA4Ui3=!wNu_}$dFQ!+HR&|w3y!oOH>KRJ9S?GhD{sHzU4bAUDGiH+*f&0g@xBHQKYH) z(_C+u@^|;g#&HG`J_Vva>M7#feHl9@Els6~f!Z2pFKSVB z&cvNTyp-a%r$y1U4jmoKS5R08 z5J|7Uek@_6S5*+o5j!>@>qGd!wwHbX2SD^z=e)3q>#EVZZ`mq;i==^#Y2E;S6#l96 z4%A*`TO<2bs@Dgqz;zuQBe-W5SXD9UF)nF3m-L7e=-tUMtz7n`64%DW#408kHl&nb zUuK)@RXdA0txC7j_{=C_Y~p)bS`TS%&JQEBCLUFj5ztNAbMxMgE!Nwk!%=NE6lSv( z>q721z}R&kAG|(yEYB7XwE!r-w*>qO>FDFaHZ0bZv zMOmtSRU4I;2=~4(ygWVoI{Rc?J<)?pIR{5pTqn^*@?NXV?p}@$XB#E5>1rM5u~id9 zYAoM~CJTg6&x*xTPm6s9%G!P+h5?ZNHP@e8jm)8_&McGZ(xBo+mJvyY_3|dGfwv$p z*HX8N_gY%%wNp(6<$oh!qh?!sdcuhm|KM&KK!BUq)rIWDWe@F7mTX5AWW;g03=Ihn zuxxSwDHpCL<3tUui5rEYvmCS|#)sv{yUjjUW?pCQWFyyie`JN3I#ze!Hh_uf;0s8H z{b@QW9`{De9sEWo)53;$GS%;U@pOL0Wh2#L8^_+l8~mi^K!xuyhGX-!1PYmI%`(ZA z3p7i=#d(-JIfr6z1`5u1pXY^YM>%GwC?FnA>wqOZO7X0!f^LZUcd}QqhinYzSlv7x zaU5S~9+V`l$Xjz&GAj`{q&qscdZ50zKTjqv9a zhLX48ygtOc(J|CGEoUl@lZU) zA(p*AoCfXNzm{A<;u{=M9F-_k>%+W+AAerjuhoR;_IeI~!Rg9waxIt;Q;B|KQ(EDCHtzIB`l=$bqyFXSL_w-wuxqOUEkQ`Sf&nH&eCQTF!SissXI*E-B;>! zE*F%OMnnuE`rH<-E3cyLa{(fW&@=7NtMKxTCvIMvb}Z@u!ppWexh6HpogBU4N9PMn z>W-!APq-fufBsTcS6;g9(-zd#S6sYNX(_yU9h_&iXYg8Nvn`gB3b|Jbb4)Q((%laV zEg3p_eI5_D1CA(Q+m*)9dqDFnGqo)iOf$zDK|L?>nc95cbdj0aams-~(UWgCC%(z! zT-W`9Xq<37kn%S}FNGXflz2xNpoYlftkJ4^F##JfslIFwzoE$C|o;!UdKfJ%-vbRh4Bv^-1q8@*Lq5F@u^AE;oT#8#<`fPeR~^=W&Opr+vv&5yZUivYF9A9x zR^z0f*fi%_St1~!Vnn4`O@hXWjs^L){fy!OI>f0}d%PliP0K!i@{nw7iZV2y+6Kp8 zt0qhb9XWj75{v<6w=+k{OGGZPu9V-pt4AKYCkTg>LdZo+hXlg^R5#Qy6t&F?gfRHc z`zUu66)-4Ge(M-k?B?*c^ZR`e@AV||W8=1v%RjabvdYdP^qp~w#WzEWM$sig2nE_pDo znL(VfvA&8M?y_7LYWt@DD)v^gG~B&3snC5st@ zLQ%?e-Tb+iJN?y{+6K4J(mm1iyiix%)g6wU{@Di@$3@phYt4rdIN@xdBDKD7s5rRd z1;e5XD_5364q0~iUsxjL$$Z9}#R@tk0Oha5mP|r!{SFZCt%OCqjb)<*ZWmxvqabqk z{##+(o;pkEa?fP2>t zHaPe&rQU`>wEpKf>-0|a5g^`&_Os=;mIw!66J_{$zj9-YNC%2sv;EiS>`|27)50HB zDvfCY6SgO}dobMq$_1qV%Nt1M*3e#X5OF3mD|KEN-_3CAkI^sB-Lyg8=6KtVWqC8x z8VYRM1KUu?J{1AzyaL;VJQA$51v5KH{Ib#GP!0JfXQBXGyQEyGWPTr1RG-PH)~Oq0 zMo42KFgK6T*}IzKKqWpJlXWCM*Dv3Pd@TqH)yIe`)_0WQ5TR2z=kp)DI_peYMZ>SdB4XVAqNJ`$?SSq^xhuon@~lr~!2l zEs7kwDq|d>MRVIp`e9wCPVfBLPkPUVOXjPJ^PiP=xq;b@?_5W7;KC?GxT<|Bh=tfsZuMp` zi5_W2dYfx5pgQvPRWazJZ5SK)w0+{x|DympnBPDG?*FuNR2reBXQJ%JEHH+85N?Bh zh_LU{O=q2_;?j-p|Jk3XOB+62*QwEQ)jTAv4B_Eo1@h)e>qoJPOb>HU<}0B_R)+n3 zkEK9uQ=i)R4WROkN(UGG5<*qKZuv$;fA)HGT+4aG8AtIfWSR zX5%zb0lL&f4>rrG#OQR_bO6lsOtaQ@m8sd7UMaZ(!t~N*50d{vX5jd@c{2v7xbz60 zH+=5odpXP8dV^K#iOspfefosh*y&T2R9HOqx==;+ zib+q7j)B$(wM-@#>wDhAm9BJF$24te=pa-6$po{l?JHXg^@ehXi_(=^5?)u?muLB8 zqlzrOHY(R*JfT(1^^tSf+GA{PUc;+lD%h?Tsd8MMhxa@8-tFj9Ba#4DIKLoG7^Of( z`N#Kw^_w=lOyb67aTVU*PwjgmQ@)OLF(PF>pU$klZBc`{MOd!|)k&_sH3e(;x8)hw z3VRENn4u%z4wS9bPewaMwGcIvGXba@lCoSZ0p+YF{_Z$KfM?49qRckaj}si-VgCjR zSZ~fqLD(7W`%NIN_Ws(fY^GCVZ$;W;=|v`UNyE!DNsg^>vlto^#yK{y)R6RApgwvQ zVBAlu?la!}>GetPl4_<`!gpTx2Vnaj;mD*FxgTx$=b1a`fWP+}EwQNUikEaym!~== zk!VmNcxaRPe;oqcwsPm>vUyCeHPF&!_}I`<$Y3)Lv+hH$+0Q=~`EG=~4AcF&WY!9G zIprOGtD`weD+_}L^*F1Gi%M|s)Qd=!-yF3OrWEhyd9$A4#MmHDQat2OiwJ9LGQ*HFi99y+}%u?i-AyX{zX@A672r{y6 zrfA7Ez1vm>&*-}>!DWfMx+7BIeeW?POLbX;b5`XU|+SRTI%#||8f}Z@T1D!q zHxU5Uv{zdHt^GR$RWb3Khcyv0gUJoTp5$P~uPt6OL|vkbD%$d~#q2K1{ug?xDkM3V zVSY~LNn3Xg4(V;HOv>pay|jUyYaXq0YmT`-s2$73aFmAC-+M6YT^b|rLHht&>(JU^ z7ri&ZxND(3Qnd7jCz-q2|AGzH_9tiK2s^BjTY}$sy;ui-2DzAw5sf1BpP@yqwx;Mz zoj0wLtZjY27aFy4G=VgkqC+cO&~C)XFfhJtG>^%8$XRD7Sg&b zcol9O#-3&3ln&R~Fd|{rYd-G7+Z5k~IQ-=nrmFtY6>j?&3_q)c!77T|9{t~TR7~2_ zrein0X@4V*(DPp88t?wv6a=bR@QW%@tq-tz`QXsDKM17Bqwe8=%f@B9Sy~$poCOf; zb$xc4)#@%EDz0P!>13%rmL$~S$5Nu>cEJX^;eAQFwTvATY5q9jXwm-LfbPSb0Rj#t z9wIs_BAO^xHXdB4mq#jlz+i02RjHd-5-9B@(8HDBVlZZq;n)^rzs-zXCJ8tAKt}Wy z4a8oZ(9yuXKuj1aj`a$Rmf!lmKsQGAH(`p=NZl2D-S-v7QT^f=y!iIBD#<~tr6ICb-KjUjoC*|tSrPPtWYo4Qm0p8J2D$6E=q z>EVS`!J+0i?=p;E`M<6mD#yhR`i_ygMFiS;7lpO)?*pY+O6Mx)fl!vhr0#Z63Zn4R zbderG`&=n1TUp?AE4*bjp6O zKqQKD0y=7z=B3G;h3|{9n1F7s-B~!`$!)&jASCp25O?kJF?f}Ajn3wpQFdJkT~jf$ zS4%_{TJ-vWt(tmXEL^NityL*N3E+K;NAlY3Qug8*1Cf<64BD1stzKn0H*waj$j6hRX`E)0vS(~1CZZB> zajV0Fx$;2gO3xFixsSN9P$%s z!#79)hcb0C>u!xqKRo{WePOfyDq-R$`d40w(Qh^(8;nX%QMpc-mgU&IqE1R^3`Mo( z=2^Ej#w8CR0f|`rYOy&U-&9owU0bl`uj7yo*E>{Co!anfkbnubB~%}W^7sKaq)|l) zP8tGANxVqDtZ$-oX4rqq9voM_uN{mKP3#K)O|#=KO8DOt=MD`D)#Ohxih+%>QGnk( z^c{RxzU#pxYu7(xk@GA5sXtmTEUXLAgaN$Ce)o9>c%5`?y`xr3Z%k*!A1Ld4=MeAU zOp8dTE1q${cZVP6^w4gCZ$qpMP|E^=8&jm;c@2chU#B*r$`;3FbH@+xMc=byu)&X) zAd&;WqJ@)ShFL*C%%@}*s3Qi*MN6)}Ao+90TBmVa4jbwHNP0el^AE)~M;v&Vyt~#h zwu>5#99}q;4ZLT(l}S`y)oynBXoqt3m?1Mf+~5wLKYz?_Xs_Z%EY(rH+9C9Y>u4`Bwc zw>?5-2|J$4t$#h`Owd@r{Ghwj7~szBd`l>(J04Yb@w8O!Tf?}m^eQ$Uqac9UhLykJ zKQIKPoMM^CCvA=eajL4!B&5SHPuIvX!&b&xqItohCY{Eai9q(}GCN}Qw4=cSsfcZM z0x4*}a4DWv??PI8s#iDZGRI;y(L*u7gfmmt_HHAifP5gkfvhTNe_mw+PPyD@f9HAH zz&bt9zyUh?VanW?YYkuNpRPZh#0}`Eb^V?kCsO`T*=+b@pq!l5{ z<>=dQI{@pAf*P!SbGz?R%Or|c!QeU1I&>dKcBri?T0K_>MD2 znP%*qRZGAUFocB#sIv1G7(91u;ntc+oLE~Po3`*~0^H~B+ciHMKCQrl^LTBoo*HGC z!s&Wcz8`t%UCtIiqe(UugJ&M~J`li^)BSco-3$g}Bx~=r=UQu)-e#D z1b|)JzdoFsacDiChz~%Swl99Bj>ok3A;gu|snT{D6@N(` zH6gSu=Fe=g*vC}m!tA&5{aMv(&75-6+4*+-JgCD_neNO8)sg)%49kErsOG8Wqqvs|M7pg|H-TArpP;5bLqyJ zNzdAg1R(B~qn4b6;o3O{oU{qX80WUA{i;h%!}d4^fa`xoFpfutCP+~qwTGQHuM-OfNWAGExxsD(8twJ#xFG8!^BlI0~!BsV*T z1=*=ROuWqpZDk0Wh**|Lfs&OA(L%2zY^8*U~NV`o7|Oa}5HDlkCC|v3#48 zEq+TX5>CR0&a0ElMhE7AQ9pNiJ*y?|#NQ27*gO-6D}XP?`**&d6q&XI>eZ3+&Gt@X z71Psq~;5b^)F+>8;j=B>e4+ue9-yY;r&suOT`>yveSn+HwA{XDG3 z;TaYzQoyDo{p(b+^rq9c;Qwu4yUF721DF@Z1>niesLQ~?Ga_o=8kz4gOM-h|$vloV zw^b}G3wYDXD|cFun*gg&ixt-Vbi6gdl0^Xr`u@pdY~QlSytJDuHLw z$BXTJ>qEDp@ELprdFG@1PO&F3S(saH^Nfy3Y$GN24@2>**V6SA%kxUkM5(7V`(rt4 zyKL10zvG?)Y!^$d8E-w134A`h+c0%l=IsAW@+ArHNZ%dO7jYFo-sQADCsi)W>9#>> z3iJSov+qMw@`E+jW*EkiQ;FZu3=Zj;K6^n#U7VR-%KXD}w;}BH%B(}t-#P7%q)X*I zzkmO>)vI*!q=Nhz3GJIK#@^gDdk`>#ix1ZL=7de~?$zW--0c5w&@yvDD0L_Ib$3ot zdM|mMt^0kp3rV}DDV6rWQ3r3m=tV0if<|D+_#N|4ec|S#pFUf+q1qy7g#g z(OZQ6ernKid=EZKy(w5j6z-|RDGVl=@l1syDmg7(pP?n1H1H<@h(INiYAoxmM- zV8+MIo=Mwr^PC19zofzjrXLHYej!`NOtv9`S=Lc*;iDYVElmWcmoh|UkFqfTE>lU0 z{zp%|q}hIL8}FP7fBY)|W@VCLaXS#^UEvFA%dWeu-O;_f{|{$zt2$>rLs^#h(buyb zGBBd!TveM6mHmpV8av^=^;2>4BOF7*7!Py6;Ko;v*#FVf_(I112?f@E0hWCrOsLi^%s7^dh}` zUz?Lz!YAs3iSHyvu{-C9fsb|X@mmm+bz^MXU-QzHJBL0$~RUaj3?}iub>@0?Scnlr>iOITjJBk0(kXk<cMKSZJ?Ycm=_mWy4#vNqo%LXaVez*`{!U<5X{Bq>`H%_d4ji4l6 zQs1oxoau=oPu3KL1Z%XP_GjVs2c_{sqi(gRRQZl<#Y54+8oY>Osil04`;VAj?WTP# zN-+M4ouRnnP*63%+;-IVz*_^CWsR(wd`DB2Da+n8|FgoGv>?O!3WrN4($YRy#fZ}8 zm+~b2V)@9=s#nEorK64mbNB24=o@lIV{}s&_1s8U$1E(K8 zZtM$$VH4zLkOhCwA>XVNNa(~k4Yl^7P?S7?uuzUNG1^_{G&n%mXO=-}S=_wWvJJ)7 zo8^C~2l&%_nl7Z*f}>?`x3~0;Q}~!2cn54`O#ePa-EA*n=kxujpw$l5)7OzpEpcL3 zW&NqopGGSZF+GbO=7@G@%U6%tE#)0{bLpq^>(pD}=e5OIj{*+pS?=*0QY{0MAhy27 z;-`ms)G0|Tr?18BuN>g1ZD2r|mv17cZ8K(0L>xH|O{nc>i2$cuI`K48wY6`CV=dy+ zD_928TvR;=>shp9Z#!~u;E#C9%Kk@z1DwcinxQD!Se0UXGC%%hvYmf=qVz0z`@_T# zCDrk(A|N(TItsSZ)2cX)Q^QR{xmd+OcX`5Q(C2%(${}s$BU}?mHopCyzJGP zN_H8aA^-HOB`lEK{rG+{+SRYCTFbWG-CTcAMg&z@*k~YrN*qzgJGjQZ&uF!JAX@|N zE0BnOCLMFC3ZFyrmICt~heN7!!;&Nx`T?I zgzA>+=e$9P=dojyKfmil?E(v%%q+ow}cB7UlBTXTQuR zSEK%}UT?0iX}8ldwAd#7+|v)4^dt_S?J(e59EbjB``imGSY$Qckf%KBw0-ed#H_xW z$b$A|)@(nVN_cD3=L=fFi=1wVRQUK=_zZkKb2NTku6~h- zO;+Gx)v7d8nx=S~N5h{&52% zsqr8_6vATy>|Le(on6?k zD#xN3qT&`8{B7u#so`XR(XUofy4@4mFiiSYN+wI-PIL0Kp|^nZ5iKg8(~t}c>;Heeq@D$uy4O$+huis*Y>>>?Ju*| zGac6w=?Yo*cG#)jHvPs>kukqa25GPh&?GXoh zk>s8E!<{ekYfko&Tt+DtK^u*7h+7gJ?K3C<^1l<6$4_tLIj0Q{k>!9hyXCF{3>TjQL?))VCksVtXs6ZzxPUU)E{Q?Sx`8 z{zrJorPdyYtsS3_MJ6xBA^$#@;sk63DZj>TVgW{6ka|fAn(18`cCT1xC>wC&wnWTJ zv{u^9ii@-vrpz;Uf4DQjWtw84)G8WC`J{k{H9LlsVKPfLUw4z?nVC!4gM{1=98=+H1>7I;6~nuQxrAhVge zksl_ITY7`>W{lNT zRg-g-x^(ZZVoA{hjzDQU7|U%0k25y)bUR0Vyo-dt$cJ|-XIE;JID}VE&YMg6zSJKq zgb{Oa#UZx7y)&|QMc3R~7Y9Nuo81u|Vir^xTmIG=Y7j7(3wP{8f{%ZGXo7Oie6>cD z&P<3IPt$M<**A!oynPE8;`G_1jjoH-gkX3r30 z)umB&4E~+4cQF)XLHq{5N?l-p<1AXzp69TVQFM(-Uh;@t%6{gu?6^7jOG!Y1(mkr@ z`LU*7i5zp{C-kN;XGrkC@n`SphW_Sc*Q;!Yky)H)*VZm0)a+4ba=q7^1OM>94KN$( zH7EY|oYWdYSu2hIe!67FYqs4;3$8Ow(2jJ|*~4HXN-vPnb-`M8%=)C9BSW#WMpQnx zrqWKoGc546@w4E8qFnE3{t$2+SdmapiaYl=*uBtDGL*8XSYQmastbZ_mvh#(X0OOP zEG)xeK=TD3;1tmzn2w#rfUC00M~ia)em)t)aR;hm9Sl#Qte+>4Jut)=9Nu%)&VC!8 zEl>>kQAe|Xk$U)_@v)m?&&a@_Cf-LMsrdJ__BYG1ogvQ4tNK6Td%k>kecV3X@))HU z(Etpoqyr&#Lte~d&;QlGBMvvnnlcP-Lm#;mkc;$`^}m37tWR>=G)lK>Y=LFWpc5UM zY;XZ;q_a3l)Up1v3ATu=TLI{8(^(IMvU10p!{O#+v&$9(mgly}4mun?9%w9O3^v7> z*WL?ohrP75w*eGWl~6iVA^5HSU`h8-bkw(W$;*JKv5}~qAtCC!k^x^|_j`wu(dYG^ z|L*2*HTZ{?ZJF|XDThukT4Y+iGRz=b7~(P|KTI@azSwE~QVLaUt7%xB1eP+41_5-C z{?bGi4yAem+r7tWnCx3DgX1~@lI+YnxcN-EIcQu~e%|JXd%!LuTbp1G0wlJ*A9}tl z)+f6DqjRPGXUS@905aPw{g8Z*jGvM_*Gt?%mv#7pM0M!7iIg8Q1URSeo z_3`>ekv;v-1@B;auR7km)wll~1wdL!9iWR4bdLX9Tl;!y(|`47xi#ZlLvv|+p&a4x zD$qk<^&3Ny=Wh!#EXFL9dc%(6_lES{yybq#Rox=x-?V{;drmsE7C(L<^!VqI@|xB# z9Wj%}r-<_un0NDPhQVfbcCL8Ld%8mv|8I%+J^s_u|D!#za=t9DWNiU3vkgv_V97IbX{ci-* zliV*M27ea)$n1!bW=s3q#JUt{D;jBZ4MRyRx5J_O}MUUDh&OeD+Ia`n^)( zTXxZByyr?ZLXW$6wTZ*tkiN|ZPfZB4OGO zFEat#h4r}KXDXb98efqRju4}fXjW8m^k`ZeGO}6nU*kT_2X24NPr32_cxiw}_OX@?eA&~*}MIfNsA&rU= z&Y=X@4Wio2F&{1HZLAd6z0Rl}hrZkSL6o3Jbc)zEz%8{Q*%j(H+%4|`Q{e7an?-L5F>C((@@gFpf@$_4MZHoYk;lt~ zpzSAnvil8Dqj@pSkHc;#B5)uW0;Wm7B(K~mqfp!B#01R%d0B!YE}oy<&>w$!j1b%? zmD|3WuN=e|VVpKUYZj6UUE0&FZj|($(N^v#Xp}m|oE%ihw~lttp5`LLUrW6kgrayO zCRI4S|AAd`$RGrtRZp*6F5-@t7uoXy`j;uX;-1?lA^}t^1@!5JPB|@6f3ebeJHedK zD)qHH3LK>VEsb$Mk*+qPxxFwwQs^rtA7t(s^32OPw0 zSzc{vkzgY#pa|LY%xq>&^B)pTX@GM*Cmd$GL>(@Q*}m2vFC%#bE{mo!J>oHS+x~FJ zKAqrf>gYjpEv864Pgkj4St^6j6qh7@uV~L@Qbr0aACIPySNcTMkH3vcqtUL z9t=8}nz8DIj(&a_n_2gs{lbOk>dpaY)PLP1nzW;zff}`b9mNqogHAj57-nX3g02;W zM0%!1qCnmsM|aTs7W#gcWrA%T%K}XBa>xc{BptG}4z~5!)@~ucl=3zWtl#j}spHG9J5=hB;ne15DxA#wse%e>1FoMv5(QmQ!^FUu>tB*pej%zDk$M@PPLA&& z#5x)#?|%~^4Vp2LNR-6LWndVqU1F&}L=?<_W`GvZqdm*>gBe<+V1lnQU6MTgokQ?b zvZ&WHq05AIN9y<2UR)}w@sL0;c685>BPD}iYsKjL>0hh^zgCBeh9sLLZv+j9qVU3E z63{TJkd=69oKt%IAxGa�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

P@F4IUqnc=n088On88LmX$H8KF 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 unsealed --> sealed. + // First create a new envelope in the clear state with the public key of the + // recipient that will eventually be used to seal the envelope. + env, _ := envelope.New(payload, envelope.WithRSAPublicKey(&key.PublicKey)) + + // Marshal the payload, generate random encryption and hmac secrets, and encrypt + // the payload, creating a new envelope in the unsealed state. + env, reject, err := env.Encrypt() + + // Two types of errors are returned from Encrypt and Seal + if err != nil { + if reject != nil { + // If both err and reject are non-nil, then a TRISA protocol error occurred + // and the rejection error can be sent back to the originator if you're + // sealing the envelope in response to a transfer request + log.Println(reject.String()) + } else { + // Otherwise log the error and handle with user-specific code + log.Fatal(err) + } + } + + // Seal the envelope by encrypting the encryption key and hmac secret on the secure + // envelope with the public key of the recipient passed in at the first step. + // Handle the reject and err errors as above. + env, reject, err = env.Seal() + + // Fetch the secure envelope and send it. + msg := env.Proto() + log.Printf("sending secure envelope with id %s", msg.Id) +} + +func ExampleOpen() { + // Receive a sealed secure envelope from the counterparty. Ensure you have the + // private key paired with the public key identified by the public key signature on + // the secure envelope in order to unseal and decrypt the payload. See testdata + // fixtures for example data. Note: we're loading an RSA private key used in other + // examples for demonstration and testing purposes. + msg, _ := loadEnvelopeFixture("testdata/sealed_envelope.json") + key, _ := loadPrivateKey("testdata/sealing_key.pem") + + // Open the secure envelope, unsealing the encryption key and hmac secret with the + // supplied private key, then decrypting, verifying, and unmarshaling the payload + // using those secrets. + payload, reject, err := envelope.Open(msg, envelope.WithRSAPrivateKey(key)) + + // Two types errors may be returned from envelope.Open + if err != nil { + if reject != nil { + // If both err and reject are non-nil, then a TRISA protocol error occurred + // and the rejection error can be sent back to the originator if you're + // opening the envelope in response to a transfer request + out, _ := envelope.Reject(reject, envelope.WithEnvelopeID(msg.Id)) + log.Printf("sending TRISA rejection for envelope %s: %s", out.Id, reject) + } else { + // Otherwise log the error and handle with user-specific code + log.Fatal(err) + } + } + + // Handle the payload with your interal compliance processing mechanism. + log.Printf("received payload sent at %s", payload.SentAt) +} + +func Example_parse() { + // Receive a sealed secure envelope from the counterparty. Ensure you have the + // private key paired with the public key identified by the public key signature on + // the secure envelope in order to unseal and decrypt the payload. See testdata + // fixtures for example data. Note: we're loading an RSA private key used in other + // examples for demonstration and testing purposes. + msg, _ := loadEnvelopeFixture("testdata/sealed_envelope.json") + key, _ := loadPrivateKey("testdata/sealing_key.pem") + + // Envelopes transition through the following states: sealed --> unsealed --> clear. + // First wrap the incoming envelope in the sealed state. + env, _ := envelope.Wrap(msg) + + // Unseal the envelope using the private key loaded above; this decrypts the + // encryption key and hmac secret using asymmetric encryption and returns a new + // unsealed envelope. + env, reject, err := env.Unseal(envelope.WithRSAPrivateKey(key)) + + // Two types of errors are returned from Unseal and Decrypt + if err != nil { + if reject != nil { + // If both err and reject are non-nil, then a TRISA protocol error occurred + // and the rejection error can be sent back to the originator if you're + // unsealing the envelope in response to a transfer request. + out, _ := env.Reject(reject) + log.Printf("sending TRISA rejection for envelope %s: %s", out.ID(), reject) + } else { + // Otherwise log the error and handle with user-specific code + log.Fatal(err) + } + } + + // Decrypt the envelope using the unsealed secrets, verify the HMAC signature, then + // unmarshal and verify the payload into new envelope in the clear state. + // Handle the reject and err errors as above. + env, reject, err = env.Decrypt() + + // Handle the payload with your interal compliance processing mechanism. + payload, _ := env.Payload() + log.Printf("received payload sent at %s", payload.SentAt) +} + +// Test the creation of an envelope from scratch, moving it through each state. +func TestSendEnvelopeWorkflow(t *testing.T) { + payload, err := loadPayloadFixture("testdata/payload.json") + require.NoError(t, err, "could not load payload") + + key, err := loadPrivateKey("testdata/sealing_key.pem") + require.NoError(t, err, "could not load sealing key") + + env, err := envelope.New(payload, envelope.WithRSAPublicKey(&key.PublicKey)) + require.NoError(t, err, "could not create envelope with no payload and no options") + require.Equal(t, envelope.Clear, env.State(), "expected clear state not %q", env.State()) + + eenv, reject, err := env.Encrypt() + require.NoError(t, err, "could not encrypt envelope") + require.Nil(t, reject, "expected no API error returned from encryption") + require.NotSame(t, env, eenv, "Encrypt should return a clone of the original envelope") + require.Equal(t, envelope.Unsealed, eenv.State(), "expected unsealed state not %q", eenv.State()) + + senv, reject, err := eenv.Seal() + require.NoError(t, err, "could not seal envelope") + require.Nil(t, reject, "expected no API error returned from sealing") + require.NotSame(t, eenv, senv, "Seal should return a clone of the original envelope") + require.Equal(t, envelope.Sealed, senv.State(), "expected sealed state not %q", senv.State()) + + // Fetch the message and check that it is ready to send + msg := senv.Proto() + require.NotEmpty(t, msg.Id, "message is missing an envelope ID") + require.NotEmpty(t, msg.Payload, "message is missing encrypted payload") + require.NotEmpty(t, msg.EncryptionKey, "message is missing encryption key") + require.Equal(t, "AES256-GCM", msg.EncryptionAlgorithm, "unexpected encryption algorithm") + require.NotEmpty(t, msg.Hmac, "message is missing HMAC digital signature") + require.NotEmpty(t, msg.HmacSecret, "message is missing HMAC secret") + require.Equal(t, "HMAC-SHA256", msg.HmacAlgorithm, "unexpected signature algorithm") + require.Nil(t, msg.Error, "unexpected error on message") + require.NotEmpty(t, msg.Timestamp, "message is missing timestamp") + require.True(t, msg.Sealed, "message is not marked as sealed") + require.NotEmpty(t, msg.PublicKeySignature, "message is missing public key signature") + require.Equal(t, "SHA256:QhEspinUU51gK0dQGqLa56BA6xyRy5/7sN5/6GlaLZw", msg.PublicKeySignature, "unexpected public key signature") +} + +// Test the handling of a secure envelope fixture through to creating a response. +func TestRecvEnvelopeWorkflow(t *testing.T) { + msg, err := loadEnvelopeFixture("testdata/sealed_envelope.json") + require.NoError(t, err, "could not load envelope") + + key, err := loadPrivateKey("testdata/sealing_key.pem") + require.NoError(t, err, "could not load sealing key") + + // Wrap the envelope ensuring it's in the sealed state + senv, err := envelope.Wrap(msg, envelope.WithRSAPrivateKey(key)) + require.NoError(t, err, "could not wrap the envelope") + require.NoError(t, senv.ValidateMessage(), "secure envelope fixture is invalid") + require.Equal(t, envelope.Sealed, senv.State(), "expected sealed state not %q", senv.State()) + + // Unseal the envelope + eenv, reject, err := senv.Unseal() + require.NoError(t, err, "could not unseal the envelope") + require.Nil(t, reject, "a rejection error was unexpectedly returned") + require.NotSame(t, senv, eenv, "Unseal should return a clone of the original envelope") + require.Equal(t, envelope.Unsealed, eenv.State(), "expected unsealed state not %q", eenv.State()) + + // Decrypt the envelope + env, reject, err := eenv.Decrypt() + require.NoError(t, err, "could not decrypt envelope") + require.Nil(t, reject, "a rejection error was unexpectedly returned") + require.NotSame(t, eenv, env, "Decrypt should return a clone of the original envelope") + require.Equal(t, envelope.Clear, env.State(), "expected clear state not %q", eenv.State()) + require.NotNil(t, env.Crypto(), "decrypted envelopes should maintain crytpo context") + require.NotNil(t, env.Sealer(), "decrypted envelopes should maintain sealer context") + + // Get the payload from the envelope + payload, err := env.Payload() + require.NoError(t, err, "could not fetch decrypted payload from envelope") + require.NotNil(t, payload, "nil payload unexpectedly returned") + + // Load the payload fixture for verification + expectedPayload, err := loadPayloadFixture("testdata/pending_payload.json") + require.NoError(t, err, "could not load payload fixture") + require.True(t, proto.Equal(payload, expectedPayload), "decrypted payload did not match payload fixture, did fixture change?") + + // Update the payload with received at and reseal the envelope + // TODO: does this modify the payload of the original message? + payload.ReceivedAt = time.Now().Format(time.RFC3339) + + oenv, err := envelope.New(payload, envelope.FromEnvelope(env), envelope.WithRSAPublicKey(&key.PublicKey)) + require.NoError(t, err, "could not create new envelope from original envelope") + + eoenv, reject, err := oenv.Encrypt() + require.NoError(t, err, "could not encrypt envelope") + require.Nil(t, reject, "a rejection error was unexpectedly returned") + require.NotSame(t, oenv, eoenv, "envelope unexpectedly not cloned") + + soenv, reject, err := eoenv.Seal() + require.NoError(t, err, "could not encrypt envelope") + require.Nil(t, reject, "a rejection error was unexpectedly returned") + require.NotSame(t, eoenv, soenv, "envelope unexpectedly not cloned") + + out := soenv.Proto() + + // NOTE: cannot use proto.Equal since the timestamp at least will have changed + require.Equal(t, msg.Id, out.Id, "mismatched envelope ID") + require.NotEmpty(t, out.Payload, "missing updated, encrypted payload") + require.NotEmpty(t, out.EncryptionKey, "sealed envelope encryption key missing") + require.Equal(t, msg.EncryptionAlgorithm, out.EncryptionAlgorithm, "mismatched envelope encryption algorithm") + require.NotEmpty(t, out.Hmac, "missing updated HMAC signature") + require.NotEmpty(t, out.HmacSecret, "sealed envelope hmac secret missing") + require.Equal(t, msg.HmacAlgorithm, out.HmacAlgorithm, "mismatched envelope HMAC algorithm") + require.Equal(t, msg.Error, out.Error, "unexpected error on envelopes") + require.NotEmpty(t, out.Timestamp, "no timestamp on outgoing envelope") + require.True(t, out.Sealed, "out is not marked as sealed") + require.Equal(t, msg.PublicKeySignature, out.PublicKeySignature, "public key signature mismatch") +} + +func TestOneLiners(t *testing.T) { + payload, err := loadPayloadFixture("testdata/pending_payload.json") + require.NoError(t, err, "could not load pending payload") + + key, err := loadPrivateKey("testdata/sealing_key.pem") + require.NoError(t, err, "could not load sealing key") + + // Create an envelope from the payload and the key + msg, reject, err := envelope.Seal(payload, envelope.WithRSAPublicKey(&key.PublicKey)) + require.NoError(t, err, "could not seal envelope") + require.Nil(t, reject, "unexpected rejection error") + + // Ensure the msg is valid + require.NotEmpty(t, msg.Id, "no envelope id on the message") + require.NotEmpty(t, msg.Payload, "no payload on the message") + require.NotEmpty(t, msg.EncryptionKey, "no encryption key on the message") + require.NotEmpty(t, msg.EncryptionAlgorithm, "no encryption algorithm on the message") + require.NotEmpty(t, msg.Hmac, "no hmac signature on the message") + require.NotEmpty(t, msg.HmacSecret, "no hmac secret on the message") + require.NotEmpty(t, msg.HmacAlgorithm, "no hmac algorithm on the message") + require.Empty(t, msg.Error, "unexpected error on the message") + require.NotEmpty(t, msg.Timestamp, "no timestamp on the message") + require.True(t, msg.Sealed, "message not marked as sealed") + require.NotEmpty(t, msg.PublicKeySignature, "no public key signature on the message") + + // Serialize and Deserialize the message + data, err := proto.Marshal(msg) + require.NoError(t, err, "could not marshal outgoing message") + + in := &api.SecureEnvelope{} + require.NoError(t, proto.Unmarshal(data, in), "could not unmarshal incoming message") + + // Open the envelope with the private key + decryptedPayload, reject, err := envelope.Open(in, envelope.WithRSAPrivateKey(key)) + require.NoError(t, err, "could not open envelope") + require.Nil(t, reject, "unexpected rejection error") + require.True(t, proto.Equal(payload, decryptedPayload), "payloads do not match") +} + +func TestEnvelopeAccessors(t *testing.T) { + // Actual value for timestamp testing + ats := time.Now() + + // Create a secure envelope with an error + in := &api.SecureEnvelope{ + Id: uuid.NewString(), + Error: &api.Error{Code: api.ComplianceCheckFail, Message: "afraid of the dark"}, + Timestamp: ats.Format(time.RFC3339Nano), + } + + env, err := envelope.Wrap(in) + require.NoError(t, err, "could not wrap envelope") + + require.Equal(t, in.Id, env.ID(), "did not return correct envelope ID") + require.Equal(t, in, env.Proto(), "proto did not return the embedded envelope") + require.Equal(t, in.Error, env.Error(), "did not return the embedded error") + require.Nil(t, env.Crypto(), "crypto should be nil for an error-only envelope") + require.Nil(t, env.Sealer(), "seal should be nil for an error-only envelope") + + payload, err := env.Payload() + require.EqualError(t, err, `envelope is in state "unsealed-error": payload may be invalid`) + require.Nil(t, payload, "payload should be nil for an error-only envelope") + + ts, err := env.Timestamp() + require.NoError(t, err, "should have been able to parse RFC3339Nano timestamp") + require.True(t, ts.Equal(ats), "should have returned now") + + // Test parsing RFC3339 timestamp + in.Timestamp = ats.Format(time.RFC3339) + ts, err = env.Timestamp() + require.NoError(t, err, "should have been able to parse RFC3339 timestamp") + require.Less(t, ats.Sub(ts), 1*time.Second, "should have returned now without nanosecond precision") + + // Test parsing empty string timestamp + in.Timestamp = "" + _, err = env.Timestamp() + require.EqualError(t, err, "trisa rejection [BAD_REQUEST]: missing ordering timestamp on secure envelope") + + // Test parsing a bad timestamp string + in.Timestamp = "2022-15-45T38:32:12Z" + _, err = env.Timestamp() + require.EqualError(t, err, "trisa rejection [BAD_REQUEST]: could not parse ordering timestamp on secure envelope as RFC3339 timestamp") + + // Create an envelope with a payload + payload, err = loadPayloadFixture("testdata/payload.json") + require.NoError(t, err, "could not load payload fixture") + + // Create a new envelope with complete options + key, err := rsa.GenerateKey(rand.Reader, 4096) + require.NoError(t, err, "could not generate RSA key") + env, err = envelope.New(payload, envelope.WithEnvelopeID(in.Id), envelope.WithTimestamp(ts), envelope.WithAESGCM(nil, nil), envelope.WithRSAPublicKey(&key.PublicKey)) + require.NoError(t, err, "could not create new envelope from payload") + + require.Equal(t, in.Id, env.ID(), "ID did not return correct envelope ID") + require.NotNil(t, env.Proto(), "proto did not return a new secure envelope") + require.Nil(t, env.Error(), "expected no error to be on envelope") + require.NotNil(t, env.Crypto(), "crypto should not be nil") + require.NotNil(t, env.Sealer(), "seal should not be nil") + + actualPayload, err := env.Payload() + require.NoError(t, err, "error should have been returned") + require.Equal(t, payload, actualPayload, "payload should match the one instantiated") + + actualTS, err := env.Timestamp() + require.NoError(t, err, "could not fetch timestamp") + require.True(t, ts.Equal(actualTS), "timestamp did not match expected timestamp") +} + +const ( + expectedEnvelopeId = "2b3b4c95-0a78-4f2a-a9fa-041970f97144" +) + +var ( + loadpb = protojson.UnmarshalOptions{ + AllowPartial: false, + DiscardUnknown: false, + } + dumppb = protojson.MarshalOptions{ + Multiline: true, + Indent: " ", + AllowPartial: true, + UseProtoNames: true, + UseEnumNumbers: false, + EmitUnpopulated: true, + } +) + +// Helper method to load a secure envelope fixture, generating the fixtures from the +// payloads if they have not yet been generated. +func loadEnvelopeFixture(path string) (msg *api.SecureEnvelope, err error) { + msg = &api.SecureEnvelope{} + if err = loadFixture(path, msg, true); err != nil { + return nil, err + } + return msg, nil +} + +// Helper method to load a payload fixture, generating it if it hasn't been yet +func loadPayloadFixture(path string) (payload *api.Payload, err error) { + payload = &api.Payload{} + if err = loadFixture(path, payload, true); err != nil { + return nil, err + } + return payload, nil +} + +// Helper method to load a fixture from JSON +func loadFixture(path string, m proto.Message, check bool) (err error) { + // Check if the path exists, if it doesn't attempt to generate the fixture. + if check { + if _, err = os.Stat(path); os.IsNotExist(err) { + if err = generateFixtures(); err != nil { + return err + } + } + } + + var data []byte + if data, err = ioutil.ReadFile(path); err != nil { + return err + } + + if err = loadpb.Unmarshal(data, m); err != nil { + return err + } + return nil +} + +// Helper method to generate secure envelopes from the payload fixtures +func generateFixtures() (err error) { + // Load the components of the various payloads that will be created + var ( + payload *api.Payload + pendingPayload *api.Payload + ) + + identity := &ivms101.IdentityPayload{} + if err = loadFixture("testdata/payload/identity.json", identity, false); err != nil { + return fmt.Errorf("could not unmarshal identity payload: %v", err) + } + + pending := &generic.Pending{} + if err = loadFixture("testdata/payload/pending.json", pending, false); err != nil { + return fmt.Errorf("could not read pending payload: %v", err) + } + + transaction := &generic.Transaction{} + if err = loadFixture("testdata/payload/transaction.json", transaction, false); err != nil { + return fmt.Errorf("could not read transaction payload: %v", err) + } + + payload = &api.Payload{ + SentAt: "2022-01-27T08:21:43Z", + ReceivedAt: "2022-01-30T16:28:39Z", + } + if payload.Identity, err = anypb.New(identity); err != nil { + return fmt.Errorf("could not create identity payload: %v", err) + } + if payload.Transaction, err = anypb.New(transaction); err != nil { + return fmt.Errorf("could not create transaction payload: %v", err) + } + + pendingPayload = &api.Payload{ + Identity: payload.Identity, + SentAt: payload.SentAt, + } + if pendingPayload.Transaction, err = anypb.New(pending); err != nil { + return fmt.Errorf("could not create pending payload: %v", err) + } + + // Serialize the payloads + if err = dumpFixture("testdata/payload.json", payload); err != nil { + return fmt.Errorf("could not marshal payload: %v", err) + } + + if err = dumpFixture("testdata/pending_payload.json", pendingPayload); err != nil { + return fmt.Errorf("could not marshal pending payload: %v", err) + } + + // Create error-only envelope + env := &api.SecureEnvelope{ + Id: expectedEnvelopeId, + Timestamp: "2022-01-27T08:21:43Z", + Error: &api.Error{ + Code: api.Error_COMPLIANCE_CHECK_FAIL, + Message: "specified account has been frozen temporarily", + }, + } + + if err = dumpFixture("testdata/error_envelope.json", env); err != nil { + return fmt.Errorf("could not marshal error only envelope: %v", err) + } + + // Create unsealed envelope + var handler *envelope.Envelope + if handler, err = envelope.New(payload); err != nil { + return err + } + + if handler, _, err = handler.Encrypt(); err != nil { + return err + } + + if err = dumpFixture("testdata/unsealed_envelope.json", handler.Proto()); err != nil { + return fmt.Errorf("could not marshal unsealed envelope: %v", err) + } + + // Create RSA keys for sealed secure envelope fixtures + key, err := rsa.GenerateKey(rand.Reader, 4096) + if err != nil { + return fmt.Errorf("could not generate RSA key fixture") + } + if err = dumpPrivateKey("testdata/sealing_key.pem", key); err != nil { + return err + } + + if env, _, err = envelope.Seal(pendingPayload, envelope.WithRSAPublicKey(&key.PublicKey)); err != nil { + return err + } + if err = dumpFixture("testdata/sealed_envelope.json", env); err != nil { + return fmt.Errorf("could not marshal sealed envelope: %v", err) + } + return nil +} + +func dumpFixture(path string, m proto.Message) (err error) { + var data []byte + if data, err = dumppb.Marshal(m); err != nil { + return err + } + return ioutil.WriteFile(path, data, 0644) +} + +func dumpPrivateKey(path string, key *rsa.PrivateKey) (err error) { + var data []byte + if data, err = x509.MarshalPKCS8PrivateKey(key); err != nil { + return err + } + + block := pem.EncodeToMemory(&pem.Block{ + Type: "RSA PRIVATE KEY", + Bytes: data, + }) + + return ioutil.WriteFile(path, block, 0600) +} + +func loadPrivateKey(path string) (key *rsa.PrivateKey, err error) { + var data []byte + if data, err = ioutil.ReadFile(path); err != nil { + return nil, err + } + + block, _ := pem.Decode(data) + if block == nil { + return nil, fmt.Errorf("could not decode PEM data") + } + + var keyt interface{} + if keyt, err = x509.ParsePKCS8PrivateKey(block.Bytes); err != nil { + return nil, err + } + + return keyt.(*rsa.PrivateKey), nil +} diff --git a/pkg/trisa/envelope/errors.go b/pkg/trisa/envelope/errors.go new file mode 100644 index 0000000..e8a0a65 --- /dev/null +++ b/pkg/trisa/envelope/errors.go @@ -0,0 +1,21 @@ +package envelope + +import "errors" + +var ( + ErrNoMessage = errors.New("invalid envelope: no wrapped message") + ErrNoEnvelopeId = errors.New("invalid envelope: no envelope id") + ErrNoTimestamp = errors.New("invalid envelope: no ordering timestamp") + ErrNoMessageData = errors.New("invalid envelope: must contain either error or payload") + ErrNoEncryptionInfo = errors.New("invalid envelope: missing encryption key or algorithm") + ErrNoHMACInfo = errors.New("invalid envelope: missing hmac signature, secret, or algorithm") + ErrNoPayload = errors.New("invalid payload: payload has not been decrypted") + ErrNoIdentityPayload = errors.New("invalid payload: payload does not contain identity data") + ErrNoTransactionPayload = errors.New("invalid payload: payload does not contain transaction data") + ErrNoSentAtPayload = errors.New("invalid payload: sent at timestamp is missing") + ErrInvalidSentAtPayload = errors.New("invalid payload: could not parse sent at timestamp in RFC3339 format") + ErrInvalidReceivedatPayload = errors.New("invalid payload: could not parse received at timestamp in RFC3339 format") + ErrCannotEncrypt = errors.New("cannot encrypt envelope: no cryptographic handler available") + ErrCannotSeal = errors.New("cannot seal envelope: no public key cryptographic handler available") + ErrCannotUnseal = errors.New("cannot unseal envelope: no private key cryptographic handler available") +) diff --git a/pkg/trisa/envelope/options.go b/pkg/trisa/envelope/options.go new file mode 100644 index 0000000..c85a057 --- /dev/null +++ b/pkg/trisa/envelope/options.go @@ -0,0 +1,94 @@ +package envelope + +import ( + "crypto/rsa" + "fmt" + "time" + + "github.com/trisacrypto/trisa/pkg/trisa/crypto" + "github.com/trisacrypto/trisa/pkg/trisa/crypto/aesgcm" + "github.com/trisacrypto/trisa/pkg/trisa/crypto/rsaoeap" +) + +type Option func(e *Envelope) error + +func FromEnvelope(env *Envelope) Option { + return func(e *Envelope) error { + e.msg.Id = env.msg.Id + e.crypto = env.crypto + return nil + } +} + +func WithEnvelopeID(id string) Option { + return func(e *Envelope) error { + e.msg.Id = id + return nil + } +} + +func WithTimestamp(ts time.Time) Option { + return func(e *Envelope) error { + e.msg.Timestamp = ts.Format(time.RFC3339Nano) + return nil + } +} + +func WithCrypto(crypto crypto.Crypto) Option { + return func(e *Envelope) error { + e.crypto = crypto + return nil + } +} + +func WithAESGCM(encryptionKey []byte, hmacSecret []byte) Option { + return func(e *Envelope) (err error) { + if e.crypto, err = aesgcm.New(encryptionKey, hmacSecret); err != nil { + return err + } + return nil + } +} + +func WithSeal(seal crypto.Cipher) Option { + return func(e *Envelope) error { + e.seal = seal + return nil + } +} + +func WithSealingKey(key interface{}) Option { + return func(e *Envelope) (err error) { + switch t := key.(type) { + case *rsa.PublicKey: + if e.seal, err = rsaoeap.New(t); err != nil { + return err + } + default: + return fmt.Errorf("could not use %T for sealing", t) + } + return nil + } +} + +func WithUnsealingKey(key interface{}) Option { + return func(e *Envelope) (err error) { + switch t := key.(type) { + case *rsa.PrivateKey: + if e.seal, err = rsaoeap.New(t); err != nil { + return err + } + default: + return fmt.Errorf("could not use %T for unsealing", t) + } + return nil + } +} + +func WithRSAPublicKey(key *rsa.PublicKey) Option { + return WithSealingKey(key) +} + +func WithRSAPrivateKey(key *rsa.PrivateKey) Option { + return WithUnsealingKey(key) +} diff --git a/pkg/trisa/envelope/state.go b/pkg/trisa/envelope/state.go new file mode 100644 index 0000000..94faf91 --- /dev/null +++ b/pkg/trisa/envelope/state.go @@ -0,0 +1,25 @@ +package envelope + +type State uint16 + +const ( + Unknown State = iota + Clear // The envelope has been decrypted and the payload is available on it + Unsealed // The envelope is unsealed and can be decrypted without any other information + Sealed // The envelope is sealed and must be unsealed with a private key or it is ready to send + Error // The envelope does not contain a payload but does contain an error field + ClearError // The envelope contains both a decrypted payload and an error + UnsealedError // The envelope contains both an error and a payload and is unsealed + SealedError // The envelope contains both an error and a payload and is sealed + Corrupted // The envelope is in an invalid state and cannot be moved into a correct state +) + +var stateNames = []string{"unknown", "clear", "unsealed", "sealed", "error", "clear-error", "unsealed-error", "sealed-error", "corrupted"} + +func (s State) String() string { + idx := int(s) + if idx >= len(stateNames) { + idx = 0 + } + return stateNames[idx] +} diff --git a/pkg/trisa/envelope/state_test.go b/pkg/trisa/envelope/state_test.go new file mode 100644 index 0000000..67556d5 --- /dev/null +++ b/pkg/trisa/envelope/state_test.go @@ -0,0 +1,34 @@ +package envelope_test + +import ( + "testing" + + "github.com/stretchr/testify/require" + "github.com/trisacrypto/trisa/pkg/trisa/envelope" +) + +func TestStateType(t *testing.T) { + require.Equal(t, "clear", envelope.Clear.String()) + require.Equal(t, "unsealed", envelope.Unsealed.String()) + require.Equal(t, "sealed", envelope.Sealed.String()) + require.Equal(t, "error", envelope.Error.String()) + require.Equal(t, "clear-error", envelope.ClearError.String()) + require.Equal(t, "unsealed-error", envelope.UnsealedError.String()) + require.Equal(t, "sealed-error", envelope.SealedError.String()) + require.Equal(t, "corrupted", envelope.Corrupted.String()) + + require.Equal(t, "unknown", envelope.State(0).String()) + require.Equal(t, "unknown", envelope.State(42).String()) + + // These only work because of the current positions of the states, e.g. Error==4 + // and Clear-Sealed and ClearError-UnsealedError are 1-3 and 5-7 respectively. + require.Equal(t, envelope.ClearError, envelope.Clear|envelope.Error) + require.Equal(t, envelope.UnsealedError, envelope.Unsealed|envelope.Error) + require.Equal(t, envelope.SealedError, envelope.Sealed|envelope.Error) +} + +func TestEnvelopeState(t *testing.T) { + // No payload, no envelope should return an invalid state + env := &envelope.Envelope{} + require.Equal(t, envelope.Unknown, env.State()) +} diff --git a/pkg/trisa/envelope/testdata/error_envelope.json b/pkg/trisa/envelope/testdata/error_envelope.json new file mode 100644 index 0000000..f4220cb --- /dev/null +++ b/pkg/trisa/envelope/testdata/error_envelope.json @@ -0,0 +1,18 @@ +{ + "id": "2b3b4c95-0a78-4f2a-a9fa-041970f97144", + "payload": "", + "encryption_key": "", + "encryption_algorithm": "", + "hmac": "", + "hmac_secret": "", + "hmac_algorithm": "", + "error": { + "code": "COMPLIANCE_CHECK_FAIL", + "message": "specified account has been frozen temporarily", + "retry": false, + "details": null + }, + "timestamp": "2022-01-27T08:21:43Z", + "sealed": false, + "public_key_signature": "" +} \ No newline at end of file diff --git a/pkg/trisa/envelope/testdata/payload.json b/pkg/trisa/envelope/testdata/payload.json new file mode 100644 index 0000000..c71c8c6 --- /dev/null +++ b/pkg/trisa/envelope/testdata/payload.json @@ -0,0 +1,243 @@ +{ + "identity": { + "@type": "type.googleapis.com/ivms101.IdentityPayload", + "originator": { + "originator_persons": [ + { + "natural_person": { + "name": { + "name_identifiers": [ + { + "primary_identifier": "Howard", + "secondary_identifier": "Jane", + "name_identifier_type": "NATURAL_PERSON_NAME_TYPE_CODE_LEGL" + }, + { + "primary_identifier": "Price", + "secondary_identifier": "Jane", + "name_identifier_type": "NATURAL_PERSON_NAME_TYPE_CODE_MAID" + } + ], + "local_name_identifiers": [], + "phonetic_name_identifiers": [] + }, + "geographic_addresses": [ + { + "address_type": "ADDRESS_TYPE_CODE_HOME", + "department": "", + "sub_department": "", + "street_name": "Greystone Street", + "building_number": "28", + "building_name": "", + "floor": "", + "post_box": "", + "room": "", + "post_code": "38017", + "town_name": "Collierville", + "town_location_name": "", + "district_name": "", + "country_sub_division": "TN", + "address_line": [], + "country": "US" + } + ], + "national_identification": { + "national_identifier": "112502920", + "national_identifier_type": "NATIONAL_IDENTIFIER_TYPE_CODE_SOCS", + "country_of_issue": "US", + "registration_authority": "RA000748" + }, + "customer_identification": "2642", + "date_and_place_of_birth": { + "date_of_birth": "1992-10-04", + "place_of_birth": "West Islip, NY" + }, + "country_of_residence": "US" + } + } + ], + "account_numbers": [ + "14HmBSwec8XrcWge9Zi1ZngNia64u3Wd2v" + ] + }, + "beneficiary": { + "beneficiary_persons": [ + { + "natural_person": { + "name": { + "name_identifiers": [ + { + "primary_identifier": "Clark", + "secondary_identifier": "Lawrence", + "name_identifier_type": "NATURAL_PERSON_NAME_TYPE_CODE_LEGL" + }, + { + "primary_identifier": "Clark", + "secondary_identifier": "Larry", + "name_identifier_type": "NATURAL_PERSON_NAME_TYPE_CODE_ALIA" + } + ], + "local_name_identifiers": [], + "phonetic_name_identifiers": [] + }, + "geographic_addresses": [ + { + "address_type": "ADDRESS_TYPE_CODE_HOME", + "department": "", + "sub_department": "", + "street_name": "Watling St", + "building_number": "249", + "building_name": "", + "floor": "", + "post_box": "", + "room": "", + "post_code": "WD7 7AL", + "town_name": "Radlett", + "town_location_name": "", + "district_name": "", + "country_sub_division": "", + "address_line": [], + "country": "United Kingdom" + } + ], + "national_identification": { + "national_identifier": "319560446", + "national_identifier_type": "NATIONAL_IDENTIFIER_TYPE_CODE_DRLC", + "country_of_issue": "GB", + "registration_authority": "RA000591" + }, + "customer_identification": "5610", + "date_and_place_of_birth": { + "date_of_birth": "1986-12-13", + "place_of_birth": "Leeds, United Kingdom" + }, + "country_of_residence": "GB" + } + } + ], + "account_numbers": [ + "14WU745djqecaJ1gmtWQGeMCFim1W5MNp3" + ] + }, + "originating_vasp": { + "originating_vasp": { + "legal_person": { + "name": { + "name_identifiers": [ + { + "legal_person_name": "AliceCoin, Inc.", + "legal_person_name_identifier_type": "LEGAL_PERSON_NAME_TYPE_CODE_LEGL" + }, + { + "legal_person_name": "Alice VASP", + "legal_person_name_identifier_type": "LEGAL_PERSON_NAME_TYPE_CODE_SHRT" + }, + { + "legal_person_name": "AliceCoin", + "legal_person_name_identifier_type": "LEGAL_PERSON_NAME_TYPE_CODE_TRAD" + } + ], + "local_name_identifiers": [], + "phonetic_name_identifiers": [] + }, + "geographic_addresses": [ + { + "address_type": "ADDRESS_TYPE_CODE_BIZZ", + "department": "", + "sub_department": "", + "street_name": "Roosevelt Place", + "building_number": "23", + "building_name": "", + "floor": "", + "post_box": "", + "room": "", + "post_code": "02151", + "town_name": "Boston", + "town_location_name": "", + "district_name": "", + "country_sub_division": "MA", + "address_line": [], + "country": "US" + } + ], + "customer_number": "", + "national_identification": { + "national_identifier": "5493004YBI24IF4TIP92", + "national_identifier_type": "NATIONAL_IDENTIFIER_TYPE_CODE_LEIX", + "country_of_issue": "US", + "registration_authority": "RA000744" + }, + "country_of_registration": "US" + } + } + }, + "beneficiary_vasp": { + "beneficiary_vasp": { + "legal_person": { + "name": { + "name_identifiers": [ + { + "legal_person_name": "Bob's Discount VASP, PLC", + "legal_person_name_identifier_type": "LEGAL_PERSON_NAME_TYPE_CODE_LEGL" + }, + { + "legal_person_name": "Bob VASP", + "legal_person_name_identifier_type": "LEGAL_PERSON_NAME_TYPE_CODE_SHRT" + } + ], + "local_name_identifiers": [], + "phonetic_name_identifiers": [] + }, + "geographic_addresses": [ + { + "address_type": "ADDRESS_TYPE_CODE_BIZZ", + "department": "", + "sub_department": "", + "street_name": "Grimsby Road", + "building_number": "762", + "building_name": "", + "floor": "", + "post_box": "", + "room": "", + "post_code": "OX8 U89", + "town_name": "Oxford", + "town_location_name": "", + "district_name": "", + "country_sub_division": "", + "address_line": [], + "country": "GB" + } + ], + "customer_number": "", + "national_identification": { + "national_identifier": "213800AQUAUP6I215N33", + "national_identifier_type": "NATIONAL_IDENTIFIER_TYPE_CODE_LEIX", + "country_of_issue": "GB", + "registration_authority": "RA000589" + }, + "country_of_registration": "GB" + } + } + }, + "transfer_path": { + "transfer_path": [] + }, + "payload_metadata": { + "transliteration_method": [] + } + }, + "transaction": { + "@type": "type.googleapis.com/trisa.data.generic.v1beta1.Transaction", + "txid": "05d9dc3fcbf48771c8ee9e95200877ef08e2766a780d4e44eee397633eb164d0", + "originator": "14HmBSwec8XrcWge9Zi1ZngNia64u3Wd2v", + "beneficiary": "14WU745djqecaJ1gmtWQGeMCFim1W5MNp3", + "amount": 0.00206412, + "network": "btc", + "timestamp": "2022-01-30T16:14:00Z", + "extra_json": "{\"value_when_transacted\": \"USD $77.86\"}", + "asset_type": "BTC", + "tag": "" + }, + "sent_at": "2022-01-27T08:21:43Z", + "received_at": "2022-01-30T16:28:39Z" +} \ No newline at end of file diff --git a/pkg/trisa/envelope/testdata/payload/identity.json b/pkg/trisa/envelope/testdata/payload/identity.json new file mode 100644 index 0000000..e7586d2 --- /dev/null +++ b/pkg/trisa/envelope/testdata/payload/identity.json @@ -0,0 +1,162 @@ +{ + "originator": { + "originator_persons": [{ + "natural_person": { + "name": { + "name_identifiers": [{ + "primary_identifier": "Howard", + "secondary_identifier": "Jane", + "name_identifier_type": "NATURAL_PERSON_NAME_TYPE_CODE_LEGL" + }, + { + "primary_identifier": "Price", + "secondary_identifier": "Jane", + "name_identifier_type": "NATURAL_PERSON_NAME_TYPE_CODE_MAID" + } + ] + }, + "geographic_addresses": [{ + "address_type": "ADDRESS_TYPE_CODE_HOME", + "street_name": "Greystone Street", + "building_number": "28", + "post_code": "38017", + "town_name": "Collierville", + "country_sub_division": "TN", + "country": "US" + }], + "national_identification": { + "national_identifier": "112502920", + "national_identifier_type": "NATIONAL_IDENTIFIER_TYPE_CODE_SOCS", + "country_of_issue": "US", + "registration_authority": "RA000748" + }, + "customer_identification": "2642", + "date_and_place_of_birth": { + "date_of_birth": "1992-10-04", + "place_of_birth": "West Islip, NY" + }, + "country_of_residence": "US" + } + }], + "account_numbers": [ + "14HmBSwec8XrcWge9Zi1ZngNia64u3Wd2v" + ] + }, + "beneficiary": { + "beneficiary_persons": [{ + "natural_person": { + "name": { + "name_identifiers": [{ + "primary_identifier": "Clark", + "secondary_identifier": "Lawrence", + "name_identifier_type": "NATURAL_PERSON_NAME_TYPE_CODE_LEGL" + }, + { + "primary_identifier": "Clark", + "secondary_identifier": "Larry", + "name_identifier_type": "NATURAL_PERSON_NAME_TYPE_CODE_ALIA" + } + ] + }, + "geographic_addresses": [{ + "address_type": "ADDRESS_TYPE_CODE_HOME", + "street_name": "Watling St", + "building_number": "249", + "post_code": "WD7 7AL", + "town_name": "Radlett", + "country": "United Kingdom" + }], + "national_identification": { + "national_identifier": "319560446", + "national_identifier_type": "NATIONAL_IDENTIFIER_TYPE_CODE_DRLC", + "country_of_issue": "GB", + "registration_authority": "RA000591" + }, + "customer_identification": "5610", + "date_and_place_of_birth": { + "date_of_birth": "1986-12-13", + "place_of_birth": "Leeds, United Kingdom" + }, + "country_of_residence": "GB" + } + }], + "account_numbers": [ + "14WU745djqecaJ1gmtWQGeMCFim1W5MNp3" + ] + }, + "originating_vasp": { + "originating_vasp": { + "legal_person": { + "name": { + "name_identifiers": [{ + "legal_person_name": "AliceCoin, Inc.", + "legal_person_name_identifier_type": "LEGAL_PERSON_NAME_TYPE_CODE_LEGL" + }, + { + "legal_person_name": "Alice VASP", + "legal_person_name_identifier_type": "LEGAL_PERSON_NAME_TYPE_CODE_SHRT" + }, + { + "legal_person_name": "AliceCoin", + "legal_person_name_identifier_type": "LEGAL_PERSON_NAME_TYPE_CODE_TRAD" + } + ] + }, + "geographic_addresses": [{ + "address_type": "ADDRESS_TYPE_CODE_BIZZ", + "street_name": "Roosevelt Place", + "building_number": "23", + "post_code": "02151", + "town_name": "Boston", + "country_sub_division": "MA", + "country": "US" + }], + "national_identification": { + "national_identifier": "5493004YBI24IF4TIP92", + "national_identifier_type": "NATIONAL_IDENTIFIER_TYPE_CODE_LEIX", + "country_of_issue": "US", + "registration_authority": "RA000744" + }, + "country_of_registration": "US" + } + } + }, + "beneficiary_vasp": { + "beneficiary_vasp": { + "legal_person": { + "name": { + "name_identifiers": [{ + "legal_person_name": "Bob's Discount VASP, PLC", + "legal_person_name_identifier_type": "LEGAL_PERSON_NAME_TYPE_CODE_LEGL" + }, + { + "legal_person_name": "Bob VASP", + "legal_person_name_identifier_type": "LEGAL_PERSON_NAME_TYPE_CODE_SHRT" + } + ] + }, + "geographic_addresses": [{ + "address_type": "ADDRESS_TYPE_CODE_BIZZ", + "street_name": "Grimsby Road", + "building_number": "762", + "post_code": "OX8 U89", + "town_name": "Oxford", + "country": "GB" + }], + "national_identification": { + "national_identifier": "213800AQUAUP6I215N33", + "national_identifier_type": "NATIONAL_IDENTIFIER_TYPE_CODE_LEIX", + "country_of_issue": "GB", + "registration_authority": "RA000589" + }, + "country_of_registration": "GB" + } + } + }, + "transfer_path": { + "transfer_path": [] + }, + "payload_metadata": { + "transliteration_method": [] + } +} \ No newline at end of file diff --git a/pkg/trisa/envelope/testdata/payload/pending.json b/pkg/trisa/envelope/testdata/payload/pending.json new file mode 100644 index 0000000..b26b54e --- /dev/null +++ b/pkg/trisa/envelope/testdata/payload/pending.json @@ -0,0 +1,20 @@ +{ + "envelope_id": "2b3b4c95-0a78-4f2a-a9fa-041970f97144", + "received_by": "BobVASP Compliance Review", + "received_at": "2022-01-27T08:21:43Z", + "message": "We have flagged this message as needing further review and will respond shortly.", + "reply_not_after": "2022-01-30T08:22:00Z", + "reply_not_before": "2022-01-27T20:22:00Z", + "extra_json": "", + "transaction": { + "txid": "", + "originator": "14HmBSwec8XrcWge9Zi1ZngNia64u3Wd2v", + "beneficiary": "14WU745djqecaJ1gmtWQGeMCFim1W5MNp3", + "amount": "0.00206412", + "network": "btc", + "timestamp": "", + "extra_json": "", + "asset_type": "BTC", + "tag": "" + } +} \ No newline at end of file diff --git a/pkg/trisa/envelope/testdata/payload/transaction.json b/pkg/trisa/envelope/testdata/payload/transaction.json new file mode 100644 index 0000000..c4d9d0a --- /dev/null +++ b/pkg/trisa/envelope/testdata/payload/transaction.json @@ -0,0 +1,11 @@ +{ + "txid": "05d9dc3fcbf48771c8ee9e95200877ef08e2766a780d4e44eee397633eb164d0", + "originator": "14HmBSwec8XrcWge9Zi1ZngNia64u3Wd2v", + "beneficiary": "14WU745djqecaJ1gmtWQGeMCFim1W5MNp3", + "amount": "0.00206412", + "network": "btc", + "timestamp": "2022-01-30T16:14:00Z", + "extra_json": "{\"value_when_transacted\": \"USD $77.86\"}", + "asset_type": "BTC", + "tag": "" +} \ No newline at end of file diff --git a/pkg/trisa/envelope/testdata/pending_payload.json b/pkg/trisa/envelope/testdata/pending_payload.json new file mode 100644 index 0000000..8e58753 --- /dev/null +++ b/pkg/trisa/envelope/testdata/pending_payload.json @@ -0,0 +1,252 @@ +{ + "identity": { + "@type": "type.googleapis.com/ivms101.IdentityPayload", + "originator": { + "originator_persons": [ + { + "natural_person": { + "name": { + "name_identifiers": [ + { + "primary_identifier": "Howard", + "secondary_identifier": "Jane", + "name_identifier_type": "NATURAL_PERSON_NAME_TYPE_CODE_LEGL" + }, + { + "primary_identifier": "Price", + "secondary_identifier": "Jane", + "name_identifier_type": "NATURAL_PERSON_NAME_TYPE_CODE_MAID" + } + ], + "local_name_identifiers": [], + "phonetic_name_identifiers": [] + }, + "geographic_addresses": [ + { + "address_type": "ADDRESS_TYPE_CODE_HOME", + "department": "", + "sub_department": "", + "street_name": "Greystone Street", + "building_number": "28", + "building_name": "", + "floor": "", + "post_box": "", + "room": "", + "post_code": "38017", + "town_name": "Collierville", + "town_location_name": "", + "district_name": "", + "country_sub_division": "TN", + "address_line": [], + "country": "US" + } + ], + "national_identification": { + "national_identifier": "112502920", + "national_identifier_type": "NATIONAL_IDENTIFIER_TYPE_CODE_SOCS", + "country_of_issue": "US", + "registration_authority": "RA000748" + }, + "customer_identification": "2642", + "date_and_place_of_birth": { + "date_of_birth": "1992-10-04", + "place_of_birth": "West Islip, NY" + }, + "country_of_residence": "US" + } + } + ], + "account_numbers": [ + "14HmBSwec8XrcWge9Zi1ZngNia64u3Wd2v" + ] + }, + "beneficiary": { + "beneficiary_persons": [ + { + "natural_person": { + "name": { + "name_identifiers": [ + { + "primary_identifier": "Clark", + "secondary_identifier": "Lawrence", + "name_identifier_type": "NATURAL_PERSON_NAME_TYPE_CODE_LEGL" + }, + { + "primary_identifier": "Clark", + "secondary_identifier": "Larry", + "name_identifier_type": "NATURAL_PERSON_NAME_TYPE_CODE_ALIA" + } + ], + "local_name_identifiers": [], + "phonetic_name_identifiers": [] + }, + "geographic_addresses": [ + { + "address_type": "ADDRESS_TYPE_CODE_HOME", + "department": "", + "sub_department": "", + "street_name": "Watling St", + "building_number": "249", + "building_name": "", + "floor": "", + "post_box": "", + "room": "", + "post_code": "WD7 7AL", + "town_name": "Radlett", + "town_location_name": "", + "district_name": "", + "country_sub_division": "", + "address_line": [], + "country": "United Kingdom" + } + ], + "national_identification": { + "national_identifier": "319560446", + "national_identifier_type": "NATIONAL_IDENTIFIER_TYPE_CODE_DRLC", + "country_of_issue": "GB", + "registration_authority": "RA000591" + }, + "customer_identification": "5610", + "date_and_place_of_birth": { + "date_of_birth": "1986-12-13", + "place_of_birth": "Leeds, United Kingdom" + }, + "country_of_residence": "GB" + } + } + ], + "account_numbers": [ + "14WU745djqecaJ1gmtWQGeMCFim1W5MNp3" + ] + }, + "originating_vasp": { + "originating_vasp": { + "legal_person": { + "name": { + "name_identifiers": [ + { + "legal_person_name": "AliceCoin, Inc.", + "legal_person_name_identifier_type": "LEGAL_PERSON_NAME_TYPE_CODE_LEGL" + }, + { + "legal_person_name": "Alice VASP", + "legal_person_name_identifier_type": "LEGAL_PERSON_NAME_TYPE_CODE_SHRT" + }, + { + "legal_person_name": "AliceCoin", + "legal_person_name_identifier_type": "LEGAL_PERSON_NAME_TYPE_CODE_TRAD" + } + ], + "local_name_identifiers": [], + "phonetic_name_identifiers": [] + }, + "geographic_addresses": [ + { + "address_type": "ADDRESS_TYPE_CODE_BIZZ", + "department": "", + "sub_department": "", + "street_name": "Roosevelt Place", + "building_number": "23", + "building_name": "", + "floor": "", + "post_box": "", + "room": "", + "post_code": "02151", + "town_name": "Boston", + "town_location_name": "", + "district_name": "", + "country_sub_division": "MA", + "address_line": [], + "country": "US" + } + ], + "customer_number": "", + "national_identification": { + "national_identifier": "5493004YBI24IF4TIP92", + "national_identifier_type": "NATIONAL_IDENTIFIER_TYPE_CODE_LEIX", + "country_of_issue": "US", + "registration_authority": "RA000744" + }, + "country_of_registration": "US" + } + } + }, + "beneficiary_vasp": { + "beneficiary_vasp": { + "legal_person": { + "name": { + "name_identifiers": [ + { + "legal_person_name": "Bob's Discount VASP, PLC", + "legal_person_name_identifier_type": "LEGAL_PERSON_NAME_TYPE_CODE_LEGL" + }, + { + "legal_person_name": "Bob VASP", + "legal_person_name_identifier_type": "LEGAL_PERSON_NAME_TYPE_CODE_SHRT" + } + ], + "local_name_identifiers": [], + "phonetic_name_identifiers": [] + }, + "geographic_addresses": [ + { + "address_type": "ADDRESS_TYPE_CODE_BIZZ", + "department": "", + "sub_department": "", + "street_name": "Grimsby Road", + "building_number": "762", + "building_name": "", + "floor": "", + "post_box": "", + "room": "", + "post_code": "OX8 U89", + "town_name": "Oxford", + "town_location_name": "", + "district_name": "", + "country_sub_division": "", + "address_line": [], + "country": "GB" + } + ], + "customer_number": "", + "national_identification": { + "national_identifier": "213800AQUAUP6I215N33", + "national_identifier_type": "NATIONAL_IDENTIFIER_TYPE_CODE_LEIX", + "country_of_issue": "GB", + "registration_authority": "RA000589" + }, + "country_of_registration": "GB" + } + } + }, + "transfer_path": { + "transfer_path": [] + }, + "payload_metadata": { + "transliteration_method": [] + } + }, + "transaction": { + "@type": "type.googleapis.com/trisa.data.generic.v1beta1.Pending", + "envelope_id": "2b3b4c95-0a78-4f2a-a9fa-041970f97144", + "received_by": "BobVASP Compliance Review", + "received_at": "2022-01-27T08:21:43Z", + "message": "We have flagged this message as needing further review and will respond shortly.", + "reply_not_after": "2022-01-30T08:22:00Z", + "reply_not_before": "2022-01-27T20:22:00Z", + "extra_json": "", + "transaction": { + "txid": "", + "originator": "14HmBSwec8XrcWge9Zi1ZngNia64u3Wd2v", + "beneficiary": "14WU745djqecaJ1gmtWQGeMCFim1W5MNp3", + "amount": 0.00206412, + "network": "btc", + "timestamp": "", + "extra_json": "", + "asset_type": "BTC", + "tag": "" + } + }, + "sent_at": "2022-01-27T08:21:43Z", + "received_at": "" +} \ No newline at end of file diff --git a/pkg/trisa/envelope/testdata/sealed_envelope.json b/pkg/trisa/envelope/testdata/sealed_envelope.json new file mode 100644 index 0000000..4c7d880 --- /dev/null +++ b/pkg/trisa/envelope/testdata/sealed_envelope.json @@ -0,0 +1,13 @@ +{ + "id": "1e7bb3cc-53ff-41b8-870f-4c22110e6f04", + "payload": "OrBvMmcdF2opQxsRGYMh6b9z+WpTKjhhRfis8ZjTkSmHnuoRlF3saF3NTAPIrKPoSA4jaAiEcL/cWa165UfHHu08W+iXgb5eB7scNSgqFPDYVlS1fVbTAUNwedgcoZ3EenF5+t1ky0SQ7ejOzp8FK6l49Meh+UfQf62ELFQi5jBMcaYokvmDJDwgOkJ7rVmZ0NA+LSxyZCoMKbQuiOHapYg1yCgAfVfpazO7d5b54BihZrW7PArpDqQeocpcwCWN4RXJYuHkjRwZmUM3Ks9gKP8tbZgf30giCejxGXgKw02sDplC+9kG8Mfe8XhkaART6NqhDZu+l1ajtI+oFuNM3tJ4XZ+MIDcvA2bs4sCu+g8AILVqNzbHPw36BbmytaFENyYILllMTPSZVxw7FxWCUXfxX0sbCGELHCju2O8Kjdam2v+KHnWM1QeUCOQNLJsXsiWgzYXArlHA1fts/axUcbyyXPYNAyEkH+zRsOsoLmkTVgTdCpFO1QIQ45zZrXZlHZ51q9fa7BBOTnDtFQOkJ6Rzqmuv/gHLq5hFwDdy1/gUOQAYQxvk0daNuBN4n+yLmn7d2Z3iQlEmWteGmU1+vJSY0L/xT0HXeLjdAAbVkA2FxqnBt8ScN48wEhulIeZQLuaIC4h/33Pi643Xy06Q1HEY4IVv9BDR/EeyeszDbr+3+5b0S8XCZmgMZyZL2Qkiny1PVYUbZx/LJAXXYTcE+zGiltGVuiN0AueF/8ymBl95Yr61UZg1YjjdjqaI56yM3Aq13HQZtLL5ccFS8mTN5YsomckQJzb5+J8rW/zcWbUsgYSgHJnv/olPvS18Y3+GjYRqQ0r3/7c3e75otf5GbqcCIwuyrVDEMK14RBG7p6vZYcfP5dNTvVOtoUrqhBze/YlZOVfk0nrQHOytluLKnHnYG5c/MTscSWkE2/r/Bi6MGwzHtrMjY2Q96NqN+G7bQyRiPHSj+aCnMuwNw2/+/rhGAQCz9gW6UbGGvH0uMbyqNtx+oTSnf5as7EbkkRK41LDyUG7AErcx9B+z/4wfFi53mqb52W6orrtEiNIAFhvuXK5mS5fBiWUi4nxrUUnNiAUWvu6CvzHVa8UOXOPsrEawoK4PtjuJO75cKNveFTBq6gh2FFyTLryhu7RkfvIwto1ec8PoFl6togcMiJqBwTuzraXAqh6GRQAp5ELJEWskc1U89UR2GCWnJB8nOp0OCw/wjx/UddFxouZ8aCHY8OzJAA9RgL7lDSSf/HplH+RdRUDMGonKl/FBjJggeGmdWaGFlJW83m+wPuCkxekS7avj1V9btrv7Qpj7qY6F3Pv+rqwbvmZ4hlY/6GI0YeT7ViB9GYbhgMRfkX03jnpH9wnsn7LlM+Dr65yB84rmhGm/Sl6mfXjbD/NOtAdldeIScgy+UnaQlKK1nZmscTe/X4XAZaFfBopEn1WGwByF9yoN5cXpbAZdSH66BYQBQObAToGIrdMjnPqMx8Ju7pDx0+AS/Q/4cgBr0bYeqTgPPcTk1Nko9zK0li+w43WFXi4jpORwj0BQD/qTM2cVuyKJBkJDlHi4mtwN8cpR/kdcRuj8HwZVYMaemDU2BhfH3w==", + "encryption_key": "pXpr/U87nsKHaUNQASTtN07Et1K+k0yZ/faQLdhmaUUiUuOh2+ikRNu2FFNBvoV3DcgJ+LVaeXjUz53lgdxr4QSzJZ9iIWJfjSsgSbYRJnw5UbdtNMBwL5CAFHb97Zg0Y1YYETEC1PbMWgYFX9nGhZqt4YBvQ2Jh08T+ct7Ui83RB7YBpOMQs3jYMLbuDFblmzBDBNLK6RVQCBYbg0HdGhl6NBqwC+FEGvHpwIta26ggQ1Mf54KnfK+yTeS2Kc0RaxvkOdpUpSfRzFsOyGiCAh+Smu4m7d+TuU10pNA9GR9yotDv2UDdJhyTNuLzYYsQAWENUmOXdeRtuNk7gAdQ3wDajAjhmia9jKjZM3Yja1ToenIsWqCdwTBoa+QxSlOlR9MB2wUx/BfVGT/IIzCSboTdqu5YRSjdNr4jo6o1oAD0v8v73qUERjzRgoWHepqq0H177tkIe+PrewmJTaZP4fQs4sMwBua6Zjlr+T4ka0NZ1sqptRTr6IO+zbuJPGlgrk7B1wMAqHgitVoMZHr4o+yZX2Zaq+npwIrjjSulGBlPyGGN0mzIuoKE3oxhOMTpBtG9ORzAXvYEw3ADkyqkfB32c+c79TzvcRgFfM2F+0Ig3mxeTAyqRv4EaOzrSZjfCsaQiemnOEBzoOM+iE8RYb/CZEC99JxcQ9V+zAWr9VU=", + "encryption_algorithm": "AES256-GCM", + "hmac": "vzKqwO072JrDvVaoOZZ9WXjA+BM1n607PFGLEdZ0DAg=", + "hmac_secret": "nmQD4a2fAuCzMviIEo59D4tsU2aZbSFZtnT9qrlo/XPCE9vi0GYPfcRwgCEeVtMtjjiOaYHmsbSrw7Z8pZ2l5/V2hARCz1IXA1iUYvDPbRTAWrAOeQ6wd/igzimtB7bBnfe7fHV8BjrsgEpAjKkbkX97KPm+O10tDlf6I8fjpBLaGMk5QmLJPOBianuNRgzMVcadMK4pHBit0I01brGYL5nKAVowF5QbBLRbyHLU/QIw0/aFpjn1GX+jeeSRtDXWQPAEHDn9FnC4zrWB8ctzKHRLp/BPDwXrP3TazUOcEe96jWR+7u++nh8U+T8/+SDeNyH2JglS99nXbVZ8KvqkBBt9wlYpK7KeKQgvx6RzMfVl4j/yP0GAf70AslWUfpd4/s1iBMsho62yrbZ3HS1vqUd+qizNEbK1WX6czDfpyia3vR4gg0UWhfWmLcqZIokvu7FwrK+yWlLQDBVoPvNGpIg6JWCCFrQOpnMRtmDYXRIrAXy7z90ZhuS5Ul7PbLAqWE2nKIzOXl8PdE4UoNolkuO4CC0I1fQyMD+m5sJPpDdcmGGKbd67IGgiuoQAoSF96pl8y4JRfKoS9yQQ+XCaSk4fwS4kMsEEmleIQPJBzH4HKaETx/Rq/4Voy0tY/Io6VU6tbrF+kS2E0bP2uKHbHrR3eSZPX7MnNkSIRf2gkGI=", + "hmac_algorithm": "HMAC-SHA256", + "error": null, + "timestamp": "2022-03-29T09:16:29.755212-05:00", + "sealed": true, + "public_key_signature": "SHA256:QhEspinUU51gK0dQGqLa56BA6xyRy5/7sN5/6GlaLZw" +} \ No newline at end of file diff --git a/pkg/trisa/envelope/testdata/sealing_key.pem b/pkg/trisa/envelope/testdata/sealing_key.pem new file mode 100644 index 0000000..cab96fe --- /dev/null +++ b/pkg/trisa/envelope/testdata/sealing_key.pem @@ -0,0 +1,52 @@ +-----BEGIN RSA PRIVATE KEY----- +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 +TEhLPeGjrU0RkgsoMo8U48UEgITbgA== +-----END RSA PRIVATE KEY----- diff --git a/pkg/trisa/envelope/testdata/unsealed_envelope.json b/pkg/trisa/envelope/testdata/unsealed_envelope.json new file mode 100644 index 0000000..da566a9 --- /dev/null +++ b/pkg/trisa/envelope/testdata/unsealed_envelope.json @@ -0,0 +1,13 @@ +{ + "id": "ca4344e6-fb1e-4ba2-9ac7-f78fab5f00c0", + "payload": "4WNgs+J/zFmcrb3ebzQTaaAomwbk6NJE6ZnQrA==", + "encryption_key": "a7syjk4CU70HkQq91ecqLsMJ4guEnByycgEfXJ9HaB8=", + "encryption_algorithm": "AES256-GCM", + "hmac": "Wmbakab7EwzGNFSiiJcvoIuQig6WDbn5d+ynsk8fg6o=", + "hmac_secret": "a7syjk4CU70HkQq91ecqLsMJ4guEnByycgEfXJ9HaB8=", + "hmac_algorithm": "HMAC-SHA256", + "error": null, + "timestamp": "2022-03-29T09:16:27.453444-05:00", + "sealed": false, + "public_key_signature": "" +} \ No newline at end of file diff --git a/pkg/trisa/handler/handler.go b/pkg/trisa/handler/handler.go index aa1eaef..d17c390 100644 --- a/pkg/trisa/handler/handler.go +++ b/pkg/trisa/handler/handler.go @@ -102,7 +102,6 @@ func Open(in *protocol.SecureEnvelope, key interface{}) (_ *Envelope, err error) if err = proto.Unmarshal(payloadData, env.Payload); err != nil { return nil, protocol.Errorf(protocol.EnvelopeDecodeFail, "could not unmarshal payload from decrypted data: %s", err) } - return env, nil } diff --git a/proto/trisa/api/v1beta1/errors.proto b/proto/trisa/api/v1beta1/errors.proto index 4266609..101892b 100644 --- a/proto/trisa/api/v1beta1/errors.proto +++ b/proto/trisa/api/v1beta1/errors.proto @@ -36,7 +36,8 @@ message Error { UNKNOWN_WALLET_ADDRESS = 51; // VASP does not have KYC information for the specified wallet address. - UNKOWN_IDENTITY = 52; + UNKNOWN_IDENTITY = 52; + UNKOWN_IDENTITY = 52; // Typo left for backwards compatibility. // Specifically, the Originator account cannot be identified. UNKNOWN_ORIGINATOR = 53;