<|start_filename|>internal/restful/controllers/health.go<|end_filename|>
/*
 * Copyright (c) 2021. The Alkaid Authors. All rights reserved.
 * Use of this source code is governed by a MIT-style
 * license that can be found in the LICENSE file.
 *
 * Alkaid is a BaaS service based on Hyperledger Fabric.
 */

package controllers

import (
	"net/http"

	"github.com/gin-gonic/gin"
	"github.com/yakumioto/alkaid/internal/restful"
	"github.com/yakumioto/alkaid/internal/versions"
)

type Health struct {
}

func (c *Health) Name() string {
	return "check_health"
}

func (c *Health) Path() string {
	return "/health"
}

func (c *Health) Method() string {
	return http.MethodGet
}

func (c *Health) HandlerFuncChain() []gin.HandlerFunc {
	v0Handler := func(ctx *restful.Context) {
		ctx.Render(gin.H{
			"status":  "ok",
			"version": versions.V0,
		})
	}

	v1handler := func(ctx *restful.Context) {
		ctx.Render(gin.H{
			"status":  "ok",
			"version": versions.V1,
		})
	}

	return restful.GenHandlerFuncChain(v1handler, v0Handler)
}


<|start_filename|>internal/common/crypto/rsa/key.go<|end_filename|>
/*
 * Copyright (c) 2022. The Alkaid Authors. All rights reserved.
 * Use of this source code is governed by a MIT-style
 * license that can be found in the LICENSE file.
 *
 * Alkaid is a BaaS service based on Hyperledger Fabric.
 */

package rsa

import (
	"crypto/rand"
	"crypto/rsa"
	"crypto/sha256"
	"crypto/x509"
	"encoding/pem"
	"errors"

	"github.com/yakumioto/alkaid/internal/common/crypto"
)

type PrivateKey struct {
	privateKey *rsa.PrivateKey
}

func (r *PrivateKey) Bytes() ([]byte, error) {
	pkcs1Encoded := x509.MarshalPKCS1PrivateKey(r.privateKey)
	return pem.EncodeToMemory(&pem.Block{Type: "PRIVATE KEY", Bytes: pkcs1Encoded}), nil
}

func (r *PrivateKey) SKI() []byte {
	pubKey, _ := r.PublicKey()
	return pubKey.SKI()
}

func (r *PrivateKey) Symmetric() bool {
	return false
}

func (r *PrivateKey) Private() bool {
	return true
}

func (r *PrivateKey) PublicKey() (crypto.Key, error) {
	return &PublicKey{publicKey: &r.privateKey.PublicKey}, nil
}

func (r *PrivateKey) Sign(digest []byte) ([]byte, error) {
	return rsa.SignPSS(rand.Reader, r.privateKey, crypto.SHA256, digest, &rsa.PSSOptions{
		SaltLength: rsa.PSSSaltLengthAuto,
	})
}

func (r *PrivateKey) Verify(_, _ []byte) bool {
	return false
}

func (r *PrivateKey) Encrypt(_ []byte) ([]byte, error) {
	return nil, errors.New("not supported")
}

func (r *PrivateKey) Decrypt(src []byte) ([]byte, error) {
	return rsa.DecryptOAEP(sha256.New(), rand.Reader, r.privateKey, src, nil)
}

type PublicKey struct {
	publicKey *rsa.PublicKey
}

func (r *PublicKey) Bytes() ([]byte, error) {
	pkcs1Encoded := x509.MarshalPKCS1PublicKey(r.publicKey)
	return pem.EncodeToMemory(&pem.Block{Type: "PUBLIC KEY", Bytes: pkcs1Encoded}), nil
}

func (r *PublicKey) SKI() []byte {
	if r.publicKey == nil {
		return nil
	}

	// Marshal the public key and hash it
	raw := x509.MarshalPKCS1PublicKey(r.publicKey)
	hash := sha256.Sum256(raw)
	return hash[:]
}

func (r *PublicKey) Symmetric() bool {
	return false
}

func (r *PublicKey) Private() bool {
	return false
}

func (r *PublicKey) PublicKey() (crypto.Key, error) {
	return r, nil
}

func (r *PublicKey) Sign(_ []byte) ([]byte, error) {
	return nil, errors.New("not supported")
}

func (r *PublicKey) Verify(hash, sig []byte) bool {
	if err := rsa.VerifyPSS(r.publicKey, crypto.SHA256, hash, sig, &rsa.PSSOptions{
		SaltLength: rsa.PSSSaltLengthAuto,
	}); err != nil {
		return false
	}

	return true
}

func (r *PublicKey) Encrypt(src []byte) ([]byte, error) {
	return rsa.EncryptOAEP(sha256.New(), rand.Reader, r.publicKey, src, nil)
}

func (r *PublicKey) Decrypt(_ []byte) ([]byte, error) {
	return nil, errors.New("not supported")
}


<|start_filename|>internal/common/crypto/opts.go<|end_filename|>
/*
 * Copyright (c) 2021. The Alkaid Authors. All rights reserved.
 * Use of this source code is governed by a MIT-style
 * license that can be found in the LICENSE file.
 *
 * Alkaid is a BaaS service based on Hyperledger Fabric.
 */

package crypto

const (
	EcdsaP256 Algorithm = "ECDSA_P256"
	EcdsaP384 Algorithm = "ECDSA_P384"

	Rsa1024 Algorithm = "RSA_1024"
	Rsa2048 Algorithm = "RSA_2048"
	Rsa4096 Algorithm = "RSA_4096"

	AesCbc128 Algorithm = "AES_CBC_128"
	AesCbc192 Algorithm = "AES_CBC_192"
	AesCbc256 Algorithm = "AES_CBC_256"

	HmacSha256 Algorithm = "HMAC_SHA256"
	HmacSha512 Algorithm = "HMAC_SHA512"
)

type Algorithm string

type ECDSAP256KeyGenOpts struct{}

func (opts *ECDSAP256KeyGenOpts) Algorithm() Algorithm {
	return EcdsaP256
}

type ECDSAP384KeyGenOpts struct{}

func (opts *ECDSAP384KeyGenOpts) Algorithm() Algorithm {
	return EcdsaP384
}

type AES128KeyImportOpts struct{}

func (opts *AES128KeyImportOpts) Algorithm() Algorithm {
	return AesCbc128
}

type AES192KeyImportOpts struct{}

func (opts *AES192KeyImportOpts) Algorithm() Algorithm {
	return AesCbc192
}

type AES256KeyImportOpts struct{}

func (opts *AES256KeyImportOpts) Algorithm() Algorithm {
	return AesCbc256
}

type HMACSha256ImportOpts struct{}

func (opts *HMACSha256ImportOpts) Algorithm() Algorithm {
	return HmacSha256
}

type HMACSha512ImportOpts struct{}

func (opts *HMACSha512ImportOpts) Algorithm() Algorithm {
	return HmacSha512
}

type RSA1024KeyImportOpts struct{}

func (opts *RSA1024KeyImportOpts) Algorithm() Algorithm {
	return Rsa1024
}

type RSA2048KeyImportOpts struct{}

func (opts *RSA2048KeyImportOpts) Algorithm() Algorithm {
	return Rsa2048
}

type RSA4096KeyImportOpts struct{}

func (opts *RSA4096KeyImportOpts) Algorithm() Algorithm {
	return Rsa4096
}


<|start_filename|>internal/common/jwt/jwt.go<|end_filename|>
/*
 * Copyright (c) 2021. The Alkaid Authors. All rights reserved.
 * Use of this source code is governed by a MIT-style
 * license that can be found in the LICENSE file.
 *
 * Alkaid is a BaaS service based on Hyperledger Fabric.
 */

package jwt

import (
	"sync"
	"time"

	"github.com/golang-jwt/jwt/v4"
	"github.com/yakumioto/alkaid/internal/common/log"
	"github.com/yakumioto/alkaid/internal/services/users"
)

var (
	logger = log.GetPackageLogger("common.jwt")
	once   sync.Once
	t      *JWT
)

func Initialize(secret string, expires time.Duration) {
	logger.Infof("jwt token expires is %v", expires)
	once.Do(func() {
		if t == nil {
			t = NewJWT(secret, expires)
		}
	})
}

func NewTokenWithUserContext(ctx *users.UserContext, now int64) (string, error) {
	return t.NewTokenWithUserContext(ctx, now)
}

func VerifyTokenWithUser(tokenString string) (*users.UserContext, error) {
	return t.VerifyTokenWithUser(tokenString)
}

type JWT struct {
	secret  []byte
	expires time.Duration
}

func NewJWT(secret string, expires time.Duration) *JWT {
	return &JWT{
		secret:  []byte(secret),
		expires: expires,
	}
}

func (t *JWT) NewTokenWithUserContext(userCtx *users.UserContext, now int64) (string, error) {
	userCtx.SetExpiresAt(now + int64(t.expires.Seconds()))

	token := jwt.NewWithClaims(jwt.SigningMethodHS256, userCtx)
	return token.SignedString(t.secret)
}

func (t *JWT) VerifyTokenWithUser(tokenString string) (*users.UserContext, error) {
	token, err := jwt.ParseWithClaims(tokenString, &users.UserContext{},
		func(token *jwt.Token) (interface{}, error) {
			return t.secret, nil
		})

	return token.Claims.(*users.UserContext), err
}


<|start_filename|>internal/restful/middlewares/recovery.go<|end_filename|>
/*
 * Copyright (c) 2021. The Alkaid Authors. All rights reserved.
 * Use of this source code is governed by a MIT-style
 * license that can be found in the LICENSE file.
 *
 * Alkaid is a BaaS service based on Hyperledger Fabric.
 */

package middlewares

import (
	"github.com/gin-gonic/gin"
)

type Recovery struct{}

func (r *Recovery) Name() string {
	return "Recovery"
}

func (r *Recovery) Sequence() int {
	return 2
}

func (r *Recovery) HandlerFunc() gin.HandlerFunc {
	return gin.Recovery()
}


<|start_filename|>internal/common/log/logrus.go<|end_filename|>
/*
 * Copyright (c) 2021. The Alkaid Authors. All rights reserved.
 * Use of this source code is governed by a MIT-style
 * license that can be found in the LICENSE file.
 *
 * Alkaid is a BaaS service based on Hyperledger Fabric.
 */

package log

import (
	"sync"

	"github.com/sirupsen/logrus"
)

var (
	once sync.Once
)

type StdLogger interface {
	Debugf(format string, args ...interface{})
	Infof(format string, args ...interface{})
	Warnf(format string, args ...interface{})
	Errorf(format string, args ...interface{})
	Fatalf(format string, args ...interface{})
	Panicf(format string, args ...interface{})
	Tracef(format string, args ...interface{})

	Debugln(args ...interface{})
	Infoln(args ...interface{})
	Warnln(args ...interface{})
	Errorln(args ...interface{})
	Fatalln(args ...interface{})
	Panicln(args ...interface{})
	Traceln(args ...interface{})
}

func Initialize(level string) {
	once.Do(func() {
		lvl, err := logrus.ParseLevel(level)
		if err != nil {
			lvl = logrus.DebugLevel
		}

		logrus.Infof("log level is: %v", level)
		logrus.SetLevel(lvl)
	})
}

func GetPackageLogger(name string) StdLogger {
	return logrus.WithField("package", name)
}

func Debugf(format string, args ...interface{}) {
	logrus.Debugf(format, args...)
}

func Infof(format string, args ...interface{}) {
	logrus.Infof(format, args...)
}

func Warnf(format string, args ...interface{}) {
	logrus.Warnf(format, args...)
}

func Errorf(format string, args ...interface{}) {
	logrus.Errorf(format, args...)
}

func Fatalf(format string, args ...interface{}) {
	logrus.Fatalf(format, args...)
}

func Panicf(format string, args ...interface{}) {
	logrus.Panicf(format, args...)
}

func Debugln(args ...interface{}) {
	logrus.Debugln(args...)
}

func Infoln(args ...interface{}) {
	logrus.Infoln(args...)
}

func Warnln(args ...interface{}) {
	logrus.Warnln(args...)
}

func Errorln(args ...interface{}) {
	logrus.Errorln(args...)
}

func Fatalln(args ...interface{}) {
	logrus.Fatalln(args...)
}

func Panicln(args ...interface{}) {
	logrus.Panicln(args...)
}


<|start_filename|>internal/common/crypto/utils/utils_test.go<|end_filename|>
/*
 * Copyright (c) 2022. The Alkaid Authors. All rights reserved.
 * Use of this source code is governed by a MIT-style
 * license that can be found in the LICENSE file.
 *
 * Alkaid is a BaaS service based on Hyperledger Fabric.
 */

package utils

import (
	"testing"

	"github.com/yakumioto/alkaid/internal/common/crypto/rsa"

	"github.com/yakumioto/alkaid/internal/common/crypto"
	"github.com/yakumioto/alkaid/internal/common/crypto/aes"
	"github.com/yakumioto/alkaid/internal/common/crypto/hmac"
)

func TestEncrypt(t *testing.T) {
	rPrivKey, _ := rsa.KeyGen(&crypto.RSA2048KeyImportOpts{})
	rPubKey, _ := rPrivKey.PublicKey()
	aKey, _ := aes.NewKey("<PASSWORD> password", &crypto.AES256KeyImportOpts{})
	hKey, _ := hmac.NewKey("<PASSWORD> password", &crypto.HMACSha256ImportOpts{})

	ciphertext, _ := Encrypt(AesCbc256HmacSha256B64, []byte("hello word"), aKey, hKey)
	t.Log(ciphertext)
	data, _ := Decrypt(ciphertext, aKey, hKey)
	t.Log(string(data))

	ciphertext, err := Encrypt(Rsa2048OaepSha256HmacShaB64, []byte("hello word"), rPubKey, hKey)
	if err != nil {
		t.Log(err)
	}
	t.Log(ciphertext)
	data, err = Decrypt(ciphertext, rPrivKey, hKey)
	if err != nil {
		t.Log(err)
	}
	t.Log(string(data))
}


<|start_filename|>internal/common/crypto/aes/key.go<|end_filename|>
/*
 * Copyright (c) 2021. The Alkaid Authors. All rights reserved.
 * Use of this source code is governed by a MIT-style
 * license that can be found in the LICENSE file.
 *
 * Alkaid is a BaaS service based on Hyperledger Fabric.
 */

package aes

import (
	"bytes"
	"crypto/aes"
	"crypto/cipher"
	"crypto/rand"
	"crypto/sha256"
	"errors"
	"fmt"

	"github.com/yakumioto/alkaid/internal/common/crypto"
)

type randomIVFunc func(len int) ([]byte, error)

var (
	randomIV randomIVFunc = func(len int) ([]byte, error) {
		iv := make([]byte, len)
		if _, err := rand.Read(iv); err != nil {
			return nil, err
		}

		return iv, nil
	}
)

type CBCKey struct {
	key []byte
}

func (a *CBCKey) Bytes() ([]byte, error) {
	return nil, errors.New("not supported")
}

