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

Feature/signing plugin #402

Open
wants to merge 5 commits into
base: beckn-onix-v1.0-develop
Choose a base branch
from
Open
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
9 changes: 9 additions & 0 deletions configs/plugin.yaml
Original file line number Diff line number Diff line change
@@ -0,0 +1,9 @@
plugins:
signing_plugin:
id: "signing_plugin"
config:
algorithm: "ED25519"
verification_plugin:
id: "verification_plugin"
config:
algorithm: "ED25519"
10 changes: 10 additions & 0 deletions go.mod
Original file line number Diff line number Diff line change
@@ -0,0 +1,10 @@
module plugins

go 1.22.1

require (
golang.org/x/crypto v0.33.0
gopkg.in/yaml.v3 v3.0.1
)

require golang.org/x/sys v0.30.0 // indirect
8 changes: 8 additions & 0 deletions go.sum
Original file line number Diff line number Diff line change
@@ -0,0 +1,8 @@
golang.org/x/crypto v0.33.0 h1:IOBPskki6Lysi0lo9qQvbxiQ+FvsCC/YWOecCHAixus=
golang.org/x/crypto v0.33.0/go.mod h1:bVdXmD7IV/4GdElGPozy6U7lWdRXA4qyRVGJV57uQ5M=
golang.org/x/sys v0.30.0 h1:QjkSwP/36a20jFYWkSue1YwXzLmsV5Gfq7Eiy72C1uc=
golang.org/x/sys v0.30.0/go.mod h1:/VUhepiaJMQUp4+oa/7Zr1D23ma6VTLIYjOOTFZPUcA=
gopkg.in/check.v1 v0.0.0-20161208181325-20d25e280405 h1:yhCVgyC4o1eVCa2tZl7eS0r+SDo693bJlVdllGtEeKM=
gopkg.in/check.v1 v0.0.0-20161208181325-20d25e280405/go.mod h1:Co6ibVJAznAaIkqp8huTwlJQCZ016jof/cbN4VW5Yz0=
gopkg.in/yaml.v3 v3.0.1 h1:fxVm/GzAzEWqLHuvctI91KS9hhNmmWOoWu0XTYJS7CA=
gopkg.in/yaml.v3 v3.0.1/go.mod h1:K4uyk7z7BCEPqu6E+C64Yfv1cQ7kz7rIZviUmN+EgEM=
46 changes: 46 additions & 0 deletions main.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,46 @@
package main

import (
"context"
"fmt"
plugins "plugins/plugin"
)

func main() {
pluginManager, err := plugins.New("./plugin/implementations", "configs/plugin.yaml")
if err != nil {
fmt.Println("Error initializing plugin manager:", err)
return
}
defer pluginManager.Close()

signer, err := pluginManager.GetSigner()
if err != nil {
fmt.Println("Error loading signer plugin:", err)
return
}

signature, err := signer.Sign(context.Background(), []byte("test payload"), "key123")
if err != nil {
fmt.Println("Error signing data:", err)
return
}
fmt.Println("Generated Signature:", signature)

verifier, err := pluginManager.GetVerifier()
if err != nil {
fmt.Println("Error loading verifier plugin:", err)
return
}

// headerString := `Authorization: Signature keyId="sub123|ukid456|ed25519",algorithm="ed25519", created="1740477430", expires="1740481030", headers="(created) (expires) digest", signature="z6OARtt7BiPr7/4jIULblMAkLdwf9JSicyfM9toa2y4MDSnuhQzJg62EdN4Zl42dfISqzJo/XnFywCW55pBOAg=="`

headerString := "w2FBqUYJrJUmwBkLM/mjFk3/eT7Sal6xI1TjoUxJwtQQEc3mze8djnbZhQGUXCv1kPTYneM2F+07vogXt1VxBA=="

valid, err := verifier.Verify(context.Background(), []byte("test payload"), []byte(headerString))
if err != nil {
fmt.Println("Error verifying signature:", err)
return
}
fmt.Println("Signature Valid:", valid)
}
33 changes: 33 additions & 0 deletions plugin/definitions.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,33 @@
package plugins

