Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Secret Keeper Application #745

Merged
merged 4 commits into from
Apr 22, 2024
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
7 changes: 7 additions & 0 deletions samples/chaincode/secret-keeper-go/.gitignore
Original file line number Diff line number Diff line change
@@ -0,0 +1,7 @@
ecc
ecc-bundle
enclave.json
private.pem
public.pem
mrenclave
details.env
9 changes: 9 additions & 0 deletions samples/chaincode/secret-keeper-go/Makefile
Original file line number Diff line number Diff line change
@@ -0,0 +1,9 @@
# Copyright 2019 Intel Corporation
# Copyright IBM Corp. All Rights Reserved.
#
# SPDX-License-Identifier: Apache-2.0

TOP = ../../..
include $(TOP)/ecc_go/build.mk

CC_NAME ?= fpc-secret-keeper-go
54 changes: 54 additions & 0 deletions samples/chaincode/secret-keeper-go/README.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,54 @@
# Secret Keeper

Secret Keeper is a demo application designed to securely store sensitive information, acting as a digital vault. It's ideal for users who need to manage access to shared secrets within a team or organization.

## Functions

Secret Keeper provides the following functionalities:

- **InitSecretKeeper**: Initializes the application with default authorization and secret values. Intended for one-time use at application setup. Note: While potential misuse is considered low-risk, it's recommended to secure access to this function.

- **RevealSecret**: Allows authorized users to view the currently stored secret.

- **LockSecret**: Enables authorized users to update the secret value. This action replaces the existing secret.

- **AddUser**: Permits authorized users to add a new user to the authorization list, granting them access to all functions.

- **RemoveUser**: Allows authorized users to remove an existing user from the authorization list, revoking their access.

## Example Usage

chenchanglew marked this conversation as resolved.
Show resolved Hide resolved
To demonstrate Secret Keeper's capabilities, you can deploy the chaincode to [the-simple-testing-network](https://github.com/hyperledger/fabric-private-chaincode/tree/main/samples/deployment/fabric-smart-client/the-simple-testing-network) and then invoke it with the [simple-cli-go](https://github.com/hyperledger/fabric-private-chaincode/tree/main/samples/application/simple-cli-go).

1. Initialize Secret Keeper:
```
./fpcclient invoke initSecretKeeper
```
2. Reveal the secret as Alice:
```
./fpcclient query revealSecret Alice
```
3. Change the secret as Bob:
```
./fpcclient invoke lockSecret Bob NewSecret
```
4. Attempt to reveal the secret as Alice (now updated):
```
./fpcclient query revealSecret Alice
```
5. Remove Bob's access as Alice:
```
./fpcclient invoke removeUser Alice Bob
```
6. Attempt to reveal the secret as Bob (should fail):
```
./fpcclient query revealSecret Bob // (will failed)
```
7. Re-add Bob to the authorization list as Alice:
```
./fpcclient invoke addUser Alice Bob
```
8. Bob can now reveal the secret successfully:
```
./fpcclient query revealSecret Bob // (will success)
```
205 changes: 205 additions & 0 deletions samples/chaincode/secret-keeper-go/chaincode/secret-keeper.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,205 @@
/*
Copyright IBM Corp. All Rights Reserved.

SPDX-License-Identifier: Apache-2.0
*/

package chaincode

import (
"encoding/json"
"fmt"

"github.com/hyperledger/fabric-contract-api-go/contractapi"
)

const OK = "OK"
const AUTH_LIST_KEY = "AUTH_LIST_KEY"
const SECRET_KEY = "SECRET_KEY"

type SecretKeeper struct {
contractapi.Contract
}

type AuthSet struct {
Pubkey map[string]struct{}
}

type Secret struct {
Value string `json:"Value"`
}