func (a *CBCKey) SKI() []byte {
	hash := sha256.New()
	// hash.Write([]byte{0x01})
	hash.Write(a.key)
	return hash.Sum(nil)
}

func (a *CBCKey) Symmetric() bool {
	return true
}

func (a *CBCKey) Private() bool {
	return true
}

func (a *CBCKey) PublicKey() (crypto.Key, error) {
	return nil, errors.New("cannot call this method on a symmetric key")
}

func (a *CBCKey) Sign(_ []byte) ([]byte, error) {
	return nil, errors.New("cannot call this method on a symmetric key")
}

func (a *CBCKey) Verify(_, _ []byte) bool {
	return false
}

func (a *CBCKey) Encrypt(text []byte) ([]byte, error) {
	paddedText := pkcs7Padding(text)

	iv, err := randomIV(aes.BlockSize)
	if err != nil {
		return nil, fmt.Errorf("random iv error: %v", err)
	}

	block, err := aes.NewCipher(a.key)
	if err != nil {
		return nil, fmt.Errorf("new chipher error: %v", err)
	}

	mode := cipher.NewCBCEncrypter(block, iv)
	dst := make([]byte, len(paddedText))
	mode.CryptBlocks(dst, paddedText)

	ciphertext := bytes.NewBuffer(nil)
	ciphertext.Write(iv)
	ciphertext.Write(dst)

	return ciphertext.Bytes(), nil
}

func (a *CBCKey) Decrypt(ciphertext []byte) ([]byte, error) {
	iv := ciphertext[0:16]
	src := ciphertext[16:]

	block, err := aes.NewCipher(a.key)
	if err != nil {
		return nil, fmt.Errorf("new chipher error: %v", err)
	}

	mode := cipher.NewCBCDecrypter(block, iv)
	paddedText := make([]byte, len(src))
	mode.CryptBlocks(paddedText, src)

	return pkcs7UnPadding(paddedText), nil
}

func pkcs7Padding(src []byte) []byte {
	padding := aes.BlockSize - len(src)%aes.BlockSize

	var paddingText []byte
	if padding == 0 {
		paddingText = bytes.Repeat([]byte{byte(aes.BlockSize)}, aes.BlockSize)
	} else {
		paddingText = bytes.Repeat([]byte{byte(padding)}, padding)
	}
	return append(src, paddingText...)
}

func pkcs7UnPadding(src []byte) []byte {
	unPadding := int(src[len(src)-1])
	return src[:(len(src) - unPadding)]
}


<|start_filename|>internal/services/systems/handler.go<|end_filename|>
/*
 * Copyright (c) 2022. The Alkaid Authors. All rights reserved.
 * Use of this source code is governed by a MIT-style
 * license that can be found in the LICENSE file.
 *
 * Alkaid is a BaaS service based on Hyperledger Fabric.
 */

package systems

import (
	"net/http"

	"github.com/yakumioto/alkaid/internal/common/log"
	"github.com/yakumioto/alkaid/internal/common/storage"
	"github.com/yakumioto/alkaid/internal/errors"
	"github.com/yakumioto/alkaid/internal/services/users"
)

var (
	logger = log.GetPackageLogger("services.system")
)

type InitRequest struct {
	ID       string `json:"id,omitempty" validate:"required"`
	Name     string `json:"name" validate:"required"`
	Email    string `json:"email" validate:"required,email"`
	Password string `json:"password" validate:"required"`
}

func SystemInit(req *InitRequest) (*System, error) {
	initialized := newSystemByID(KSystemInitialized)
	if err := initialized.findByID(); err != nil {
		if err != storage.ErrNotFound {
			logger.Errorf("query system [%v] error: %v", KSystemInitialized, err)
			return nil, errors.NewError(http.StatusInternalServerError, errors.ErrServerUnknownError,
				"server unknown error")
		}
	}

	if initialized.Value == VSystemInitialized {
		logger.Warnln("system is initialized")
		return nil, errors.NewError(http.StatusForbidden, errors.ErrForbidden,
			"system is initialized")
	}

	// 处理未初始化情况
	user, err := users.Create(&users.CreateRequest{
		ID:       req.ID,
		Name:     req.Name,
		Email:    req.Email,
		Root:     true,
		Password: req.Password,
	})
	if err != nil {
		logger.Errorf("[%v] initialize root user error: %v", user.UserID, err)
		return nil, errors.NewError(http.StatusInternalServerError, errors.ErrServerUnknownError,
			"failed to initialize root user")
	}

	sys := newSystem(KSystemInitialized, VSystemInitialized)
	if err := sys.create(); err != nil {
		logger.Errorf("[%v] create system error: %v", sys.Key, err)
		return nil, errors.NewError(http.StatusInternalServerError, errors.ErrServerUnknownError,
			"failed to create user")
	}
	return sys, nil
}


<|start_filename|>internal/common/crypto/aes/keyimport_test.go<|end_filename|>
/*
 * Copyright (c) 2021. The Alkaid Authors. All rights reserved.
 * Use of this source code is governed by a MIT-style
 * license that can be found in the LICENSE file.
 *
 * Alkaid is a BaaS service based on Hyperledger Fabric.
 */

package aes

import (
	"testing"

	"github.com/pkg/errors"
	"github.com/stretchr/testify/assert"
	"github.com/yakumioto/alkaid/internal/common/crypto"
)

func TestKeyImport(t *testing.T) {
	tcs := []struct {
		qPassword interface{}
		aError    error
	}{
		{
			[]byte("test_key"),
			nil,
		},
		{
			struct {
			}{},
			errors.New("only supports []byte type of key"),
		},
	}

	for _, tc := range tcs {
		ki := &keyImporter{}
		_, err := ki.KeyImport(tc.qPassword, &crypto.AES128KeyImportOpts{})
		if tc.aError == nil {
			assert.NoError(t, err)
			continue
		}

		assert.Contains(t, err.Error(), tc.aError.Error())
	}

}


<|start_filename|>internal/common/crypto/hmac/keyimport.go<|end_filename|>
/*
 * Copyright (c) 2022. The Alkaid Authors. All rights reserved.
 * Use of this source code is governed by a MIT-style
 * license that can be found in the LICENSE file.
 *
 * Alkaid is a BaaS service based on Hyperledger Fabric.
 */

package hmac

import (
	"fmt"

	"github.com/yakumioto/alkaid/internal/common/crypto"
)

type KeyImporter struct{}

func NewKey(raw interface{}, opts crypto.KeyImportOpts) (crypto.Key, error) {
	return new(KeyImporter).KeyImport(raw, opts)
}

func (k *KeyImporter) KeyImport(raw interface{}, opts crypto.KeyImportOpts) (crypto.Key, error) {
	var key []byte

	switch raw := raw.(type) {
	case []byte:
		key = raw
	case string:
		key = []byte(raw)
	default:
		return nil, fmt.Errorf("only supports string or []byte type of key")
	}

	switch opts.Algorithm() {
	case crypto.HmacSha256, crypto.HmacSha512:
		return &Key{
			key:       key,
			algorithm: opts.Algorithm(),
		}, nil
	}

	return nil, fmt.Errorf("unsupported aes algorithm: %v", opts.Algorithm())
}


<|start_filename|>internal/common/crypto/utils/utils.go<|end_filename|>
/*
 * Copyright (c) 2022. The Alkaid Authors. All rights reserved.
 * Use of this source code is governed by a MIT-style
 * license that can be found in the LICENSE file.
 *
 * Alkaid is a BaaS service based on Hyperledger Fabric.
 */

package utils

import (
	"bytes"
	"crypto/rand"
	"crypto/sha256"
	"encoding/base64"
	"errors"
	"fmt"
	"strconv"
	"strings"

	"github.com/lithammer/shortuuid"
	"github.com/yakumioto/alkaid/internal/common/crypto"
	"github.com/yakumioto/alkaid/internal/common/crypto/aes"
	"github.com/yakumioto/alkaid/internal/common/crypto/hmac"
	"github.com/yakumioto/alkaid/internal/common/crypto/rsa"
	"golang.org/x/crypto/hkdf"
	"golang.org/x/crypto/pbkdf2"
)

type EncType int

const (
	AesCbc256B64 = iota
	AesCbc256HmacSha256B64
	Rsa2048OaepSha256B64
	Rsa2048OaepSha256HmacShaB64
)

type StretchedKey struct {
	Enc []byte
	Mac []byte
}

func (s *StretchedKey) Key() []byte {
	return append(s.Enc, s.Mac...)
}

func GetStretchedKey(masterKey []byte) (*StretchedKey, error) {
	encData := make([]byte, 32)
	macData := make([]byte, 32)
	_, err := hkdf.Expand(sha256.New, masterKey, []byte("enc")).Read(encData)
	if err != nil {
		return nil, err
	}
	_, err = hkdf.Expand(sha256.New, masterKey, []byte("mac")).Read(macData)
	if err != nil {
		return nil, err
	}
	return &StretchedKey{
		Enc: encData,
		Mac: macData,
	}, nil
}

func GenSymmetricKey() (*StretchedKey, error) {
	encData := make([]byte, 32)
	macData := make([]byte, 32)
	_, err := rand.Read(encData)
	if err != nil {
		return nil, err
	}
	_, err = rand.Read(macData)
	if err != nil {
		return nil, err
	}

	return &StretchedKey{
		Enc: encData,
		Mac: macData,
	}, nil
}

func GetMasterKey(password, salt string) []byte {
	return pbkdf2.Key([]byte(password), []byte(salt), 100000, 32, sha256.New)
}

func HashPassword(password, salt string, iter int) string {
	passwordHash := pbkdf2.Key([]byte(password), []byte(salt), iter, 32, sha256.New)
	passwordHashBase64 := base64.StdEncoding.EncodeToString(passwordHash)
	return fmt.Sprintf("%d.%s", iter, passwordHashBase64)
}

// PBKDF2WithSha256 用于生成扩展密钥
func PBKDF2WithSha256(password, salt []byte, keyLen int) []byte {
	return pbkdf2.Key(password, salt, 1, keyLen, sha256.New)
}

func ValidatePassword(password, salt, passwordHash string) bool {
	iter, err := strconv.Atoi(strings.SplitN(passwordHash, ".", 2)[0])
	if err != nil {
		return false
	}

	if passwordHash != HashPassword(password, salt, iter) {
		return false
	}

	return true
}

func GenResourceID(namespace string) string {
	return fmt.Sprintf("%s-%s", namespace, shortuuid.New())
}

func Encrypt(typ EncType, text []byte, keys ...interface{}) (string, error) {
	var (
		ak crypto.Key
		rk crypto.Key
		hk crypto.Key
	)

	for _, key := range keys {
		switch key := key.(type) {
		case *aes.CBCKey:
			ak = key
		case *hmac.Key:
			hk = key
		case *rsa.PublicKey:
			rk = key
		}
	}

	var (
		ciphertextBytes []byte
		err             error
	)
	switch typ {
	case AesCbc256HmacSha256B64:
		if ak == nil || hk == nil {
			return "", errors.New("not found aes key or hmac key")
		}
		ciphertextBytes, err = ak.Encrypt(text)
		if err != nil {
			return "", err
		}

	case Rsa2048OaepSha256HmacShaB64:
		if rk == nil || hk == nil {
			return "", errors.New("not found rsa key or hmac key")
		}
		ciphertextBytes, err = rk.Encrypt(text)
		if err != nil {
			return "", err
		}
	}

	ciphertext := base64.StdEncoding.EncodeToString(ciphertextBytes)
	sigBytes, err := hk.Sign([]byte(ciphertext))
	if err != nil {
		return "", err
	}
	sig := base64.StdEncoding.EncodeToString(sigBytes)

	data := bytes.NewBuffer(nil)
	data.WriteString(strconv.Itoa(int(typ)))
	data.WriteString(".")
	data.WriteString(ciphertext)
	data.WriteString(".")
	data.WriteString(sig)

	return data.String(), nil
}

func Decrypt(text string, keys ...crypto.Key) ([]byte, error) {
	var (
		ak      crypto.Key
		rk      crypto.Key
		hk      crypto.Key
		sig     bool
		sigText string
	)

	for _, key := range keys {
		switch key := key.(type) {
		case *aes.CBCKey:
			ak = key
		case *hmac.Key:
			hk = key
		case *rsa.PrivateKey:
			rk = key
		}
	}

	texts := strings.Split(text, ".")
	if len(texts) < 2 && len(texts) > 3 {
		return nil, errors.New("irregular encrypted data format")
	}
	if len(texts) == 3 {
		sig = true
	}
	typ, err := strconv.Atoi(texts[0])
	if err != nil {
		return nil, errors.New("irregular encrypted data format")
	}
	ciphertext := texts[1]
	if sig {
		sigText = texts[2]
	}

	switch EncType(typ) {
	case AesCbc256HmacSha256B64:
		if hk == nil || ak == nil {
			return nil, errors.New("not found aes key or hmac key")
		}

		sigBytes, err := base64.StdEncoding.DecodeString(sigText)
		if err != nil {
			return nil, fmt.Errorf("base64 decode sig error: %v", err)
		}
		if !hk.Verify([]byte(ciphertext), sigBytes) {
			return nil, fmt.Errorf("hmac verify error: %v", err)
		}

		ciphertextBytes, err := base64.StdEncoding.DecodeString(ciphertext)
		if err != nil {
			return nil, fmt.Errorf("base64 decode ciphertext error: %v", err)
		}
		dataBytes, err := ak.Decrypt(ciphertextBytes)
		if err != nil {
			return nil, err
		}

		return dataBytes, nil
	case Rsa2048OaepSha256HmacShaB64:
		if hk == nil || rk == nil {
			return nil, errors.New("not found aes key or hmac key")
		}
		sigBytes, err := base64.StdEncoding.DecodeString(sigText)
		if err != nil {
			return nil, fmt.Errorf("base64 decode sig error: %v", err)
		}
		if !hk.Verify([]byte(ciphertext), sigBytes) {
			return nil, fmt.Errorf("hmac verify error: %v", err)
		}

		ciphertextBytes, err := base64.StdEncoding.DecodeString(ciphertext)
		if err != nil {
			return nil, fmt.Errorf("base64 decode ciphertext error: %v", err)
		}
		dataBytes, err := rk.Decrypt(ciphertextBytes)
		if err != nil {
			return nil, err
		}

		return dataBytes, nil
	}

	return nil, nil
}


<|start_filename|>internal/common/crypto/rsa/keyimport.go<|end_filename|>
/*
 * Copyright (c) 2022. The Alkaid Authors. All rights reserved.
 * Use of this source code is governed by a MIT-style
 * license that can be found in the LICENSE file.
 *
 * Alkaid is a BaaS service based on Hyperledger Fabric.
 */

package rsa

import (
	"crypto/x509"
	"errors"
	"fmt"

	"github.com/yakumioto/alkaid/internal/common/crypto"
)

type KeyImporter struct{}

func KeyImport(raw interface{}) (crypto.Key, error) {
	return new(KeyImporter).KeyImport(raw, nil)
}

