-
Notifications
You must be signed in to change notification settings - Fork 1
Commit
This commit does not belong to any branch on this repository, and may belong to a fork outside of the repository.
implemented all 3 elliptic curve algs
- Loading branch information
1 parent
124a723
commit 9c73e64
Showing
10 changed files
with
950 additions
and
30 deletions.
There are no files selected for viewing
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,141 @@ | ||
package es256 | ||
|
||
import ( | ||
"crypto" | ||
"crypto/ecdsa" | ||
"crypto/elliptic" | ||
"crypto/rand" | ||
"crypto/sha256" | ||
"encoding/base64" | ||
"encoding/json" | ||
"errors" | ||
"fmt" | ||
"math/big" | ||
) | ||
|
||
type PublicKey struct { | ||
Kty string `json:"kty"` | ||
Crv string `json:"crv"` | ||
X string `json:"x"` | ||
Y string `json:"y"` | ||
} | ||
|
||
func (pubKey *PublicKey) Equal(x PublicKey) bool { | ||
if pubKey.X == x.X && pubKey.Y == x.Y && pubKey.Kty == x.Kty && pubKey.Crv == x.Crv { | ||
return true | ||
} | ||
return false | ||
} | ||
|
||
type ES256 struct{} | ||
|
||
func (signer *ES256) ValidateSignature(token, signature string, publicKeyJson string) (bool, error) { | ||
curve := elliptic.P256() | ||
|
||
var publicKey PublicKey | ||
err := json.Unmarshal([]byte(publicKeyJson), &publicKey) | ||
if err != nil { | ||
return false, errors.New("provided public key json isn't valid es256 public key") | ||
} | ||
|
||
xBytes, err := base64.RawURLEncoding.DecodeString(publicKey.X) | ||
if err != nil { | ||
return false, err | ||
} | ||
|
||
yBytes, err := base64.RawURLEncoding.DecodeString(publicKey.Y) | ||
if err != nil { | ||
return false, err | ||
} | ||
|
||
pk := &ecdsa.PublicKey{ | ||
Curve: curve, | ||
X: big.NewInt(0).SetBytes(xBytes), | ||
Y: big.NewInt(0).SetBytes(yBytes), | ||
} | ||
|
||
bodyHash := sha256.Sum256([]byte(token)) | ||
|
||
r, s, err := signer.extractRSFromSignature(signature) | ||
if err != nil { | ||
return false, err | ||
} | ||
|
||
return ecdsa.Verify(pk, bodyHash[:], r, s), nil | ||
} | ||
|
||
func (signer *ES256) extractRSFromSignature(signature string) (*big.Int, *big.Int, error) { | ||
decodedSignature, err := base64.RawURLEncoding.DecodeString(signature) | ||
if err != nil { | ||
return nil, nil, err | ||
} | ||
|
||
if len(decodedSignature) != 64 { | ||
return nil, nil, errors.New("signature should be 64 bytes for ES256") | ||
} | ||
rb := decodedSignature[:32] | ||
sb := decodedSignature[32:] | ||
|
||
r := big.NewInt(0).SetBytes(rb) | ||
s := big.NewInt(0).SetBytes(sb) | ||
|
||
return r, s, nil | ||
} | ||
|
||
func (signer *ES256) Sign(body map[string]interface{}, headerKeys map[string]string) (*string, crypto.PrivateKey, crypto.PublicKey, error) { | ||
curve := elliptic.P256() | ||
pk, err := ecdsa.GenerateKey(curve, rand.Reader) | ||
if err != nil { | ||
return nil, nil, nil, err | ||
} | ||
|
||
header := headerKeys | ||
header["alg"] = "ES256" | ||
|
||
headerBytes, err := json.Marshal(header) | ||
if err != nil { | ||
return nil, nil, nil, errors.New("failed to marshal header to bytes") | ||
} | ||
base64Header := base64.RawURLEncoding.EncodeToString(headerBytes) | ||
|
||
bodyBytes, err := json.Marshal(body) | ||
if err != nil { | ||
return nil, nil, nil, errors.New("failed to marshal body to bytes") | ||
} | ||
base64Body := base64.RawURLEncoding.EncodeToString(bodyBytes) | ||
|
||
token := fmt.Sprintf("%s.%s", base64Header, base64Body) | ||
|
||
digest := sha256.Sum256([]byte(token)) | ||
|
||
r, s, err := ecdsa.Sign(rand.Reader, pk, digest[:]) | ||
if err != nil { | ||
return nil, nil, nil, err | ||
} | ||
|
||
rb := r.Bytes() | ||
sb := s.Bytes() | ||
|
||
sigBytes := append(rb, sb...) | ||
|
||
base64Sig := base64.RawURLEncoding.EncodeToString(sigBytes) | ||
signedToken := fmt.Sprintf("%s.%s", token, base64Sig) | ||
|
||
cryptoPubKey := pk.PublicKey | ||
|
||
x := base64.RawURLEncoding.EncodeToString(cryptoPubKey.X.Bytes()) | ||
y := base64.RawURLEncoding.EncodeToString(cryptoPubKey.Y.Bytes()) | ||
|
||
pubKey := PublicKey{ | ||
Kty: "EC", | ||
Crv: "P-256", | ||
X: x, | ||
Y: y, | ||
} | ||
|
||
return &signedToken, pk, pubKey, nil | ||
|
||
} | ||
func (signer *ES256) SignWithKey(body map[string]interface{}, headerKeys map[string]string, privateKey string) (*string, error) { | ||
return nil, nil //todo | ||
} |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,72 @@ | ||
package es256 | ||
|
||
import ( | ||
"encoding/json" | ||
"strings" | ||
"testing" | ||
) | ||
|
||
func TestValidateSignatureES256(t *testing.T) { | ||
publicKey := "{\"kty\":\"EC\",\"crv\":\"P-256\",\"x\":\"b28d4MwZMjw8-00CG4xfnn9SLMVMM19SlqZpVb_uNtQ\",\"y\":\"Xv5zWwuoaTgdS6hV43yI6gBwTnjukmFQQnJ_kCxzqk8\"}" | ||
|
||
token := "eyJhbGciOiAiRVMyNTYifQ.eyJfc2QiOiBbIkNyUWU3UzVrcUJBSHQtbk1ZWGdjNmJkdDJTSDVhVFkxc1VfTS1QZ2tqUEkiLCAiSnpZakg0c3ZsaUgwUjNQeUVNZmVadTZKdDY5dTVxZWhabzdGN0VQWWxTRSIsICJQb3JGYnBLdVZ1Nnh5bUphZ3ZrRnNGWEFiUm9jMkpHbEFVQTJCQTRvN2NJIiwgIlRHZjRvTGJnd2Q1SlFhSHlLVlFaVTlVZEdFMHc1cnREc3JaemZVYW9tTG8iLCAiWFFfM2tQS3QxWHlYN0tBTmtxVlI2eVoyVmE1TnJQSXZQWWJ5TXZSS0JNTSIsICJYekZyendzY002R242Q0pEYzZ2Vks4QmtNbmZHOHZPU0tmcFBJWmRBZmRFIiwgImdiT3NJNEVkcTJ4Mkt3LXc1d1BFemFrb2I5aFYxY1JEMEFUTjNvUUw5Sk0iLCAianN1OXlWdWx3UVFsaEZsTV8zSmx6TWFTRnpnbGhRRzBEcGZheVF3TFVLNCJdLCAiaXNzIjogImh0dHBzOi8vZXhhbXBsZS5jb20vaXNzdWVyIiwgImlhdCI6IDE2ODMwMDAwMDAsICJleHAiOiAxODgzMDAwMDAwLCAic3ViIjogInVzZXJfNDIiLCAibmF0aW9uYWxpdGllcyI6IFt7Ii4uLiI6ICJwRm5kamtaX1ZDem15VGE2VWpsWm8zZGgta284YUlLUWM5RGxHemhhVllvIn0sIHsiLi4uIjogIjdDZjZKa1B1ZHJ5M2xjYndIZ2VaOGtoQXYxVTFPU2xlclAwVmtCSnJXWjAifV0sICJfc2RfYWxnIjogInNoYS0yNTYiLCAiY25mIjogeyJqd2siOiB7Imt0eSI6ICJFQyIsICJjcnYiOiAiUC0yNTYiLCAieCI6ICJUQ0FFUjE5WnZ1M09IRjRqNFc0dmZTVm9ISVAxSUxpbERsczd2Q2VHZW1jIiwgInkiOiAiWnhqaVdXYlpNUUdIVldLVlE0aGJTSWlyc1ZmdWVjQ0U2dDRqVDlGMkhaUSJ9fX0" | ||
|
||
signature := "kmx687kUBiIDvKWgo2Dub-TpdCCRLZwtD7TOj4RoLsUbtFBI8sMrtH2BejXtm_P6fOAjKAVc_7LRNJFgm3PJhg" | ||
|
||
es256 := &ES256{} | ||
|
||
valid, err := es256.ValidateSignature(token, signature, publicKey) | ||
if err != nil { | ||
t.Error("no error should be thrown", err) | ||
} | ||
if !valid { | ||
t.Error("signature is not valid") | ||
} | ||
} | ||
|
||
func TestES256_Sign(t *testing.T) { | ||
body := map[string]interface{}{ | ||
"firstname": "john", | ||
"surname": "smith", | ||
"address": map[string]string{ | ||
"street": "Long Lane", | ||
"number": "15", | ||
"city": "Edinburgh", | ||
}, | ||
} | ||
|
||
headerKeys := map[string]string{ | ||
"typ": "jwt", | ||
} | ||
|
||
es256 := &ES256{} | ||
|
||
token, privateKey, publicKey, err := es256.Sign(body, headerKeys) | ||
if err != nil { | ||
t.Error("no error should be thrown", err) | ||
} | ||
if token == nil { | ||
t.Error("token should not be nil") | ||
} | ||
if privateKey == nil { | ||
t.Error("private key should not be nil") | ||
} | ||
if publicKey == nil { | ||
t.Error("public key should not be nil") | ||
} | ||
jsonPk, err := json.Marshal(publicKey) | ||
if err != nil { | ||
t.Error("no error should be thrown", err) | ||
} | ||
t.Log(*token) | ||
t.Log(string(jsonPk)) | ||
|
||
components := strings.Split(*token, ".") | ||
valid, err := es256.ValidateSignature(strings.Join(components[0:2], "."), components[2], string(jsonPk)) | ||
if err != nil { | ||
t.Error("no error should be thrown", err) | ||
} | ||
if !valid { | ||
t.Error("signature is not valid") | ||
} | ||
} |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,141 @@ | ||
package es384 | ||
|
||
import ( | ||
"crypto" | ||
"crypto/ecdsa" | ||
"crypto/elliptic" | ||
"crypto/rand" | ||
"crypto/sha512" | ||
"encoding/base64" | ||
"encoding/json" | ||
"errors" | ||
"fmt" | ||
"math/big" | ||
) | ||
|
||
type PublicKey struct { | ||
Kty string `json:"kty"` | ||
Crv string `json:"crv"` | ||
X string `json:"x"` | ||
Y string `json:"y"` | ||
} | ||
|
||
func (pubKey *PublicKey) Equal(x PublicKey) bool { | ||
if pubKey.X == x.X && pubKey.Y == x.Y && pubKey.Kty == x.Kty && pubKey.Crv == x.Crv { | ||
return true | ||
} | ||
return false | ||
} | ||
|
||
type ES384 struct{} | ||
|
||
func (signer *ES384) ValidateSignature(token, signature string, publicKeyJson string) (bool, error) { | ||
curve := elliptic.P384() | ||
|
||
var publicKey PublicKey | ||
err := json.Unmarshal([]byte(publicKeyJson), &publicKey) | ||
if err != nil { | ||
return false, errors.New("provided public key json isn't valid es384 public key") | ||
} | ||
|
||
xBytes, err := base64.RawURLEncoding.DecodeString(publicKey.X) | ||
if err != nil { | ||
return false, err | ||
} | ||
|
||
yBytes, err := base64.RawURLEncoding.DecodeString(publicKey.Y) | ||
if err != nil { | ||
return false, err | ||
} | ||
|
||
pk := &ecdsa.PublicKey{ | ||
Curve: curve, | ||
X: big.NewInt(0).SetBytes(xBytes), | ||
Y: big.NewInt(0).SetBytes(yBytes), | ||
} | ||
|
||
bodyHash := sha512.Sum384([]byte(token)) | ||
|
||
r, s, err := signer.extractRSFromSignature(signature) | ||
if err != nil { | ||
return false, err | ||
} | ||
|
||
return ecdsa.Verify(pk, bodyHash[:], r, s), nil | ||
} | ||
|
||
func (signer *ES384) extractRSFromSignature(signature string) (*big.Int, *big.Int, error) { | ||
decodedSignature, err := base64.RawURLEncoding.DecodeString(signature) | ||
if err != nil { | ||
return nil, nil, err | ||
} | ||
|
||
if len(decodedSignature) != 96 { | ||
return nil, nil, errors.New("signature should be 64 bytes for ES384") | ||
} | ||
rb := decodedSignature[:48] | ||
sb := decodedSignature[48:] | ||
|
||
r := big.NewInt(0).SetBytes(rb) | ||
s := big.NewInt(0).SetBytes(sb) | ||
|
||
return r, s, nil | ||
} | ||
|
||
func (signer *ES384) Sign(body map[string]interface{}, headerKeys map[string]string) (*string, crypto.PrivateKey, crypto.PublicKey, error) { | ||
curve := elliptic.P384() | ||
pk, err := ecdsa.GenerateKey(curve, rand.Reader) | ||
if err != nil { | ||
return nil, nil, nil, err | ||
} | ||
|
||
header := headerKeys | ||
header["alg"] = "ES384" | ||
|
||
headerBytes, err := json.Marshal(header) | ||
if err != nil { | ||
return nil, nil, nil, errors.New("failed to marshal header to bytes") | ||
} | ||
base64Header := base64.RawURLEncoding.EncodeToString(headerBytes) | ||
|
||
bodyBytes, err := json.Marshal(body) | ||
if err != nil { | ||
return nil, nil, nil, errors.New("failed to marshal body to bytes") | ||
} | ||
base64Body := base64.RawURLEncoding.EncodeToString(bodyBytes) | ||
|
||
token := fmt.Sprintf("%s.%s", base64Header, base64Body) | ||
|
||
digest := sha512.Sum384([]byte(token)) | ||
|
||
r, s, err := ecdsa.Sign(rand.Reader, pk, digest[:]) | ||
if err != nil { | ||
return nil, nil, nil, err | ||
} | ||
|
||
rb := r.Bytes() | ||
sb := s.Bytes() | ||
|
||
sigBytes := append(rb, sb...) | ||
|
||
base64Sig := base64.RawURLEncoding.EncodeToString(sigBytes) | ||
signedToken := fmt.Sprintf("%s.%s", token, base64Sig) | ||
|
||
cryptoPubKey := pk.PublicKey | ||
|
||
x := base64.RawURLEncoding.EncodeToString(cryptoPubKey.X.Bytes()) | ||
y := base64.RawURLEncoding.EncodeToString(cryptoPubKey.Y.Bytes()) | ||
|
||
pubKey := PublicKey{ | ||
Kty: "EC", | ||
Crv: "P-384", | ||
X: x, | ||
Y: y, | ||
} | ||
|
||
return &signedToken, pk, pubKey, nil | ||
|
||
} | ||
func (signer *ES384) SignWithKey(body map[string]interface{}, headerKeys map[string]string, privateKey string) (*string, error) { | ||
return nil, nil //todo | ||
} |
Oops, something went wrong.