mirror of
https://github.com/kataras/iris.git
synced 2025-02-02 07:20:35 +01:00
Remove the two day old crypto package, let's not confuse users between that and JWT - keep only JWT. And also, Drop older go versions support, only go1.12 and above which comes with a lot of performance improvements and security patches
Former-commit-id: a1d791ce45b87ecc440ed9c5aee5d5d204763f0d
This commit is contained in:
parent
2567ed22be
commit
29deac3270
|
@ -3,9 +3,6 @@ os:
|
|||
- linux
|
||||
- osx
|
||||
go:
|
||||
- 1.9.x
|
||||
- 1.10.x
|
||||
- 1.11.x
|
||||
- 1.12.x
|
||||
go_import_path: github.com/kataras/iris
|
||||
# we disable test caching via GOCACHE=off
|
||||
|
|
32
FAQ.md
32
FAQ.md
|
@ -24,6 +24,8 @@ Add a `badge` to your open-source projects powered by [Iris](https://iris-go.com
|
|||
go get -u github.com/kataras/iris
|
||||
```
|
||||
|
||||
Go installed version 1.12 and above is required.
|
||||
|
||||
## Learning
|
||||
|
||||
More than 100 practical examples, tutorials and articles at:
|
||||
|
@ -39,36 +41,6 @@ More than 100 practical examples, tutorials and articles at:
|
|||
|
||||
Want to help and join to the greatest community? Describe your skills and push your own sections at: https://github.com/kataras/build-a-better-web-together/issues/new
|
||||
|
||||
### common errors that new gophers may meet
|
||||
|
||||
#### type aliases
|
||||
|
||||
| build error | reason | solution |
|
||||
| -----------|--------|--------|
|
||||
| `undefined iris.Context` | caused of using the **optional type alias** `iris.Context` instead of the `context.Context` when building with Go 1.8 | import the original package `github.com/kataras/iris/context` and declare as `func(context.Context){})` **or** download and install the [latest go version](https://golang.org/dl) _recommended_ |
|
||||
|
||||
Type alias is a new feature, introduced at Go version 1.9, so if you want to use Iris' type aliases you have to build using the latest Go version. Nothing really changes for your application if you use type alias or not, Iris' type aliases helps you to omit import statements -- to reduce lines of code, nothing more.
|
||||
|
||||
**Details...**
|
||||
|
||||
Go version 1.9 introduced the [type alias](https://golang.org/doc/go1.9#language) feature.
|
||||
|
||||
Iris uses the `type alias` feature to help you writing less code by omitting some package imports. The examples and documentation are written using Go 1.9 as well.
|
||||
|
||||
If you build your Go app with Go 1.9 you can, optionally, use all Iris web framework's features by importing one single package, the `github.com/kataras/iris`.
|
||||
|
||||
Available type aliases;
|
||||
|
||||
| Go 1.8 | Go 1.8 usage | Go 1.9 usage (optionally) |
|
||||
| -----------|--------|--------|
|
||||
| `import "github.com/kataras/iris/context"` | `func(context.Context) {}`, `context.Handler`, `context.Map` | `func(iris.Context) {}`, `iris.Handler`, `iris.Map` |
|
||||
| `import "github.com/kataras/iris/core/router"` | `app.PartyFunc("/users", func(p router.Party) {})`, `router.ExecutionOptions`, `router.ExecutionRules` | `app.PartyFunc("/users", func(p iris.Party) {})`, `iris.ExecutionOptions`, `iris.ExecutionRules` |
|
||||
| `import "github.com/kataras/iris/core/host"` | `app.ConfigureHost(func(s *host.Supervisor) {})` | `app.ConfigureHost(func(s *iris.Supervisor) {})` |
|
||||
|
||||
You can find all type aliases and their original package import statements at the [./context.go file](context.go).
|
||||
|
||||
> Remember; this doesn't mean that you have to use those type aliases, you can still import the original packages as you did with Go version 1.8, it's up to you.
|
||||
|
||||
## Active development mode
|
||||
|
||||
Iris may have reached version 11, but we're not stopping there. We have many feature ideas on our board that we're anxious to add and other innovative web development solutions that we're planning to build into Iris.
|
||||
|
|
|
@ -44,13 +44,13 @@ Iris does not force you to use any specific ORM or template engine. With support
|
|||
|
||||
## Installation
|
||||
|
||||
The only requirement is the [Go Programming Language](https://golang.org/dl/)
|
||||
The only requirement is the [Go Programming Language](https://golang.org/dl/).
|
||||
|
||||
```sh
|
||||
$ go get -u github.com/kataras/iris
|
||||
```
|
||||
|
||||
Iris takes advantage of the [vendor directory](https://docs.google.com/document/d/1Bz5-UB7g2uPBdOx-rw5t9MxJwkfpx90cqG9AFL0JAYo) feature. You get truly reproducible builds, as this method guards against upstream renames and deletes.
|
||||
Iris takes advantage of the [vendor directory](https://docs.google.com/document/d/1Bz5-UB7g2uPBdOx-rw5t9MxJwkfpx90cqG9AFL0JAYo) feature. You get truly reproducible builds, as this method guards against upstream renames and deletes. The minimum required Go version to use Iris is go1.12.
|
||||
|
||||
<details>
|
||||
<summary>Known issues for code editors and IDEs at general</summary>
|
||||
|
|
|
@ -363,7 +363,6 @@ You can serve [quicktemplate](https://github.com/valyala/quicktemplate) and [her
|
|||
|
||||
- [Basic Authentication](authentication/basicauth/main.go)
|
||||
- [OAUth2](authentication/oauth2/main.go)
|
||||
- [Request Auth](request/main.go) **NEW**
|
||||
- [Request Auth(JWT)](experimental-handlers/jwt/main.go)
|
||||
- [Sessions](#sessions)
|
||||
|
||||
|
|
|
@ -2,6 +2,5 @@
|
|||
|
||||
- [Basic Authentication](basicauth/main.go)
|
||||
- [OAUth2](oauth2/main.go)
|
||||
- [Request Auth](request/main.go)
|
||||
- [Request Auth(JWT)](https://github.com/kataras/iris/blob/master/_examples/experimental-handlers/jwt/main.go)
|
||||
- [Sessions](https://github.com/kataras/iris/tree/master/_examples/#sessions)
|
|
@ -1,138 +0,0 @@
|
|||
package main
|
||||
|
||||
import (
|
||||
"io/ioutil"
|
||||
|
||||
"github.com/kataras/iris"
|
||||
"github.com/kataras/iris/crypto"
|
||||
)
|
||||
|
||||
var (
|
||||
// Change that to your own key.
|
||||
// Usually you have an ECDSA private key
|
||||
// per identify, let's say a user, stored in a database
|
||||
// or somewhere else and you use its public key
|
||||
// to sign a user's payload and when this client
|
||||
// wants to use this payload, on another route,
|
||||
// you verify it comparing the signature of the payload
|
||||
// with the user's public key.
|
||||
//
|
||||
// Use the crypto.MustGenerateKey to generate a random key
|
||||
// or
|
||||
// crypto.ParsePrivateKey to convert data or local file to an *ecdsa.PrivateKey.
|
||||
// and `crypto.ParsePublicKey` if you only have access to the public one.
|
||||
testPrivateKey = crypto.MustGenerateKey()
|
||||
testPublicKey = &testPrivateKey.PublicKey
|
||||
)
|
||||
|
||||
type testPayloadStructure struct {
|
||||
Key string `json:"key"`
|
||||
Value string `json:"value"`
|
||||
}
|
||||
|
||||
// The Iris crypto package offers
|
||||
// authentication (with optional encryption in top of) and verification
|
||||
// of raw []byte data with `crypto.Marshal/Unmarshal` functions
|
||||
// and JSON payloads with `crypto.SignJSON/VerifyJSON` functions.
|
||||
//
|
||||
// Let's use the `SignJSON` and `VerifyJSON` here as an example,
|
||||
// as this is the most common scenario for a web application.
|
||||
func main() {
|
||||
app := iris.New()
|
||||
|
||||
app.Post("/auth/json", func(ctx iris.Context) {
|
||||
ticket, err := crypto.SignJSON(testPrivateKey, ctx.Request().Body)
|
||||
if err != nil {
|
||||
ctx.StatusCode(iris.StatusUnprocessableEntity)
|
||||
return
|
||||
}
|
||||
|
||||
// Send just the signature back
|
||||
// ctx.WriteString(ticket.Signature)
|
||||
// or the whole payload + the signature:
|
||||
ctx.JSON(ticket)
|
||||
})
|
||||
|
||||
app.Post("/verify/json", func(ctx iris.Context) {
|
||||
var verificatedPayload testPayloadStructure // this can be anything.
|
||||
|
||||
// The VerifyJSON excepts the body to be a JSON structure of
|
||||
// {
|
||||
// "signature": the generated signature from /auth/json,
|
||||
// "payload": the JSON client payload
|
||||
// }
|
||||
// That is the form of the `crypto.Ticket` structure.
|
||||
//
|
||||
// However, you are not limited to use that form, another common practise is to
|
||||
// have the signature and the payload we need to check in the same string representation
|
||||
// and for a better security you add encryption in top of it, so an outsider cannot understand what is what.
|
||||
// Let's say that the signature can be optionally provided by a URL ENCODED parameter
|
||||
// and the request body is the payload without any encryption
|
||||
// -
|
||||
// of course you can pass an GCM type of encryption/decryption as Marshal's and Unmarshal's last input argument,
|
||||
// see more about this at the iris/crypto/gcm subpackage for ready-to-use solutions.
|
||||
// -
|
||||
// So we will check if a url parameter is given, if so we will combine the signature and the body into one slice of bytes
|
||||
// and we will make use of the `crypto.Unmarshal` instead of the `crypto.VerifyJSON` function
|
||||
// -
|
||||
if signature := ctx.URLParam("signature"); signature != "" {
|
||||
payload, err := ioutil.ReadAll(ctx.Request().Body)
|
||||
if err != nil {
|
||||
ctx.StatusCode(iris.StatusInternalServerError)
|
||||
return
|
||||
}
|
||||
|
||||
data := append([]byte(signature), payload...)
|
||||
|
||||
originalPayloadBytes, ok := crypto.Unmarshal(testPublicKey, data, nil)
|
||||
|
||||
if !ok {
|
||||
ctx.Writef("this does not match, please try again\n")
|
||||
ctx.StatusCode(iris.StatusUnprocessableEntity)
|
||||
return
|
||||
}
|
||||
|
||||
ctx.ContentType("application/json")
|
||||
ctx.Write(originalPayloadBytes)
|
||||
return
|
||||
}
|
||||
|
||||
ok, err := crypto.VerifyJSON(testPublicKey, ctx.Request().Body, &verificatedPayload)
|
||||
if err != nil {
|
||||
ctx.Writef("error on verification: %v\n", err)
|
||||
ctx.StatusCode(iris.StatusBadRequest)
|
||||
return
|
||||
}
|
||||
|
||||
if !ok {
|
||||
ctx.Writef("this does not match, please try again\n")
|
||||
ctx.StatusCode(iris.StatusUnprocessableEntity)
|
||||
return
|
||||
}
|
||||
|
||||
// Give back the verificated payload or use it.
|
||||
ctx.JSON(verificatedPayload)
|
||||
})
|
||||
|
||||
// 1.
|
||||
// curl -X POST -H "Content-Type: application/json" -d '{"key": "this is a key", "value": "this is a value"}' http://localhost:8080/auth/json
|
||||
// 2. The result will be something like this:
|
||||
// {"payload":{"key":"this is a key","value":"this is a value"},"signature":"UgXgbXXvs9nAB3Pg0mG1WR0KBn2KpD/xBIsyOv1o4ZpzKs45hB/yxXiGN1k4Y+mgjdBxP6Gg26qajK6216pAGA=="}
|
||||
// 3. Copy-paste the whole result and do:
|
||||
// curl -X POST -H "Content-Type: application/json" -d '{"payload":{"key":"this is a key","value":"this is a value"},"signature":"UgXgbXXvs9nAB3Pg0mG1WR0KBn2KpD/xBIsyOv1o4ZpzKs45hB/yxXiGN1k4Y+mgjdBxP6Gg26qajK6216pAGA=="}' http://localhost:8080/verify/json
|
||||
// 4. Or pass by ?signature encoded URL parameter:
|
||||
// curl -X POST -H "Content-Type: application/json" -d '{"key": "this is a key", "value": "this is a value"}' http://localhost:8080/verify/json?signature=UgXgbXXvs9nAB3Pg0mG1WR0KBn2KpD%2FxBIsyOv1o4ZpzKs45hB%2FyxXiGN1k4Y%2BmgjdBxP6Gg26qajK6216pAGA%3D%3D
|
||||
// 5. At both cases the result should be:
|
||||
// {"key":"this is a key","value":"this is a value"}
|
||||
// Otherise the verification failed.
|
||||
//
|
||||
// Note that each time server is restarted a new private and public key pair is generated,
|
||||
// look at the start of the program.
|
||||
app.Run(iris.Addr(":8080"))
|
||||
}
|
||||
|
||||
// You can read more examples and run testable code
|
||||
// at the `iris/crypto`, `iris/crypto/sign`
|
||||
// and `iris/crypto/gcm` packages themselves.
|
||||
//
|
||||
// See experimental-handlers/jwt example to use JWT for request authentication instead.
|
153
crypto/crypto.go
153
crypto/crypto.go
|
@ -1,153 +0,0 @@
|
|||
package crypto
|
||||
|
||||
import (
|
||||
"crypto/ecdsa"
|
||||
"encoding/base64"
|
||||
|
||||
"github.com/kataras/iris/crypto/gcm"
|
||||
"github.com/kataras/iris/crypto/sign"
|
||||
)
|
||||
|
||||
var (
|
||||
// MustGenerateKey generates an ecdsa private and public key pair.
|
||||
// It panics if any error occurred.
|
||||
MustGenerateKey = sign.MustGenerateKey
|
||||
// ParsePrivateKey accepts a pem x509-encoded private key and decodes to *ecdsa.PrivateKey.
|
||||
ParsePrivateKey = sign.ParsePrivateKey
|
||||
// ParsePublicKey accepts a pem x509-encoded public key and decodes to *ecdsa.PrivateKey.
|
||||
ParsePublicKey = sign.ParsePublicKey
|
||||
|
||||
// MustGenerateAESKey generates an aes key.
|
||||
// It panics if any error occurred.
|
||||
MustGenerateAESKey = gcm.MustGenerateKey
|
||||
// DefaultADATA is the default associated data used for `Encrypt` and `Decrypt`
|
||||
// when "additionalData" is empty.
|
||||
DefaultADATA = []byte("FFA0A43EA6B8C829AD403817B2F5B7A2")
|
||||
)
|
||||
|
||||
// Encryption is the method signature when data should be signed and returned as encrypted.
|
||||
type Encryption func(privateKey *ecdsa.PrivateKey, data []byte) ([]byte, error)
|
||||
|
||||
// Decryption is the method signature when data should be decrypted before signed.
|
||||
type Decryption func(publicKey *ecdsa.PublicKey, data []byte) ([]byte, error)
|
||||
|
||||
// Encrypt returns an `Encryption` option to be used on `Marshal`.
|
||||
// If "aesKey" is not empty then the "data" associated with the "additionalData" will be encrypted too.
|
||||
// If "aesKey" is not empty but "additionalData" is, then the `DefaultADATA` will be used to encrypt "data".
|
||||
// If "aesKey" is empty then encryption is disabled, the return value will be only signed.
|
||||
//
|
||||
// See `Unmarshal` and `Decrypt` too.
|
||||
func Encrypt(aesKey, additionalData []byte) Encryption {
|
||||
if len(aesKey) == 0 {
|
||||
return nil
|
||||
}
|
||||
|
||||
if len(additionalData) == 0 {
|
||||
additionalData = DefaultADATA
|
||||
}
|
||||
|
||||
return func(_ *ecdsa.PrivateKey, plaintext []byte) ([]byte, error) {
|
||||
return gcm.Encrypt(aesKey, plaintext, additionalData)
|
||||
}
|
||||
}
|
||||
|
||||
// Decrypt returns an `Decryption` option to be used on `Unmarshal`.
|
||||
// If "aesKey" is not empty then the result will be decrypted.
|
||||
// If "aesKey" is not empty but "additionalData" is,
|
||||
// then the `DefaultADATA` will be used to decrypt the encrypted "data".
|
||||
// If "aesKey" is empty then decryption is disabled.
|
||||
//
|
||||
// If `Marshal` had an `Encryption` then `Unmarshal` must have also.
|
||||
//
|
||||
// See `Marshal` and `Encrypt` too.
|
||||
func Decrypt(aesKey, additionalData []byte) Decryption {
|
||||
if len(aesKey) == 0 {
|
||||
return nil
|
||||
}
|
||||
|
||||
if len(additionalData) == 0 {
|
||||
additionalData = DefaultADATA
|
||||
}
|
||||
|
||||
return func(_ *ecdsa.PublicKey, ciphertext []byte) ([]byte, error) {
|
||||
return gcm.Decrypt(aesKey, ciphertext, additionalData)
|
||||
}
|
||||
}
|
||||
|
||||
// Marshal signs and, optionally, encrypts the "data".
|
||||
//
|
||||
// The form of the output value is: signature_of_88_length followed by the raw_data_or_encrypted_data,
|
||||
// i.e "R+eqxA3LslRif0KoxpevpNILAs4Kh4mccCCoE0sRjICkj9xy0/gsxeUd2wfcGK5mzIZ6tM3A939Wjif0xwZCog==7001f30..."
|
||||
//
|
||||
//
|
||||
// Returns non-nil error if any error occurred.
|
||||
//
|
||||
// Usage:
|
||||
// data, _ := ioutil.ReadAll(ctx.Request().Body)
|
||||
// signedData, err := crypto.Marshal(testPrivateKey, data, nil)
|
||||
// ctx.Write(signedData)
|
||||
// Or if data should be encrypted:
|
||||
// signedEncryptedData, err := crypto.Marshal(testPrivateKey, data, crypto.Encrypt(aesKey, nil))
|
||||
func Marshal(privateKey *ecdsa.PrivateKey, data []byte, encrypt Encryption) ([]byte, error) {
|
||||
if encrypt != nil {
|
||||
b, err := encrypt(privateKey, data)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
data = b
|
||||
}
|
||||
|
||||
// sign the encrypted data if "encrypt" exists.
|
||||
sig, err := sign.Sign(privateKey, data)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
buf := make([]byte, base64.StdEncoding.EncodedLen(len(sig)))
|
||||
base64.StdEncoding.Encode(buf, sig)
|
||||
|
||||
return append(buf, data...), nil
|
||||
}
|
||||
|
||||
// Unmarshal verifies the "data" and, optionally, decrypts the output.
|
||||
//
|
||||
// Returns returns the signed raw data; without the signature and decrypted if "decrypt" is not nil.
|
||||
// The second output value reports whether the verification and any decryption of the data succeed or not.
|
||||
//
|
||||
// Usage:
|
||||
// data, _ := ioutil.ReadAll(ctx.Request().Body)
|
||||
// verifiedPlainPayload, err := crypto.Unmarshal(ecdsaPublicKey, data, nil)
|
||||
// ctx.Write(verifiedPlainPayload)
|
||||
// Or if data are encrypted and they should be decrypted:
|
||||
// verifiedDecryptedPayload, err := crypto.Unmarshal(ecdsaPublicKey, data, crypto.Decrypt(aesKey, nil))
|
||||
func Unmarshal(publicKey *ecdsa.PublicKey, data []byte, decrypt Decryption) ([]byte, bool) {
|
||||
if len(data) <= 90 {
|
||||
return nil, false
|
||||
}
|
||||
|
||||
sig, body := data[0:88], data[88:]
|
||||
|
||||
buf := make([]byte, base64.StdEncoding.DecodedLen(len(sig)))
|
||||
n, err := base64.StdEncoding.Decode(buf, sig)
|
||||
if err != nil {
|
||||
return nil, false
|
||||
}
|
||||
sig = buf[:n]
|
||||
|
||||
// verify the encrypted data as they are, the signature is linked with these.
|
||||
ok, err := sign.Verify(publicKey, sig, body)
|
||||
if !ok || err != nil {
|
||||
return nil, false
|
||||
}
|
||||
|
||||
// try to decrypt the body and finally return it as plain, its original form.
|
||||
if decrypt != nil {
|
||||
body, err = decrypt(publicKey, body)
|
||||
if err != nil {
|
||||
return nil, false
|
||||
}
|
||||
}
|
||||
|
||||
return body, ok && err == nil
|
||||
}
|
|
@ -1,96 +0,0 @@
|
|||
package crypto
|
||||
|
||||
import (
|
||||
"bytes"
|
||||
"io/ioutil"
|
||||
"net/http"
|
||||
"net/http/httptest"
|
||||
"testing"
|
||||
)
|
||||
|
||||
var (
|
||||
testPrivateKey = MustGenerateKey()
|
||||
testPublicKey = &testPrivateKey.PublicKey
|
||||
testAESKey = MustGenerateAESKey()
|
||||
)
|
||||
|
||||
func TestMarshalAndUnmarshal(t *testing.T) {
|
||||
testPayloadData := []byte(`{"mykey":"myvalue","mysecondkey@":"mysecondv#lu3@+!"}!+,==||any<data>[here]`)
|
||||
|
||||
signHandler := func(w http.ResponseWriter, r *http.Request) {
|
||||
data, _ := ioutil.ReadAll(r.Body)
|
||||
signedEncryptedPayload, err := Marshal(testPrivateKey, data, Encrypt(testAESKey, nil))
|
||||
if err != nil {
|
||||
w.WriteHeader(http.StatusUnprocessableEntity)
|
||||
return
|
||||
}
|
||||
|
||||
w.Write(signedEncryptedPayload)
|
||||
}
|
||||
|
||||
verifyHandler := func(w http.ResponseWriter, r *http.Request) {
|
||||
publicKey := testPublicKey
|
||||
if r.URL.Path == "/verify/otherkey" {
|
||||
// test with other, generated, public key.
|
||||
publicKey = &MustGenerateKey().PublicKey
|
||||
}
|
||||
data, _ := ioutil.ReadAll(r.Body)
|
||||
payload, ok := Unmarshal(publicKey, data, Decrypt(testAESKey, nil))
|
||||
if !ok {
|
||||
w.WriteHeader(http.StatusUnprocessableEntity)
|
||||
return
|
||||
}
|
||||
|
||||
// re-send the payload.
|
||||
w.Write(payload)
|
||||
}
|
||||
|
||||
testPayload := testPayloadData
|
||||
t.Logf("signing: sending payload: %s", testPayload)
|
||||
|
||||
signRequest := httptest.NewRequest("POST", "/sign", bytes.NewBuffer(testPayload))
|
||||
signRec := httptest.NewRecorder()
|
||||
signHandler(signRec, signRequest)
|
||||
|
||||
gotSignedEncrypted, _ := ioutil.ReadAll(signRec.Body)
|
||||
|
||||
// Looks like this:
|
||||
// jWQIL5gqTd1JqyHoTDXSaEtOmJdpYuzU0cyEn/9uDMW2JcPi4FkYfkkCfKyLFzlwhbykXsSJXOV11yVnS3EG4w==885c46964d92cce1fb36f9dfd76f2003000338e8605cd59fd0b5a84abf8175c41bf8bdbac0327cbc3cec17bf42ff9c
|
||||
t.Logf("verification: sending signed encrypted payload:\n%s", gotSignedEncrypted)
|
||||
verifyRequest := httptest.NewRequest("POST", "/verify", bytes.NewBuffer(gotSignedEncrypted))
|
||||
verifyRec := httptest.NewRecorder()
|
||||
verifyHandler(verifyRec, verifyRequest)
|
||||
verifyRequest.Body.Close()
|
||||
|
||||
if expected, got := http.StatusOK, verifyRec.Code; expected != got {
|
||||
t.Fatalf("verification: expected status code: %d but got: %d", expected, got)
|
||||
}
|
||||
|
||||
gotPayload, err := ioutil.ReadAll(verifyRec.Body)
|
||||
if err != nil {
|
||||
t.Fatal(err)
|
||||
}
|
||||
if !bytes.Equal(testPayload, gotPayload) {
|
||||
t.Fatalf("verification: expected payload: '%s' but got: '%s'", testPayload, gotPayload)
|
||||
}
|
||||
|
||||
t.Logf("got plain payload:\n%s\n\n", gotPayload)
|
||||
|
||||
// test the same payload, with the same signature but with other public key (see handler checks the path for that).
|
||||
t.Logf("verification: sending the same signed encrypted data which should not be verified due to a different key pair...")
|
||||
verifyRequest = httptest.NewRequest("POST", "/verify/otherkey", bytes.NewBuffer(gotSignedEncrypted))
|
||||
verifyRec = httptest.NewRecorder()
|
||||
verifyHandler(verifyRec, verifyRequest)
|
||||
verifyRequest.Body.Close()
|
||||
|
||||
if expected, got := http.StatusUnprocessableEntity, verifyRec.Code; expected != got {
|
||||
t.Fatalf("verification: expected status code: %d but got: %d", expected, got)
|
||||
}
|
||||
|
||||
gotPayload, _ = ioutil.ReadAll(verifyRec.Body)
|
||||
if len(gotPayload) > 0 {
|
||||
t.Fatalf("verification should fail and no payload should return but got: '%s'", gotPayload)
|
||||
}
|
||||
|
||||
t.Logf("correct, it didn't match")
|
||||
}
|
|
@ -1,134 +0,0 @@
|
|||
// Package gcm implements encryption/decription using the AES algorithm and the Galois/Counter Mode.
|
||||
package gcm
|
||||
|
||||
import (
|
||||
"crypto/aes"
|
||||
"crypto/cipher"
|
||||
"crypto/rand"
|
||||
"crypto/sha512"
|
||||
"encoding/hex"
|
||||
)
|
||||
|
||||
// MustGenerateKey generates an aes key.
|
||||
// It panics if any error occurred.
|
||||
func MustGenerateKey() []byte {
|
||||
aesKey, err := GenerateKey()
|
||||
if err != nil {
|
||||
panic(err)
|
||||
}
|
||||
|
||||
return aesKey
|
||||
}
|
||||
|
||||
// GenerateKey returns a random aes key.
|
||||
func GenerateKey() ([]byte, error) {
|
||||
key := make([]byte, 64)
|
||||
n, err := rand.Read(key)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
return encode(key[:n]), nil
|
||||
}
|
||||
|
||||
// Encrypt encrypts and authenticates the plain data and additional data
|
||||
// and returns the ciphertext of it.
|
||||
// It uses the AEAD cipher mode providing authenticated encryption with associated
|
||||
// data.
|
||||
// The same additional data must be kept the same for `Decrypt`.
|
||||
func Encrypt(aesKey, data, additionalData []byte) ([]byte, error) {
|
||||
key, err := decode(aesKey)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
h := sha512.New()
|
||||
h.Write(key)
|
||||
digest := encode(h.Sum(nil))
|
||||
|
||||
// key based on the hash itself, we have space because of sha512.
|
||||
newKey, err := decode(digest[:64])
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
// nonce based on the hash itself.
|
||||
nonce, err := decode(digest[64:(64 + 24)])
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
aData, err := decode(additionalData)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
block, err := aes.NewCipher(newKey)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
gcm, err := cipher.NewGCM(block)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
ciphertext := encode(gcm.Seal(nil, nonce, data, aData))
|
||||
return ciphertext, nil
|
||||
}
|
||||
|
||||
// Decrypt decrypts and authenticates ciphertext, authenticates the
|
||||
// additional data and, if successful, returns the resulting plain data.
|
||||
// The additional data must match the value passed to `Encrypt`.
|
||||
func Decrypt(aesKey, ciphertext, additionalData []byte) ([]byte, error) {
|
||||
key, err := decode(aesKey)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
h := sha512.New()
|
||||
h.Write(key)
|
||||
digest := encode(h.Sum(nil))
|
||||
|
||||
newKey, err := decode(digest[:64])
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
nonce, err := decode(digest[64:(64 + 24)])
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
additionalData, err = decode(additionalData)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
block, err := aes.NewCipher(newKey)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
gcm, err := cipher.NewGCM(block)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
ciphertext, err = decode(ciphertext)
|
||||
return gcm.Open(nil, nonce, ciphertext, additionalData)
|
||||
}
|
||||
|
||||
func decode(src []byte) ([]byte, error) {
|
||||
buf := make([]byte, hex.DecodedLen(len(src)))
|
||||
n, err := hex.Decode(buf, src)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
return buf[:n], nil
|
||||
}
|
||||
|
||||
func encode(src []byte) []byte {
|
||||
buf := make([]byte, hex.EncodedLen(len(src)))
|
||||
hex.Encode(buf, src)
|
||||
return buf
|
||||
}
|
|
@ -1,46 +0,0 @@
|
|||
package gcm
|
||||
|
||||
import (
|
||||
"bytes"
|
||||
"testing"
|
||||
)
|
||||
|
||||
var testKey = MustGenerateKey()
|
||||
|
||||
func TestEncryptDecrypt(t *testing.T) {
|
||||
if len(testKey) == 0 {
|
||||
t.Fatalf("testKey is empty??")
|
||||
}
|
||||
|
||||
tests := []struct {
|
||||
payload []byte
|
||||
aData []byte // IV of a random aes-256-cbc, 32 size.
|
||||
}{
|
||||
{[]byte("test my content 1"), []byte("FFA0A43EA6B8C829AD403817B2F5B7A2")},
|
||||
{[]byte("test my content 2"), []byte("364787B9AF1AEE4BE26690EA8CBF4AB7")},
|
||||
}
|
||||
|
||||
for i, tt := range tests {
|
||||
ciphertext, err := Encrypt(testKey, tt.payload, tt.aData)
|
||||
if err != nil {
|
||||
t.Fatalf("[%d] encrypt error: %v", i, err)
|
||||
}
|
||||
|
||||
payload, err := Decrypt(testKey, ciphertext, tt.aData)
|
||||
if err != nil {
|
||||
t.Fatalf("[%d] decrypt error: %v", i, err)
|
||||
}
|
||||
|
||||
if !bytes.Equal(payload, tt.payload) {
|
||||
t.Fatalf("[%d] expected data to be decrypted to: '%s' but got: '%s'", i, tt.payload, payload)
|
||||
}
|
||||
|
||||
// test with other, invalid key, should fail to decrypt.
|
||||
tempKey := MustGenerateKey()
|
||||
|
||||
payload, err = Decrypt(tempKey, ciphertext, tt.aData)
|
||||
if err == nil || len(payload) > 0 {
|
||||
t.Fatalf("[%d] verification should fail but passed for '%s'", i, tt.payload)
|
||||
}
|
||||
}
|
||||
}
|
|
@ -1,94 +0,0 @@
|
|||
package crypto
|
||||
|
||||
import (
|
||||
"crypto/ecdsa"
|
||||
"encoding/base64"
|
||||
"encoding/json"
|
||||
"io"
|
||||
"io/ioutil"
|
||||
|
||||
"github.com/kataras/iris/crypto/sign"
|
||||
)
|
||||
|
||||
// Ticket contains the original payload raw data
|
||||
// and the generated signature.
|
||||
//
|
||||
// Look `SignJSON` and `VerifyJSON` for more details.
|
||||
type Ticket struct {
|
||||
Payload json.RawMessage `json:"payload"`
|
||||
Signature string `json:"signature"`
|
||||
}
|
||||
|
||||
// SignJSON signs the incoming JSON request payload based on
|
||||
// client's "privateKey" and the "r" (could be ctx.Request().Body).
|
||||
//
|
||||
// It generates the signature and returns a structure called `Ticket`.
|
||||
// The `Ticket` just contains the original client's payload raw data
|
||||
// and the generated signature.
|
||||
//
|
||||
// Returns non-nil error if any error occurred.
|
||||
//
|
||||
// Usage:
|
||||
// ticket, err := crypto.SignJSON(testPrivateKey, ctx.Request().Body)
|
||||
// b, err := json.Marshal(ticket)
|
||||
// ctx.Write(b)
|
||||
func SignJSON(privateKey *ecdsa.PrivateKey, r io.Reader) (Ticket, error) {
|
||||
data, err := ioutil.ReadAll(r)
|
||||
if err != nil || len(data) == 0 {
|
||||
return Ticket{}, err
|
||||
}
|
||||
|
||||
sig, err := sign.Sign(privateKey, data)
|
||||
if err != nil {
|
||||
return Ticket{}, err
|
||||
}
|
||||
|
||||
ticket := Ticket{
|
||||
Payload: data,
|
||||
Signature: base64.StdEncoding.EncodeToString(sig),
|
||||
}
|
||||
return ticket, nil
|
||||
}
|
||||
|
||||
// VerifyJSON verifies the incoming JSON request,
|
||||
// by reading the "r" which should decodes to a `Ticket`.
|
||||
// The `Ticket` is verified against the given "publicKey", the `Ticket#Signature` and
|
||||
// `Ticket#Payload` data (original request's payload data which was signed by `SignJSON`).
|
||||
//
|
||||
// Returns true whether the verification succeed or not.
|
||||
// The "toPayloadPtr" should be a pointer to a value of the same payload structure the client signed on.
|
||||
// If and only if the verification succeed the payload value is filled from the `Ticket.Payload` raw data.
|
||||
//
|
||||
// Check for both output arguments in order to:
|
||||
// 1. verification (true/false and error) and
|
||||
// 2. ticket's original json payload parsed and "toPayloadPtr" is filled successfully (error).
|
||||
//
|
||||
// Usage:
|
||||
// var myPayload myJSONStruct
|
||||
// ok, err := crypto.VerifyJSON(publicKey, ctx.Request().Body, &myPayload)
|
||||
func VerifyJSON(publicKey *ecdsa.PublicKey, r io.Reader, toPayloadPtr interface{}) (bool, error) {
|
||||
data, err := ioutil.ReadAll(r)
|
||||
if err != nil {
|
||||
return false, err
|
||||
}
|
||||
|
||||
ticket := new(Ticket)
|
||||
err = json.Unmarshal(data, ticket)
|
||||
if err != nil {
|
||||
return false, err
|
||||
}
|
||||
|
||||
sig, err := base64.StdEncoding.DecodeString(ticket.Signature)
|
||||
if err != nil {
|
||||
return false, err
|
||||
}
|
||||
|
||||
ok, err := sign.Verify(publicKey, sig, ticket.Payload)
|
||||
if ok && toPayloadPtr != nil {
|
||||
// if and only if the verification succeed we
|
||||
// set the payload to the structured/map value of "toPayloadPtr".
|
||||
err = json.Unmarshal(ticket.Payload, toPayloadPtr)
|
||||
}
|
||||
|
||||
return ok, err
|
||||
}
|
|
@ -1,114 +0,0 @@
|
|||
package crypto
|
||||
|
||||
import (
|
||||
"bytes"
|
||||
"encoding/json"
|
||||
"io/ioutil"
|
||||
"net/http"
|
||||
"net/http/httptest"
|
||||
"testing"
|
||||
)
|
||||
|
||||
func TestJSONSignAndVerify(t *testing.T) {
|
||||
type testJSON struct {
|
||||
Key string `json:"key"`
|
||||
Value string `json:"value"`
|
||||
}
|
||||
|
||||
signHandler := func(w http.ResponseWriter, r *http.Request) {
|
||||
ticket, err := SignJSON(testPrivateKey, r.Body)
|
||||
if err != nil {
|
||||
// https://developer.mozilla.org/en-US/docs/Web/HTTP/Status/422
|
||||
w.WriteHeader(http.StatusUnprocessableEntity)
|
||||
return
|
||||
}
|
||||
|
||||
b, err := json.Marshal(ticket)
|
||||
if err != nil {
|
||||
t.Fatal(err)
|
||||
}
|
||||
w.Write(b)
|
||||
// or
|
||||
// fmt.Fprintf(w, "%s", ticket.Signature)
|
||||
// to send just the signature.
|
||||
}
|
||||
|
||||
verifyHandler := func(w http.ResponseWriter, r *http.Request) {
|
||||
publicKey := testPublicKey
|
||||
if r.URL.Path == "/verify/otherkey" {
|
||||
// test with other, generated, public key.
|
||||
publicKey = &MustGenerateKey().PublicKey
|
||||
}
|
||||
|
||||
var payload testJSON
|
||||
ok, err := VerifyJSON(publicKey, r.Body, &payload)
|
||||
if err != nil {
|
||||
t.Fatal(err)
|
||||
w.WriteHeader(http.StatusInternalServerError)
|
||||
return
|
||||
}
|
||||
|
||||
if !ok {
|
||||
w.WriteHeader(http.StatusUnprocessableEntity) // or forbidden or unauthorized.
|
||||
return
|
||||
}
|
||||
|
||||
// re-send the payload.
|
||||
b, err := json.Marshal(payload)
|
||||
if err != nil {
|
||||
t.Fatal(err)
|
||||
}
|
||||
w.Write(b)
|
||||
}
|
||||
|
||||
// Looks like this:
|
||||
// {"key":"mykey","value":"myvalue"}
|
||||
testPayload := testJSON{"mykey", "myvalue"}
|
||||
payload, _ := json.Marshal(testPayload)
|
||||
t.Logf("signing: sending payload: %s", payload)
|
||||
|
||||
signRequest := httptest.NewRequest("POST", "/sign", bytes.NewBuffer(payload))
|
||||
signRec := httptest.NewRecorder()
|
||||
signHandler(signRec, signRequest)
|
||||
|
||||
gotTicketPayload, _ := ioutil.ReadAll(signRec.Body)
|
||||
|
||||
// Looks like this:
|
||||
// {
|
||||
// "signature": "D4PF6Hc0CrsO6MXAPxsLdhrVLKdmUOsN3Qm/Dr1y8yS80FQSgpU8Frr81fAJSKNwwW3dHhpoYvRi0t04MrukOQ==",
|
||||
// "payload": {"key":"mykey","value":"myvalue"}
|
||||
// }
|
||||
t.Logf("verification: sending ticket: %s", gotTicketPayload)
|
||||
verifyRequest := httptest.NewRequest("POST", "/verify", bytes.NewBuffer(gotTicketPayload))
|
||||
verifyRec := httptest.NewRecorder()
|
||||
verifyHandler(verifyRec, verifyRequest)
|
||||
verifyRequest.Body.Close()
|
||||
|
||||
if expected, got := http.StatusOK, verifyRec.Code; expected != got {
|
||||
t.Fatalf("verification: expected status code: %d but got: %d", expected, got)
|
||||
}
|
||||
|
||||
gotPayload, err := ioutil.ReadAll(verifyRec.Body)
|
||||
if err != nil {
|
||||
t.Fatal(err)
|
||||
}
|
||||
if !bytes.Equal(payload, gotPayload) {
|
||||
t.Fatalf("verification: expected payload: '%s' but got: '%s'", payload, gotTicketPayload)
|
||||
}
|
||||
|
||||
// test the same payload, with the same signature but with other public key (see handler checks the path for that).
|
||||
t.Logf("verification: sending the same ticket which should not be verified due to a different key pair...")
|
||||
verifyRequest = httptest.NewRequest("POST", "/verify/otherkey", bytes.NewBuffer(gotTicketPayload))
|
||||
verifyRec = httptest.NewRecorder()
|
||||
verifyHandler(verifyRec, verifyRequest)
|
||||
verifyRequest.Body.Close()
|
||||
|
||||
if expected, got := http.StatusUnprocessableEntity, verifyRec.Code; expected != got {
|
||||
t.Fatalf("verification: expected status code: %d but got: %d", expected, got)
|
||||
}
|
||||
|
||||
gotPayload, _ = ioutil.ReadAll(verifyRec.Body)
|
||||
if len(gotPayload) > 0 {
|
||||
t.Fatalf("verification should fail and no payload should return but got: '%s'", gotPayload)
|
||||
}
|
||||
}
|
|
@ -1,145 +0,0 @@
|
|||
// Package sign signs and verifies any format of data by
|
||||
// using the ECDSA P-384 digital signature and authentication algorithm.
|
||||
//
|
||||
// https://en.wikipedia.org/wiki/Elliptic_Curve_Digital_Signature_Algorithm
|
||||
// https://apps.nsa.gov/iaarchive/library/ia-guidance/ia-solutions-for-classified/algorithm-guidance/suite-b-implementers-guide-to-fips-186-3-ecdsa.cfm
|
||||
// https://www.nsa.gov/Portals/70/documents/resources/everyone/csfc/csfc-faqs.pdf
|
||||
package sign
|
||||
|
||||
import (
|
||||
"crypto/ecdsa"
|
||||
"crypto/elliptic"
|
||||
"crypto/rand"
|
||||
"crypto/x509" // the key encoding.
|
||||
"encoding/pem" // the data encoding format.
|
||||
"errors"
|
||||
"math/big"
|
||||
|
||||
// the, modern, hash implementation,
|
||||
// commonly used in popular crypto concurrencies too.
|
||||
"golang.org/x/crypto/sha3"
|
||||
)
|
||||
|
||||
// MustGenerateKey generates a private and public key pair.
|
||||
// It panics if any error occurred.
|
||||
func MustGenerateKey() *ecdsa.PrivateKey {
|
||||
privateKey, err := GenerateKey()
|
||||
if err != nil {
|
||||
panic(err)
|
||||
}
|
||||
|
||||
return privateKey
|
||||
}
|
||||
|
||||
// GenerateKey generates a private and public key pair.
|
||||
func GenerateKey() (*ecdsa.PrivateKey, error) {
|
||||
return ecdsa.GenerateKey(elliptic.P256(), rand.Reader)
|
||||
}
|
||||
|
||||
// GeneratePrivateKey generates a private key as pem text.
|
||||
// It returns empty on any error.
|
||||
func GeneratePrivateKey() string {
|
||||
privateKey, err := GenerateKey()
|
||||
if err != nil {
|
||||
return ""
|
||||
}
|
||||
|
||||
privateKeyB, err := marshalPrivateKey(privateKey)
|
||||
if err != nil {
|
||||
return ""
|
||||
}
|
||||
|
||||
return string(privateKeyB)
|
||||
}
|
||||
|
||||
// Sign signs the "data" using the "privateKey".
|
||||
// It returns the signature.
|
||||
func Sign(privateKey *ecdsa.PrivateKey, data []byte) ([]byte, error) {
|
||||
h := sha3.New256()
|
||||
_, err := h.Write(data)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
digest := h.Sum(nil)
|
||||
|
||||
r, s, err := ecdsa.Sign(rand.Reader, privateKey, digest)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
// sig := elliptic.Marshal(elliptic.P256(), r, s)
|
||||
sig := append(r.Bytes(), s.Bytes()...)
|
||||
|
||||
return sig, nil
|
||||
}
|
||||
|
||||
// Verify verifies the "data" in signature "sig" (96 length if 384) using the "publicKey".
|
||||
// It reports whether the signature is valid or not.
|
||||
func Verify(publicKey *ecdsa.PublicKey, sig, data []byte) (bool, error) {
|
||||
h := sha3.New256()
|
||||
_, err := h.Write(data)
|
||||
if err != nil {
|
||||
return false, err
|
||||
}
|
||||
|
||||
digest := h.Sum(nil)
|
||||
|
||||
// 0:32 & 32:64 for 256, always because it's constant.
|
||||
// 0:48 & 48:96 for 384 but it is not constant-time, so it's 96 or 97 length,
|
||||
// also something like that elliptic.Unmarshal(elliptic.P384(), sig)
|
||||
// doesn't work.
|
||||
|
||||
r := new(big.Int).SetBytes(sig[0:32])
|
||||
s := new(big.Int).SetBytes(sig[32:64])
|
||||
|
||||
return ecdsa.Verify(publicKey, digest, r, s), nil
|
||||
}
|
||||
|
||||
var errNotValidBlock = errors.New("invalid block")
|
||||
|
||||
// ParsePrivateKey accepts a pem x509-encoded private key and decodes to *ecdsa.PrivateKey.
|
||||
func ParsePrivateKey(key []byte) (*ecdsa.PrivateKey, error) {
|
||||
block, _ := pem.Decode(key)
|
||||
if block == nil {
|
||||
return nil, errNotValidBlock
|
||||
}
|
||||
return x509.ParseECPrivateKey(block.Bytes)
|
||||
}
|
||||
|
||||
// ParsePublicKey accepts a pem x509-encoded public key and decodes to *ecdsa.PrivateKey.
|
||||
func ParsePublicKey(key []byte) (*ecdsa.PublicKey, error) {
|
||||
block, _ := pem.Decode(key)
|
||||
if block == nil {
|
||||
return nil, errNotValidBlock
|
||||
}
|
||||
|
||||
publicKeyV, err := x509.ParsePKIXPublicKey(block.Bytes)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
publicKey, ok := publicKeyV.(*ecdsa.PublicKey)
|
||||
if !ok {
|
||||
return nil, errNotValidBlock
|
||||
}
|
||||
|
||||
return publicKey, nil
|
||||
}
|
||||
|
||||
func marshalPrivateKey(key *ecdsa.PrivateKey) ([]byte, error) {
|
||||
privateKeyAnsDer, err := x509.MarshalECPrivateKey(key)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
return pem.EncodeToMemory(&pem.Block{Type: "PRIVATE KEY", Bytes: privateKeyAnsDer}), nil
|
||||
}
|
||||
|
||||
func marshalPublicKey(key *ecdsa.PublicKey) ([]byte, error) {
|
||||
publicKeyAnsDer, err := x509.MarshalPKIXPublicKey(key)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
return pem.EncodeToMemory(&pem.Block{Type: "PUBLIC KEY", Bytes: publicKeyAnsDer}), nil
|
||||
}
|
|
@ -1,74 +0,0 @@
|
|||
package sign
|
||||
|
||||
import (
|
||||
"reflect"
|
||||
"testing"
|
||||
)
|
||||
|
||||
var (
|
||||
testPrivateKey = MustGenerateKey()
|
||||
testPublicKey = &testPrivateKey.PublicKey
|
||||
)
|
||||
|
||||
func TestGenerateKey(t *testing.T) {
|
||||
privateKeyB, err := marshalPrivateKey(testPrivateKey)
|
||||
if err != nil {
|
||||
t.Fatalf("private key: %v", err)
|
||||
}
|
||||
publicKeyB, err := marshalPublicKey(testPublicKey)
|
||||
if err != nil {
|
||||
t.Fatalf("public key: %v", err)
|
||||
}
|
||||
|
||||
t.Logf("%s", privateKeyB)
|
||||
t.Logf("%s", publicKeyB)
|
||||
|
||||
privateKeyParsed, err := ParsePrivateKey(privateKeyB)
|
||||
if err != nil {
|
||||
t.Fatalf("private key: %v", err)
|
||||
}
|
||||
|
||||
publicKeyParsed, err := ParsePublicKey(publicKeyB)
|
||||
if err != nil {
|
||||
t.Fatalf("public key: %v", err)
|
||||
}
|
||||
|
||||
if !reflect.DeepEqual(testPrivateKey, privateKeyParsed) {
|
||||
t.Fatalf("expected private key to be:\n%#+v\nbut got:\n%#+v", testPrivateKey, privateKeyParsed)
|
||||
}
|
||||
if !reflect.DeepEqual(testPublicKey, publicKeyParsed) {
|
||||
t.Fatalf("expected public key to be:\n%#+v\nbut got:\n%#+v", testPublicKey, publicKeyParsed)
|
||||
}
|
||||
}
|
||||
|
||||
func TestSignAndVerify(t *testing.T) {
|
||||
tests := []struct {
|
||||
payload []byte
|
||||
}{
|
||||
{[]byte("test my content 1")},
|
||||
{[]byte("test my content 2")},
|
||||
}
|
||||
|
||||
for i, tt := range tests {
|
||||
sig, err := Sign(testPrivateKey, tt.payload)
|
||||
if err != nil {
|
||||
t.Fatalf("[%d] sign error: %v", i, err)
|
||||
}
|
||||
|
||||
ok, err := Verify(testPublicKey, sig, tt.payload)
|
||||
if err != nil {
|
||||
t.Fatalf("[%d] verify error: %v", i, err)
|
||||
}
|
||||
if !ok {
|
||||
t.Fatalf("[%d] verification failed for '%s'", i, tt.payload)
|
||||
}
|
||||
|
||||
// test with other, invalid public key, should fail to verify.
|
||||
tempPublicKey := &MustGenerateKey().PublicKey
|
||||
|
||||
ok, err = Verify(tempPublicKey, sig, tt.payload)
|
||||
if ok {
|
||||
t.Fatalf("[%d] verification should fail but passed for '%s'", i, tt.payload)
|
||||
}
|
||||
}
|
||||
}
|
Loading…
Reference in New Issue
Block a user