func (k *KeyImporter) KeyImport(raw interface{}, opts crypto.KeyImportOpts) (crypto.Key, error) {
	var der []byte

	switch raw := raw.(type) {
	case []byte:
		der = raw
	case string:
		der = []byte(raw)
	default:
		return nil, fmt.Errorf("only supports string or []byte type of key")
	}

	privKey, err := x509.ParsePKCS1PrivateKey(der)
	if err == nil {
		return &PrivateKey{privateKey: privKey}, nil
	}

	pubKey, err := x509.ParsePKCS1PublicKey(der)
	if err == nil {
		return &PublicKey{publicKey: pubKey}, nil
	}

	return nil, errors.New("is not ecdsa key")
}


<|start_filename|>internal/common/certificate/certificate.go<|end_filename|>
/*
 * Copyright (c) 2022. The Alkaid Authors. All rights reserved.
 * Use of this source code is governed by a MIT-style
 * license that can be found in the LICENSE file.
 *
 * Alkaid is a BaaS service based on Hyperledger Fabric.
 */

package certificate

import (
	"crypto/ecdsa"
	"crypto/x509"
	"encoding/pem"
	"net"

	"github.com/pkg/errors"
	"github.com/yakumioto/alkaid/internal/services/identities"

	"github.com/yakumioto/alkaid/third_party/github.com/hyperledger/fabric/common/crypto"
)

type PkixName struct {
	OrgName       string
	Domain        string
	CommonName    string
	Country       string
	Province      string
	Locality      string
	OrgUnit       string
	StreetAddress string
	PostalCode    string
}

func NewCA(pkikName *PkixName, priv *ecdsa.PrivateKey) (*x509.Certificate, error) {
	template := crypto.X509Template()

	// this is a CA
	template.IsCA = true
	template.KeyUsage |= x509.KeyUsageDigitalSignature |
		x509.KeyUsageKeyEncipherment | x509.KeyUsageCertSign |
		x509.KeyUsageCRLSign
	template.ExtKeyUsage = []x509.ExtKeyUsage{
		x509.ExtKeyUsageClientAuth,
		x509.ExtKeyUsageServerAuth,
	}

	// set the organization for the subject
	template.Subject = crypto.SubjectTemplateAdditional(
		pkikName.Domain,
		pkikName.CommonName,
		pkikName.Country,
		pkikName.Province,
		pkikName.Locality,
		pkikName.OrgUnit,
		pkikName.StreetAddress,
		pkikName.PostalCode,
	)
	template.SubjectKeyId = crypto.ComputeSKI(priv)

	x509Cert, err := crypto.GenCertificateECDSA(
		&template,
		&template,
		&priv.PublicKey,
		priv,
	)
	if err != nil {
		return nil, err
	}

	return x509Cert, nil
}

func SignCertificate(
	name *PkixName,
	commonName,
	orgUnits string,
	alternateNames []string,
	pub *ecdsa.PublicKey,
	caPrivKey *ecdsa.PrivateKey,
	caCertificate *x509.Certificate) (*x509.Certificate, error) {
	template := crypto.X509Template()
	switch orgUnits {
	case identities.MSPTypeOrderer, identities.MSPTypePeer:
		template.KeyUsage = x509.KeyUsageDigitalSignature
	case identities.MSPTypeAdmin, identities.MSPTypeClient:
		template.KeyUsage = x509.KeyUsageDigitalSignature | x509.KeyUsageKeyEncipherment
		template.ExtKeyUsage = []x509.ExtKeyUsage{
			x509.ExtKeyUsageServerAuth,
			x509.ExtKeyUsageClientAuth,
		}
	}

	subject := crypto.SubjectTemplateAdditional(
		"",
		commonName,
		name.Country,
		name.Province,
		name.Locality,
		name.OrgUnit,
		name.StreetAddress,
		name.PostalCode,
	)
	subject.OrganizationalUnit = append(subject.OrganizationalUnit, orgUnits)

	template.Subject = subject

	for _, san := range alternateNames {
		// try to parse as an IP address first
		ip := net.ParseIP(san)
		if ip != nil {
			template.IPAddresses = append(template.IPAddresses, ip)
		} else {
			template.DNSNames = append(template.DNSNames, san)
		}
	}

	return crypto.GenCertificateECDSA(
		&template,
		caCertificate,
		pub,
		caPrivKey)
}

// SignCert load a ecdsa cert from Certificate
func SignCert(certByte []byte) (*x509.Certificate, error) {
	var cert *x509.Certificate
	var err error

	block, _ := pem.Decode(certByte)
	if block == nil || block.Type != "CERTIFICATE" {
		return nil, errors.Errorf("bytes are not PEM encoded")
	}
	cert, err = x509.ParseCertificate(block.Bytes)

	return cert, err
}

func Signer(privKey []byte) (*crypto.ECDSASigner, error) {
	block, _ := pem.Decode(privKey)
	if block == nil {
		return nil, errors.New("bytes are not PEM encoded")
	}

	key, err := x509.ParsePKCS8PrivateKey(block.Bytes)
	if err != nil {
		return nil, errors.WithMessage(err, "pem bytes are not PKCS8 encoded ")
	}

	priv, ok := key.(*ecdsa.PrivateKey)
	if !ok {
		return nil, errors.New("pem bytes do not contain an EC private key")
	}
	return &crypto.ECDSASigner{PrivateKey: priv}, nil
}


<|start_filename|>internal/common/storage/storage.go<|end_filename|>
/*
 * Copyright (c) 2021. The Alkaid Authors. All rights reserved.
 * Use of this source code is governed by a MIT-style
 * license that can be found in the LICENSE file.
 *
 * Alkaid is a BaaS service based on Hyperledger Fabric.
 */

package storage

import (
	"errors"
	"sync"

	"github.com/gin-gonic/gin"
)

var (
	once   sync.Once
	global Storage

	ErrNotinitializedGlobalStorage = errors.New("the global storage instance is not initialized")
	ErrNeedUpdateOptions           = errors.New("must need update options")

	ErrNotFound = errors.New("not found")
)

func Initialize(storage Storage) {
	once.Do(func() {
		if global == nil {
			global = storage
		}
	})
}

type Storage interface {
	AutoMigrate(dst ...interface{}) error
	Create(value interface{}) error
	Update(values interface{}, options *UpdateOptions) error
	FindByID(dest interface{}, conditions ...interface{}) error
	FindByQuery(dest interface{}, options *QueryOptions) error
	Delete(value interface{}, conditions ...interface{}) error
	Begin() Storage
	Commit() error
}

func AutoMigrate(dst ...interface{}) error {
	if err := checkGlobal(); err != nil {
		return err
	}

	return global.AutoMigrate(dst...)
}

func Create(value interface{}) error {
	if err := checkGlobal(); err != nil {
		return err
	}

	return global.Create(value)
}

func Update(values interface{}, options *UpdateOptions) error {
	if err := checkGlobal(); err != nil {
		return err
	}

	return global.Update(values, options)
}

func FindByID(dest interface{}, conditions ...interface{}) error {
	if err := checkGlobal(); err != nil {
		return err
	}

	return global.FindByID(dest, conditions)
}

func FindByQuery(dest interface{}, options *QueryOptions) error {
	if err := checkGlobal(); err != nil {
		return err
	}

	return global.FindByQuery(dest, options)
}

func Delete(value interface{}, conditions ...interface{}) error {
	if err := checkGlobal(); err != nil {
		return err
	}

	return global.Delete(value, conditions)
}

func Begin() Storage {
	if err := checkGlobal(); err != nil {
		return nil
	}

	return global.Begin()
}

func Commit() error {
	if err := checkGlobal(); err != nil {
		return err
	}

	return global.Commit()
}

func checkGlobal() error {
	if global == nil {
		return ErrNotinitializedGlobalStorage
	}

	return nil
}

type condition struct {
	Query interface{}
	Args  []interface{}
}

type UpdateOptions struct {
	*condition
}

func NewUpdateOptions(query interface{}, args ...interface{}) *UpdateOptions {
	return &UpdateOptions{
		condition: &condition{Query: query, Args: args},
	}
}

type QueryOptions struct {
	where  *condition
	not    *condition
	ors    []*condition
	order  interface{}
	limit  int
	offset int
}

// NewQueryOptionsWithCtx 通过 gin context 获取 options
// 示例：example.com/users?type=name&q=mioto&createdAt=1649902088&page=20&limit=20
func NewQueryOptionsWithCtx(ctx *gin.Context) *QueryOptions {
	// todo: 目前只支持一个字段的模糊查询以及翻页查询
	// typ := ctx.Query("type")
	// q := ctx.Query("query")
	// ctx.BindQuery()

	return NewQueryOptions()
}

func NewQueryOptions() *QueryOptions {
	return &QueryOptions{
		limit:  -1,
		offset: -1,
	}
}

func (q *QueryOptions) GetWhere() *condition {
	return q.where
}

func (q *QueryOptions) Where(query interface{}, args ...interface{}) *QueryOptions {
	q.where = &condition{Query: query, Args: args}
	return q
}

func (q *QueryOptions) GetOrs() []*condition {
	return q.ors
}

func (q *QueryOptions) Or(query interface{}, args ...interface{}) *QueryOptions {
	if q.ors == nil {
		q.ors = make([]*condition, 0)
	}

	q.ors = append(q.ors, &condition{Query: query, Args: args})

	return q
}

func (q *QueryOptions) GetNot() *condition {
	return q.not
}

func (q *QueryOptions) Not(query interface{}, args ...interface{}) *QueryOptions {
	q.not = &condition{Query: query, Args: args}
	return q
}

func (q *QueryOptions) GetOrder() interface{} {
	return q.order
}

func (q *QueryOptions) Order(order interface{}) *QueryOptions {
	q.order = order
	return q
}

func (q *QueryOptions) GetLimit() int {
	return q.limit
}

func (q *QueryOptions) Limit(limit int) *QueryOptions {
	q.limit = limit
	return q
}

func (q *QueryOptions) GetOffset() int {
	return q.offset
}

func (q *QueryOptions) Offset(offset int) *QueryOptions {
	q.offset = offset
	return q
}


<|start_filename|>cmd/alkaid/main.go<|end_filename|>
/*
 * Copyright (c) 2021. The Alkaid Authors. All rights reserved.
 * Use of this source code is governed by a MIT-style
 * license that can be found in the LICENSE file.
 *
 * Alkaid is a BaaS service based on Hyperledger Fabric.
 */

package main

import (
	stdLog "log"
	"os"
	"strings"

	"github.com/spf13/viper"
	"github.com/yakumioto/alkaid/internal/common/jwt"
	"github.com/yakumioto/alkaid/internal/common/log"
	"github.com/yakumioto/alkaid/internal/common/storage"
	"github.com/yakumioto/alkaid/internal/common/storage/sqlite3"
	"github.com/yakumioto/alkaid/internal/restful"
	"github.com/yakumioto/alkaid/internal/restful/controllers"
	"github.com/yakumioto/alkaid/internal/restful/middlewares"
	"github.com/yakumioto/alkaid/internal/services/systems"
	"github.com/yakumioto/alkaid/internal/services/users"
)

func main() {

	initConfig()

	log.Initialize(viper.GetString("logging.level"))

	initStorage()

	jwt.Initialize(viper.GetString("auth.jwt.secret"), viper.GetDuration("auth.jwt.expires"))

	service := restful.NewService(
		restful.WithMode(viper.GetString("restful.mode")),
		restful.WithRequestTimeout(viper.GetDuration("restful.request.timeout")),
	)

	service.RegisterMiddlewares(
		new(middlewares.Logger),
		new(middlewares.Recovery),
		new(middlewares.ResolveVersion),
		middlewares.NewAuth(viper.GetString("auth.casbin.model"), viper.GetString("auth.casbin.policy")),
	)

	service.RegisterControllers(
		new(controllers.Health),
		new(controllers.InitializeSystem),
		new(controllers.Login),
		new(controllers.CreateUser),
		new(controllers.GetUserDetailByID),
	)

	if err := service.Run(viper.GetString("restful.address")); err != nil {
		log.Panicf("running restful service error: %v", err)
	}
}

func initConfig() {
	viper.SetConfigName("alkaid")
	viper.AddConfigPath("/etc/alkaid")
	viper.AddConfigPath("$HOME/.alkaid")
	viper.AddConfigPath(".")
	viper.AutomaticEnv()
	viper.SetEnvKeyReplacer(strings.NewReplacer(".", "_"))

	config, ok := os.LookupEnv("CONFIG_PATH")
	if ok {
		viper.SetConfigFile(config)
	}

	if err := viper.ReadInConfig(); err != nil {
		stdLog.Panicf("Fatal error config file: %v", err)
	}
}

func initStorage() {
	var (
		db  storage.Storage
		err error
	)

	switch viper.GetString("database.use") {
	case sqlite3.Driver:
		db, err = sqlite3.NewDB(viper.GetString("database.sqlite3.path"))
		if err != nil {
			log.Panicf("new sqlite3 database error: %v", err)
		}
	}
	storage.Initialize(db)
	if err := storage.AutoMigrate(
		new(systems.System),
		new(users.User),
		new(users.UserOrganizations),
	); err != nil {
		log.Panicf("storage auto migrate error: %v", err)
	}
}


<|start_filename|>internal/common/utils/utils.go<|end_filename|>
/*
 * Copyright (c) 2021. The Alkaid Authors. All rights reserved.
 * Use of this source code is governed by a MIT-style
 * license that can be found in the LICENSE file.
 *
 * Alkaid is a BaaS service based on Hyperledger Fabric.
 */

package utils

import (
	"crypto/sha256"
	"encoding/base64"
	"encoding/binary"
	stdErrors "errors"
	"fmt"
	"net/http"
	"strconv"
	"strings"

	"github.com/gin-gonic/gin"
	"github.com/lithammer/shortuuid"
	"github.com/yakumioto/alkaid/internal/errors"
	"golang.org/x/crypto/hkdf"
	"golang.org/x/crypto/pbkdf2"
)

func GetStretchedKey(masterKey []byte) []byte {
	encData := make([]byte, 32)
	macData := make([]byte, 32)
	_, _ = hkdf.Expand(sha256.New, masterKey, []byte("enc")).Read(encData)
	_, _ = hkdf.Expand(sha256.New, masterKey, []byte("mac")).Read(macData)
	return append(encData, macData...)
}

func GetMasterKey(password, salt string) []byte {
	return pbkdf2.Key([]byte(password), []byte(salt), 100000, 32, sha256.New)
}

func HashPassword(password, salt string, iter int) string {
	passwordHash := pbkdf2.Key([]byte(password), []byte(salt), iter, 32, sha256.New)
	passwordHashBase64 := base64.StdEncoding.EncodeToString(passwordHash)
	return fmt.Sprintf("%d.%s", iter, passwordHashBase64)
}

// PBKDF2WithSha256 用于生成扩展密钥
func PBKDF2WithSha256(password, salt []byte, keyLen int) []byte {
	return pbkdf2.Key(password, salt, 1, keyLen, sha256.New)
}