func (t *SecretKeeper) InitSecretKeeper(ctx contractapi.TransactionContextInterface) error {
// init authSet
pubkeyset := make(map[string]struct{})
pubkeyset["Alice"] = struct{}{}
pubkeyset["Bob"] = struct{}{}
authSet := AuthSet{
Pubkey: pubkeyset,
}

authSetJson, err := json.Marshal(authSet)
if err != nil {
return err
}

err = ctx.GetStub().PutState(AUTH_LIST_KEY, authSetJson)
if err != nil {
return fmt.Errorf("failed to put %s to world state. %v", AUTH_LIST_KEY, err)
}

// init secret
secret := Secret{
Value: "DefaultSecret",
}

secretJson, err := json.Marshal(secret)
if err != nil {
return err
}

err = ctx.GetStub().PutState(SECRET_KEY, secretJson)
if err != nil {
return fmt.Errorf("failed to put %s to world state. %v", SECRET_KEY, err)
}

return nil
}

func (t *SecretKeeper) AddUser(ctx contractapi.TransactionContextInterface, sig string, pubkey string) error {
// check if the user allow to update authSet
valid, err := VerifySig(ctx, sig)
if err != nil {
return err
}
if !valid {
return fmt.Errorf("user are not allowed to perform this action")
}

// update the value
authSet, _ := GetAuthList(ctx)
authSet.Pubkey[pubkey] = struct{}{}

authSetJson, err := json.Marshal(authSet)
if err != nil {
return err
}

err = ctx.GetStub().PutState(AUTH_LIST_KEY, authSetJson)
if err != nil {
return fmt.Errorf("failed to put %s to world state. %v", AUTH_LIST_KEY, err)
}

return nil
}

func (t *SecretKeeper) RemoveUser(ctx contractapi.TransactionContextInterface, sig string, pubkey string) error {
// check if the user allow to update authSet
valid, err := VerifySig(ctx, sig)
if err != nil {
return err
}
if !valid {
return fmt.Errorf("user are not allowed to perform this action")
}

// update the value
authSet, _ := GetAuthList(ctx)
delete(authSet.Pubkey, pubkey)

authSetJson, err := json.Marshal(authSet)
if err != nil {
return err
}

err = ctx.GetStub().PutState(AUTH_LIST_KEY, authSetJson)
if err != nil {
return fmt.Errorf("failed to put %s to world state. %v", AUTH_LIST_KEY, err)
}

return nil
}

func (t *SecretKeeper) LockSecret(ctx contractapi.TransactionContextInterface, sig string, value string) error {
// check if the user allow to update secret
valid, err := VerifySig(ctx, sig)
if err != nil {
return err
}
if !valid {
return fmt.Errorf("user are not allowed to perform this action")
}

// update the value
newSecret := Secret{
Value: value,
}

newSecretJson, err := json.Marshal(newSecret)
if err != nil {
return err
}

err = ctx.GetStub().PutState(SECRET_KEY, newSecretJson)
if err != nil {
return fmt.Errorf("failed to put %s to world state. %v", SECRET_KEY, err)
}

return nil
}

func (t *SecretKeeper) RevealSecret(ctx contractapi.TransactionContextInterface, sig string) (*Secret, error) {
// check if the user allow to view the secret.
valid, err := VerifySig(ctx, sig)
if err != nil {
return nil, err
}
if !valid {
return nil, fmt.Errorf("user are not allowed to perform this action")
}

// reveal secret
secretJson, err := ctx.GetStub().GetState(SECRET_KEY)
if err != nil {
return nil, fmt.Errorf("failed to read from world state: %v", err)
}
if secretJson == nil {
return nil, fmt.Errorf("the asset %s does not exist", SECRET_KEY)
}
var secret Secret
err = json.Unmarshal(secretJson, &secret)
if err != nil {
return nil, err
}
return &secret, nil
}

func GetAuthList(ctx contractapi.TransactionContextInterface) (*AuthSet, error) {
authSetJson, err := ctx.GetStub().GetState(AUTH_LIST_KEY)
if err != nil {
return nil, fmt.Errorf("failed to read from world state: %v", err)
}
if authSetJson == nil {
return nil, fmt.Errorf("the asset %s does not exist", AUTH_LIST_KEY)
}

var authSet AuthSet
err = json.Unmarshal(authSetJson, &authSet)
if err != nil {
return nil, err
}
return &authSet, nil
}

func VerifySig(ctx contractapi.TransactionContextInterface, sig string) (bool, error) {
authSet, err := GetAuthList(ctx)
if err != nil {
return false, err
}

if _, exist := authSet.Pubkey[sig]; exist {
return true, nil
}

return false, nil
}
Loading