Skip to content

Commit

Permalink
feat: add jwk secret and some deploy configs
Browse files Browse the repository at this point in the history
  • Loading branch information
saitofun committed May 8, 2024
1 parent 408ba14 commit 5e21259
Show file tree
Hide file tree
Showing 13 changed files with 274 additions and 120 deletions.
17 changes: 17 additions & 0 deletions .github/workflows/image.yml
Original file line number Diff line number Diff line change
Expand Up @@ -29,6 +29,23 @@ jobs:
ref: ${{ github.event.pull_request.head.sha }}
fetch-depth: 0

- name: Setup Go
uses: actions/setup-go@v3
with:
go-version: 1.22

- name: Build targets
run: make targets

- name: Run Unit tests
run: make test

- name: Upload code coverage
uses: codecov/[email protected]
with:
token: ${{secrets.CODECOV_TOKEN}}
file: ./cover.out

- name: Setup QEMU
uses: docker/setup-qemu-action@v2

Expand Down
17 changes: 17 additions & 0 deletions Makefile
Original file line number Diff line number Diff line change
@@ -1,4 +1,6 @@
## build all targets to ./build/
MOD=$(shell cat go.mod | grep ^module -m 1 | awk '{ print $$2; }' || '')

.PHONY: targets
targets:
@cd cmd && for target in * ; \
Expand Down Expand Up @@ -28,3 +30,18 @@ images:
fi; \
echo "\033[32mdone!\033[0m\n"; \
done

.PHONY: fmt
fmt:
@if [ -z $$MOD ]; then \
goimports -w . ; \
else \
goimports -w -local "${MOD}" . ; \
fi

.PHONY: test
test:
@CGO_LDFLAGS='-L./pkg/ioconnect/lib/linux-x86_64 -lioConnectCore' go test ./... -v -covermode=atomic -coverprofile cover.out



Binary file removed cmd/didctl/didctl
Binary file not shown.
9 changes: 8 additions & 1 deletion cmd/srv-did-vc/Dockerfile
Original file line number Diff line number Diff line change
@@ -1,10 +1,17 @@
FROM --platform=linux/amd64 golang:1.22 AS builder

WORKDIR /go/src
COPY ./ ./

COPY go.mod go.mod
COPY go.sum go.sum

RUN GO111MODULE=on go mod download

COPY . .

RUN cd ./cmd/srv-did-vc && make build


# runtime
FROM --platform=linux/amd64 scratch AS runtime

Expand Down
36 changes: 35 additions & 1 deletion cmd/srv-did-vc/main.go
Original file line number Diff line number Diff line change
@@ -1,12 +1,46 @@
package main

import (
"log"
"log/slog"
"os"

"github.com/pkg/errors"

"github.com/machinefi/ioconnect-go/pkg/ioconnect"
)

var config = &struct {
JWKSecrets ioconnect.JWKSecrets `env:"SRV_DID_VC__JWKSecrets"`
}{}

func init() {
}

func init() {
slog.SetLogLoggerLevel(slog.LevelDebug)
logger := slog.New(slog.NewJSONHandler(os.Stdout, &slog.HandlerOptions{Level: slog.LevelDebug}))
slog.SetDefault(logger)
}

func init() {
k := "SRV_DID_VC__JWKSecrets"
v := os.Getenv(k)

if v != "" {
log.Printf("secret from env: %s", v)
if err := config.JWKSecrets.UnmarshalText([]byte(v)); err != nil {
panic(errors.Errorf("invalid jwk secrets from evn: %s", v))
}
return
}

config.JWKSecrets = ioconnect.NewJWKSecrets()
log.Printf("volatile secret: %s", config.JWKSecrets.String())
}