import "context"

// Signer inteface defines the method for signing
type Signer interface {
Sign(ctx context.Context, body []byte, keyID string) (string, error)
}

type SignerProvider interface {
// initialize a new signer instance with given config
New(ctx context.Context, config map[string]string) (Signer, error)
}

// KeyManager is the interface for key management plugin.
type KeyManager interface {
PrivateKey(ctx context.Context, keyID string) (string, error)
}

// Validator inteface defines the method for signing
type Validator interface {
Verify(ctx context.Context, body []byte, sigtnature []byte) (bool, error)
}

type ValidatorProvider interface {
// initialize a new validator instance with given config
New(ctx context.Context, config map[string]string) (Validator, error)
}

// KeyManager is the interface for key management plugin.
type PublicKeyManager interface {
PublicKey(ctx context.Context, subscriberID string, keyID string) (string, error)
}
76 changes: 76 additions & 0 deletions plugin/implementations/signing_plugin/signing_plugin.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,76 @@
package main

import (
"context"
"crypto/ed25519"
"encoding/base64"
"fmt"
plugins "plugins/plugin"
"time"

"golang.org/x/crypto/blake2b"
)

type Signing struct{}

func NewSigning() *Signing {
return &Signing{}
}

func createSigningString(payload []byte, createdTimestamp, expiredTimestamp int64) string {
hasher, _ := blake2b.New512(nil)
hasher.Write(payload)
hashSum := hasher.Sum(nil)
digestB64 := base64.StdEncoding.EncodeToString(hashSum)

// commenting for now as we are not sending timestamp for verification
// return fmt.Sprintf("(created): %d\n(expires): %d\ndigest: BLAKE-512=%s",
// createdTimestamp, expiredTimestamp, digestB64)
return string(digestB64)
}

func SignerFunc(signingString []byte, privateKeyBase64 string) ([]byte, error) {
privateKeyBytes, err := base64.StdEncoding.DecodeString(privateKeyBase64)
if err != nil {
return nil, fmt.Errorf("error decoding private key: %w", err)
}
privateKey := ed25519.PrivateKey(privateKeyBytes)

signature := ed25519.Sign(privateKey, signingString)
return signature, nil
}

func SignPayload(payload []byte, privateKey string, createdTimestamp, expiredTimestamp int64) (string, error) {
signingString := createSigningString(payload, createdTimestamp, expiredTimestamp)
signature, err := SignerFunc([]byte(signingString), privateKey)
if err != nil {
return "", err
}
return base64.StdEncoding.EncodeToString(signature), nil
}

func (s *Signing) Sign(ctx context.Context, body []byte, keyID string) (string, error) {
privateKey := "XQTrvH1kZippFmizR25yvBD12+5bJCSfUkFw5nFHKnEO7wkaMFk+hHgGVafenaXXKEWBWoWFMJVX8EtRGXUhew=="
createdTimestamp := time.Now().Unix()
expiredTimestamp := createdTimestamp + (60 * 60)

signature, err := SignPayload(body, privateKey, createdTimestamp, expiredTimestamp)
if err != nil {
return "", err
}

fmt.Println("Created timestamp from signing : ", createdTimestamp)
fmt.Println("Expired timestamp from signing : ", expiredTimestamp)

return signature, nil
}

type SignerProvider struct{}

func (sp *SignerProvider) New(ctx context.Context, config map[string]string) (plugins.Signer, error) {
return NewSigning(), nil
}

func GetPlugin() plugins.SignerProvider {
return &SignerProvider{}
}
Binary file not shown.
134 changes: 134 additions & 0 deletions plugin/implementations/verification_plugin/verification_plugin.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,134 @@
package main

import (
"context"
"crypto/ed25519"
"encoding/base64"
"fmt"
plugins "plugins/plugin"
"strings"

"golang.org/x/crypto/blake2b"
)

