Skip to content

Commit

Permalink
Merge pull request #1 from inxy-payments/v0.0.1
Browse files Browse the repository at this point in the history
add signature package
  • Loading branch information
apvalkov authored May 23, 2024
2 parents 6be5c8a + c5cb828 commit 24a8baa
Show file tree
Hide file tree
Showing 14 changed files with 433 additions and 5 deletions.
39 changes: 39 additions & 0 deletions .github/workflows/go.yaml
Original file line number Diff line number Diff line change
@@ -0,0 +1,39 @@
name: Go

on:
push:
branches: [ master ]
pull_request:
branches: [ master ]

jobs:
test:
runs-on: ubuntu-latest
steps:
- uses: actions/checkout@v4

- name: Set up Go
uses: actions/setup-go@v5
with:
go-version: '1.22.2'
cache-dependency-path: go.sum

- name: Test
run: go test -v ./...

linter:
name: lint
runs-on: ubuntu-latest
steps:
- uses: actions/checkout@v4

- uses: actions/setup-go@v5
with:
go-version: '1.22.2'
cache: false

- name: golangci-lint
uses: golangci/golangci-lint-action@v6
with:
version: v1.58.1
args: --timeout=30m --config=./golangci.pipeline.yml
3 changes: 2 additions & 1 deletion .gitignore
Original file line number Diff line number Diff line change
@@ -1 +1,2 @@
.idea
.idea
vendor
54 changes: 50 additions & 4 deletions README.md
Original file line number Diff line number Diff line change
@@ -1,7 +1,53 @@
# Documentation
# signature-sdk-go

## Introduction

Welcome to the documentation for **inxy-payments/signature-sdk-go**! This library is designed to help you sign messages using a private key. Whether you're a beginner or an experienced developer, this guide will help you get the most out of this library.
The `signature-sdk-go` package provides functionality for signing messages using RSA private keys and verifying signatures using the corresponding RSA public keys. This package is useful for ensuring the integrity and authenticity of messages in your Go applications.

## Installation