func main() {
if err := RunServer(9999, nil); err != nil {
if err := RunServer(9999, config.JWKSecrets); err != nil {
slog.Error("http server down", "error", err)
os.Exit(-1)
}
Expand Down
19 changes: 11 additions & 8 deletions cmd/srv-did-vc/server.go
Original file line number Diff line number Diff line change
Expand Up @@ -7,11 +7,12 @@ import (
"sync"

"github.com/gin-gonic/gin"

"github.com/machinefi/ioconnect-go/cmd/srv-did-vc/apis"
"github.com/machinefi/ioconnect-go/pkg/ioconnect"
)

func RunServer(port int, doc []byte) error {
func RunServer(port int, secrets ioconnect.JWKSecrets) error {
// TODO parse doc from env
s := &Server{}

Expand All @@ -22,16 +23,18 @@ func RunServer(port int, doc []byte) error {

s.eng = eng

key, err := ioconnect.NewMasterJWK("io")
key, err := ioconnect.NewJWKBySecret(secrets)
if err != nil {
panic(err)
}
s.jwk = key

slog.Info("server did:io ", s.jwk.DID("io"))
slog.Info("server did:io#key ", s.jwk.KID("io"))
slog.Info("server ka did:io ", s.jwk.DID("io"))
slog.Info("server ka did:io#key", s.jwk.KID("io"))
slog.Debug("jwk generated",
"did:io", key.DID(),
"did:io#key", key.KID(),
"ka did:io", key.KeyAgreementDID(),
"ka did:io#key", key.KeyAgreementKID(),
"doc", key.Doc(),
)

return eng.Run(fmt.Sprintf(":%d", port))
}
Expand All @@ -50,7 +53,7 @@ func (s *Server) IssueToken(c *gin.Context) {
return
}

token, err := s.jwk.SignTokenBySubject(req.ClientID)
token, err := s.jwk.SignToken(req.ClientID)
if err != nil {
c.String(http.StatusInternalServerError, "failed to sign token: %v", err)
return
Expand Down
73 changes: 26 additions & 47 deletions pkg/ioconnect/jwk.go
Original file line number Diff line number Diff line change
Expand Up @@ -4,10 +4,8 @@ package ioconnect
//#include <ioconnect.h>
import "C"
import (
"encoding/binary"
"encoding/json"
"fmt"
"math/rand"
"unsafe"

"github.com/pkg/errors"
Expand All @@ -23,12 +21,8 @@ func NewJWKFromDoc(content []byte) (*JWK, error) {
return doc.ParseJWK()
}

func newJWKBySecret(secret [4]uint64, tpe JwkType, keyAlg JwkSupportKeyAlg, lifetime JwkLifetime, usage PsaKeyUsageType, alg PsaHashType) (*JWK, error) {
_secret := make([]byte, 32)
for i := 0; i < 4; i++ {
binary.LittleEndian.PutUint64(_secret, secret[i])
}
c_secret := (*C.uint8_t)(C.CBytes(_secret))
func newJWKBySecret(secret JWKSecret, tpe JwkType, keyAlg JwkSupportKeyAlg, lifetime JwkLifetime, usage PsaKeyUsageType, alg PsaHashType) (*JWK, error) {
c_secret := (*C.uint8_t)(C.CBytes(secret.Bytes()))
defer C.free(unsafe.Pointer(c_secret))

k := &JWK{}
Expand All @@ -53,9 +47,9 @@ func newJWKBySecret(secret [4]uint64, tpe JwkType, keyAlg JwkSupportKeyAlg, life
return k, nil
}

func NewJWKBySecret(secret [2][4]uint64) (*JWK, error) {
func NewJWKBySecret(secrets JWKSecrets) (*JWK, error) {
k, err := newJWKBySecret(
secret[0],
secrets[0],
JwkType_EC,
JwkSupportKeyAlg_P256,
JwkLifetime_Volatile,
Expand All @@ -67,7 +61,7 @@ func NewJWKBySecret(secret [2][4]uint64) (*JWK, error) {
}

ka, err := newJWKBySecret(
secret[1],
secrets[1],
JwkType_EC,
JwkSupportKeyAlg_P256,
JwkLifetime_Volatile,
Expand All @@ -78,7 +72,7 @@ func NewJWKBySecret(secret [2][4]uint64) (*JWK, error) {
return nil, errors.Wrap(err, "failed to generate key agreement key")
}
k.ka = ka
k.secret = secret
k.secrets = secrets

if err = k.register(); err != nil {
return nil, err
Expand All @@ -89,15 +83,9 @@ func NewJWKBySecret(secret [2][4]uint64) (*JWK, error) {
}

func NewJWK() (*JWK, error) {
secret := [2][4]uint64{}

for i := 0; i < 2; i++ {
for j := 0; j < 4; j++ {
secret[i][j] = rand.Uint64()
}
}
secrets := NewJWKSecrets()

return NewJWKBySecret(secret)
return NewJWKBySecret(secrets)
}

type JWK struct {
Expand All @@ -109,7 +97,7 @@ type JWK struct {
ka *JWK
doc *Doc

secret [2][4]uint64
secrets JWKSecrets
}

// register bind kid and JWK
Expand Down Expand Up @@ -227,10 +215,6 @@ func (k *JWK) DID() string { return k.did }

func (k *JWK) KID() string { return k.kid }

func (k *JWK) KeyAgreement() *JWK {
return k.ka
}

func (k *JWK) KeyAgreementDID() string {
return k.ka.DID()
}
Expand Down Expand Up @@ -285,19 +269,22 @@ func (k *JWK) SignTokenByVC(vc *VerifiableCredential) (string, error) {
if err != nil {
return "", err
}
c_data := C.CString(string(data))
defer C.free(unsafe.Pointer(c_data))

handle := C.iotex_jwt_claim_new()
if handle == nil {
return "", errors.Errorf("failed to call C.iotex_jwt_claim_new, nil returned")
}
defer C.iotex_jwt_claim_destroy(handle)

object := C.cJSON_Parse(C.CString(string(data)))
object := C.cJSON_Parse(c_data)
if object == nil {
return "", errors.Errorf("failed to call C.cJSON_Parse, nil returned")
}
// TODO this invoke triggers segment fault if c-language need release this memory?
// C.cJSON_Delete(object)
// TODO this C.free invoke triggers double free fault if c-language hold this memory?
// TODO ownership not transferred, go should release object
// defer C.cJSON_Delete(object)

C.iotex_jwt_claim_set_value(handle, C.JWT_CLAIM_TYPE_ISS, nil, unsafe.Pointer(c_issuer))

Expand Down Expand Up @@ -348,6 +335,7 @@ func (k *JWK) Encrypt(plain []byte, recipient string) ([]byte, error) {
alg := (C.enum_KWAlgorithms)(C.Ecdh1puA256kw)
enc := (C.enum_EncAlgorithm)(C.A256cbcHs512)
did := C.CString(k.DID()) // sender
defer C.free(unsafe.Pointer(did))

c_recipient := C.CString(recipient)
defer C.free(unsafe.Pointer(c_recipient))
Expand All @@ -364,22 +352,7 @@ func (k *JWK) Encrypt(plain []byte, recipient string) ([]byte, error) {
}

func (k *JWK) Decrypt(cipher []byte, sender *JWK) ([]byte, error) {
data := (*C.char)(C.CBytes(cipher))
defer C.free(unsafe.Pointer(data))

alg := (C.enum_KWAlgorithms)(C.Ecdh1puA256kw)
enc := (C.enum_EncAlgorithm)(C.A256cbcHs512)
did := C.CString(sender.DID()) // sender

recipient := C.CString(k.KeyAgreementKID())
defer C.free(unsafe.Pointer(recipient))

c := C.iotex_jwe_decrypt(data, alg, enc, did, sender._ptr, recipient)
if c == nil {
return nil, errors.Errorf("failed to decrypt data")
}
defer C.free(unsafe.Pointer(c))
return C.GoBytes(unsafe.Pointer(c), (C.int)(C.strlen(c))), nil
return k.DecryptBySenderDID(cipher, sender.DID())
}

func (k *JWK) DecryptBySenderDID(cipher []byte, sender string) ([]byte, error) {
Expand All @@ -389,6 +362,7 @@ func (k *JWK) DecryptBySenderDID(cipher []byte, sender string) ([]byte, error) {
alg := (C.enum_KWAlgorithms)(C.Ecdh1puA256kw)
enc := (C.enum_EncAlgorithm)(C.A256cbcHs512)
did := C.CString(sender) // sender
defer C.free(unsafe.Pointer(did))

recipient := C.CString(k.KeyAgreementKID())
defer C.free(unsafe.Pointer(recipient))
Expand All @@ -399,11 +373,12 @@ func (k *JWK) DecryptBySenderDID(cipher []byte, sender string) ([]byte, error) {
}
defer C.free(unsafe.Pointer(c))

return C.GoBytes(unsafe.Pointer(c), (C.int)(C.strlen(c))), nil
plain := C.GoString(c)
return []byte(plain), nil
}

func (k *JWK) Export() [2][4]uint64 {
return k.secret
func (k *JWK) Export() JWKSecrets {
return k.secrets
}

func (k *JWK) Destroy() {
Expand All @@ -414,5 +389,9 @@ func (k *JWK) Destroy() {
if k.ka._ptr != nil {
C.iotex_jwk_destroy(k.ka._ptr)
k.ka._ptr = nil

kid := C.CString(k.ka.KID())
defer C.free(unsafe.Pointer(kid))
C.iotex_registry_item_unregister(kid)
}
}
Loading

0 comments on commit 5e21259

Please sign in to comment.