func ValidatePassword(password, salt, passwordHash string) bool {
	iter, err := strconv.Atoi(strings.SplitN(passwordHash, ".", 2)[0])
	if err != nil {
		return false
	}

	if passwordHash != HashPassword(password, salt, iter) {
		return false
	}

	return true
}

func GenResourceID(namespace string) string {
	return fmt.Sprintf("%s-%s", namespace, shortuuid.New())
}

func GetVarintBytesWithMaxVarintLen64(x int64) []byte {
	buf := make([]byte, 10)
	binary.PutVarint(buf, x)
	return buf
}

func MustVarint(buf []byte) int64 {
	num, _ := binary.Varint(buf)
	return num
}

func Render(ctx *gin.Context, property string, obj interface{}) *gin.Context {
	statusCode := http.StatusOK

	if obj, ok := obj.(error); ok {
		err := new(errors.Error)
		if stdErrors.As(obj, &err) {
			statusCode = err.StatusCode
		}
	}

	switch property {
	case "xml":
		ctx.XML(statusCode, obj)
	case "yaml":
		ctx.YAML(statusCode, obj)
	case "json":
		ctx.JSON(statusCode, obj)
	case "javascript":
		ctx.JSONP(statusCode, obj)
	default:
		ctx.JSON(statusCode, obj)
	}

	return ctx
}


<|start_filename|>internal/restful/controllers/controllers.go<|end_filename|>
/*
 * Copyright (c) 2021. The Alkaid Authors. All rights reserved.
 * Use of this source code is governed by a MIT-style
 * license that can be found in the LICENSE file.
 *
 * Alkaid is a BaaS service based on Hyperledger Fabric.
 */

package controllers

import "github.com/yakumioto/alkaid/internal/common/log"

var (
	logger = log.GetPackageLogger("restful.controllers")
)

// type Controllers struct{}
//
// func (c *Controllers) RenderFormat(ctx *gin.Context) string {
// 	return ctx.GetString("AcceptFormat")
// }
//
// func (c *Controllers) MatchVersion(ctx *gin.Context, version string) bool {
// 	if ctx.GetString("AcceptVersion") != version {
// 		ctx.Next()
// 		return false
// 	}
//
// 	return true
// }
//
// func (c *Controllers) Render(ctx *gin.Context, obj interface{}) *gin.Context {
// 	return util.Render(ctx, c.RenderFormat(ctx), obj)
// }


<|start_filename|>internal/common/crypto/aes/key_test.go<|end_filename|>
/*
 * Copyright (c) 2021. The Alkaid Authors. All rights reserved.
 * Use of this source code is governed by a MIT-style
 * license that can be found in the LICENSE file.
 *
 * Alkaid is a BaaS service based on Hyperledger Fabric.
 */

package aes

import (
	"encoding/base64"
	"testing"

	"github.com/stretchr/testify/assert"
	"github.com/yakumioto/alkaid/internal/common/crypto"
)

func TestAESCBCPrivateEncryptAndDecrypt(t *testing.T) {
	tcs := []struct {
		password         string
		algorithm        crypto.KeyImportOpts
		randomIVFunc     randomIVFunc
		text             string
		base64Ciphertext string
	}{
		{
			"aes128",
			&crypto.AES128KeyImportOpts{},
			func(len int) ([]byte, error) {
				return []byte{68, 161, 247, 38, 29, 93, 40, 189, 245, 37, 160, 123, 196, 137, 53, 69}, nil
			},
			"hello world",
			"RKH3Jh1dKL31JaB7xIk1RUtBGvZwQAXKrrn8Szgs/z0=",
		},
		{
			"aes192",
			&crypto.AES192KeyImportOpts{},
			func(len int) ([]byte, error) {
				return []byte{116, 83, 64, 105, 45, 5, 144, 131, 16, 66, 7, 54, 175, 180, 140, 217}, nil
			},
			"hello world",
			"dFNAaS0FkIMQQgc2r7SM2QnA4W8RrrDJbNN7aKPd2oA=",
		},
		{
			"aes256",
			&crypto.AES256KeyImportOpts{},
			func(len int) ([]byte, error) {
				return []byte{89, 29, 100, 99, 47, 20, 209, 119, 122, 133, 110, 49, 231, 200, 124, 49}, nil
			},
			"hello world",
			"<KEY>
		},
	}

	for _, tc := range tcs {
		ack, _ := new(keyImporter).KeyImport(tc.password, tc.algorithm)
		randomIV = tc.randomIVFunc
		ciphertext, err := ack.Encrypt([]byte(tc.text))
		assert.NoError(t, err)
		assert.Equal(t, base64.StdEncoding.EncodeToString(ciphertext), tc.base64Ciphertext, "ciphertext error")
		text, err := ack.Decrypt(ciphertext)
		assert.NoError(t, err)
		assert.Equal(t, string(text), tc.text, "text error")
		t.Logf("text:%v; ciphertext: %v", string(text), base64.StdEncoding.EncodeToString(ciphertext))
	}
}


<|start_filename|>internal/versions/versions.go<|end_filename|>
/*
 * Copyright (c) 2021. The Alkaid Authors. All rights reserved.
 * Use of this source code is governed by a MIT-style
 * license that can be found in the LICENSE file.
 *
 * Alkaid is a BaaS service based on Hyperledger Fabric.
 */

package versions

const (
	Latest = V0
	V0     = "v0"
	V1     = "v1"
)


<|start_filename|>internal/common/crypto/factory/factory.go<|end_filename|>
/*
 * Copyright (c) 2021. The Alkaid Authors. All rights reserved.
 * Use of this source code is governed by a MIT-style
 * license that can be found in the LICENSE file.
 *
 * Alkaid is a BaaS service based on Hyperledger Fabric.
 */

package factory

import (
	"fmt"
	"github.com/yakumioto/alkaid/internal/common/crypto"
	"github.com/yakumioto/alkaid/internal/common/crypto/aes"
	"github.com/yakumioto/alkaid/internal/common/crypto/ecdsa"
	"github.com/yakumioto/alkaid/internal/common/crypto/hmac"
	"github.com/yakumioto/alkaid/internal/common/crypto/rsa"
)

func CryptoKeyGen(algorithm crypto.Algorithm) (crypto.Key, error) {
	switch algorithm {
	case crypto.EcdsaP256:
		return ecdsa.KeyGen(&crypto.ECDSAP256KeyGenOpts{})
	case crypto.EcdsaP384:
		return ecdsa.KeyGen(&crypto.ECDSAP384KeyGenOpts{})
	case crypto.Rsa1024:
		return rsa.KeyGen(&crypto.RSA1024KeyImportOpts{})
	case crypto.Rsa2048:
		return rsa.KeyGen(&crypto.RSA2048KeyImportOpts{})
	case crypto.Rsa4096:
		return rsa.KeyGen(&crypto.RSA4096KeyImportOpts{})
	}

	return nil, fmt.Errorf("not found key generator: %v", algorithm)
}

func CryptoKeyImport(raw interface{}, algorithm crypto.Algorithm) (crypto.Key, error) {
	switch algorithm {
	case crypto.AesCbc128:
		return aes.NewKey(raw, &crypto.AES128KeyImportOpts{})
	case crypto.AesCbc192:
		return aes.NewKey(raw, &crypto.AES192KeyImportOpts{})
	case crypto.AesCbc256:
		return aes.NewKey(raw, &crypto.AES256KeyImportOpts{})
	case crypto.HmacSha256:
		return hmac.NewKey(raw, &crypto.HMACSha256ImportOpts{})
	case crypto.HmacSha512:
		return hmac.NewKey(raw, &crypto.HMACSha512ImportOpts{})
	case crypto.EcdsaP256, crypto.EcdsaP384:
		return ecdsa.KeyImport(raw)
	case crypto.Rsa1024, crypto.Rsa2048, crypto.Rsa4096:
		return rsa.KeyImport(raw)
	}

	return nil, fmt.Errorf("not found key importer: %v", algorithm)
}


<|start_filename|>internal/restful/middlewares/logger.go<|end_filename|>
/*
 * Copyright (c) 2021. The Alkaid Authors. All rights reserved.
 * Use of this source code is governed by a MIT-style
 * license that can be found in the LICENSE file.
 *
 * Alkaid is a BaaS service based on Hyperledger Fabric.
 */

package middlewares

import (
	"time"

	"github.com/gin-gonic/gin"
	"github.com/yakumioto/alkaid/internal/common/log"
)

type Logger struct {
	logger log.StdLogger
}

func (l *Logger) Name() string {
	return "Logger"
}

func (l *Logger) Sequence() int {
	return 1
}

func (l *Logger) HandlerFunc() gin.HandlerFunc {
	l.logger = log.GetPackageLogger("middlewares.logger")

	return func(c *gin.Context) {
		// Start timer
		start := time.Now()
		path := c.Request.URL.Path
		raw := c.Request.URL.RawQuery

		// Process request
		c.Next()

		stop := time.Now()

		if raw != "" {
			path = path + "?" + raw
		}

		l.logger.Debugf("Status Code: %d | Time Consuming: %v | Client IP: %s | Request Method: %s | Request Path: %s",
			c.Writer.Status(),
			stop.Sub(start),
			c.ClientIP(),
			c.Request.Method,
			path)

		// todo: c.Error 是否需要使用
	}
}


<|start_filename|>internal/common/crypto/rsa/keygen.go<|end_filename|>
/*
 * Copyright (c) 2022. The Alkaid Authors. All rights reserved.
 * Use of this source code is governed by a MIT-style
 * license that can be found in the LICENSE file.
 *
 * Alkaid is a BaaS service based on Hyperledger Fabric.
 */

package rsa

import (
	"crypto/rand"
	"crypto/rsa"
	"fmt"
	"github.com/yakumioto/alkaid/internal/common/crypto"
)

type keyGenerator struct{}

func KeyGen(opts crypto.KeyGenOpts) (crypto.Key, error) {
	return new(keyGenerator).KeyGen(opts)
}

func (k *keyGenerator) KeyGen(opts crypto.KeyGenOpts) (crypto.Key, error) {
	bits := 2048
	switch opts.Algorithm() {
	case crypto.Rsa1024:
		bits = 1024
	case crypto.Rsa2048:
		bits = 2048
	case crypto.Rsa4096:
		bits = 4096

	}

	privateKey, err := rsa.GenerateKey(rand.Reader, bits)
	if err != nil {
		return nil, fmt.Errorf("generating RSA key for [%v] error: [%s]", bits, err)
	}

	return &PrivateKey{privateKey: privateKey}, nil
}


<|start_filename|>internal/restful/middlewares/resolve_version.go<|end_filename|>
/*
 * Copyright (c) 2021. The Alkaid Authors. All rights reserved.
 * Use of this source code is governed by a MIT-style
 * license that can be found in the LICENSE file.
 *
 * Alkaid is a BaaS service based on Hyperledger Fabric.
 */

package middlewares

import (
	"strings"

	"github.com/gin-gonic/gin"
	"github.com/yakumioto/alkaid/internal/common/log"
)

type ResolveVersion struct {
}

func (r *ResolveVersion) Name() string {
	return "ResolveVersion"
}

func (r *ResolveVersion) Sequence() int {
	return 3
}

func (r *ResolveVersion) HandlerFunc() gin.HandlerFunc {
	return func(ctx *gin.Context) {
		// XXX: 是否需要基于特定的供应商实现 Accept 的解析
		// eg：application/vnd.[alkaid][.version][+json]
		// 感觉不是太有必要
		accept := ctx.GetHeader("Accept")
		r.handler(ctx, accept)
		ctx.Next()
	}
}

// handler 处理 accept 的 调用版本以及格式化方式
// case: No Accept
// case: application/json
// case: application/vnd.alkaid[.version][+json,xml,yaml,javascript]
func (r *ResolveVersion) handler(ctx *gin.Context, accept string) {
	var (
		version string
		format  string
	)
	if accept == "" {
		return
	}

	data := strings.SplitN(accept, "/", 2)
	if len(data) != 2 || data[0] != "application" {
		return
	}

	// 处理需要返回的格式
	data = strings.SplitN(data[1], "+", 2)
	if len(data) == 2 {
		format = data[1]
		ctx.Set("AcceptFormat", format)
	}

	data = strings.SplitN(data[0], ".", 3)
	if len(data) == 3 {
		version = data[2]
		ctx.Set("AcceptVersion", version)
	}

	log.Debugf("resolve version middleware: version is [%v], format is [%v]", version, format)
}


<|start_filename|>internal/services/users/handler.go<|end_filename|>
/*
 * Copyright (c) 2021. The Alkaid Authors. All rights reserved.
 * Use of this source code is governed by a MIT-style
 * license that can be found in the LICENSE file.
 *
 * Alkaid is a BaaS service based on Hyperledger Fabric.
 */

package users

import (
	"net/http"

	"github.com/yakumioto/alkaid/internal/common/crypto"
	"github.com/yakumioto/alkaid/internal/common/crypto/factory"
	"github.com/yakumioto/alkaid/internal/common/crypto/utils"
	"github.com/yakumioto/alkaid/internal/common/log"
	"github.com/yakumioto/alkaid/internal/common/storage"
	"github.com/yakumioto/alkaid/internal/errors"
)

var (
	logger = log.GetPackageLogger("services.users")
)

type CreateRequest struct {
	ID       string `json:"id" validate:"required"`
	Name     string `json:"name" validate:"required"`
	Email    string `json:"email" validate:"required,email"`
	Root     bool   `json:"-"`
	Password string `json:"password" validate:"required"`
}

