refactor message util

This commit is contained in:
Roland Osborne 2022-01-19 23:45:53 -08:00
parent 060f826da4
commit 06958c80c5
6 changed files with 282 additions and 127 deletions

View File

@ -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]

View File

@ -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)
}

View File

@ -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"

View 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
}

View File

@ -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"`
}

View File

@ -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()