mirror of
https://github.com/balzack/databag.git
synced 2025-02-12 03:29:16 +00:00
refactor message util
This commit is contained in:
parent
060f826da4
commit
06958c80c5
90
doc/api.oa3
90
doc/api.oa3
@ -4408,74 +4408,110 @@ components:
|
||||
Authenticate:
|
||||
type: object
|
||||
required:
|
||||
- guid
|
||||
- token
|
||||
- timestamp
|
||||
properties:
|
||||
guid:
|
||||
type: string
|
||||
token:
|
||||
type: string
|
||||
timestamp:
|
||||
|
||||
Identity:
|
||||
type: object
|
||||
required:
|
||||
- revision
|
||||
- version
|
||||
- node
|
||||
properties:
|
||||
revision:
|
||||
type: integer
|
||||
format: int64
|
||||
handle:
|
||||
type: string
|
||||
name:
|
||||
type: string
|
||||
description:
|
||||
type: string
|
||||
location:
|
||||
type: string
|
||||
image:
|
||||
type: string
|
||||
format: base64 encoded image
|
||||
version:
|
||||
type: string
|
||||
node:
|
||||
type: string
|
||||
|
||||
Connect:
|
||||
type: object
|
||||
required:
|
||||
- requestorId
|
||||
- requestedId
|
||||
- timestamp
|
||||
- profile
|
||||
- contact
|
||||
- token
|
||||
- contentRevision
|
||||
properties:
|
||||
requestorcardId:
|
||||
contact:
|
||||
type: string
|
||||
requestedcardId:
|
||||
type: string
|
||||
timestamp:
|
||||
type: integer
|
||||
format: int64
|
||||
profile:
|
||||
$ref: '#/components/schemas/Profile'
|
||||
token:
|
||||
type: string
|
||||
contentRevision:
|
||||
type: integer
|
||||
format: int64
|
||||
profileRevision:
|
||||
type: integer
|
||||
format: int64
|
||||
handle:
|
||||
type: string
|
||||
name:
|
||||
type: string
|
||||
description:
|
||||
type: string
|
||||
location:
|
||||
type: string
|
||||
image:
|
||||
type: string
|
||||
format: base64 encoded image
|
||||
version:
|
||||
type: string
|
||||
node:
|
||||
type: string
|
||||
|
||||
Disconnect:
|
||||
type: object
|
||||
required:
|
||||
- requestorId
|
||||
- requestedId
|
||||
- timestamp
|
||||
- contact
|
||||
properties:
|
||||
requestorId:
|
||||
contact:
|
||||
type: string
|
||||
requestedId:
|
||||
|
||||
SignedData:
|
||||
type: object
|
||||
required:
|
||||
- guid
|
||||
- timestamp
|
||||
- messageType
|
||||
- value
|
||||
properties:
|
||||
guid:
|
||||
type: string
|
||||
timestamp:
|
||||
type: integer
|
||||
format: int64
|
||||
messageType:
|
||||
type: string
|
||||
enum: [Connect, Disconnect, Identity, Authenticate]
|
||||
value:
|
||||
type: string
|
||||
format: json string of Connect, Disconnect, Authenticate, or Profile
|
||||
|
||||
DataMessage:
|
||||
type: object
|
||||
required:
|
||||
- message
|
||||
- messageType
|
||||
- keyType
|
||||
- publicKey
|
||||
- signature
|
||||
- signatureType
|
||||
properties:
|
||||
messageType:
|
||||
type: string
|
||||
enum: [Connect, Disconnect, Profile, Authenticate]
|
||||
message:
|
||||
type: string
|
||||
format: base64 encoded object
|
||||
format: base64 encoded json string of SignedData
|
||||
keyType:
|
||||
type: string
|
||||
enum: [RSA4096, RSA2048]
|
||||
|
@ -1,74 +1,38 @@
|
||||
package databag
|
||||
|
||||
import (
|
||||
"crypto"
|
||||
"crypto/rand"
|
||||
"crypto/sha256"
|
||||
"crypto/rsa"
|
||||
"net/http"
|
||||
"encoding/json"
|
||||
"encoding/base64"
|
||||
"time"
|
||||
)
|
||||
|
||||
func Authorize(w http.ResponseWriter, r *http.Request) {
|
||||
|
||||
account, res := BearerAppToken(r, true);
|
||||
if res != nil {
|
||||
w.WriteHeader(http.StatusUnauthorized)
|
||||
ErrResponse(w, http.StatusUnauthorized, res)
|
||||
return
|
||||
}
|
||||
if account.Disabled {
|
||||
w.WriteHeader(http.StatusGone);
|
||||
ErrResponse(w, http.StatusGone, res)
|
||||
return
|
||||
}
|
||||
detail := account.AccountDetail
|
||||
|
||||
// extract token from body
|
||||
var token string
|
||||
err := ParseRequest(r, w, &token)
|
||||
if err != nil {
|
||||
w.WriteHeader(http.StatusBadRequest)
|
||||
return
|
||||
}
|
||||
|
||||
// load details to sign data
|
||||
if account.AccountDetail.KeyType != APP_RSA2048 && account.AccountDetail.KeyType != APP_RSA4096 {
|
||||
w.WriteHeader(http.StatusServiceUnavailable)
|
||||
return
|
||||
}
|
||||
privateKey, res := ParseRsaPrivateKeyFromPemStr(account.AccountDetail.PrivateKey);
|
||||
res = ParseRequest(r, w, &token)
|
||||
if res != nil {
|
||||
w.WriteHeader(http.StatusInternalServerError)
|
||||
ErrResponse(w, http.StatusBadRequest, res)
|
||||
return
|
||||
}
|
||||
|
||||
// generate message
|
||||
auth := Authenticate{
|
||||
Guid: account.Guid,
|
||||
Token: token,
|
||||
Timestamp: time.Now().Unix(),
|
||||
}
|
||||
var data []byte
|
||||
data, err = json.Marshal(auth);
|
||||
if err != nil {
|
||||
w.WriteHeader(http.StatusInternalServerError)
|
||||
// generate auth DataMessage
|
||||
auth := Authenticate{ Token: token }
|
||||
msg, res := WriteDataMessage(detail.PrivateKey, detail.PublicKey, detail.KeyType,
|
||||
APP_SIGNPKCS1V15, account.Guid, APP_MSGAUTHENTICATE, &auth)
|
||||
if res != nil {
|
||||
ErrResponse(w, http.StatusInternalServerError, res)
|
||||
return
|
||||
}
|
||||
hash := sha256.Sum256(data);
|
||||
var signature []byte
|
||||
signature, err = rsa.SignPKCS1v15(rand.Reader, privateKey, crypto.SHA256, hash[:])
|
||||
if err != nil {
|
||||
w.WriteHeader(http.StatusInternalServerError)
|
||||
return
|
||||
}
|
||||
msg := DataMessage{
|
||||
MessageType: "authenticate",
|
||||
Message: base64.StdEncoding.EncodeToString([]byte(data)),
|
||||
KeyType: account.AccountDetail.KeyType,
|
||||
PublicKey: base64.StdEncoding.EncodeToString([]byte(account.AccountDetail.PublicKey)),
|
||||
Signature: base64.StdEncoding.EncodeToString(signature),
|
||||
SignatureType: "PKCS1v15",
|
||||
}
|
||||
|
||||
WriteResponse(w, msg)
|
||||
}
|
||||
|
@ -7,3 +7,9 @@ const APP_CREATEEXPIRE = 86400
|
||||
const APP_KEYSIZE = 4096
|
||||
const APP_RSA4096 = "RSA4096"
|
||||
const APP_RSA2048 = "RSA2048"
|
||||
const APP_SIGNPKCS1V15 = "PKCS1v15"
|
||||
const APP_SIGNPSS = "PSS"
|
||||
const APP_MSGAUTHENTICATE = "authenticate"
|
||||
const APP_MSGIDENTITY = "identity"
|
||||
const APP_MSGCONNECT = "connect"
|
||||
const APP_MSGDISCONNECT = "disconnect"
|
||||
|
143
net/server/internal/messageUtil.go
Normal file
143
net/server/internal/messageUtil.go
Normal file
@ -0,0 +1,143 @@
|
||||
package databag
|
||||
|
||||
import (
|
||||
"errors"
|
||||
"crypto"
|
||||
"crypto/rand"
|
||||
"crypto/sha256"
|
||||
"crypto/rsa"
|
||||
"encoding/json"
|
||||
"encoding/hex"
|
||||
"encoding/base64"
|
||||
"time"
|
||||
)
|
||||
|
||||
func ReadDataMessage(msg *DataMessage, obj interface{}) (string, string, int64, error) {
|
||||
|
||||
var data []byte
|
||||
var hash [32]byte
|
||||
var err error
|
||||
var publicKey *rsa.PublicKey
|
||||
|
||||
if msg.KeyType != APP_RSA4096 && msg.KeyType != APP_RSA2048 {
|
||||
return "", "", 0, errors.New("unsupported key type")
|
||||
}
|
||||
|
||||
// extract public key
|
||||
data, err = base64.StdEncoding.DecodeString(msg.PublicKey)
|
||||
if err != nil {
|
||||
return "", "", 0, err
|
||||
}
|
||||
publicKey, err = ParseRsaPublicKeyFromPemStr(string(data))
|
||||
if err != nil {
|
||||
return "", "", 0, err
|
||||
}
|
||||
|
||||
// compute guid
|
||||
hash = sha256.Sum256(data)
|
||||
guid := hex.EncodeToString(hash[:])
|
||||
|
||||
// extract signature
|
||||
data, err = base64.StdEncoding.DecodeString(msg.Signature)
|
||||
if err != nil {
|
||||
return "", "", 0, err
|
||||
}
|
||||
signature := data
|
||||
|
||||
// verify hash
|
||||
data, err = base64.StdEncoding.DecodeString(msg.Message)
|
||||
if err != nil {
|
||||
return "", "", 0, err
|
||||
}
|
||||
hash = sha256.Sum256(data)
|
||||
if msg.SignatureType == APP_SIGNPKCS1V15 {
|
||||
err = rsa.VerifyPKCS1v15(publicKey, crypto.SHA256, hash[:], signature)
|
||||
if err != nil {
|
||||
return "", "", 0, err
|
||||
}
|
||||
} else if msg.SignatureType == APP_SIGNPSS {
|
||||
err = rsa.VerifyPSS(publicKey, crypto.SHA256, hash[:], signature, nil)
|
||||
if err != nil {
|
||||
return "", "", 0, err
|
||||
}
|
||||
} else {
|
||||
return "", "", 0, errors.New("unsupported signature type")
|
||||
}
|
||||
|
||||
// extract data
|
||||
var signedData SignedData
|
||||
err = json.Unmarshal(data, &signedData);
|
||||
if err != nil {
|
||||
return "", "", 0, err
|
||||
}
|
||||
|
||||
// validate signer
|
||||
if signedData.Guid != guid {
|
||||
return "", "", 0, errors.New("invalid message source")
|
||||
}
|
||||
|
||||
// extract data
|
||||
err = json.Unmarshal([]byte(signedData.Value), obj)
|
||||
if err != nil {
|
||||
return "", "", 0, err
|
||||
}
|
||||
|
||||
return guid, signedData.MessageType, signedData.Timestamp, nil
|
||||
}
|
||||
|
||||
func WriteDataMessage(privateKey string, publicKey string, keyType string,
|
||||
signType string, guid string, messageType string, obj interface{}) (*DataMessage, error) {
|
||||
|
||||
var data []byte
|
||||
var hash [32]byte
|
||||
var err error
|
||||
var private *rsa.PrivateKey
|
||||
|
||||
// create message to sign
|
||||
data, err = json.Marshal(obj)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
var signedData SignedData
|
||||
signedData.Guid = guid
|
||||
signedData.Timestamp = time.Now().Unix()
|
||||
signedData.MessageType = messageType
|
||||
signedData.Value = string(data)
|
||||
data, err = json.Marshal(&signedData)
|
||||
message := base64.StdEncoding.EncodeToString(data)
|
||||
|
||||
if keyType != APP_RSA2048 && keyType != APP_RSA4096 {
|
||||
return nil, errors.New("unsupported key type")
|
||||
}
|
||||
|
||||
// get private key
|
||||
private, err = ParseRsaPrivateKeyFromPemStr(privateKey)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
key := base64.StdEncoding.EncodeToString([]byte(publicKey))
|
||||
|
||||
// compute signature
|
||||
hash = sha256.Sum256(data)
|
||||
if signType == APP_SIGNPKCS1V15 {
|
||||
data, err = rsa.SignPKCS1v15(rand.Reader, private, crypto.SHA256, hash[:])
|
||||
} else if signType == APP_SIGNPSS {
|
||||
data, err = rsa.SignPSS(rand.Reader, private, crypto.SHA256, hash[:], nil)
|
||||
} else {
|
||||
return nil, errors.New("unsupported signature type")
|
||||
}
|
||||
signature := base64.StdEncoding.EncodeToString(data)
|
||||
|
||||
dataMessage := DataMessage{
|
||||
PublicKey: key,
|
||||
KeyType: keyType,
|
||||
Signature: signature,
|
||||
SignatureType: signType,
|
||||
Message: message,
|
||||
}
|
||||
return &dataMessage, nil
|
||||
}
|
||||
|
||||
|
||||
|
||||
|
@ -68,12 +68,6 @@ type Asset struct {
|
||||
Status string `json:"status,omitempty"`
|
||||
}
|
||||
|
||||
type Authenticate struct {
|
||||
Guid string `json:"guid"`
|
||||
Token string `json:"token"`
|
||||
Timestamp int64 `json:"timestamp"`
|
||||
}
|
||||
|
||||
type Card struct {
|
||||
CardId string `json:"cardId"`
|
||||
CardProfile *CardProfile `json:"cardProfile"`
|
||||
@ -107,15 +101,6 @@ type CardView struct {
|
||||
ContentRevision int64 `json:"contentRevision"`
|
||||
}
|
||||
|
||||
type Connect struct {
|
||||
RequestorcardId string `json:"requestorcardId,omitempty"`
|
||||
RequestedcardId string `json:"requestedcardId,omitempty"`
|
||||
Timestamp int64 `json:"timestamp"`
|
||||
Profile *Profile `json:"profile"`
|
||||
Token string `json:"token"`
|
||||
ContentRevision int64 `json:"contentRevision"`
|
||||
}
|
||||
|
||||
type ContentArticlesBody struct {
|
||||
Labels []string `json:"labels"`
|
||||
Groups []string `json:"groups"`
|
||||
@ -126,15 +111,6 @@ type ContentLabelsBody struct {
|
||||
Data string `json:"data"`
|
||||
}
|
||||
|
||||
type DataMessage struct {
|
||||
MessageType string `json:"messageType"`
|
||||
Message string `json:"message"`
|
||||
KeyType string `json:"keyType"`
|
||||
PublicKey string `json:"publicKey"`
|
||||
Signature string `json:"signature"`
|
||||
SignatureType string `json:"signatureType"`
|
||||
}
|
||||
|
||||
type Dialogue struct {
|
||||
DialogueId string `json:"dialogueId"`
|
||||
DialogueRevison int64 `json:"dialogueRevison,omitempty"`
|
||||
@ -156,12 +132,6 @@ type DialogueInsights struct {
|
||||
Status string `json:"status,omitempty"`
|
||||
}
|
||||
|
||||
type Disconnect struct {
|
||||
RequestorId string `json:"requestorId"`
|
||||
RequestedId string `json:"requestedId"`
|
||||
Timestamp int64 `json:"timestamp"`
|
||||
}
|
||||
|
||||
type Group struct {
|
||||
GroupId string `json:"groupId"`
|
||||
GroupRevision int64 `json:"groupRevision"`
|
||||
@ -305,3 +275,52 @@ type Tunnel struct {
|
||||
Type_ string `json:"type"`
|
||||
Data string `json:"data,omitempty"`
|
||||
}
|
||||
|
||||
type DataMessage struct {
|
||||
Message string `json:"message"`
|
||||
KeyType string `json:"keyType"`
|
||||
PublicKey string `json:"publicKey"`
|
||||
Signature string `json:"signature"`
|
||||
SignatureType string `json:"signatureType"`
|
||||
}
|
||||
|
||||
type SignedData struct {
|
||||
Guid string `json:"guid"`
|
||||
Timestamp int64 `json:"timestamp"`
|
||||
MessageType string `json:"messageType"`
|
||||
Value string `json:"value"`
|
||||
}
|
||||
|
||||
type Identity struct {
|
||||
Revision int64 `json:"revision"`
|
||||
Handle string `json:"handle,omitempty"`
|
||||
Name string `json:"name,omitempty"`
|
||||
Description string `json:"description,omitempty"`
|
||||
Location string `json:"location,omitempty"`
|
||||
Image string `json:"image,omitempty"`
|
||||
Version string `json:"version"`
|
||||
Node string `json:"node"`
|
||||
}
|
||||
|
||||
type Connect struct {
|
||||
Contact string `json:"contact"`
|
||||
Token string `json:"token"`
|
||||
ContentRevision int64 `json:"contentRevision"`
|
||||
ProfileRevision int64 `json:"profileRevision,omitempty"`
|
||||
Handle string `json:"handle,omitempty"`
|
||||
Name string `json:"name,omitempty"`
|
||||
Description string `json:"description,omitempty"`
|
||||
Location string `json:"location,omitempty"`
|
||||
Image string `json:"image,omitempty"`
|
||||
Version string `json:"version,omitempty"`
|
||||
Node string `json:"node,omitempty"`
|
||||
}
|
||||
|
||||
type Disconnect struct {
|
||||
Contact string `json:"contact"`
|
||||
}
|
||||
|
||||
type Authenticate struct {
|
||||
Token string `json:"token"`
|
||||
}
|
||||
|
||||
|
@ -2,12 +2,7 @@ package databag
|
||||
|
||||
import (
|
||||
"testing"
|
||||
"encoding/hex"
|
||||
"encoding/json"
|
||||
"encoding/base64"
|
||||
"crypto/sha256"
|
||||
"crypto/rsa"
|
||||
"crypto"
|
||||
"time"
|
||||
"github.com/gorilla/websocket"
|
||||
"github.com/stretchr/testify/assert"
|
||||
@ -56,24 +51,16 @@ func TestAttachAccount(t *testing.T) {
|
||||
assert.NoError(t, ReadResponse(w, &message))
|
||||
|
||||
// validate message
|
||||
assert.Equal(t, "PKCS1v15", message.SignatureType)
|
||||
var data []byte
|
||||
var hash [32]byte
|
||||
data, _ = base64.StdEncoding.DecodeString(message.PublicKey)
|
||||
hash = sha256.Sum256(data)
|
||||
guid := hex.EncodeToString(hash[:])
|
||||
publicKey, _ := ParseRsaPublicKeyFromPemStr(string(data))
|
||||
signature, _ := base64.StdEncoding.DecodeString(message.Signature)
|
||||
data, _ = base64.StdEncoding.DecodeString(message.Message)
|
||||
hash = sha256.Sum256(data)
|
||||
assert.NoError(t, rsa.VerifyPKCS1v15(publicKey, crypto.SHA256, hash[:], signature))
|
||||
var auth Authenticate
|
||||
assert.NoError(t, json.Unmarshal(data,&auth))
|
||||
assert.Equal(t, "aabbccdd", auth.Token)
|
||||
assert.Equal(t, guid, auth.Guid)
|
||||
guid, msgType, ts, err := ReadDataMessage(&message, &auth)
|
||||
if err != nil {
|
||||
PrintMsg(err)
|
||||
}
|
||||
cur := time.Now().Unix()
|
||||
assert.GreaterOrEqual(t, cur, auth.Timestamp)
|
||||
assert.Less(t, cur - 60, auth.Timestamp)
|
||||
assert.GreaterOrEqual(t, cur, ts)
|
||||
assert.Less(t, cur - 60, ts)
|
||||
assert.Equal(t, "aabbccdd", auth.Token)
|
||||
assert.Equal(t, msgType, APP_MSGAUTHENTICATE)
|
||||
|
||||
// app connects websocket
|
||||
ws := getTestWebsocket()
|
||||
|
Loading…
Reference in New Issue
Block a user