1
0
mirror of https://github.com/kataras/iris.git synced 2026-01-08 12:31:58 +00:00

implement the Iris Crypto Library for Request Authentication and Verification. With Examples and Tests.

Relative to this one as well: https://github.com/kataras/iris/issues/1200


Former-commit-id: 3a29e7398b7fdeb9b48a118b742d419d5681d56b
This commit is contained in:
Gerasimos (Makis) Maropoulos
2019-07-02 19:19:43 +03:00
parent 35389c6ef8
commit 9dbb300d9b
12 changed files with 992 additions and 2 deletions

145
crypto/sign/sign.go Normal file
View File

@@ -0,0 +1,145 @@
// 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 public and private 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 public and private 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
}

74
crypto/sign/sign_test.go Normal file
View File

@@ -0,0 +1,74 @@
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)
}
}
}