To use this package, you need to have Go installed. You can download it from [golang.org](https://golang.org/dl/).

To include this package in your project, run:

```sh
go get github.com/inxy-payments/signature-sdk-go
```

## Usage
Below is an example of how to use the signature-sdk-go package to sign a message and verify the signature.

## Example

```go
package main

import (
"fmt"
"github.com/inxy-payments/signature-sdk-go/pkg/converter"
"github.com/inxy-payments/signature-sdk-go/pkg/model"
rsaSignatureService "github.com/inxy-payments/signature-sdk-go/pkg/service/rsa"
"log"
)

func main() {
const privateKeyExample = "MIIEowIBAAKCAQEAviXbecnlVp3qPbTAREhr5t8mDTLjJD3cE5SI8LsIjE2wbs23nkCYe47HveFJf+yqD1FqAaDYi+svRsPqVKVD/3HcAkx+Qn1cPQyVmFrbj0Q5U6vP7EuVi4ICG3BX5+l5DCAp5uIcLP9sr9h+4KxaMGaztYzutHjfsZRX99AwLJfw5axVyGIhZb3fZ1YyeI/P2AGQn8iY2XZQGYwc8emyqh3B0zByKLSMuuRDu20jZYXTrWDf+uZDSjZoqcXKmZfJIWxuufICa1H1xIxCxPjV91zG6AKPsqA74TbO91nZGE1yhPE/BA8dkgE+1NEQDWYNMs8cFqQLrLouRqY+g+HVzQIDAQABAoIBADXD+JooR2vFfS1zhYYJQFFouZaz09w0jZ0Pu+Ttzc32TbQXARuDQlp1le8P27uLTM7GA4ZwV6rAln6Y+RJ0JJT/OemAfZcJYWJ1w3rv/fM1pEwPYdx7xs5KtZPSoViXAL43/gEl4DetBat3OPEIavwSni/wqLJpFz9cJb+Ro32H//TECzivHvSxDDUrhMDTQHQeBw7ATiIVNT2iPutprKqjMNga4cg/vxm5BgiMTMjo2adbKQPKa4k5q3YOuFrkacjwbuRBpZtnqWd568moERMym8iHrdsUI/tgprQI7wxev/gj+nk5cbZoEr0fBvoTH/HDna3G5rGOE5uti5adn6ECgYEA6xvoG961ovmZinScEOx/xcrWT2Q1TuazOQesPf0ywfT/BAUCqxVQeEVHV78089HSP5QHHes5iq+MFAL8/hGlPq0EoHDDE1hyxS0gbZcR6oEmRKyOfSbShGn22Cuw0JWt7rOHzdHj8KCpZgpXLJUyKL0Tx2vCKaM3zb7lOFkIshkCgYEAzws06a5LJY4cuKjZkwews0MxCm7SpQXuoSnkOo1ROLukgJ209Le5IdNYC47GYDcwhkuY4tSRxyfq7rd4Upui9XjkaqiblpqOVcdajAI8XgmCbzwxaUcvomQpvC8lFocoOGtAw2P8TZ3WGOb3GoF/o1pQ0Dh9HLOrAPo79RONv9UCgYEA0s7L+SlhPgeF17KlOTuFecldDgSxE9UhwDIUC+Ua/PR7MJR5hwNuiti7ln8YsMJjPaSyGO6QQr0S4eKoC/uwahli+6UAFTmKdyf2Wq1JYDZ7JLqAbNFBk38b2UqbmPuM4GpTi4X2Vw0HtznwXkZMmmCm+nmxt/nkkHPpPfP/KwkCgYBQDphGJ2PdQKcwa/G9XYLgvgFvdEy1DKcp4CXk0hHu6vd/1/tJiOToBG2OAoYIXC7CLucOBn3b0T6RUZYP8yg+3KEN8OZAhMC2wF/ttUucXPb3hgHhIGp1018j6eLgZCCUODyRkM7VQEux01UHBb3R7zFCYiVWfM6JkTiv2gC8hQKBgHHFdpHeTu0vJf480UHd8e0vLfpRPqWhAaZBHRtY05Hv9/ClGR8sZ471e1Nk3LJNmOzzL/HN89oD8gHSJGmqHjmbUwpIQK8zCCpuwzAjbdghaHgupss4TJApnS8HuybW+eGBu/AIUlJqOCCImfoB5Gurk5VsjEpo2+lWIFKEJrlG"

privateKey, err := converter.ToRSAPrivateKeyFromString(privateKeyExample)
if err != nil {
log.Fatal(err)
}

service := rsaSignatureService.NewRSASignatureService(privateKey)
message := model.NewMessage("test message")

signature, err := service.SignMessage(message)
if err != nil {
log.Fatal(err)
}

fmt.Println(signature.Time)
fmt.Println(signature.Signature)
}



```
11 changes: 11 additions & 0 deletions go.mod
Original file line number Diff line number Diff line change
@@ -0,0 +1,11 @@
module github.com/inxy-payments/signature-sdk-go

go 1.22.2

require github.com/stretchr/testify v1.9.0

require (
github.com/davecgh/go-spew v1.1.1 // indirect
github.com/pmezard/go-difflib v1.0.0 // indirect
gopkg.in/yaml.v3 v3.0.1 // indirect
)
10 changes: 10 additions & 0 deletions go.sum
Original file line number Diff line number Diff line change
@@ -0,0 +1,10 @@
github.com/davecgh/go-spew v1.1.1 h1:vj9j/u1bqnvCEfJOwUhtlOARqs3+rkHYY13jYWTU97c=
github.com/davecgh/go-spew v1.1.1/go.mod h1:J7Y8YcW2NihsgmVo/mv3lAwl/skON4iLHjSsI+c5H38=
github.com/pmezard/go-difflib v1.0.0 h1:4DBwDE0NGyQoBHbLQYPwSUPoCMWR5BEzIk/f1lZbAQM=
github.com/pmezard/go-difflib v1.0.0/go.mod h1:iKH77koFhYxTK1pcRnkKkqfTogsbg7gZNVY4sRDYZ/4=
github.com/stretchr/testify v1.9.0 h1:HtqpIVDClZ4nwg75+f6Lvsy/wHu+3BoSGCbBAcpTsTg=
github.com/stretchr/testify v1.9.0/go.mod h1:r2ic/lqez/lEtzL7wO/rwa5dbSLXVDPFyf8C91i36aY=
gopkg.in/check.v1 v0.0.0-20161208181325-20d25e280405 h1:yhCVgyC4o1eVCa2tZl7eS0r+SDo693bJlVdllGtEeKM=
gopkg.in/check.v1 v0.0.0-20161208181325-20d25e280405/go.mod h1:Co6ibVJAznAaIkqp8huTwlJQCZ016jof/cbN4VW5Yz0=
gopkg.in/yaml.v3 v3.0.1 h1:fxVm/GzAzEWqLHuvctI91KS9hhNmmWOoWu0XTYJS7CA=
gopkg.in/yaml.v3 v3.0.1/go.mod h1:K4uyk7z7BCEPqu6E+C64Yfv1cQ7kz7rIZviUmN+EgEM=
57 changes: 57 additions & 0 deletions golangci.pipeline.yml
Original file line number Diff line number Diff line change
@@ -0,0 +1,57 @@
# More info on config here: https://golangci-lint.run/usage/configuration/#config-file
run:
concurrency: 8
timeout: 10m
issues-exit-code: 1
tests: true
issues:
exclude-dirs:
- bin
- vendor
- var
- tmp
- .cache
exclude-files:
- \.pb\.go$
- \.pb\.gw\.go$

output:
formats:
- format: colored-line-number
print-issued-lines: true
print-linter-name: true

linters-settings:
govet:
shadow: true
dupl:
threshold: 100
goconst:
min-len: 2
min-occurrences: 2

linters:
disable-all: true
enable:
- errcheck
- goconst
- goimports
- gosec
- govet
- gosimple
- gofmt
- ineffassign
- revive
- typecheck
- unused
- staticcheck

issues:
exclude-use-default: false
exclude:
# _ instead of err checks
- G104
- exported func .* returns unexported types .*, which can be annoying to use
- should have a package comment
- don't use an underscore in package name
- or be unexported
21 changes: 21 additions & 0 deletions pkg/converter/private_key.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,21 @@
package converter

import (
"crypto/rsa"
"crypto/x509"
"encoding/base64"
)

func ToRSAPrivateKeyFromString(privateKey string) (*rsa.PrivateKey, error) {
decodedPrivateKey, err := base64.StdEncoding.DecodeString(privateKey)
if err != nil {
return nil, err
}

rsaPrivateKey, err := x509.ParsePKCS1PrivateKey(decodedPrivateKey)
if err != nil {
return nil, err
}

return rsaPrivateKey, nil
}
75 changes: 75 additions & 0 deletions pkg/converter/tests/to_rsa_private_key_from_string_test.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,75 @@
package tests

import (
"crypto/rsa"
"crypto/x509"
"encoding/asn1"
"encoding/base64"
"testing"

"github.com/inxy-payments/signature-sdk-go/pkg/converter"
"github.com/stretchr/testify/assert"
)

const privateKeyExample = "MIIEowIBAAKCAQEAviXbecnlVp3qPbTAREhr5t8mDTLjJD3cE5SI8LsIjE2wbs23nkCYe47HveFJf+yqD1FqAaDYi+svRsPqVKVD/3HcAkx+Qn1cPQyVmFrbj0Q5U6vP7EuVi4ICG3BX5+l5DCAp5uIcLP9sr9h+4KxaMGaztYzutHjfsZRX99AwLJfw5axVyGIhZb3fZ1YyeI/P2AGQn8iY2XZQGYwc8emyqh3B0zByKLSMuuRDu20jZYXTrWDf+uZDSjZoqcXKmZfJIWxuufICa1H1xIxCxPjV91zG6AKPsqA74TbO91nZGE1yhPE/BA8dkgE+1NEQDWYNMs8cFqQLrLouRqY+g+HVzQIDAQABAoIBADXD+JooR2vFfS1zhYYJQFFouZaz09w0jZ0Pu+Ttzc32TbQXARuDQlp1le8P27uLTM7GA4ZwV6rAln6Y+RJ0JJT/OemAfZcJYWJ1w3rv/fM1pEwPYdx7xs5KtZPSoViXAL43/gEl4DetBat3OPEIavwSni/wqLJpFz9cJb+Ro32H//TECzivHvSxDDUrhMDTQHQeBw7ATiIVNT2iPutprKqjMNga4cg/vxm5BgiMTMjo2adbKQPKa4k5q3YOuFrkacjwbuRBpZtnqWd568moERMym8iHrdsUI/tgprQI7wxev/gj+nk5cbZoEr0fBvoTH/HDna3G5rGOE5uti5adn6ECgYEA6xvoG961ovmZinScEOx/xcrWT2Q1TuazOQesPf0ywfT/BAUCqxVQeEVHV78089HSP5QHHes5iq+MFAL8/hGlPq0EoHDDE1hyxS0gbZcR6oEmRKyOfSbShGn22Cuw0JWt7rOHzdHj8KCpZgpXLJUyKL0Tx2vCKaM3zb7lOFkIshkCgYEAzws06a5LJY4cuKjZkwews0MxCm7SpQXuoSnkOo1ROLukgJ209Le5IdNYC47GYDcwhkuY4tSRxyfq7rd4Upui9XjkaqiblpqOVcdajAI8XgmCbzwxaUcvomQpvC8lFocoOGtAw2P8TZ3WGOb3GoF/o1pQ0Dh9HLOrAPo79RONv9UCgYEA0s7L+SlhPgeF17KlOTuFecldDgSxE9UhwDIUC+Ua/PR7MJR5hwNuiti7ln8YsMJjPaSyGO6QQr0S4eKoC/uwahli+6UAFTmKdyf2Wq1JYDZ7JLqAbNFBk38b2UqbmPuM4GpTi4X2Vw0HtznwXkZMmmCm+nmxt/nkkHPpPfP/KwkCgYBQDphGJ2PdQKcwa/G9XYLgvgFvdEy1DKcp4CXk0hHu6vd/1/tJiOToBG2OAoYIXC7CLucOBn3b0T6RUZYP8yg+3KEN8OZAhMC2wF/ttUucXPb3hgHhIGp1018j6eLgZCCUODyRkM7VQEux01UHBb3R7zFCYiVWfM6JkTiv2gC8hQKBgHHFdpHeTu0vJf480UHd8e0vLfpRPqWhAaZBHRtY05Hv9/ClGR8sZ471e1Nk3LJNmOzzL/HN89oD8gHSJGmqHjmbUwpIQK8zCCpuwzAjbdghaHgupss4TJApnS8HuybW+eGBu/AIUlJqOCCImfoB5Gurk5VsjEpo2+lWIFKEJrlG"

func TestToRSAPrivateKeyFromString(t *testing.T) {
type args struct {
privateKey string
}

decodedPrivateKey, err := base64.StdEncoding.DecodeString(privateKeyExample)
if err != nil {
t.Fatal(err)
}

rsaPrivateKey, err := x509.ParsePKCS1PrivateKey(decodedPrivateKey)
if err != nil {
t.Fatal(err)
}

tests := []struct {
name string
args args
want *rsa.PrivateKey
err error
}{
{
name: "success",
args: args{
privateKey: privateKeyExample,
},
want: rsaPrivateKey,
err: nil,
},
{
name: "fail base64",
args: args{
privateKey: "a",
},
want: nil,
err: base64.CorruptInputError(0),
},
{
name: "fail private key",
args: args{
privateKey: base64.StdEncoding.EncodeToString([]byte("test")),
},
want: nil,
err: asn1.StructuralError{Msg: "tags don't match (16 vs {class:1 tag:20 length:101 isCompound:true}) {optional:false explicit:false application:false private:false defaultValue:<nil> tag:<nil> stringType:0 timeType:0 set:false omitEmpty:false} pkcs1PrivateKey @2"},
},
}

for _, tt := range tests {
tt := tt
t.Run(tt.name, func(t *testing.T) {
t.Parallel()

privateKey, err := converter.ToRSAPrivateKeyFromString(tt.args.privateKey)

assert.Equal(t, tt.want, privateKey)
assert.Equal(t, tt.err, err)

})
}
}
22 changes: 22 additions & 0 deletions pkg/model/message.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,22 @@
package model

import "time"

type Message struct {
Payload string `json:"payload"`
Time int64 `json:"time"`
}

func NewMessage(payload string) Message {
return Message{
Payload: payload,
Time: time.Now().Unix(),
}
}

func NewMessageWithTimestamp(payload string, time int64) Message {
return Message{
Payload: payload,
Time: time,
}
}
6 changes: 6 additions & 0 deletions pkg/model/signature.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,6 @@
package model

type Signature struct {
Time int64 `json:"time"`
Signature string `json:"signature"`
}
15 changes: 15 additions & 0 deletions pkg/service/rsa/service.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,15 @@
package rsa

import (
"crypto/rsa"

"github.com/inxy-payments/signature-sdk-go/pkg/service"
)

type rsaSignatureService struct {
privateKey *rsa.PrivateKey
}

func NewRSASignatureService(privateKey *rsa.PrivateKey) service.SignatureService {
return &rsaSignatureService{privateKey: privateKey}
}
27 changes: 27 additions & 0 deletions pkg/service/rsa/sign_message.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,27 @@
package rsa

import (
"crypto"
"crypto/rand"
"crypto/rsa"
"crypto/sha256"
"encoding/base64"
"strconv"
"strings"

"github.com/inxy-payments/signature-sdk-go/pkg/model"
)

func (rss *rsaSignatureService) SignMessage(message model.Message) (*model.Signature, error) {
payload := strings.ToLower(message.Payload) + "_" + strconv.Itoa(int(message.Time))
hashed := sha256.Sum256([]byte(payload))
signature, err := rsa.SignPKCS1v15(rand.Reader, rss.privateKey, crypto.SHA256, hashed[:])
if err != nil {
return nil, err
}

return &model.Signature{
Time: message.Time,
Signature: base64.StdEncoding.EncodeToString(signature),
}, nil
}
Loading

0 comments on commit 24a8baa

Please sign in to comment.