func Create(req *CreateRequest) (*User, error) {
	u := newUserByCreateRequest(req)

	// 生成扩展密钥
	stretchedKey, err := u.StretchedKey(req.Password)
	if err != nil {
		logger.Errorf("[%v] generate stretch key error: %v", u.UserID, err)
		return nil, errors.NewError(http.StatusInternalServerError, errors.ErrServerUnknownError,
			"failed to generate stretch key")
	}

	// 生成用户唯一的对称密钥
	symmetricKey, err := utils.GenSymmetricKey()
	if err != nil {
		logger.Errorf("[%v] generate symmetric key error: %v", u.UserID, err)
		return nil, errors.NewError(http.StatusInternalServerError, errors.ErrServerUnknownError,
			"failed to generate symmetric key")
	}

	signPrivateKey, err := factory.CryptoKeyGen(crypto.EcdsaP256)
	if err != nil {
		logger.Errorf("[%v] generate signature key error: %v", u.UserID, err)
		return nil, errors.NewError(http.StatusInternalServerError, errors.ErrServerUnknownError,
			"failed to generate signature key")
	}
	signPrivateKeyPem, err := signPrivateKey.Bytes()
	if err != nil {
		logger.Errorf("[%v] convert the signature key to pem format error: %v", u.UserID, err)
		return nil, errors.NewError(http.StatusInternalServerError, errors.ErrServerUnknownError,
			"failed to convert the signature key to pem format")
	}

	tlsPrivateKey, err := factory.CryptoKeyGen(crypto.EcdsaP256)
	if err != nil {
		logger.Errorf("[%v] generate tls key error: %v", u.UserID, err)
		return nil, errors.NewError(http.StatusInternalServerError, errors.ErrServerUnknownError,
			"failed to generate tls key")
	}
	tlsPrivateKeyPem, err := tlsPrivateKey.Bytes()
	if err != nil {
		logger.Errorf("[%v] convert the tls key to pem format error: %v", u.UserID, err)
		return nil, errors.NewError(http.StatusInternalServerError, errors.ErrServerUnknownError,
			"failed to convert the tls key to pem format")
	}

	rsaPrivateKey, err := factory.CryptoKeyGen(crypto.Rsa2048)
	if err != nil {
		logger.Errorf("[%v] generate rsa key error: %v", u.UserID, err)
		return nil, errors.NewError(http.StatusInternalServerError, errors.ErrServerUnknownError,
			"failed to generate rsa key")
	}
	rsaPrivateKeyPem, err := rsaPrivateKey.Bytes()
	if err != nil {
		logger.Errorf("[%v] convert the rsa key to pem format error: %v", u.UserID, err)
		return nil, errors.NewError(http.StatusInternalServerError, errors.ErrServerUnknownError,
			"failed to convert the rsa key to pem format")
	}

	symmetricKeyAesKey, err := factory.CryptoKeyImport(symmetricKey.Enc, crypto.AesCbc256)
	if err != nil {
		logger.Errorf("[%v] import aes password error: %v", u.UserID, err)
		return nil, errors.NewError(http.StatusInternalServerError, errors.ErrServerUnknownError,
			"failed to import aes password key")
	}
	symmetricKeyHashKey, err := factory.CryptoKeyImport(symmetricKey.Mac, crypto.HmacSha256)
	if err != nil {
		logger.Errorf("[%v] import hash password error: %v", u.UserID, err)
		return nil, errors.NewError(http.StatusInternalServerError, errors.ErrServerUnknownError,
			"failed to import hash password key")
	}

	protectedSignPrivateKey, err := utils.Encrypt(utils.AesCbc256HmacSha256B64, signPrivateKeyPem, symmetricKeyAesKey, symmetricKeyHashKey)
	if err != nil {
		logger.Errorf("[%v] encryption signing private key error: %v", u.UserID, err)
		return nil, errors.NewError(http.StatusInternalServerError, errors.ErrServerUnknownError,
			"encryption signing private key failed")
	}
	protectedTLSPrivateKey, err := utils.Encrypt(utils.AesCbc256HmacSha256B64, tlsPrivateKeyPem, symmetricKeyAesKey, symmetricKeyHashKey)
	if err != nil {
		logger.Errorf("[%v] encryption tls private key error: %v", u.UserID, err)
		return nil, errors.NewError(http.StatusInternalServerError, errors.ErrServerUnknownError,
			"encryption tls private key failed")
	}
	protectedRSAPrivateKey, err := utils.Encrypt(utils.AesCbc256HmacSha256B64, rsaPrivateKeyPem, symmetricKeyAesKey, symmetricKeyHashKey)
	if err != nil {
		logger.Errorf("[%v] encryption rsa private key error: %v", u.UserID, err)
		return nil, errors.NewError(http.StatusInternalServerError, errors.ErrServerUnknownError,
			"encryption rsa private key failed")
	}
	u.ProtectedSignPrivateKey = protectedSignPrivateKey
	u.ProtectedTLSPrivateKey = protectedTLSPrivateKey
	u.ProtectedRSAPrivateKey = protectedRSAPrivateKey

	pubKey, _ := signPrivateKey.PublicKey()
	pubKeyPem, err := pubKey.Bytes()
	if err != nil {
		logger.Errorf("[%v] convert the signing public key to pem format error: %v", u.UserID, err)
		return nil, errors.NewError(http.StatusInternalServerError, errors.ErrServerUnknownError,
			"failed to convert the signing public key to pem format")
	}
	u.SignPublicKey = string(pubKeyPem)

	pubKey, _ = tlsPrivateKey.PublicKey()
	pubKeyPem, err = pubKey.Bytes()
	if err != nil {
		logger.Errorf("[%v] convert the tls public key to pem format error: %v", u.UserID, err)
		return nil, errors.NewError(http.StatusInternalServerError, errors.ErrServerUnknownError,
			"failed to convert the tls public key to pem format")
	}
	u.TLSPublicKey = string(pubKeyPem)

	pubKey, _ = rsaPrivateKey.PublicKey()
	pubKeyPem, err = pubKey.Bytes()
	if err != nil {
		logger.Errorf("[%v] convert the rsa public key to pem format error: %v", u.UserID, err)
		return nil, errors.NewError(http.StatusInternalServerError, errors.ErrServerUnknownError,
			"failed to convert the rsa public key to pem format")
	}
	u.RSAPublicKey = string(pubKeyPem)

	stretchedKeyAesKey, err := factory.CryptoKeyImport(stretchedKey.Enc, crypto.AesCbc256)
	if err != nil {
		logger.Errorf("[%v] import aes password error: %v", u.UserID, err)
		return nil, errors.NewError(http.StatusInternalServerError, errors.ErrServerUnknownError,
			"failed to import aes password key")
	}
	stretchedKeyHashKey, err := factory.CryptoKeyImport(stretchedKey.Mac, crypto.HmacSha256)
	if err != nil {
		logger.Errorf("[%v] import hash password error: %v", u.UserID, err)
		return nil, errors.NewError(http.StatusInternalServerError, errors.ErrServerUnknownError,
			"failed to import hash password key")
	}
	protectedSymmetricKey, err := utils.Encrypt(utils.AesCbc256HmacSha256B64, symmetricKey.Key(), stretchedKeyAesKey, stretchedKeyHashKey)
	if err != nil {
		logger.Errorf("[%v] encryption symmetric key error: %v", u.UserID, err)
		return nil, errors.NewError(http.StatusInternalServerError, errors.ErrServerUnknownError,
			"encryption symmetric key failed")
	}
	u.ProtectedSymmetricKey = protectedSymmetricKey

	if err = u.Create(); err != nil {
		logger.Errorf("[%v] create user error: %v", u.UserID, err)
		return nil, errors.NewError(http.StatusInternalServerError, errors.ErrServerUnknownError,
			"failed to create user")
	}

	return u, nil
}

func GetList() ([]*User, error) {
	return nil, nil
}

func GetDetailByID(id string) (*User, error) {
	user, err := FindUserByID(id)
	if err != nil {
		if err == storage.ErrNotFound {
			logger.Warnf("[%v] user not found", id)
			return nil, errors.NewError(http.StatusNotFound, errors.ErrUserNotFount,
				"user not found")
		}
		logger.Errorf("[%v] query user error: %v", id, err)
		return nil, errors.NewError(http.StatusInternalServerError, errors.ErrServerUnknownError,
			"server unknown error")
	}

	return user, nil
}

type LoginRequest struct {
	ID       string `json:"id,omitempty"`
	Password string `json:"password,omitempty"`
}

func Login(req *LoginRequest) (*User, []*UserOrganizations, error) {
	user, err := FindUserByID(req.ID)
	if err != nil {
		if err == storage.ErrNotFound {
			logger.Infof("[%v] user not found", req.ID)
			return nil, nil, errors.NewError(http.StatusNotFound, errors.ErrUserNotFount,
				"user not found")
		}

		logger.Errorf("[%v] query user error: %v", req.ID, err)
		return nil, nil, errors.NewError(http.StatusInternalServerError, errors.ErrServerUnknownError,
			"server unknown error")
	}

	if !utils.ValidatePassword(req.Password, user.Email, user.Password) {
		logger.Infof("[%v] wrong user password", req.ID)
		return nil, nil, errors.NewError(http.StatusInternalServerError, errors.ErrServerUnknownError,
			"wrong user password")
	}

	organizations, err := FindUserOrganizationsByUserID(user.UserID)
	if err != nil && err != storage.ErrNotFound {
		logger.Errorf("[%v] query user organizations error: %v", req.ID, err)
		return nil, nil, errors.NewError(http.StatusInternalServerError, errors.ErrServerUnknownError,
			"server unknown error")
	}

	return user, organizations, nil
}


<|start_filename|>internal/services/users/users.go<|end_filename|>
/*
 * Copyright (c) 2021. The Alkaid Authors. All rights reserved.
 * Use of this source code is governed by a MIT-style
 * license that can be found in the LICENSE file.
 *
 * Alkaid is a BaaS service based on Hyperledger Fabric.
 */

package users

import (
	"errors"
	"strconv"
	"time"

	"github.com/yakumioto/alkaid/internal/common/crypto/utils"
	"github.com/yakumioto/alkaid/internal/common/storage"
)

const ResourceNamespace = "User"

const (
	RoleRoot Role = iota
	RoleOrganization
	RoleNetwork
	RoleUser
	RoleNone
)

var TimeNowFunc = func() int64 {
	return time.Now().Unix()
}

type Role int

var roleNames = []string{
	"root",
	"organization",
	"network",
	"user",
	"none",
}

var roleMap = map[string]Role{
	"root":         RoleRoot,
	"organization": RoleOrganization,
	"network":      RoleNetwork,
	"user":         RoleUser,
	"none":         RoleNone,
}

func LookRole(str string) Role {
	role, ok := roleMap[str]
	if !ok {
		return RoleUser
	}

	return role
}

func (r Role) String() string {
	if RoleRoot <= r && r <= RoleUser {
		return roleNames[r]
	}

	return "%!Role(" + strconv.Itoa(int(r)) + ")"
}

func (r Role) GE(role Role) bool {
	return r >= role
}

func (r Role) LE(role Role) bool {
	return r <= role
}

// User 实体用户，每个用户会生成三对公私密钥，用于签名，通讯认证以及组织对称密钥管理。
type User struct {
	ResourceID              string `json:"resourceId,omitempty" gorm:"primaryKey"`
	UserID                  string `json:"userId,omitempty" gorm:"uniqueIndex"`
	Name                    string `json:"name,omitempty"`
	Email                   string `json:"email,omitempty" gorm:"uniqueIndex"`
	Password                string `json:"-"`
	Root                    bool   `json:"root,omitempty"`
	ProtectedSymmetricKey   string `json:"protectedSymmetricKey,omitempty"`
	ProtectedSignPrivateKey string `json:"protectedSignPrivateKey,omitempty"`
	SignPublicKey           string `json:"signPublicKey,omitempty"`
	ProtectedTLSPrivateKey  string `json:"protectedTlsPrivateKey,omitempty"`
	TLSPublicKey            string `json:"tlsPublicKey,omitempty"`
	ProtectedRSAPrivateKey  string `json:"protectedRSAPrivateKey,omitempty"`
	RSAPublicKey            string `json:"rsaPublicKey,omitempty"`
	Deactivate              bool   `json:"deactivate,omitempty"`
	CreatedAt               int64  `json:"createdAt,omitempty" gorm:"autoCreateTime"`
	UpdatedAt               int64  `json:"updatedAt,omitempty" gorm:"autoUpdateTime"`
	DeactivateAt            int64  `json:"deactivateAt,omitempty"`
}

func newUserByCreateRequest(req *CreateRequest) *User {
	return &User{
		UserID:   req.ID,
		Email:    req.Email,
		Name:     req.Name,
		Root:     req.Root,
		Password: <PASSWORD>,
	}
}

func (u *User) StretchedKey(password string) (*utils.StretchedKey, error) {
	return utils.GetStretchedKey(utils.GetMasterKey(password, u.Email))
}

func (u *User) Create() error {
	u.ResourceID = utils.GenResourceID(ResourceNamespace)
	u.Password = utils.HashPassword(
		string(utils.GetMasterKey(u.Password, u.Email)), u.Password, 1)
	return storage.Create(u)
}

func FindUserByID(id string) (*User, error) {
	user := new(User)
	return user, storage.FindByQuery(user,
		storage.NewQueryOptions().
			Or(&User{UserID: id}).
			Or(&User{Email: id}).
			Or(&User{ResourceID: id}))
}

type UserOrganizations struct {
	ResourceID     string `json:"resourceId,omitempty" gorm:"primaryKey"`
	UserID         string `json:"UserId,omitempty" gorm:"index"`
	OrganizationID string `json:"OrganizationId,omitempty" gorm:"index"`
	Role           Role   `json:"role,omitempty"`
	Status         string `json:"status,omitempty"`
	Deactivate     bool   `json:"deactivate,omitempty"`
	CreatedAt      int64  `json:"createdAt,omitempty" gorm:"autoCreateTime"`
	UpdatedAt      int64  `json:"updatedAt,omitempty" gorm:"autoUpdateTime"`
	DeactivateAt   int64  `json:"deactivateAt,omitempty"`
}

func FindUserOrganizationsByUserID(id string) ([]*UserOrganizations, error) {
	organizations := make([]*UserOrganizations, 0)
	return organizations, storage.FindByQuery(&organizations,
		storage.NewQueryOptions().
			Where(UserOrganizations{UserID: id}))
}

type UserContext struct {
	ID            string          `json:"id,omitempty"`
	Root          bool            `json:"root,omitempty"`
	Organizations []*organization `json:"organizations,omitempty"`
	ExpiredAt     int64           `json:"expiredAt"`
}

type organization struct {
	OrganizationID string `json:"organizationId,omitempty"`
	Role           Role   `json:"role"`
}

func NewUserContext(user *User, orgs []*UserOrganizations) *UserContext {
	organizations := make([]*organization, 0)
	for _, org := range orgs {
		organizations = append(organizations, &organization{
			OrganizationID: org.OrganizationID,
			Role:           org.Role,
		})
	}

	return &UserContext{
		ID:            user.UserID,
		Root:          user.Root,
		Organizations: organizations,
	}
}

func (u *UserContext) Role(orgID string) string {
	if u.Root {
		return RoleRoot.String()
	}

	for _, org := range u.Organizations {
		if org.OrganizationID == orgID {
			return org.Role.String()
		}
	}

	return RoleNone.String()
}

func (u *UserContext) Valid() error {
	if !u.verifyExpiresAt() {
		return errors.New("token is expired")
	}

	return nil
}

func (u *UserContext) verifyExpiresAt() bool {
	return TimeNowFunc() < u.ExpiredAt
}

func (u *UserContext) SetExpiresAt(expiredAt int64) {
	u.ExpiredAt = expiredAt
}


<|start_filename|>internal/restful/controllers/users.go<|end_filename|>
/*
 * Copyright (c) 2021. The Alkaid Authors. All rights reserved.
 * Use of this source code is governed by a MIT-style
 * license that can be found in the LICENSE file.
 *
 * Alkaid is a BaaS service based on Hyperledger Fabric.
 */

package controllers

import (
	"net/http"
	"time"

	"github.com/gin-gonic/gin"
	"github.com/yakumioto/alkaid/internal/common/jwt"
	"github.com/yakumioto/alkaid/internal/errors"
	"github.com/yakumioto/alkaid/internal/restful"
	"github.com/yakumioto/alkaid/internal/services/users"
	"github.com/yakumioto/alkaid/internal/versions"
)

type Login struct {
}

func (l *Login) Name() string {
	return "login"
}

func (l *Login) Path() string {
	return "/login"
}

func (l *Login) Method() string {
	return http.MethodPost
}