type Verification struct{}

func NewVerification() *Verification {
return &Verification{}
}

func (v *Verification) Verify(ctx context.Context, body []byte, header []byte) (bool, error) {
// headerString := string(header)

// signatureHeader := extractHeaderValue("Authorization", headerString)
// if signatureHeader == "" {
// return false, fmt.Errorf("authorization header missing")
// }

// signatureParts := parseSignatureHeader(signatureHeader)

// timestamp, err := strconv.ParseInt(signatureParts["created"], 10, 64)
// if err != nil {
// return false, fmt.Errorf("invalid created timestamp: %w", err)
// }
// createdTime := time.Unix(timestamp, 0)

// expires, err := strconv.ParseInt(signatureParts["expires"], 10, 64)
// if err != nil {
// return false, fmt.Errorf("invalid expires timestamp: %w", err)
// }
// expiredTime := time.Unix(expires, 0)

// currentTime := time.Now().Unix()
// if timestamp > currentTime || currentTime > expires {
// return false, fmt.Errorf("signature is expired or not yet valid")
// }

// signingString := createSigningString(body, createdTime.Unix(), expiredTime.Unix())

signingString := createSigningString(body)

publicKey := "Du8JGjBZPoR4BlWn3p2l1yhFgVqFhTCVV/BLURl1IXs="

// isVerified := verifySignaturePK(signatureParts["signature"], signingString, publicKey)
isVerified := verifySignaturePK(string(header), signingString, publicKey)
return isVerified, nil
}

func verifySignaturePK(signatureBase64, signingString, publicKeyBase64 string) bool {
signature, err := base64.StdEncoding.DecodeString(signatureBase64)
if err != nil {
fmt.Println("Error decoding signature:", err)
return false
}

publicKeyBytes, err := base64.StdEncoding.DecodeString(publicKeyBase64)
if err != nil {
fmt.Println("Error decoding public key:", err)
return false
}

signingStringBytes := []byte(signingString)

publicKey := ed25519.PublicKey(publicKeyBytes)
return ed25519.Verify(publicKey, signingStringBytes, signature)
}

func parseSignatureHeader(header string) map[string]string {
header = strings.TrimPrefix(header, "Signature ")

parts := strings.Split(header, ",")
signatureMap := make(map[string]string)

for _, part := range parts {
keyValue := strings.SplitN(strings.TrimSpace(part), "=", 2)
if len(keyValue) == 2 {
key := strings.TrimSpace(keyValue[0])
value := strings.Trim(keyValue[1], "\"")
signatureMap[key] = value
}
}

fmt.Println("Parsed Signature Map:", signatureMap)
return signatureMap
}

func extractHeaderValue(key, headers string) string {
lines := strings.Split(headers, "\n")
for _, line := range lines {
if strings.HasPrefix(line, key+":") {
return strings.TrimSpace(strings.TrimPrefix(line, key+":"))
}
}
return ""
}

// Creates a signing string for verification.
// func createSigningString(payload []byte, createdTimestamp, expiredTimestamp int64) string {
// hasher, _ := blake2b.New512(nil)
// hasher.Write(payload)
// hashSum := hasher.Sum(nil)
// digestB64 := base64.StdEncoding.EncodeToString(hashSum)

// return fmt.Sprintf("(created): %d\n(expires): %d\ndigest: BLAKE-512=%s",
// createdTimestamp, expiredTimestamp, digestB64)
// }

func createSigningString(payload []byte) string {
hasher, _ := blake2b.New512(nil)
hasher.Write(payload)
hashSum := hasher.Sum(nil)
digestB64 := base64.StdEncoding.EncodeToString(hashSum)

return string(digestB64)
}

type VerifierProvider struct{}

func (vp *VerifierProvider) New(ctx context.Context, config map[string]string) (plugins.Validator, error) {
return NewVerification(), nil
}

func GetPlugin() plugins.ValidatorProvider {
return &VerifierProvider{}
}
Binary file not shown.
Loading