Move JWK base64 decoding to JSON parsing step

This commit is contained in:
Tulir Asokan 2023-07-16 13:33:51 +03:00
parent 360fe208a8
commit e6cec49353
4 changed files with 45 additions and 75 deletions

View file

@ -1,6 +1,9 @@
package libgm package libgm
import ( import (
"crypto/ecdsa"
"crypto/rand"
"crypto/sha256"
"encoding/base64" "encoding/base64"
"encoding/json" "encoding/json"
"fmt" "fmt"
@ -333,9 +336,10 @@ func (c *Client) refreshAuthToken() error {
requestID := uuid.NewString() requestID := uuid.NewString()
timestamp := time.Now().UnixMilli() * 1000 timestamp := time.Now().UnixMilli() * 1000
sig, sigErr := jwk.SignRequest(requestID, int64(timestamp)) signBytes := sha256.Sum256([]byte(fmt.Sprintf("%s:%d", requestID, timestamp)))
if sigErr != nil { sig, err := ecdsa.SignASN1(rand.Reader, jwk.GetPrivateKey(), signBytes[:])
return sigErr if err != nil {
return err
} }
payloadMessage, messageErr := payload.RegisterRefresh(sig, requestID, int64(timestamp), c.authData.DevicePair.Browser, c.authData.TachyonAuthToken) payloadMessage, messageErr := payload.RegisterRefresh(sig, requestID, int64(timestamp), c.authData.DevicePair.Browser, c.authData.TachyonAuthToken)

View file

@ -4,84 +4,51 @@ import (
"crypto/ecdsa" "crypto/ecdsa"
"crypto/elliptic" "crypto/elliptic"
"crypto/rand" "crypto/rand"
"crypto/sha256"
"crypto/x509"
"encoding/base64" "encoding/base64"
"fmt" "fmt"
"math/big" "math/big"
) )
type RawURLBytes []byte
func (rub RawURLBytes) MarshalJSON() ([]byte, error) {
out := make([]byte, 2+base64.RawURLEncoding.EncodedLen(len(rub)))
out[0] = '"'
base64.RawURLEncoding.Encode(out[1:], rub)
out[len(out)-1] = '"'
return out, nil
}
func (rub *RawURLBytes) UnmarshalJSON(in []byte) error {
if len(in) < 2 || in[0] != '"' || in[len(in)-1] != '"' {
return fmt.Errorf("invalid value for RawURLBytes: not a JSON string")
}
*rub = make([]byte, base64.RawURLEncoding.DecodedLen(len(in)-2))
_, err := base64.RawURLEncoding.Decode(*rub, in[1:len(in)-1])
return err
}
type JWK struct { type JWK struct {
KeyType string `json:"kty"` KeyType string `json:"kty"`
Curve string `json:"crv"` Curve string `json:"crv"`
D string `json:"d"` D RawURLBytes `json:"d"`
X string `json:"x"` X RawURLBytes `json:"x"`
Y string `json:"y"` Y RawURLBytes `json:"y"`
} }
func (t *JWK) GetPrivateKey() (*ecdsa.PrivateKey, error) { func (t *JWK) GetPrivateKey() *ecdsa.PrivateKey {
curve := elliptic.P256() return &ecdsa.PrivateKey{
xBytes, err := base64.RawURLEncoding.DecodeString(t.X) PublicKey: *t.GetPublicKey(),
if err != nil { D: new(big.Int).SetBytes(t.D),
return nil, err
} }
yBytes, err := base64.RawURLEncoding.DecodeString(t.Y)
if err != nil {
return nil, err
}
dBytes, err := base64.RawURLEncoding.DecodeString(t.D)
if err != nil {
return nil, err
} }
priv := &ecdsa.PrivateKey{ func (t *JWK) GetPublicKey() *ecdsa.PublicKey {
PublicKey: ecdsa.PublicKey{ return &ecdsa.PublicKey{
Curve: curve,
X: new(big.Int).SetBytes(xBytes),
Y: new(big.Int).SetBytes(yBytes),
},
D: new(big.Int).SetBytes(dBytes),
}
return priv, nil
}
func (t *JWK) GetPublicKey() (*ecdsa.PublicKey, error) {
xBytes, err := base64.RawURLEncoding.DecodeString(t.X)
if err != nil {
return nil, err
}
yBytes, err := base64.RawURLEncoding.DecodeString(t.Y)
if err != nil {
return nil, err
}
x := new(big.Int).SetBytes(xBytes)
y := new(big.Int).SetBytes(yBytes)
pubKey := &ecdsa.PublicKey{
Curve: elliptic.P256(), Curve: elliptic.P256(),
X: x, X: new(big.Int).SetBytes(t.X),
Y: y, Y: new(big.Int).SetBytes(t.Y),
} }
return pubKey, nil
}
func (t *JWK) MarshalX509PublicKey() ([]byte, error) {
pubKey, err := t.GetPublicKey()
if err != nil {
return nil, err
}
return x509.MarshalPKIXPublicKey(pubKey)
}
func (t *JWK) SignRequest(requestID string, timestamp int64) ([]byte, error) {
signBytes := sha256.Sum256([]byte(fmt.Sprintf("%s:%d", requestID, timestamp)))
privKey, privErr := t.GetPrivateKey()
if privErr != nil {
return nil, privErr
}
return ecdsa.SignASN1(rand.Reader, privKey, signBytes[:])
} }
// GenerateECDSAKey generates a new ECDSA private key with P-256 curve // GenerateECDSAKey generates a new ECDSA private key with P-256 curve
@ -93,8 +60,8 @@ func GenerateECDSAKey() (*JWK, error) {
return &JWK{ return &JWK{
KeyType: "EC", KeyType: "EC",
Curve: "P-256", Curve: "P-256",
D: base64.RawURLEncoding.EncodeToString(privKey.D.Bytes()), D: privKey.D.Bytes(),
X: base64.RawURLEncoding.EncodeToString(privKey.X.Bytes()), X: privKey.X.Bytes(),
Y: base64.RawURLEncoding.EncodeToString(privKey.Y.Bytes()), Y: privKey.Y.Bytes(),
}, nil }, nil
} }

View file

@ -22,9 +22,6 @@ type Pairer struct {
pairingKey []byte pairingKey []byte
} }
/*
refreshQrCodeTime is the interval to refresh the qr code in seconds, this is usually 20 seconds.
*/
func (c *Client) NewPairer(keyData *crypto.JWK, refreshQrCodeTime int) (*Pairer, error) { func (c *Client) NewPairer(keyData *crypto.JWK, refreshQrCodeTime int) (*Pairer, error) {
if keyData == nil { if keyData == nil {
var err error var err error

View file

@ -1,6 +1,8 @@
package payload package payload
import ( import (
"crypto/x509"
"github.com/google/uuid" "github.com/google/uuid"
"google.golang.org/protobuf/proto" "google.golang.org/protobuf/proto"
@ -9,7 +11,7 @@ import (
) )
func RegisterPhoneRelay(jwk *crypto.JWK) ([]byte, *binary.AuthenticationContainer, error) { func RegisterPhoneRelay(jwk *crypto.JWK) ([]byte, *binary.AuthenticationContainer, error) {
key, err := jwk.MarshalX509PublicKey() key, err := x509.MarshalPKIXPublicKey(jwk.GetPublicKey())
if err != nil { if err != nil {
return nil, nil, err return nil, nil, err
} }