func (l *Login) HandlerFuncChain() []gin.HandlerFunc {
	handler := func(ctx *restful.Context) {
		req := new(users.LoginRequest)
		if err := ctx.ShouldBindJSON(req); err != nil {
			ctx.Render(errors.NewErrorf(http.StatusBadRequest, errors.ErrBadRequestParameters,
				"%v", err)).Abort()
			return
		}
		user, organizations, err := users.Login(req)
		if err != nil {
			ctx.Render(err).Abort()
			return
		}

		token, err := jwt.NewTokenWithUserContext(users.NewUserContext(user, organizations), time.Now().Unix())
		if err != nil {
			logger.Errorf("[%v] new jwt token error: %v", req.ID, err)
			ctx.Render(errors.NewError(http.StatusInternalServerError, errors.ErrServerUnknownError,
				"server unknown error")).Abort()
		}

		ctx.Render(gin.H{
			"token": token,
		})
	}

	return []gin.HandlerFunc{
		func(c *gin.Context) {
			ctx := restful.NewContext(c)
			if !ctx.MatchVersion(versions.V1) {
				return
			}

			handler(ctx)
			ctx.Abort()
		},
		func(c *gin.Context) {
			ctx := restful.NewContext(c)
			handler(ctx)
		},
	}
}

type CreateUser struct {
}

func (c *CreateUser) Name() string {
	return "create_user"
}

func (c *CreateUser) Path() string {
	return "/users"
}

func (c *CreateUser) Method() string {
	return http.MethodPost
}

func (c *CreateUser) HandlerFuncChain() []gin.HandlerFunc {
	handler := func(ctx *restful.Context) {
		req := new(users.CreateRequest)
		if err := ctx.ShouldBindJSON(req); err != nil {
			ctx.Render(err).Abort()
			return
		}

		user, err := users.Create(req)
		if err != nil {
			ctx.Render(err).Abort()
			return
		}

		ctx.Render(user)
	}

	return []gin.HandlerFunc{
		func(c *gin.Context) {
			ctx := restful.NewContext(c)
			if !ctx.MatchVersion(versions.V1) {
				return
			}

			handler(ctx)
			ctx.Abort()
		},
		func(c *gin.Context) {
			ctx := restful.NewContext(c)
			handler(ctx)
		},
	}
}

type GetUserDetailByID struct {
}

func (c *GetUserDetailByID) Name() string {
	return "find_user_by_id"
}

func (c *GetUserDetailByID) Path() string {
	return "/users/:id"
}

func (c *GetUserDetailByID) Method() string {
	return http.MethodGet
}

func (c *GetUserDetailByID) HandlerFuncChain() []gin.HandlerFunc {
	handler := func(ctx *restful.Context) {
		id := ctx.Param("id")

		user, err := users.GetDetailByID(id)
		if err != nil {
			ctx.Render(err).Abort()
			return
		}

		ctx.Render(user)
	}

	return []gin.HandlerFunc{
		func(c *gin.Context) {
			ctx := restful.NewContext(c)
			if !ctx.MatchVersion(versions.V1) {
				return
			}

			handler(ctx)
			ctx.Abort()
		},
		func(c *gin.Context) {
			ctx := restful.NewContext(c)
			handler(ctx)
		},
	}
}


<|start_filename|>internal/services/identities/identities.go<|end_filename|>
/*
 * Copyright (c) 2022. The Alkaid Authors. All rights reserved.
 * Use of this source code is governed by a MIT-style
 * license that can be found in the LICENSE file.
 *
 * Alkaid is a BaaS service based on Hyperledger Fabric.
 */

package identities

const (
	MSPTypeOrderer = "orderer"
	MSPTypePeer    = "peer"
	MSPTypeAdmin   = "admin"
	MSPTypeClient  = "client"
)


<|start_filename|>internal/common/crypto/crypto.go<|end_filename|>
/*
 * Copyright (c) 2021. The Alkaid Authors. All rights reserved.
 * Use of this source code is governed by a MIT-style
 * license that can be found in the LICENSE file.
 *
 * Alkaid is a BaaS service based on Hyperledger Fabric.
 */

package crypto

import "crypto"

var (
	SHA256 = crypto.SHA256
)

type Key interface {
	Bytes() ([]byte, error)
	SKI() []byte
	Symmetric() bool
	Private() bool
	PublicKey() (Key, error)
	Sign(digest []byte) ([]byte, error)
	Verify(hash, sig []byte) bool
	Encrypt(src []byte) ([]byte, error)
	Decrypt(src []byte) ([]byte, error)
}

type KeyGenerator interface {
	KeyGen(opts KeyGenOpts) (Key, error)
}

type KeyGenOpts interface {
	Algorithm() Algorithm
}

type KeyImporter interface {
	KeyImport(raw interface{}, opts KeyImportOpts) (Key, error)
}

type KeyImportOpts interface {
	Algorithm() Algorithm
}


<|start_filename|>internal/common/jwt/jwt_test.go<|end_filename|>
/*
 * Copyright (c) 2021. The Alkaid Authors. All rights reserved.
 * Use of this source code is governed by a MIT-style
 * license that can be found in the LICENSE file.
 *
 * Alkaid is a BaaS service based on Hyperledger Fabric.
 */

package jwt

import (
	"testing"
	"time"

	"github.com/stretchr/testify/assert"
	"github.com/yakumioto/alkaid/internal/common/log"
	"github.com/yakumioto/alkaid/internal/services/users"
)

func testInit() {
	log.Initialize("debug")
	Initialize("secret", time.Hour*24)
}

func TestNewTokenWithUser(t *testing.T) {
	testInit()
	user := &users.User{
		UserID:     "yakumioto",
		ResourceID: "users-njoVd5PKVywnZdgmhTC8EV",
		Role:       users.RoleRoot.String(),
	}
	token, err := NewTokenWithUserContext(user, 1636527720)
	assert.NoError(t, err, "new token error: %v", err)
	t.Logf("token is: %v", token)
}

func TestVerifyTokenWithUser(t *testing.T) {
	testInit()
	tokenString := `<KEY>`
	users.TimeNowFunc = func() int64 {
		return 1636527721
	}

	_, err := VerifyTokenWithUser(tokenString)
	assert.NoError(t, err)
}


<|start_filename|>internal/common/storage/sqlite3/sqlite3_test.go<|end_filename|>
/*
 * Copyright (c) 2021. The Alkaid Authors. All rights reserved.
 * Use of this source code is governed by a MIT-style
 * license that can be found in the LICENSE file.
 *
 * Alkaid is a BaaS service based on Hyperledger Fabric.
 */

package sqlite3

import (
	"testing"

	"github.com/DATA-DOG/go-sqlmock"
	"github.com/stretchr/testify/assert"
	"gorm.io/driver/sqlite"
	"gorm.io/gorm"
)

func testMockSQL() (*sqlite3, sqlmock.Sqlmock) {
	stdDB, mock, err := sqlmock.New()
	if err != nil {
		panic(err)
	}

	db, err := gorm.Open(&sqlite.Dialector{Conn: stdDB})
	if err != nil {
		panic(err)
	}

	return &sqlite3{
		db: db,
	}, mock
}

func TestNewDB(t *testing.T) {
	_, err := NewDB("file::memory:?cache=shared")
	if err != nil {
		assert.NoErrorf(t, err, "new db error: %v", err)
	}
}

type TestDocuments struct {
	Message string
}

func TestSqlite3_Create(t *testing.T) {
	db, mock := testMockSQL()

	test := &TestDocuments{
		Message: "Hello World!",
	}

	mock.ExpectBegin()
	mock.ExpectExec("INSERT INTO `test_documents`").
		WithArgs(test.Message).
		WillReturnResult(sqlmock.NewResult(1, 1))
	mock.ExpectCommit()

	if err := db.Create(test); err != nil {
		assert.NoErrorf(t, err, "create data error: %v", err)
	}
}


<|start_filename|>internal/services/systems/system.go<|end_filename|>
/*
 * Copyright (c) 2022. The Alkaid Authors. All rights reserved.
 * Use of this source code is governed by a MIT-style
 * license that can be found in the LICENSE file.
 *
 * Alkaid is a BaaS service based on Hyperledger Fabric.
 */

package systems

import "github.com/yakumioto/alkaid/internal/common/storage"

const (
	KSystemInitialized = "system_initialized"
)

var (
	VSystemInitialized = "initialized"
)

type System struct {
	Key       string `json:"key,omitempty" gorm:"primaryKey"`
	Value     string `json:"value,omitempty"`
	CreatedAt int64  `json:"createdAt,omitempty" gorm:"autoCreateTime"`
	UpdatedAt int64  `json:"updatedAt,omitempty" gorm:"autoUpdateTime"`
}

func newSystem(k, v string) *System {
	return &System{
		Key:   k,
		Value: v,
	}
}

func newSystemByID(id string) *System {
	return &System{
		Key: id,
	}
}

func (s *System) create() error {
	return storage.Create(s)
}

func (s *System) findByID() error {
	return storage.FindByID(s, s.Key)
}


<|start_filename|>internal/restful/middlewares/auth.go<|end_filename|>
/*
 * Copyright (c) 2021. The Alkaid Authors. All rights reserved.
 * Use of this source code is governed by a MIT-style
 * license that can be found in the LICENSE file.
 *
 * Alkaid is a BaaS service based on Hyperledger Fabric.
 */

package middlewares

import (
	"fmt"
	"net/http"
	"strings"

	"github.com/casbin/casbin/v2"
	"github.com/gin-gonic/gin"
	"github.com/yakumioto/alkaid/internal/common/jwt"
	"github.com/yakumioto/alkaid/internal/common/log"
	"github.com/yakumioto/alkaid/internal/errors"
	"github.com/yakumioto/alkaid/internal/restful"
	"github.com/yakumioto/alkaid/internal/services/users"
)

var (
	logger = log.GetPackageLogger("middlewares.auth")
)

type Auth struct {
	enforcer *casbin.Enforcer
}

func NewAuth(model, policy string) *Auth {
	enforcer, err := casbin.NewEnforcer(model, policy)
	if err != nil {
		logger.Panicf("new enforcer error: %v", err)
	}

	return &Auth{
		enforcer: enforcer,
	}
}

func (a *Auth) Name() string {
	return "Auth"
}

func (a *Auth) Sequence() int {
	return 4
}

func (a *Auth) HandlerFunc() gin.HandlerFunc {
	return func(c *gin.Context) {
		var (
			noAuthorization bool
			userCtx         *users.UserContext
			err             error
		)

		ctx := restful.NewContext(c)
		authorization := c.GetHeader("Authorization")
		contents := strings.SplitN(authorization, " ", 2)
		if len(contents) != 2 {
			noAuthorization = true
		}

		if noAuthorization {
			if ok, _ := a.enforcer.Enforce("*", "*", ctx.FullPath(), ctx.Request.Method); ok {
				ctx.Next()
				return
			}

			ctx.Render(errors.NewError(http.StatusUnauthorized, errors.ErrUnauthorized,
				"no access"))
			ctx.Abort()
			return
		}

		typ := contents[0]
		credentials := contents[1]

		switch typ {
		case "Bearer":
			userCtx, err = jwt.VerifyTokenWithUser(credentials)
			if err != nil {
				logger.Errorf("JWT verify error: %v", err)
				ctx.Render(errors.NewError(http.StatusForbidden, errors.ErrUnauthorized,
					"jwt verification failed"))
				c.Abort()
				return
			}
			c.Set("UserContext", userCtx)
		}

		if ok, _ := a.enforcer.Enforce(
			fmt.Sprintf("%v::role", userCtx.Role(ctx.GetString("organizationId"))),
			userCtx.ID, ctx.FullPath(), ctx.Request.Method); !ok {
			ctx.Render(errors.NewError(http.StatusUnauthorized, errors.ErrUnauthorized,
				"no access"))
			ctx.Abort()
			return
		}

		c.Next()
	}
}


<|start_filename|>internal/common/storage/sqlite3/sqlite3.go<|end_filename|>
/*
 * Copyright (c) 2021. The Alkaid Authors. All rights reserved.
 * Use of this source code is governed by a MIT-style
 * license that can be found in the LICENSE file.
 *
 * Alkaid is a BaaS service based on Hyperledger Fabric.
 */

package sqlite3

import (
	"github.com/yakumioto/alkaid/internal/common/storage"
	"gorm.io/driver/sqlite"
	"gorm.io/gorm"
)

const Driver = "sqlite3"

type sqlite3 struct {
	db *gorm.DB
}

func NewDB(path string) (*sqlite3, error) {
	db, err := gorm.Open(sqlite.Open(path), &gorm.Config{})
	if err != nil {
		return nil, err
	}

	return &sqlite3{
		db: db,
	}, nil
}

func (s *sqlite3) AutoMigrate(dst ...interface{}) error {
	return s.db.AutoMigrate(dst...)
}

func (s *sqlite3) Create(value interface{}) error {
	if tx := s.db.Create(value); tx.Error != nil {
		return tx.Error
	}

	return nil
}

func (s *sqlite3) Update(values interface{}, options *storage.UpdateOptions) error {
	if options == nil {
		return storage.ErrNeedUpdateOptions
	}

	if tx := s.db.Model(values).Where(options.Query, options.Args).Updates(values); tx.Error != nil {
		return tx.Error
	}

	return nil
}

func (s *sqlite3) FindByID(dest interface{}, conditions ...interface{}) error {
	if tx := s.db.First(dest, conditions); tx.Error != nil {
		if tx.Error == gorm.ErrRecordNotFound {
			return storage.ErrNotFound
		}
		return tx.Error
	}

	return nil
}

func (s *sqlite3) FindByQuery(dest interface{}, options *storage.QueryOptions) error {
	if options == nil {
		options = storage.NewQueryOptions()
	}

	tx := s.db.Order(options.GetOrder()).Limit(options.GetLimit()).Offset(options.GetOffset())

	if where := options.GetWhere(); where != nil {
		tx.Where(where.Query, where.Args)
	}

	if ors := options.GetOrs(); ors != nil {
		for _, or := range ors {
			tx.Or(or.Query, or.Args)
		}
	}

	if not := options.GetNot(); not != nil {
		tx.Not(not.Query, not.Args)
	}

	tx.Find(dest)

	if tx.Error != nil {
		return tx.Error
	}

	return nil
}

func (s *sqlite3) Delete(value interface{}, conditions ...interface{}) error {
	if tx := s.db.Delete(value, conditions); tx.Error != nil {
		return tx.Error
	}

	return nil
}

func (s *sqlite3) Begin() storage.Storage {
	return &sqlite3{db: s.db.Begin()}
}

func (s *sqlite3) Commit() error {
	if tx := s.db.Commit(); tx.Error != nil {
		return tx.Error
	}

	return nil
}


<|start_filename|>internal/restful/controllers/systems.go<|end_filename|>
/*
 * Copyright (c) 2022. The Alkaid Authors. All rights reserved.
 * Use of this source code is governed by a MIT-style
 * license that can be found in the LICENSE file.
 *
 * Alkaid is a BaaS service based on Hyperledger Fabric.
 */

