diff --git a/server/smc/smc.go b/server/smc/smc.go new file mode 100644 index 00000000..9db7d232 --- /dev/null +++ b/server/smc/smc.go @@ -0,0 +1,9 @@ +package smc + +import "go.dedis.ch/hbt/server/registration/registry" + +// SmcSecret contains the secret for the SMC +type Secret struct { + Data string `json:"secret"` + Id registry.RegistrationID +} diff --git a/server/smc/smccli/controller/reveal.go b/server/smc/smccli/controller/reveal.go deleted file mode 100644 index cdfb7162..00000000 --- a/server/smc/smccli/controller/reveal.go +++ /dev/null @@ -1,35 +0,0 @@ -package controller - -import ( - "go.dedis.ch/dela" - "go.dedis.ch/kyber/v3" - "golang.org/x/xerrors" -) - -// reveal decrypts a reencrypted message. -func reveal(XhatEnc kyber.Point, dkgPk kyber.Point, userPrivateKey kyber.Scalar, Cs []kyber.Point) ([]byte, error) { - dela.Logger.Info().Msgf("XhatEnc:%v", XhatEnc) - dela.Logger.Info().Msgf("dkgPk:%v", dkgPk) - dela.Logger.Info().Msgf("Cs:%v", Cs) - - xcInv := suite.Scalar().Neg(userPrivateKey) - XhatDec := suite.Point().Mul(xcInv, dkgPk) - Xhat := suite.Point().Add(XhatEnc, XhatDec) - XhatInv := suite.Point().Neg(Xhat) - - msg := make([]byte, 0, 128*len(Cs)) - - // Decrypt Cs to keyPointHat - for _, C := range Cs { - keyPointHat := suite.Point().Add(C, XhatInv) - keyPart, err := keyPointHat.Data() - if err != nil { - e := xerrors.Errorf("Error while decrypting Cs: %v", err) - dela.Logger.Error().Err(e).Msg("Failed revealing message") - return nil, e - } - msg = append(msg, keyPart...) - } - - return msg, nil -} diff --git a/server/test/admin/blockchain.go b/server/test/admin/blockchain.go index b595945b..c503f886 100644 --- a/server/test/admin/blockchain.go +++ b/server/test/admin/blockchain.go @@ -1,10 +1,14 @@ package admin import ( + "encoding/json" + "fmt" + "io" "net/http" "github.com/rs/zerolog/log" "go.dedis.ch/hbt/server/registration/registry" + "go.dedis.ch/hbt/server/smc" "go.dedis.ch/kyber/v3" "go.dedis.ch/kyber/v3/suites" ) @@ -29,8 +33,54 @@ func BlockchainGetDocIDs(adminPubkey kyber.Point) []registry.RegistrationID { defer resp.Body.Close() + // Reading the response body + body, err := io.ReadAll(resp.Body) + if err != nil { + fmt.Println("Error reading response body:", err) + return nil + } + + var items []registry.RegistrationID + // Checking if the request was successful (status code 200) + if resp.StatusCode == http.StatusOK { + // Parsing JSON data + if err := json.Unmarshal(body, &items); err != nil { + fmt.Println("Error parsing JSON:", err) + return nil + } + + // Printing the list of IDs + fmt.Println("List of IDs:") + for _, item := range items { + fmt.Println(item.ID) + } + } else { + fmt.Println("Failed to fetch items. Status code:", resp.StatusCode) + } + + return items +} + +// BlockchainGetDocument polls the blockchain to get the encrypted document +func BlockchainGetSecret(id registry.RegistrationID, pk kyber.Point) smc.Secret { + encodedPk, err := pk.MarshalBinary() + if err != nil { + log.Fatal().Msgf("error: %v", err) + } + + resp, err := http.Get(blockchainServer + "/secret?pubkey=" + string(encodedPk) + "&id=" + string(id.ID)) + if err != nil { + log.Fatal().Msgf("error: %v", err) + } + + defer resp.Body.Close() + // Decode the response - var data []string + var secret smc.Secret + err = json.NewDecoder(resp.Body).Decode(&secret) + if err != nil { + log.Error().Msgf("error decoding response: %v", err) + } - // TODO: Decode the response and return the list of doc IDs + return secret } diff --git a/server/test/admin/database.go b/server/test/admin/database.go index 22646d1e..4d8bc991 100644 --- a/server/test/admin/database.go +++ b/server/test/admin/database.go @@ -2,9 +2,11 @@ package admin import ( "bytes" + "context" "encoding/json" "net/http" + "github.com/rs/zerolog/log" "go.dedis.ch/hbt/server/registration/registry" ) diff --git a/server/test/admin/smc.go b/server/test/admin/smc.go index d78da5da..b5e6b8bd 100644 --- a/server/test/admin/smc.go +++ b/server/test/admin/smc.go @@ -1 +1,176 @@ package admin + +import ( + "bytes" + "encoding/hex" + "encoding/json" + "net/http" + "strings" + + "github.com/rs/zerolog/log" + "go.dedis.ch/dela" + "go.dedis.ch/kyber/v3" + "go.dedis.ch/kyber/v3/suites" + "golang.org/x/xerrors" +) + +const smcServer = "localhost:4000" + +// SmcReencryptSecret re-encrypts the secret with the new public key +// and returns a xhatenc value that can be used to reveal the secret +func SmcReencryptSecret(pk kyber.Point, secret string) (kyber.Point, error) { + resp, err := http.Post(smcServer+"/reencrypt", "application/json", + bytes.NewBuffer([]byte(`{"pubk": "`+encodePublickey(pk)+`", "encrypted": "`+secret+`"}`))) + if err != nil { + log.Error().Msgf("error: %v", err) + return nil, err + } + + defer resp.Body.Close() + + // Decode the response + var xhatenc string + err = json.NewDecoder(resp.Body).Decode(&xhatenc) + if err != nil { + log.Error().Msgf("error decoding response: %v", err) + return nil, err + } + + xhatencbuff, err := decodeReencrypted(xhatenc) + if err != nil { + log.Error().Msgf("error decoding response: %v", err) + return nil, err + } + + return xhatencbuff, nil +} + +func encodePublickey(pk kyber.Point) string { + pkbuff, err := pk.MarshalBinary() + if err != nil { + return "" + } + + return hex.EncodeToString(pkbuff) +} + +func encodeSecret(secret []byte) string { + return hex.EncodeToString(secret) +} + +func decodeReencrypted(xhatencstring string) (kyber.Point, error) { + xhatencbuff, err := hex.DecodeString(xhatencstring) + if err != nil { + return nil, err + } + + var xhatenc kyber.Point + err = xhatenc.UnmarshalBinary(xhatencbuff) + if err != nil { + return nil, err + } + + return xhatenc, nil +} + +func SmcGetKey() kyber.Point { + resp, err := http.Get(smcServer + "/key") + if err != nil { + log.Fatal().Msgf("error: %v", err) + } + + defer resp.Body.Close() + + // Decode the response + var data kyber.Point + + err = json.NewDecoder(resp.Body).Decode(&data) + if err != nil { + log.Error().Msgf("error decoding response: %v", err) + } + + return data +} + +// SmcReveal decrypts a reencrypted message. +func SmcReveal( + XhatEnc kyber.Point, + dkgPk kyber.Point, + userPrivateKey kyber.Scalar, + secret string, +) ([]byte, error) { + _, Cs, err := decodeEncrypted(secret) + if err != nil { + log.Fatal().Msgf("error: %v", err) + } + + log.Info().Msgf("XhatEnc:%v", XhatEnc) + log.Info().Msgf("dkgPk:%v", dkgPk) + log.Info().Msgf("Cs:%v", Cs) + + suite := suites.MustFind("Ed25519") + + xcInv := suite.Scalar().Neg(userPrivateKey) + XhatDec := suite.Point().Mul(xcInv, dkgPk) + Xhat := suite.Point().Add(XhatEnc, XhatDec) + XhatInv := suite.Point().Neg(Xhat) + + msg := make([]byte, 0, 128*len(Cs)) + + // Decrypt Cs to keyPointHat + for _, C := range Cs { + keyPointHat := suite.Point().Add(C, XhatInv) + keyPart, err := keyPointHat.Data() + if err != nil { + e := xerrors.Errorf("Error while decrypting Cs: %v", err) + dela.Logger.Error().Err(e).Msg("Failed revealing message") + return nil, e + } + msg = append(msg, keyPart...) + } + + return msg, nil +} + +// straight from pedersen/controller/action +func decodeEncrypted(str string) (kyber.Point, []kyber.Point, error) { + const separator = ":" + parts := strings.Split(str, separator) + if len(parts) < 2 { + return nil, nil, xerrors.Errorf("malformed encoded: %s", str) + } + + // Decode K + kbuff, err := hex.DecodeString(parts[0]) + if err != nil { + return nil, nil, xerrors.Errorf("failed to decode k point: %v", err) + } + + k := suite.Point() + + err = k.UnmarshalBinary(kbuff) + if err != nil { + return nil, nil, xerrors.Errorf("failed to unmarshal k point: %v", err) + } + + // Decode Cs + cs := make([]kyber.Point, 0, len(parts)-1) + + for _, p := range parts[1:] { + cbuff, err := hex.DecodeString(p) + if err != nil { + return nil, nil, xerrors.Errorf("failed to decode c point: %v", err) + } + + c := suite.Point() + + err = c.UnmarshalBinary(cbuff) + if err != nil { + return nil, nil, xerrors.Errorf("failed to unmarshal c point: %v", err) + } + + cs = append(cs, c) + } + + return k, cs, nil +} diff --git a/server/test/test.go b/server/test/test.go index 9a61bcf6..3a89e9ff 100644 --- a/server/test/test.go +++ b/server/test/test.go @@ -25,14 +25,15 @@ func main() { // add the document to the registry docid := user.RegistrationAdd(doc, symKey) - log.Info().Msgf("SUCCESS! added document id: %v", docid) + log.Info().Msgf("SUCCESS! added document ID=%v", docid) // get the SMC pub key smcKey := user.SmcGetKey() - log.Info().Msgf("SUCCESS! added document id: %v", docid) + log.Info().Msgf("SUCCESS! got SMC key: %v", smcKey) // add secret = symKey to the blockchain - user.BlockchainEncryptAndAddSecret(smcKey, symKey, docid) + secret := user.BlockchainEncryptAndAddSecret(smcKey, symKey, docid) + log.Info().Msgf("SUCCESS! added secret=%v with ID=%v to blockchain", secret, docid) // PRETEND TO BE AN ADMIN // --------------------------------------------------------- @@ -40,15 +41,32 @@ func main() { pk, sk := key.NewAsymmetric() // fetch the list of docs from the blockchain + // give it the admin pub key for audit purpose docIDs := admin.BlockchainGetDocIDs(pk) for _, id := range docIDs { - doc := admin.BlockchainGetDocument(id) - log.Info().Msgf("document: %v", doc) + secret := admin.BlockchainGetSecret(id, pk) + log.Info().Msgf("secret: %v", secret) - reencrypted := admin.SmcReencryptSecret(pk, id) + xhatenc, err := admin.SmcReencryptSecret(pk, secret.Data) + if err != nil { + log.Fatal().Msgf("error: %v", err) + } - encryptedDoc = admin.registrationGetDocument(id) + smcKeyAdmin := admin.SmcGetKey() + if smcKey != smcKeyAdmin { + log.Fatal().Msg("SMC key mismatch") + } + + // secret.Data = K:Cs in a string format + symKey2, err := admin.SmcReveal(xhatenc, smcKey, sk, secret.Data) + + if false == compare2ByteArrays(symKey, symKey2) { + log.Fatal().Msg("symmetric key mismatch") + } + + // TO DO: get the encrypted document from the registry + // TO DO: decrypt the document } } @@ -71,3 +89,17 @@ func createDocument(name, passport string, role uint64, picture string) registry Registered: false, } } + +func compare2ByteArrays(a []byte, b []byte) bool { + if len(a) != len(b) { + return false + } + + for i := range a { + if a[i] != b[i] { + return false + } + } + + return true +} diff --git a/server/test/user/blockchain.go b/server/test/user/blockchain.go index 043f87f9..710fc65c 100644 --- a/server/test/user/blockchain.go +++ b/server/test/user/blockchain.go @@ -1,13 +1,16 @@ package user import ( + "encoding/hex" "net/http" "net/url" "github.com/rs/zerolog/log" + "go.dedis.ch/dela" "go.dedis.ch/hbt/server/registration/registry" "go.dedis.ch/kyber/v3" "go.dedis.ch/kyber/v3/suites" + "golang.org/x/xerrors" ) const blockchainServer = "localhost:4000" @@ -15,14 +18,21 @@ const blockchainServer = "localhost:4000" // suite is the Kyber suite for Pedersen. var suite = suites.MustFind("Ed25519") -func BlockchainEncryptAndAddSecret(key kyber.Point, secret []byte, id registry.RegistrationID) { +func BlockchainEncryptAndAddSecret( + key kyber.Point, + secret []byte, + id registry.RegistrationID, +) string { // Encrypt the secret - encryptedSecret := suite.Point().Mul(suite.Scalar().SetBytes(secret), key) + encryptedSecret, err := encryptToKCs(key, secret) + if err != nil { + log.Fatal().Msgf("error: %v", err) + } // Add the secret to the blockchain resp, err := http.PostForm(blockchainServer+"/secret", url.Values{ - "secret": {encryptedSecret.String()}, + "secret": {encryptedSecret}, "id": {string(id.ID)}, }) @@ -31,4 +41,60 @@ func BlockchainEncryptAndAddSecret(key kyber.Point, secret []byte, id registry.R } defer resp.Body.Close() + + return encryptedSecret +} + +func encryptToKCs(key kyber.Point, msg []byte) (string, error) { + // ElGamal-encrypt the point to produce ciphertext (K,C). + r := suite.Scalar().Pick(suite.RandomStream()) + + K := suite.Point().Mul(r, nil) + dela.Logger.Debug().Msgf("K: %v", K.String()) + + C := suite.Point().Mul(r, key) + dela.Logger.Debug().Msgf("C: %v", C) + + // S: ephemeral DH shared secret + S := suite.Point().Mul(r, key) + dela.Logger.Debug().Msgf("S: %v", S.String()) + + Cs := make([]kyber.Point, 0, 16) + for len(msg) > 0 { + kp := suite.Point().Embed(msg, suite.RandomStream()) + dela.Logger.Debug().Msgf("kp: %v", kp.String()) + + // message blinded with secret + c := suite.Point().Add(C, kp) + dela.Logger.Debug().Msgf("c: %v", c) + + Cs = append(Cs, c) + dela.Logger.Debug().Msgf("Cs: %v", Cs) + + msg = msg[min(len(msg), kp.EmbedLen()):] + } + + return encodeEncrypted(K, Cs) +} + +// from pedersen/controller/action +const separator = ":" + +func encodeEncrypted(k kyber.Point, cs []kyber.Point) (string, error) { + kbuff, err := k.MarshalBinary() + if err != nil { + return "", xerrors.Errorf("failed to marshal k: %v", err) + } + + encoded := hex.EncodeToString(kbuff) + + for _, c := range cs { + cbuff, err := c.MarshalBinary() + if err != nil { + return "", xerrors.Errorf("failed to marshal c: %v", err) + } + encoded += separator + hex.EncodeToString(cbuff) + } + + return encoded, nil } diff --git a/server/test/user/smc.go b/server/test/user/smc.go index 0d2a5bbd..5e5c23c1 100644 --- a/server/test/user/smc.go +++ b/server/test/user/smc.go @@ -8,8 +8,10 @@ import ( "go.dedis.ch/kyber/v3" ) +const smcServer = "localhost:3000" + func SmcGetKey() kyber.Point { - resp, err := http.Get("localhost:3000/key") + resp, err := http.Get(smcServer + "/key") if err != nil { log.Fatal().Msgf("error: %v", err) }