From cab3fc7e31d5bb0883ef5e7fa0b04039066e686c Mon Sep 17 00:00:00 2001 From: ehsan shariati Date: Thu, 3 Aug 2023 18:15:23 -0400 Subject: [PATCH 1/6] Added methods to store and fetch by identity We want to be able to store root Cid of encrypted hamt tree from WNFS in the backend. But it should be retrievable only with identiy (which is something we can recreate). We added two methods for store and get. One receives the encrypted versio of root Cid and returns the encrypted version and JAVA code must handle the encryption and decryption, and one receives a key and handles the decryption and encryption itself --- mobile/client.go | 208 +++++++++++++++++++++++++++++++++++++++++++++++ 1 file changed, 208 insertions(+) diff --git a/mobile/client.go b/mobile/client.go index 6f988121..0f6b4add 100644 --- a/mobile/client.go +++ b/mobile/client.go @@ -3,7 +3,11 @@ package fulamobile import ( "bytes" "context" + "crypto/aes" + "crypto/cipher" + "crypto/rand" "errors" + "io" "github.com/functionland/go-fula/blockchain" "github.com/functionland/go-fula/exchange" @@ -256,3 +260,207 @@ func (c *Client) Shutdown() error { return xErr } } + +// This stores the encrypted root Cid in an IPLD node and links rootCid to it. +// We use the identity as the value of a new IPLD node and link rootCid to it. +// Essentially, you're creating a new IPLD node with the content of identity and a link to rootCid. +func (c *Client) StoreWithIdentity(identity []byte, encryptedRootCidStr string) (string, error) { + ctx := context.TODO() + + // Convert encryptedRootCidStr to []byte + encryptedRootCid := []byte(encryptedRootCidStr) + + // Create a new node builder for the identity + identityBuilder := basicnode.Prototype.Any.NewBuilder() + + // Create a new node with the identity as the value + err := identityBuilder.AssignBytes(identity) + if err != nil { + return "", err + } + identityNode := identityBuilder.Build() + + // Store the new node + identityLink, err := c.ls.Store(ipld.LinkContext{Ctx: ctx}, + cidlink.LinkPrototype{ + Prefix: cid.Prefix{ + Version: 1, + Codec: uint64(multicodec.Raw), + MhType: uint64(multicodec.Sha2_256), + MhLength: -1, + }, + }, + identityNode) + + if err != nil { + return "", err + } + + // Create a new node builder for the encryptedRootCid + rootCidBuilder := basicnode.Prototype.Any.NewBuilder() + + // Create a new node with the encryptedRootCid as the value + err = rootCidBuilder.AssignBytes(encryptedRootCid) + if err != nil { + return "", err + } + rootCidNode := rootCidBuilder.Build() + + // Now link the encryptedRootCid node to this new identity node + _, err = c.ls.Store(ipld.LinkContext{Ctx: ctx}, + cidlink.LinkPrototype{ + Prefix: cid.Prefix{ + Version: 1, + Codec: uint64(multicodec.Raw), + MhType: uint64(multicodec.Sha2_256), + MhLength: -1, + }, + }, + rootCidNode) + + if err != nil { + return "", err + } + + // Convert identityLink to string + return identityLink.String(), nil +} + +func (c *Client) GetByIdentity(identity string) (string, error) { + // Convert the identity to a link + identityCid, err := cid.Decode(identity) + if err != nil { + return "", err + } + + // Create a cidlink.Link from the cid.Cid + identityLink := cidlink.Link{Cid: identityCid} + + // Load the rootCid node + rootCidNode, err := c.ls.Load(ipld.LinkContext{Ctx: context.TODO()}, identityLink, basicnode.Prototype.Any) + if err != nil { + return "", err + } + + // The node should be Bytes, so convert it + encryptedRootCid, err := rootCidNode.AsBytes() + if err != nil { + return "", err + } + + return string(encryptedRootCid), nil +} + +// This function will encrypt the provided plaintext with the provided key using AES +func encrypt(plaintext []byte, key []byte) ([]byte, error) { + c, err := aes.NewCipher(key) + if err != nil { + return nil, err + } + + gcm, err := cipher.NewGCM(c) + if err != nil { + return nil, err + } + + nonce := make([]byte, gcm.NonceSize()) + if _, err = io.ReadFull(rand.Reader, nonce); err != nil { + return nil, err + } + + return gcm.Seal(nonce, nonce, plaintext, nil), nil +} + +// This function will decrypt the provided ciphertext with the provided key using AES +func decrypt(ciphertext []byte, key []byte) ([]byte, error) { + c, err := aes.NewCipher(key) + if err != nil { + return nil, err + } + + gcm, err := cipher.NewGCM(c) + if err != nil { + return nil, err + } + + nonceSize := gcm.NonceSize() + if len(ciphertext) < nonceSize { + return nil, errors.New("ciphertext too short") + } + + nonce, ciphertext := ciphertext[:nonceSize], ciphertext[nonceSize:] + return gcm.Open(nil, nonce, ciphertext, nil) +} + +func (c *Client) StoreWithIdentityAndEncrypt(identity []byte, rootCidStr string, key []byte) (string, error) { + ctx := context.TODO() + + // Convert rootCidStr to []byte + rootCid := []byte(rootCidStr) + + // Encrypt rootCid + encryptedRootCid, err := encrypt(rootCid, key) + if err != nil { + return "", err + } + + // Create a new node builder + builder := basicnode.Prototype.Any.NewBuilder() + + // Create a new node with the encryptedRootCid as the value + err = builder.AssignBytes(encryptedRootCid) + if err != nil { + return "", err + } + node := builder.Build() + + // Store the new node + identityLink, err := c.ls.Store(ipld.LinkContext{Ctx: ctx}, + cidlink.LinkPrototype{ + Prefix: cid.Prefix{ + Version: 1, + Codec: uint64(multicodec.Raw), + MhType: uint64(multicodec.Sha2_256), + MhLength: -1, + }, + }, + node) + + if err != nil { + return "", err + } + + // Convert identityLink to string + return identityLink.String(), nil +} + +func (c *Client) GetByIdentityAndDecrypt(identity string, key []byte) (string, error) { + // Convert the identity to a link + identityCid, err := cid.Decode(identity) + if err != nil { + return "", err + } + + // Create a cidlink.Link from the cid.Cid + identityLink := cidlink.Link{Cid: identityCid} + + // Load the rootCid node + rootCidNode, err := c.ls.Load(ipld.LinkContext{Ctx: context.TODO()}, identityLink, basicnode.Prototype.Any) + if err != nil { + return "", err + } + + // The node should be Bytes, so convert it + encryptedRootCid, err := rootCidNode.AsBytes() + if err != nil { + return "", err + } + + // Decrypt the rootCid + decryptedRootCid, err := decrypt(encryptedRootCid, key) + if err != nil { + return "", err + } + + return string(decryptedRootCid), nil +} From 17abf09904087ed13285d84f7c7fedba53a08185 Mon Sep 17 00:00:00 2001 From: ehsan shariati Date: Thu, 3 Aug 2023 18:36:06 -0400 Subject: [PATCH 2/6] removed redundant code and added appID as well we need to also provide appId on top of identity as the WNFS tree for each appID is different --- mobile/client.go | 69 +++++++++--------------------------------------- 1 file changed, 13 insertions(+), 56 deletions(-) diff --git a/mobile/client.go b/mobile/client.go index 0f6b4add..871ef375 100644 --- a/mobile/client.go +++ b/mobile/client.go @@ -264,17 +264,20 @@ func (c *Client) Shutdown() error { // This stores the encrypted root Cid in an IPLD node and links rootCid to it. // We use the identity as the value of a new IPLD node and link rootCid to it. // Essentially, you're creating a new IPLD node with the content of identity and a link to rootCid. -func (c *Client) StoreWithIdentity(identity []byte, encryptedRootCidStr string) (string, error) { +func (c *Client) StoreWithIdentity(identity string, appID string, encryptedRootCidStr string) (string, error) { ctx := context.TODO() // Convert encryptedRootCidStr to []byte encryptedRootCid := []byte(encryptedRootCidStr) + // Concatenate identity and appID + identityAppID := append([]byte(identity), []byte(appID)...) + // Create a new node builder for the identity identityBuilder := basicnode.Prototype.Any.NewBuilder() // Create a new node with the identity as the value - err := identityBuilder.AssignBytes(identity) + err := identityBuilder.AssignBytes(identityAppID) if err != nil { return "", err } @@ -326,9 +329,10 @@ func (c *Client) StoreWithIdentity(identity []byte, encryptedRootCidStr string) return identityLink.String(), nil } -func (c *Client) GetByIdentity(identity string) (string, error) { +func (c *Client) GetByIdentity(identity string, appID string) (string, error) { // Convert the identity to a link - identityCid, err := cid.Decode(identity) + identityAppID := append([]byte(identity), []byte(appID)...) + identityCid, err := cid.Decode(string(identityAppID)) if err != nil { return "", err } @@ -392,9 +396,7 @@ func decrypt(ciphertext []byte, key []byte) ([]byte, error) { return gcm.Open(nil, nonce, ciphertext, nil) } -func (c *Client) StoreWithIdentityAndEncrypt(identity []byte, rootCidStr string, key []byte) (string, error) { - ctx := context.TODO() - +func (c *Client) StoreWithIdentityAndEncrypt(identity string, appID string, rootCidStr string, key []byte) (string, error) { // Convert rootCidStr to []byte rootCid := []byte(rootCidStr) @@ -404,60 +406,15 @@ func (c *Client) StoreWithIdentityAndEncrypt(identity []byte, rootCidStr string, return "", err } - // Create a new node builder - builder := basicnode.Prototype.Any.NewBuilder() - - // Create a new node with the encryptedRootCid as the value - err = builder.AssignBytes(encryptedRootCid) - if err != nil { - return "", err - } - node := builder.Build() - - // Store the new node - identityLink, err := c.ls.Store(ipld.LinkContext{Ctx: ctx}, - cidlink.LinkPrototype{ - Prefix: cid.Prefix{ - Version: 1, - Codec: uint64(multicodec.Raw), - MhType: uint64(multicodec.Sha2_256), - MhLength: -1, - }, - }, - node) - - if err != nil { - return "", err - } - - // Convert identityLink to string - return identityLink.String(), nil + return c.StoreWithIdentity(identity, appID, string(encryptedRootCid)) } -func (c *Client) GetByIdentityAndDecrypt(identity string, key []byte) (string, error) { - // Convert the identity to a link - identityCid, err := cid.Decode(identity) - if err != nil { - return "", err - } - - // Create a cidlink.Link from the cid.Cid - identityLink := cidlink.Link{Cid: identityCid} - - // Load the rootCid node - rootCidNode, err := c.ls.Load(ipld.LinkContext{Ctx: context.TODO()}, identityLink, basicnode.Prototype.Any) - if err != nil { - return "", err - } - +func (c *Client) GetByIdentityAndDecrypt(identity string, appID string, key []byte) (string, error) { // The node should be Bytes, so convert it - encryptedRootCid, err := rootCidNode.AsBytes() - if err != nil { - return "", err - } + encryptedRootCid, err := c.GetByIdentity(identity, appID) // Decrypt the rootCid - decryptedRootCid, err := decrypt(encryptedRootCid, key) + decryptedRootCid, err := decrypt([]byte(encryptedRootCid), key) if err != nil { return "", err } From 5d42ffc0653847ffea17d24f4439543a4992b6b2 Mon Sep 17 00:00:00 2001 From: ehsan shariati Date: Thu, 3 Aug 2023 18:43:04 -0400 Subject: [PATCH 3/6] fixed unused error --- mobile/client.go | 3 +++ 1 file changed, 3 insertions(+) diff --git a/mobile/client.go b/mobile/client.go index 871ef375..9f9d5a51 100644 --- a/mobile/client.go +++ b/mobile/client.go @@ -412,6 +412,9 @@ func (c *Client) StoreWithIdentityAndEncrypt(identity string, appID string, root func (c *Client) GetByIdentityAndDecrypt(identity string, appID string, key []byte) (string, error) { // The node should be Bytes, so convert it encryptedRootCid, err := c.GetByIdentity(identity, appID) + if err != nil { + return "", err + } // Decrypt the rootCid decryptedRootCid, err := decrypt([]byte(encryptedRootCid), key) From 1d6e22b61ee3f611e32d7365990a23289d060ebe Mon Sep 17 00:00:00 2001 From: ehsan shariati Date: Thu, 3 Aug 2023 21:16:32 -0400 Subject: [PATCH 4/6] First try at making it a link system First try at 1- using current Put and Get 2- store it in an interconected link system, which right now is like: `identity -> CID(appID -> encryptedRootCID)` --- mobile/client.go | 101 +++++++++++++++++------------------------------ 1 file changed, 36 insertions(+), 65 deletions(-) diff --git a/mobile/client.go b/mobile/client.go index 9f9d5a51..45d2ce8a 100644 --- a/mobile/client.go +++ b/mobile/client.go @@ -6,6 +6,8 @@ import ( "crypto/aes" "crypto/cipher" "crypto/rand" + "encoding/base64" + "encoding/json" "errors" "io" @@ -264,95 +266,64 @@ func (c *Client) Shutdown() error { // This stores the encrypted root Cid in an IPLD node and links rootCid to it. // We use the identity as the value of a new IPLD node and link rootCid to it. // Essentially, you're creating a new IPLD node with the content of identity and a link to rootCid. -func (c *Client) StoreWithIdentity(identity string, appID string, encryptedRootCidStr string) (string, error) { - ctx := context.TODO() - - // Convert encryptedRootCidStr to []byte - encryptedRootCid := []byte(encryptedRootCidStr) - - // Concatenate identity and appID - identityAppID := append([]byte(identity), []byte(appID)...) - - // Create a new node builder for the identity - identityBuilder := basicnode.Prototype.Any.NewBuilder() - - // Create a new node with the identity as the value - err := identityBuilder.AssignBytes(identityAppID) +func (c *Client) StoreEncryptedWithIdentity(identity string, appID string, encryptedRootCID string) (string, error) { + // Create appID-encryptedRootCID map + appIDEncryptedRootCIDMap := map[string]interface{}{appID: encryptedRootCID} + appIDEncryptedRootCIDMapBytes, err := json.Marshal(appIDEncryptedRootCIDMap) if err != nil { return "", err } - identityNode := identityBuilder.Build() - - // Store the new node - identityLink, err := c.ls.Store(ipld.LinkContext{Ctx: ctx}, - cidlink.LinkPrototype{ - Prefix: cid.Prefix{ - Version: 1, - Codec: uint64(multicodec.Raw), - MhType: uint64(multicodec.Sha2_256), - MhLength: -1, - }, - }, - identityNode) + // Store appID-encryptedRootCID map + appIDEncryptedRootCIDLink, err := c.Put(appIDEncryptedRootCIDMapBytes, int64(multicodec.Json)) if err != nil { return "", err } - // Create a new node builder for the encryptedRootCid - rootCidBuilder := basicnode.Prototype.Any.NewBuilder() - - // Create a new node with the encryptedRootCid as the value - err = rootCidBuilder.AssignBytes(encryptedRootCid) + // Create identity-appIDEncryptedRootCIDLink map + identityLinkMap := map[string]interface{}{identity: appIDEncryptedRootCIDLink} + identityLinkMapBytes, err := json.Marshal(identityLinkMap) if err != nil { return "", err } - rootCidNode := rootCidBuilder.Build() - - // Now link the encryptedRootCid node to this new identity node - _, err = c.ls.Store(ipld.LinkContext{Ctx: ctx}, - cidlink.LinkPrototype{ - Prefix: cid.Prefix{ - Version: 1, - Codec: uint64(multicodec.Raw), - MhType: uint64(multicodec.Sha2_256), - MhLength: -1, - }, - }, - rootCidNode) + // Store identity-appIDEncryptedRootCIDLink map + identityLink, err := c.Put(identityLinkMapBytes, int64(multicodec.Json)) if err != nil { return "", err } - // Convert identityLink to string - return identityLink.String(), nil + // Convert byte slice to base64 string + identityLinkStr := base64.StdEncoding.EncodeToString(identityLink) + + return identityLinkStr, nil } -func (c *Client) GetByIdentity(identity string, appID string) (string, error) { - // Convert the identity to a link - identityAppID := append([]byte(identity), []byte(appID)...) - identityCid, err := cid.Decode(string(identityAppID)) +func (c *Client) GetEncryptedRootCID(identity string, appID string) ([]byte, error) { + // Load identity link map + identityLinkMapBytes, err := c.Get([]byte(identity)) if err != nil { - return "", err + return nil, err } - - // Create a cidlink.Link from the cid.Cid - identityLink := cidlink.Link{Cid: identityCid} - - // Load the rootCid node - rootCidNode, err := c.ls.Load(ipld.LinkContext{Ctx: context.TODO()}, identityLink, basicnode.Prototype.Any) + var identityLinkMap map[string][]byte + err = json.Unmarshal(identityLinkMapBytes, &identityLinkMap) if err != nil { - return "", err + return nil, err } - // The node should be Bytes, so convert it - encryptedRootCid, err := rootCidNode.AsBytes() + // Load appID-encryptedRootCID map + appIDEncryptedRootCIDMapBytes, err := c.Get(identityLinkMap[identity]) if err != nil { - return "", err + return nil, err + } + var appIDEncryptedRootCIDMap map[string][]byte + err = json.Unmarshal(appIDEncryptedRootCIDMapBytes, &appIDEncryptedRootCIDMap) + if err != nil { + return nil, err } - return string(encryptedRootCid), nil + encryptedRootCID := appIDEncryptedRootCIDMap[appID] + return encryptedRootCID, nil } // This function will encrypt the provided plaintext with the provided key using AES @@ -406,12 +377,12 @@ func (c *Client) StoreWithIdentityAndEncrypt(identity string, appID string, root return "", err } - return c.StoreWithIdentity(identity, appID, string(encryptedRootCid)) + return c.StoreEncryptedWithIdentity(identity, appID, string(encryptedRootCid)) } func (c *Client) GetByIdentityAndDecrypt(identity string, appID string, key []byte) (string, error) { // The node should be Bytes, so convert it - encryptedRootCid, err := c.GetByIdentity(identity, appID) + encryptedRootCid, err := c.GetEncryptedRootCID(identity, appID) if err != nil { return "", err } From 3bb9dfea07b83e6c5df700a2e23b232b5fd6dd5a Mon Sep 17 00:00:00 2001 From: ehsan shariati Date: Thu, 3 Aug 2023 22:24:27 -0400 Subject: [PATCH 5/6] return encryptedRootCID instead of link --- mobile/client.go | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/mobile/client.go b/mobile/client.go index 45d2ce8a..8a34ece7 100644 --- a/mobile/client.go +++ b/mobile/client.go @@ -294,9 +294,9 @@ func (c *Client) StoreEncryptedWithIdentity(identity string, appID string, encry } // Convert byte slice to base64 string - identityLinkStr := base64.StdEncoding.EncodeToString(identityLink) + _ = base64.StdEncoding.EncodeToString(identityLink) - return identityLinkStr, nil + return encryptedRootCID, nil } func (c *Client) GetEncryptedRootCID(identity string, appID string) ([]byte, error) { From 8f6e402407c085622e4c31c2a10d32ffe2e38c60 Mon Sep 17 00:00:00 2001 From: ehsan shariati Date: Fri, 4 Aug 2023 00:32:30 -0400 Subject: [PATCH 6/6] Draft of IPNSPublish We probabely need to change IPNS to DNSLink --- mobile/client.go | 25 ++++++++++++++++++++----- 1 file changed, 20 insertions(+), 5 deletions(-) diff --git a/mobile/client.go b/mobile/client.go index 8a34ece7..481ee6f4 100644 --- a/mobile/client.go +++ b/mobile/client.go @@ -6,7 +6,6 @@ import ( "crypto/aes" "crypto/cipher" "crypto/rand" - "encoding/base64" "encoding/json" "errors" "io" @@ -263,6 +262,16 @@ func (c *Client) Shutdown() error { } } +func (c *Client) IPNSPublish(identity string, identityLink []byte) string { + //TODO: Implement IPNSPublish + return "/ipns/QmRrFsi8WQH4MZEW3QjF74CJ4VzRTvYVGQgb2rP1eLJxAf" +} + +func (c *Client) IPNSResolve(identity string) ([]byte, error) { + //TODO: Implement IPNSResolve + return []byte("QmRrFsi8WQH4MZEW3QjF74CJ4VzRTvYVGQgb2rP1eLJxAf"), nil +} + // This stores the encrypted root Cid in an IPLD node and links rootCid to it. // We use the identity as the value of a new IPLD node and link rootCid to it. // Essentially, you're creating a new IPLD node with the content of identity and a link to rootCid. @@ -293,15 +302,21 @@ func (c *Client) StoreEncryptedWithIdentity(identity string, appID string, encry return "", err } - // Convert byte slice to base64 string - _ = base64.StdEncoding.EncodeToString(identityLink) + // Publish new identity link to IPNS + ipnsName := c.IPNSPublish(identity, identityLink) - return encryptedRootCID, nil + return ipnsName, nil } func (c *Client) GetEncryptedRootCID(identity string, appID string) ([]byte, error) { + // Retrieve the IPNS record + identityLink, err := c.IPNSResolve(identity) + if err != nil { + return nil, err + } + // Load identity link map - identityLinkMapBytes, err := c.Get([]byte(identity)) + identityLinkMapBytes, err := c.Get(identityLink) if err != nil { return nil, err }