package controllers

import (
	"net/http"

	"github.com/gin-gonic/gin"
	"github.com/yakumioto/alkaid/internal/restful"
	"github.com/yakumioto/alkaid/internal/services/systems"
	"github.com/yakumioto/alkaid/internal/versions"
)

type InitializeSystem struct {
}

func (i *InitializeSystem) Name() string {
	return "initialize_system"
}

func (i *InitializeSystem) Path() string {
	return "/initialize"
}

func (i *InitializeSystem) Method() string {
	return http.MethodPost
}

func (i *InitializeSystem) HandlerFuncChain() []gin.HandlerFunc {
	handler := func(ctx *restful.Context) {
		req := new(systems.InitRequest)
		if err := ctx.ShouldBindJSON(req); err != nil {
			ctx.Render(err).Abort()
			return
		}

		sys, err := systems.SystemInit(req)
		if err != nil {
			ctx.Render(err).Abort()
			return
		}

		ctx.Render(sys)
	}

	return []gin.HandlerFunc{
		func(c *gin.Context) {
			ctx := restful.NewContext(c)
			if !ctx.MatchVersion(versions.V1) {
				return
			}

			handler(ctx)
			ctx.Abort()
		},
		func(c *gin.Context) {
			ctx := restful.NewContext(c)
			handler(ctx)
		},
	}
}


<|start_filename|>internal/errors/codes.go<|end_filename|>
/*
 * Copyright (c) 2021. The Alkaid Authors. All rights reserved.
 * Use of this source code is governed by a MIT-style
 * license that can be found in the LICENSE file.
 *
 * Alkaid is a BaaS service based on Hyperledger Fabric.
 */

package errors

type Code int

const (
	ErrServerUnknownError   Code = 100001
	ErrUnauthorized         Code = 100002
	ErrForbidden            Code = 100003
	ErrBadRequestParameters Code = 100004

	ErrUserNotFount        Code = 200001
	ErrUserCreateVerifying Code = 200002
)


<|start_filename|>internal/restful/restful.go<|end_filename|>
/*
 * Copyright (c) 2021. The Alkaid Authors. All rights reserved.
 * Use of this source code is governed by a MIT-style
 * license that can be found in the LICENSE file.
 *
 * Alkaid is a BaaS service based on Hyperledger Fabric.
 */

package restful

import (
	"sort"
	"time"

	"github.com/gin-gonic/gin"
	"github.com/yakumioto/alkaid/internal/common/utils"
	"github.com/yakumioto/alkaid/internal/versions"
)

const (
	ReleaseMode = "release"
	DebugMode   = "debug"
)

type Service interface {
	RegisterMiddlewares(...Middleware)
	RegisterHandlerMiddlewares(...Middleware)
	RegisterControllers(...Controller)
	Run(addr string) error
}

type Middleware interface {
	Name() string
	Sequence() int
	HandlerFunc() gin.HandlerFunc
}

type Controller interface {
	Name() string
	Path() string
	Method() string
	HandlerFuncChain() []gin.HandlerFunc
}

type HandlerFunc func(ctx *Context)

type middlewares []Middleware

type controllers []Controller

func (m middlewares) Len() int {
	return len(m)
}

func (m middlewares) Less(i, j int) bool {
	return m[i].Sequence() < m[j].Sequence()
}

func (m middlewares) Swap(i, j int) {
	m[i], m[j] = m[j], m[i]
}

type options struct {
	mode           string
	requestTimeout time.Duration
}

type OptionFunc func(opt *options)

func WithMode(mode string) OptionFunc {
	return func(opt *options) {
		switch mode {
		case ReleaseMode:
			opt.mode = ReleaseMode
		case DebugMode:
			opt.mode = DebugMode
		default:
			opt.mode = DebugMode
		}
	}
}

func WithRequestTimeout(duration time.Duration) OptionFunc {
	return func(opt *options) {
		opt.requestTimeout = duration
	}
}

var (
	defaultOptions = options{
		mode:           ReleaseMode,
		requestTimeout: 10 * time.Second,
	}
)

type service struct {
	opts               *options
	engine             *gin.Engine
	middlewares        middlewares
	handlerMiddlewares middlewares
	controllers        controllers
}

func NewService(optsFunc ...OptionFunc) Service {
	opts := defaultOptions
	for _, f := range optsFunc {
		f(&opts)
	}

	gin.SetMode(opts.mode)

	return &service{
		opts:   &opts,
		engine: gin.New(),
	}
}

func (s *service) RegisterMiddlewares(middlewares ...Middleware) {
	for _, middleware := range middlewares {
		middleware := middleware
		s.middlewares = append(s.middlewares, middleware)
	}

	sort.Sort(s.middlewares)
}

func (s *service) RegisterHandlerMiddlewares(middlewares ...Middleware) {
	for _, middleware := range middlewares {
		middleware := middleware
		s.handlerMiddlewares = append(s.handlerMiddlewares, middleware)
	}

	sort.Sort(s.handlerMiddlewares)
}

func (s *service) RegisterControllers(controllers ...Controller) {
	for _, controller := range controllers {
		controller := controller
		s.controllers = append(s.controllers, controller)
	}
}

func (s *service) Run(addr string) error {
	for _, middleware := range s.middlewares {
		s.engine.Use(middleware.HandlerFunc())
	}

	hMiddlewaresChain := make([]gin.HandlerFunc, 0, len(s.handlerMiddlewares))

	for _, hMiddleware := range s.handlerMiddlewares {
		hMiddlewaresChain = append(hMiddlewaresChain, hMiddleware.HandlerFunc())
	}

	for _, controller := range s.controllers {
		handlerChain := make([]gin.HandlerFunc, 0)
		handlerChain = append(handlerChain, hMiddlewaresChain...)
		handlerChain = append(handlerChain, controller.HandlerFuncChain()...)
		s.engine.Handle(controller.Method(), controller.Path(), handlerChain...)
	}

	return s.engine.Run(addr)
}

type Base interface {
	RenderFormat() string
	MatchVersion(version string) bool
	Render(obj interface{}) *gin.Context
}

type Context struct {
	Base
	*gin.Context
}

func NewContext(ctx *gin.Context) *Context {
	return &Context{
		Context: ctx,
	}
}

func (c *Context) RenderFormat() string {
	return c.GetString("AcceptFormat")
}

func (c *Context) MatchVersion(version string) bool {
	if c.GetString("AcceptVersion") != version {
		c.Next()
		return false
	}

	return true
}

func (c *Context) Render(obj interface{}) *gin.Context {
	return utils.Render(c.Context, c.RenderFormat(), obj)
}

func GenHandlerFuncChain(defaultFunc HandlerFunc, vers ...HandlerFunc) []gin.HandlerFunc {
	chain := make([]gin.HandlerFunc, 0)
	for ver, handler := range vers {
		version := versions.Latest
		handler := handler

		switch ver {
		case 0:
			version = versions.V0
		case 1:
			version = versions.V1
		}

		chain = append(chain, func(c *gin.Context) {
			ctx := NewContext(c)
			if !ctx.MatchVersion(version) {
				return
			}

			handler(ctx)
			ctx.Abort()
		})
	}

	chain = append(chain, func(c *gin.Context) {
		ctx := NewContext(c)
		defaultFunc(ctx)
	})

	return chain
}


<|start_filename|>internal/common/crypto/ecdsa/keyimport.go<|end_filename|>
/*
 * Copyright (c) 2022. The Alkaid Authors. All rights reserved.
 * Use of this source code is governed by a MIT-style
 * license that can be found in the LICENSE file.
 *
 * Alkaid is a BaaS service based on Hyperledger Fabric.
 */

package ecdsa

import (
	"crypto/ecdsa"
	"crypto/x509"
	"errors"
	"fmt"

	"github.com/yakumioto/alkaid/internal/common/crypto"
)

type KeyImporter struct{}

func KeyImport(raw interface{}) (crypto.Key, error) {
	return new(KeyImporter).KeyImport(raw, nil)
}

func (k *KeyImporter) KeyImport(raw interface{}, _ crypto.KeyImportOpts) (crypto.Key, error) {
	var der []byte

	switch raw := raw.(type) {
	case []byte:
		der = raw
	case string:
		der = []byte(raw)
	default:
		return nil, fmt.Errorf("only supports string or []byte type of key")
	}

	key, err := x509.ParsePKCS8PrivateKey(der)
	if err == nil {
		return &ecdsaPrivateKey{privateKey: key.(*ecdsa.PrivateKey)}, nil
	}

	key, err = x509.ParsePKIXPublicKey(der)
	if err == nil {
		return &ecdsaPublicKey{publicKey: key.(*ecdsa.PublicKey)}, nil
	}

	return nil, errors.New("is not ecdsa key")
}


<|start_filename|>internal/common/crypto/ecdsa/key.go<|end_filename|>
/*
 * Copyright (c) 2021. The Alkaid Authors. All rights reserved.
 * Use of this source code is governed by a MIT-style
 * license that can be found in the LICENSE file.
 *
 * Alkaid is a BaaS service based on Hyperledger Fabric.
 */

package ecdsa

import (
	cryptoStd "crypto"
	"crypto/ecdsa"
	"crypto/elliptic"
	"crypto/rand"
	"crypto/sha256"
	"crypto/x509"
	"encoding/pem"

	"github.com/pkg/errors"
	"github.com/yakumioto/alkaid/internal/common/crypto"
)

type ecdsaPrivateKey struct {
	privateKey *ecdsa.PrivateKey
}

func (e *ecdsaPrivateKey) Bytes() ([]byte, error) {
	pkcs8Encoded, err := x509.MarshalPKCS8PrivateKey(e.privateKey)
	if err != nil {
		return nil, errors.WithMessage(err, "failed to marshal private key")
	}

	return pem.EncodeToMemory(&pem.Block{Type: "PRIVATE KEY", Bytes: pkcs8Encoded}), nil
}

func (e *ecdsaPrivateKey) SKI() []byte {
	pubKey, _ := e.PublicKey()
	return pubKey.SKI()
}

func (e *ecdsaPrivateKey) Symmetric() bool {
	return false
}

func (e *ecdsaPrivateKey) Private() bool {
	return true
}

func (e *ecdsaPrivateKey) PublicKey() (crypto.Key, error) {
	return &ecdsaPublicKey{publicKey: &e.privateKey.PublicKey}, nil
}

func (e *ecdsaPrivateKey) Sign(digest []byte) ([]byte, error) {
	return e.privateKey.Sign(rand.Reader, digest, cryptoStd.SHA256)
}

func (e *ecdsaPrivateKey) Verify(_, _ []byte) bool {
	return false
}

func (e *ecdsaPrivateKey) Encrypt(_ []byte) ([]byte, error) {
	return nil, errors.New("not supported")
}

func (e *ecdsaPrivateKey) Decrypt(_ []byte) ([]byte, error) {
	return nil, errors.New("not supported")
}

type ecdsaPublicKey struct {
	publicKey *ecdsa.PublicKey
}

func (e *ecdsaPublicKey) Bytes() ([]byte, error) {
	pkcs8Encoded, err := x509.MarshalPKIXPublicKey(e.publicKey)
	if err != nil {
		return nil, errors.WithMessage(err, "failed to marshal public key")
	}

	return pem.EncodeToMemory(&pem.Block{Type: "PUBLIC KEY", Bytes: pkcs8Encoded}), nil
}

func (e *ecdsaPublicKey) SKI() []byte {
	// Marshall the public key
	raw := elliptic.Marshal(e.publicKey.Curve, e.publicKey.X, e.publicKey.Y)

	// Hash it
	hash := sha256.New()
	hash.Write(raw)
	return hash.Sum(nil)
}

func (e *ecdsaPublicKey) Symmetric() bool {
	return false
}

func (e *ecdsaPublicKey) Private() bool {
	return false
}

func (e *ecdsaPublicKey) PublicKey() (crypto.Key, error) {
	return e, nil
}

func (e *ecdsaPublicKey) Sign(_ []byte) ([]byte, error) {
	return nil, errors.New("not supported")
}

func (e *ecdsaPublicKey) Verify(hash, sig []byte) bool {
	return ecdsa.VerifyASN1(e.publicKey, hash, sig)
}

func (e *ecdsaPublicKey) Encrypt(_ []byte) ([]byte, error) {
	return nil, errors.New("not supported")
}

func (e *ecdsaPublicKey) Decrypt(_ []byte) ([]byte, error) {
	return nil, errors.New("not supported")
}


<|start_filename|>internal/services/organizations/organizations.go<|end_filename|>
/*
 * Copyright (c) 2022. The Alkaid Authors. All rights reserved.
 * Use of this source code is governed by a MIT-style
 * license that can be found in the LICENSE file.
 *
 * Alkaid is a BaaS service based on Hyperledger Fabric.
 */

package organizations

import (
	"github.com/yakumioto/alkaid/internal/common/storage"
	"github.com/yakumioto/alkaid/internal/common/utils"
)

const ResourceNamespace = "Organization"

// Organization 组织，组织中包含了加密后的 Sign CA，TLS CA 密钥。
// 所以在创建组织时需要填入一个交易密码，此密码用来加解密上述的两个 CA 密钥。
type Organization struct {
	ResourceID                string `json:"resourceID,omitempty" gorm:"primaryKey"`
	OrganizationID            string `json:"organizationId,omitempty" gorm:"uniqueIndex"`
	Name                      string `json:"name,omitempty"`
	Domain                    string `json:"domain,omitempty" gorm:"uniqueIndex"`
	Description               string `json:"description,omitempty"`
	Country                   string `json:"country,omitempty"`
	Province                  string `json:"province,omitempty"`
	Locality                  string `json:"locality,omitempty"`
	OrganizationalUnit        string `json:"organizationalUnit,omitempty"`
	StreetAddress             string `json:"streetAddress,omitempty"`
	PostalCode                string `json:"postalCode,omitempty"`
	ProtectedSignCAPrivateKey string `json:"protectedSignCAPrivateKey,omitempty"`
	ProtectedTLSCAPrivateKey  string `json:"protectedTlsCAPrivateKey,omitempty"`
	SignCACertificate         string `json:"signCACertificate,omitempty"`
	TlsCACertificate          string `json:"tlsCACertificate,omitempty"`
	CreatedAt                 int64  `json:"createdAt,omitempty"`
	UpdatedAt                 int64  `json:"updatedAt,omitempty"`
}

func newOrganizationByCreateRequest(req *CreateRequest) *Organization {
	org := &Organization{
		OrganizationID:     req.OrganizationID,
		Name:               req.Name,
		Domain:             req.Domain,
		Description:        req.Description,
		Country:            req.Country,
		Province:           req.Province,
		Locality:           req.Locality,
		OrganizationalUnit: req.OrganizationalUnit,
		StreetAddress:      req.StreetAddress,
		PostalCode:         req.PostalCode,
	}

	org.SetCountry(org.Country)
	org.SetProvince(org.Province)
	org.SetLocality(org.Locality)
	org.SetOrganizationalUnit(org.OrganizationalUnit)

	return org
}

