github.com/futurehomeno/fimpgo@v1.14.0/transport/signed_msg.go (about)

     1  package transport
     2  
     3  import (
     4  	"crypto"
     5  	"encoding/base64"
     6  	"errors"
     7  	"strings"
     8  
     9  	"github.com/golang-jwt/jwt"
    10  
    11  	"github.com/futurehomeno/fimpgo"
    12  	"github.com/futurehomeno/fimpgo/security"
    13  )
    14  
    15  /*
    16  https://tools.ietf.org/html/rfc7518#section-3
    17  +--------------+-------------------------------+--------------------+
    18  | "alg" Param  | Digital Signature or MAC      | Implementation     |
    19  | Value        | Algorithm                     | Requirements       |
    20  +--------------+-------------------------------+--------------------+
    21  | HS256        | HMAC using SHA-256            | Required           |
    22  | HS384        | HMAC using SHA-384            | Optional           |
    23  | HS512        | HMAC using SHA-512            | Optional           |
    24  | RS256        | RSASSA-PKCS1-v1_5 using       | Recommended        |
    25  |              | SHA-256                       |                    |
    26  | RS384        | RSASSA-PKCS1-v1_5 using       | Optional           |
    27  |              | SHA-384                       |                    |
    28  | RS512        | RSASSA-PKCS1-v1_5 using       | Optional           |
    29  |              | SHA-512                       |                    |
    30  | ES256        | ECDSA using P-256 and SHA-256 | Recommended+       |
    31  | ES384        | ECDSA using P-384 and SHA-384 | Optional           |
    32  | ES512        | ECDSA using P-521 and SHA-512 | Optional           |
    33  | PS256        | RSASSA-PSS using SHA-256 and  | Optional           |
    34  |              | MGF1 with SHA-256             |                    |
    35  | PS384        | RSASSA-PSS using SHA-384 and  | Optional           |
    36  |              | MGF1 with SHA-384             |                    |
    37  | PS512        | RSASSA-PSS using SHA-512 and  | Optional           |
    38  |              | MGF1 with SHA-512             |                    |
    39  | none         | No digital signature or MAC   | Optional           |
    40  |              | performed                     |                    |
    41  +--------------+-------------------------------+--------------------+
    42  */
    43  
    44  // Message encapsulation
    45  // Message signing
    46  // Plain fimp msg -> serialize into []binary -> generate signature -> base64 encode original message -> encapsulate into evt.transport.signed or cmd.transport.signed message -> add signature to props
    47  // {
    48  //  "type": "evt.transport.signed",
    49  //  "serv": "sensor_presence",
    50  //  "val_t": "bin",
    51  //  "val": "ewogICJ0eXBlIjogImV2dC5wcmVzZW5jZS5yZXBvcnQiLAogICJzZXJ2IjogInNlbnNvcl9wcmVzZW5jZSIsCiAgInZhbF90IjogImJvb2wiLAogICJ2YWwiOiB0cnVlLAogICJ0YWdzIjogbnVsbCwKICAicHJvcHMiOiBudWxsLAogICJ2ZXIiOiAiMSIsCiAgImNvcmlkIjogIiIsCiAgImN0aW1lIjogIjIwMjAtMDUtMDZUMDk6Mjk6NTkuNTI3KzA1OjAwIiwKICAidWlkIjogIjczZjYxMDMwLTQzOTktNGQyMS1iYjk3LTRjYTdjMTYyM2FjMyIKfQ==",
    52  //  "tags": null,
    53  //  "props": {
    54  //  	"user_id":"aleks@gmail.com",
    55  //      "device_id":"dafdsafsf",
    56  //      "sig":"<computed signature>",
    57  //		"alg":"ES256"
    58  //  },
    59  //  "ver": "1",
    60  //  "corid": "",
    61  //  "ctime": "2020-05-06T09:29:59.527+05:00",
    62  //  "uid": "73f61030-4399-4d21-bb97-4ca7c1623ac3"
    63  //}
    64  
    65  // SignMessageES256 encapsulate original message into special transport message with added signature.
    66  func SignMessageES256(payload *fimpgo.FimpMessage, requestMsg *fimpgo.FimpMessage, userId string, keys *security.EcdsaKey, props *fimpgo.Props) (*fimpgo.FimpMessage, error) {
    67  	serializedMsg, err := payload.SerializeToJson()
    68  	if err != nil {
    69  		return nil, err
    70  	}
    71  	if props == nil {
    72  		props = &fimpgo.Props{"user_id": userId}
    73  	}
    74  	msgType := "evt.transport.signed"
    75  	if strings.Contains(payload.Type, "cmd") {
    76  		msgType = "cmd.transport.signed"
    77  	}
    78  	signedMsg := fimpgo.NewBinaryMessage(msgType, payload.Service, serializedMsg, *props, nil, requestMsg)
    79  
    80  	signingMethodES256 := &jwt.SigningMethodECDSA{Name: "ES256", Hash: crypto.SHA256, KeySize: 32, CurveBits: 256}
    81  	signature, err := signingMethodES256.Sign(signedMsg.Value.(string), keys.PrivateKey())
    82  	if err != nil {
    83  		return nil, err
    84  	}
    85  	signedMsg.Properties["sig"] = signature
    86  	return signedMsg, nil
    87  }
    88  
    89  func GetVerifiedMessageES256(signedMsg *fimpgo.FimpMessage, key *security.EcdsaKey) (*fimpgo.FimpMessage, error) {
    90  
    91  	if signedMsg.Type != "cmd.transport.signed" && signedMsg.Type != "evt.transport.signed" {
    92  		return nil, errors.New("incorrect message type")
    93  	}
    94  	origMsgBin, ok1 := signedMsg.Value.(string)
    95  	if !ok1 {
    96  		return nil, errors.New("incorrect encapsulated message format")
    97  	}
    98  	sig, ok2 := signedMsg.Properties["sig"]
    99  	if !ok2 {
   100  		return nil, errors.New("missing signature")
   101  	}
   102  	signingMethodES256 := &jwt.SigningMethodECDSA{Name: "ES256", Hash: crypto.SHA256, KeySize: 32, CurveBits: 256}
   103  	err := signingMethodES256.Verify(origMsgBin, sig, key.PublicKey())
   104  	if err == nil {
   105  		decodedPayloadBin, err := base64.StdEncoding.DecodeString(origMsgBin)
   106  		if err != nil {
   107  			return nil, err
   108  		}
   109  		return fimpgo.NewMessageFromBytes(decodedPayloadBin)
   110  	} else {
   111  		return nil, err
   112  	}
   113  }