func (o *Organization) Create() error {
	o.ResourceID = utils.GenResourceID(ResourceNamespace)
	o.SetCountry(o.Country)
	o.SetProvince(o.Province)
	o.SetLocality(o.Locality)
	o.SetOrganizationalUnit(o.OrganizationalUnit)
	return storage.Create(o)
}

func (o *Organization) SetCountry(country string) {
	if country != "" {
		o.Country = country
	}

	o.Country = "China"
}

func (o *Organization) SetProvince(province string) {
	if province != "" {
		o.Province = province
	}

	o.Province = "Beijing"
}

func (o *Organization) SetLocality(locality string) {
	if locality != "" {
		o.Locality = locality
	}

	o.Locality = "Beijing"
}

func (o *Organization) SetOrganizationalUnit(organizationalUnit string) {
	if organizationalUnit != "" {
		o.OrganizationalUnit = organizationalUnit
	}

	o.OrganizationalUnit = "Alkaid"
}

func FindOrganizationByID(id string) (*Organization, error) {
	return nil, nil
}


<|start_filename|>internal/errors/errors.go<|end_filename|>
/*
 * Copyright (c) 2021. The Alkaid Authors. All rights reserved.
 * Use of this source code is governed by a MIT-style
 * license that can be found in the LICENSE file.
 *
 * Alkaid is a BaaS service based on Hyperledger Fabric.
 */

package errors

import (
	"fmt"
)

type Error struct {
	StatusCode int    `json:"-"`
	Code       Code   `json:"code,omitempty"`
	Message    string `json:"message,omitempty"`
}

func (r *Error) Error() string {
	return r.Message
}

func NewError(status int, code Code, message string) *Error {
	return &Error{
		StatusCode: status,
		Code:       code,
		Message:    message,
	}
}

func NewErrorf(status int, code Code, format string, a ...interface{}) *Error {
	return &Error{
		StatusCode: status,
		Code:       code,
		Message:    fmt.Sprintf(format, a...),
	}
}


<|start_filename|>internal/common/crypto/aes/keyimport.go<|end_filename|>
/*
 * Copyright (c) 2021. The Alkaid Authors. All rights reserved.
 * Use of this source code is governed by a MIT-style
 * license that can be found in the LICENSE file.
 *
 * Alkaid is a BaaS service based on Hyperledger Fabric.
 */

package aes

import (
	"crypto/sha256"
	"fmt"

	"github.com/yakumioto/alkaid/internal/common/crypto"
	"golang.org/x/crypto/pbkdf2"
)

func NewKey(raw interface{}, opts crypto.KeyImportOpts) (crypto.Key, error) {
	return new(keyImporter).KeyImport(raw, opts)
}

type keyImporter struct{}

func (kg *keyImporter) KeyImport(raw interface{}, opts crypto.KeyImportOpts) (crypto.Key, error) {
	var key []byte

	switch raw := raw.(type) {
	case []byte:
		key = raw
	case string:
		key = []byte(raw)
	default:
		return nil, fmt.Errorf("only supports string or []byte type of key")
	}

	keyLen := 0

	switch opts.Algorithm() {
	case crypto.AesCbc128:
		keyLen = 128 / 8
	case crypto.AesCbc192:
		keyLen = 192 / 8
	case crypto.AesCbc256:
		keyLen = 256 / 8
	}

	if len(key) != keyLen {
		key = pbkdf2.Key(key, key, 1000, keyLen, sha256.New)
	}

	switch opts.Algorithm() {
	case crypto.AesCbc128, crypto.AesCbc192, crypto.AesCbc256:
		return &CBCKey{
			key: key,
		}, nil
	}

	return nil, fmt.Errorf("unsupported aes algorithm: %v", opts.Algorithm())
}


<|start_filename|>internal/common/crypto/ecdsa/keygen.go<|end_filename|>
/*
 * Copyright (c) 2021. The Alkaid Authors. All rights reserved.
 * Use of this source code is governed by a MIT-style
 * license that can be found in the LICENSE file.
 *
 * Alkaid is a BaaS service based on Hyperledger Fabric.
 */

package ecdsa

import (
	"crypto/ecdsa"
	"crypto/elliptic"
	"crypto/rand"
	"fmt"

	"github.com/yakumioto/alkaid/internal/common/crypto"
)

func KeyGen(opts crypto.KeyGenOpts) (crypto.Key, error) {
	return new(keyGenerator).KeyGen(opts)
}

type keyGenerator struct{}

func (kg *keyGenerator) KeyGen(opts crypto.KeyGenOpts) (crypto.Key, error) {
	var curve elliptic.Curve
	switch opts.Algorithm() {
	case crypto.EcdsaP256:
		curve = elliptic.P256()
	case crypto.EcdsaP384:
		curve = elliptic.P384()
	default:
		return nil, fmt.Errorf("unsupported ECDSA algorithm: %v", opts.Algorithm())
	}

	privateKey, err := ecdsa.GenerateKey(curve, rand.Reader)
	if err != nil {
		return nil, fmt.Errorf("generating ECDSA key for [%v] error: [%s]", curve, err)
	}

	return &ecdsaPrivateKey{privateKey: privateKey}, nil
}


<|start_filename|>internal/services/organizations/handler.go<|end_filename|>
/*
 * Copyright (c) 2022. The Alkaid Authors. All rights reserved.
 * Use of this source code is governed by a MIT-style
 * license that can be found in the LICENSE file.
 *
 * Alkaid is a BaaS service based on Hyperledger Fabric.
 */

package organizations

import (
	"encoding/base64"
	"net/http"

	"github.com/yakumioto/alkaid/internal/common/crypto"
	"github.com/yakumioto/alkaid/internal/common/crypto/factory"
	"github.com/yakumioto/alkaid/internal/common/log"
	"github.com/yakumioto/alkaid/internal/errors"
)

var (
	logger = log.GetPackageLogger("services.organizations")
)

type CreateRequest struct {
	OrganizationID      string `json:"organizationId,omitempty" validate:"required"`
	Name                string `json:"name,omitempty" validate:"required"`
	Domain              string `json:"domain,omitempty" validate:"required,fqdn"`
	Description         string `json:"description,omitempty"`
	Country             string `json:"country,omitempty"`
	Province            string `json:"province,omitempty"`
	Locality            string `json:"locality,omitempty"`
	OrganizationalUnit  string `json:"organizationalUnit,omitempty"`
	StreetAddress       string `json:"streetAddress,omitempty"`
	PostalCode          string `json:"postalCode,omitempty"`
	TransactionPassword string `json:"transactionPassword" validate:"required"` // 交易密码仅用来加解密 PrivateKey
}

func Create(req *CreateRequest) (*Organization, error) {
	org := newOrganizationByCreateRequest(req)

	signCAPrivateKey, err := factory.CryptoKeyGen(crypto.EcdsaP256)
	if err != nil {
		logger.Errorf("[%v] generate signature key error: %v", req.OrganizationID, err)
		return nil, errors.NewError(http.StatusInternalServerError, errors.ErrServerUnknownError,
			"failed to generate signature key")
	}
	signCAPrivateKeyPem, err := signCAPrivateKey.Bytes()
	if err != nil {
		logger.Errorf("[%v] convert the signature key to pem format error: %v", req.OrganizationID, err)
		return nil, errors.NewError(http.StatusInternalServerError, errors.ErrServerUnknownError,
			"failed to convert the signature key to pem format")
	}

	tlsCAPrivateKey, err := factory.CryptoKeyGen(crypto.EcdsaP256)
	if err != nil {
		logger.Errorf("[%v] generate tls key error: %v", req.OrganizationID, err)
		return nil, errors.NewError(http.StatusInternalServerError, errors.ErrServerUnknownError,
			"failed to generate tls key")
	}
	tlsCAPrivateKeyPem, err := tlsCAPrivateKey.Bytes()
	if err != nil {
		logger.Errorf("[%v] convert the tls key to pem format error: %v", req.OrganizationID, err)
		return nil, errors.NewError(http.StatusInternalServerError, errors.ErrServerUnknownError,
			"failed to convert the tls key to pem format")
	}

	aesKey, err := factory.CryptoKeyImport([]byte(req.TransactionPassword), crypto.AesCbc256)
	if err != nil {
		logger.Errorf("[%v] import transaction password error: %v", req.OrganizationID, err)
		return nil, errors.NewError(http.StatusInternalServerError, errors.ErrServerUnknownError,
			"failed to import transaction password key")
	}
	protectedSignPrivateKey, err := aesKey.Encrypt(signCAPrivateKeyPem)
	if err != nil {
		logger.Errorf("[%v] encryption signing key error: %v", req.OrganizationID, err)
		return nil, errors.NewError(http.StatusInternalServerError, errors.ErrServerUnknownError,
			"encryption signing key failed")
	}
	protectedTLSPrivateKey, err := aesKey.Encrypt(tlsCAPrivateKeyPem)
	if err != nil {
		logger.Errorf("[%v] encryption tls key error: %v", req.OrganizationID, err)
		return nil, errors.NewError(http.StatusInternalServerError, errors.ErrServerUnknownError,
			"encryption tls key failed")
	}

	org.ProtectedSignCAPrivateKey = base64.StdEncoding.EncodeToString(protectedSignPrivateKey)
	org.ProtectedTLSCAPrivateKey = base64.StdEncoding.EncodeToString(protectedTLSPrivateKey)

	// TODO: 生成证书

	return nil, nil
}


<|start_filename|>cmd/demo/demo.go<|end_filename|>
/*
 * Copyright (c) 2022. The Alkaid Authors. All rights reserved.
 * Use of this source code is governed by a MIT-style
 * license that can be found in the LICENSE file.
 *
 * Alkaid is a BaaS service based on Hyperledger Fabric.
 */

package main

import (
	"bytes"
	"crypto/aes"
	"crypto/cipher"
	"crypto/hmac"
	"crypto/sha256"
	"encoding/base64"
	"fmt"

	"golang.org/x/crypto/hkdf"
	"golang.org/x/crypto/pbkdf2"
)

func main() {
	fmt.Printf("Email: %v\nMaster password: %<PASSWORD>", "<EMAIL>", "<PASSWORD>#Uh<PASSWORD>")
	dk := pbkdf2.Key([]byte("s9qn#UhhaDir5V2B"), []byte("<EMAIL>"), 100000, 32, sha256.New)
	fmt.Println("Master Key:", base64.StdEncoding.EncodeToString(dk))
	enc := make([]byte, 32)
	hkdf.Expand(sha256.New, dk, []byte("enc")).Read(enc)
	mac := make([]byte, 32)
	hkdf.Expand(sha256.New, dk, []byte("mac")).Read(mac)
	fmt.Println("Stretched Master Key:", base64.StdEncoding.EncodeToString(append(enc, mac...)))
	fmt.Println("Encryption Key:", base64.StdEncoding.EncodeToString(enc))
	fmt.Println("MAC Key:", base64.StdEncoding.EncodeToString(mac))

	paddedText := pkcs7Padding([]byte("This is a secret."))
	symmetricKey, _ := base64.StdEncoding.DecodeString("<KEY>)
	iv, err := base64.StdEncoding.DecodeString("c4LZ3sCdyOl7U7mzKuAUtg==")
	block, err := aes.NewCipher(symmetricKey[:32])
	if err != nil {
		panic(err)
	}
	mode := cipher.NewCBCEncrypter(block, iv)
	ciphertext := make([]byte, len(paddedText))
	mode.CryptBlocks(ciphertext, paddedText)
	fmt.Println("ciphertext:", base64.StdEncoding.EncodeToString(ciphertext))

	hc := hmac.New(sha256.New, symmetricKey[32:])
	var text []byte
	text = append(text, iv...)
	// text = append(text, []byte("|")...)
	text = append(text, ciphertext...)
	hc.Write(text)
	fmt.Println("sign text:", base64.StdEncoding.EncodeToString(hc.Sum(nil)))

	// paddedText = pkcs7Padding(ciphertext)
	// symmetricKey, _ = base64.StdEncoding.DecodeString("<KEY>)
	// iv, err = base64.StdEncoding.DecodeString("c4LZ3sCdyOl7U7mzKuAUtg==")
	// block, err = aes.NewCipher(symmetricKey[32:])
	// if err != nil {
	// 	panic(err)
	// }
	// mode = cipher.NewCBCEncrypter(block, iv)
	// signText := make([]byte, len(paddedText))
	// mode.CryptBlocks(signText, paddedText)
	// fmt.Println("sign text:", base64.StdEncoding.EncodeToString(signText))
}

func pkcs7Padding(src []byte) []byte {
	padding := aes.BlockSize - len(src)%aes.BlockSize

	var paddingText []byte
	if padding == 0 {
		paddingText = bytes.Repeat([]byte{byte(aes.BlockSize)}, aes.BlockSize)
	} else {
		paddingText = bytes.Repeat([]byte{byte(padding)}, padding)
	}
	return append(src, paddingText...)
}

func pkcs7UnPadding(src []byte) []byte {
	unPadding := int(src[len(src)-1])
	return src[:(len(src) - unPadding)]
}


<|start_filename|>internal/common/crypto/hmac/key.go<|end_filename|>
/*
 * Copyright (c) 2022. The Alkaid Authors. All rights reserved.
 * Use of this source code is governed by a MIT-style
 * license that can be found in the LICENSE file.
 *
 * Alkaid is a BaaS service based on Hyperledger Fabric.
 */

package hmac

import (
	"bytes"
	"crypto/hmac"
	"crypto/sha256"
	"crypto/sha512"
	"errors"
	"fmt"
	"hash"

	"github.com/yakumioto/alkaid/internal/common/crypto"
)

type Key struct {
	key       []byte
	algorithm crypto.Algorithm
}

func (h *Key) Bytes() ([]byte, error) {
	return nil, errors.New("not supported")
}

func (h *Key) SKI() []byte {
	sha := sha256.New()
	sha.Write(h.key)
	return sha.Sum(nil)
}

func (h *Key) Symmetric() bool {
	return false
}

func (h *Key) Private() bool {
	return true
}

func (h *Key) PublicKey() (crypto.Key, error) {
	return nil, errors.New("cannot call this method on a hmac key")
}

func (h *Key) Sign(digest []byte) ([]byte, error) {
	var hc hash.Hash
	switch h.algorithm {
	case crypto.HmacSha256:
		hc = hmac.New(sha256.New, h.key)
	case crypto.HmacSha512:
		hc = hmac.New(sha512.New, h.key)
	default:
		return nil, fmt.Errorf("not support %v algorithm", h.algorithm)
	}

	hc.Write(digest)

	return hc.Sum(nil), nil
}

func (h *Key) Verify(hash, sig []byte) bool {
	digest, err := h.Sign(hash)
	if err != nil {
		return false
	}

	return bytes.Equal(digest, sig)
}

func (h *Key) Encrypt(_ []byte) ([]byte, error) {
	return nil, errors.New("cannot call this method on a hmac hash")
}

func (h *Key) Decrypt(_ []byte) ([]byte, error) {
	return nil, errors.New("cannot call this method on a hmac hash")
}
