Initial QSfera import
This commit is contained in:
+31
@@ -0,0 +1,31 @@
|
||||
load("@rules_go//go:def.bzl", "go_library", "go_test")
|
||||
|
||||
go_library(
|
||||
name = "keyconv",
|
||||
srcs = ["keyconv.go"],
|
||||
importpath = "github.com/lestrrat-go/jwx/v3/internal/keyconv",
|
||||
visibility = ["//:__subpackages__"],
|
||||
deps = [
|
||||
"//jwk",
|
||||
"@com_github_lestrrat_go_blackmagic//:blackmagic",
|
||||
"@org_golang_x_crypto//ed25519",
|
||||
],
|
||||
)
|
||||
|
||||
go_test(
|
||||
name = "keyconv_test",
|
||||
srcs = ["keyconv_test.go"],
|
||||
deps = [
|
||||
":keyconv",
|
||||
"//internal/jwxtest",
|
||||
"//jwa",
|
||||
"//jwk",
|
||||
"@com_github_stretchr_testify//require",
|
||||
],
|
||||
)
|
||||
|
||||
alias(
|
||||
name = "go_default_library",
|
||||
actual = ":keyconv",
|
||||
visibility = ["//:__subpackages__"],
|
||||
)
|
||||
+354
@@ -0,0 +1,354 @@
|
||||
package keyconv
|
||||
|
||||
import (
|
||||
"crypto"
|
||||
"crypto/ecdh"
|
||||
"crypto/ecdsa"
|
||||
"crypto/ed25519"
|
||||
"crypto/elliptic"
|
||||
"crypto/rsa"
|
||||
"fmt"
|
||||
"math/big"
|
||||
|
||||
"github.com/lestrrat-go/blackmagic"
|
||||
"github.com/lestrrat-go/jwx/v3/jwk"
|
||||
)
|
||||
|
||||
// RSAPrivateKey assigns src to dst.
|
||||
// `dst` should be a pointer to a rsa.PrivateKey.
|
||||
// `src` may be rsa.PrivateKey, *rsa.PrivateKey, or a jwk.Key
|
||||
func RSAPrivateKey(dst, src any) error {
|
||||
if jwkKey, ok := src.(jwk.Key); ok {
|
||||
var raw rsa.PrivateKey
|
||||
if err := jwk.Export(jwkKey, &raw); err != nil {
|
||||
return fmt.Errorf(`failed to produce rsa.PrivateKey from %T: %w`, src, err)
|
||||
}
|
||||
src = &raw
|
||||
}
|
||||
|
||||
var ptr *rsa.PrivateKey
|
||||
switch src := src.(type) {
|
||||
case rsa.PrivateKey:
|
||||
ptr = &src
|
||||
case *rsa.PrivateKey:
|
||||
ptr = src
|
||||
default:
|
||||
return fmt.Errorf(`keyconv: expected rsa.PrivateKey or *rsa.PrivateKey, got %T`, src)
|
||||
}
|
||||
|
||||
return blackmagic.AssignIfCompatible(dst, ptr)
|
||||
}
|
||||
|
||||
// RSAPublicKey assigns src to dst
|
||||
// `dst` should be a pointer to a non-zero rsa.PublicKey.
|
||||
// `src` may be rsa.PublicKey, *rsa.PublicKey, or a jwk.Key
|
||||
func RSAPublicKey(dst, src any) error {
|
||||
if jwkKey, ok := src.(jwk.Key); ok {
|
||||
pk, err := jwk.PublicRawKeyOf(jwkKey)
|
||||
if err != nil {
|
||||
return fmt.Errorf(`keyconv: failed to produce public key from %T: %w`, src, err)
|
||||
}
|
||||
src = pk
|
||||
}
|
||||
|
||||
var ptr *rsa.PublicKey
|
||||
switch src := src.(type) {
|
||||
case rsa.PrivateKey:
|
||||
ptr = &src.PublicKey
|
||||
case *rsa.PrivateKey:
|
||||
ptr = &src.PublicKey
|
||||
case rsa.PublicKey:
|
||||
ptr = &src
|
||||
case *rsa.PublicKey:
|
||||
ptr = src
|
||||
default:
|
||||
return fmt.Errorf(`keyconv: expected rsa.PublicKey/rsa.PrivateKey or *rsa.PublicKey/*rsa.PrivateKey, got %T`, src)
|
||||
}
|
||||
|
||||
return blackmagic.AssignIfCompatible(dst, ptr)
|
||||
}
|
||||
|
||||
// ECDSAPrivateKey assigns src to dst, converting its type from a
|
||||
// non-pointer to a pointer
|
||||
func ECDSAPrivateKey(dst, src any) error {
|
||||
if jwkKey, ok := src.(jwk.Key); ok {
|
||||
var raw ecdsa.PrivateKey
|
||||
if err := jwk.Export(jwkKey, &raw); err != nil {
|
||||
return fmt.Errorf(`keyconv: failed to produce ecdsa.PrivateKey from %T: %w`, src, err)
|
||||
}
|
||||
src = &raw
|
||||
}
|
||||
|
||||
var ptr *ecdsa.PrivateKey
|
||||
switch src := src.(type) {
|
||||
case ecdsa.PrivateKey:
|
||||
ptr = &src
|
||||
case *ecdsa.PrivateKey:
|
||||
ptr = src
|
||||
default:
|
||||
return fmt.Errorf(`keyconv: expected ecdsa.PrivateKey or *ecdsa.PrivateKey, got %T`, src)
|
||||
}
|
||||
return blackmagic.AssignIfCompatible(dst, ptr)
|
||||
}
|
||||
|
||||
// ECDSAPublicKey assigns src to dst, converting its type from a
|
||||
// non-pointer to a pointer
|
||||
func ECDSAPublicKey(dst, src any) error {
|
||||
if jwkKey, ok := src.(jwk.Key); ok {
|
||||
pk, err := jwk.PublicRawKeyOf(jwkKey)
|
||||
if err != nil {
|
||||
return fmt.Errorf(`keyconv: failed to produce public key from %T: %w`, src, err)
|
||||
}
|
||||
src = pk
|
||||
}
|
||||
|
||||
var ptr *ecdsa.PublicKey
|
||||
switch src := src.(type) {
|
||||
case ecdsa.PrivateKey:
|
||||
ptr = &src.PublicKey
|
||||
case *ecdsa.PrivateKey:
|
||||
ptr = &src.PublicKey
|
||||
case ecdsa.PublicKey:
|
||||
ptr = &src
|
||||
case *ecdsa.PublicKey:
|
||||
ptr = src
|
||||
default:
|
||||
return fmt.Errorf(`keyconv: expected ecdsa.PublicKey/ecdsa.PrivateKey or *ecdsa.PublicKey/*ecdsa.PrivateKey, got %T`, src)
|
||||
}
|
||||
return blackmagic.AssignIfCompatible(dst, ptr)
|
||||
}
|
||||
|
||||
func ByteSliceKey(dst, src any) error {
|
||||
if jwkKey, ok := src.(jwk.Key); ok {
|
||||
var raw []byte
|
||||
if err := jwk.Export(jwkKey, &raw); err != nil {
|
||||
return fmt.Errorf(`keyconv: failed to produce []byte from %T: %w`, src, err)
|
||||
}
|
||||
src = raw
|
||||
}
|
||||
|
||||
if _, ok := src.([]byte); !ok {
|
||||
return fmt.Errorf(`keyconv: expected []byte, got %T`, src)
|
||||
}
|
||||
return blackmagic.AssignIfCompatible(dst, src)
|
||||
}
|
||||
|
||||
func Ed25519PrivateKey(dst, src any) error {
|
||||
if jwkKey, ok := src.(jwk.Key); ok {
|
||||
var raw ed25519.PrivateKey
|
||||
if err := jwk.Export(jwkKey, &raw); err != nil {
|
||||
return fmt.Errorf(`failed to produce ed25519.PrivateKey from %T: %w`, src, err)
|
||||
}
|
||||
src = &raw
|
||||
}
|
||||
|
||||
var ptr *ed25519.PrivateKey
|
||||
switch src := src.(type) {
|
||||
case ed25519.PrivateKey:
|
||||
ptr = &src
|
||||
case *ed25519.PrivateKey:
|
||||
ptr = src
|
||||
default:
|
||||
return fmt.Errorf(`expected ed25519.PrivateKey or *ed25519.PrivateKey, got %T`, src)
|
||||
}
|
||||
return blackmagic.AssignIfCompatible(dst, ptr)
|
||||
}
|
||||
|
||||
func Ed25519PublicKey(dst, src any) error {
|
||||
if jwkKey, ok := src.(jwk.Key); ok {
|
||||
pk, err := jwk.PublicRawKeyOf(jwkKey)
|
||||
if err != nil {
|
||||
return fmt.Errorf(`keyconv: failed to produce public key from %T: %w`, src, err)
|
||||
}
|
||||
src = pk
|
||||
}
|
||||
|
||||
switch key := src.(type) {
|
||||
case ed25519.PrivateKey:
|
||||
src = key.Public()
|
||||
case *ed25519.PrivateKey:
|
||||
src = key.Public()
|
||||
}
|
||||
|
||||
var ptr *ed25519.PublicKey
|
||||
switch src := src.(type) {
|
||||
case ed25519.PublicKey:
|
||||
ptr = &src
|
||||
case *ed25519.PublicKey:
|
||||
ptr = src
|
||||
case *crypto.PublicKey:
|
||||
tmp, ok := (*src).(ed25519.PublicKey)
|
||||
if !ok {
|
||||
return fmt.Errorf(`failed to retrieve ed25519.PublicKey out of *crypto.PublicKey`)
|
||||
}
|
||||
ptr = &tmp
|
||||
case crypto.PublicKey:
|
||||
tmp, ok := src.(ed25519.PublicKey)
|
||||
if !ok {
|
||||
return fmt.Errorf(`failed to retrieve ed25519.PublicKey out of crypto.PublicKey`)
|
||||
}
|
||||
ptr = &tmp
|
||||
default:
|
||||
return fmt.Errorf(`expected ed25519.PublicKey or *ed25519.PublicKey, got %T`, src)
|
||||
}
|
||||
return blackmagic.AssignIfCompatible(dst, ptr)
|
||||
}
|
||||
|
||||
type privECDHer interface {
|
||||
ECDH() (*ecdh.PrivateKey, error)
|
||||
}
|
||||
|
||||
func ECDHPrivateKey(dst, src any) error {
|
||||
var privECDH *ecdh.PrivateKey
|
||||
if jwkKey, ok := src.(jwk.Key); ok {
|
||||
var rawECDH ecdh.PrivateKey
|
||||
if err := jwk.Export(jwkKey, &rawECDH); err == nil {
|
||||
privECDH = &rawECDH
|
||||
} else {
|
||||
// If we cannot export the key as an ecdh.PrivateKey, we try to export it as an ecdsa.PrivateKey
|
||||
var rawECDSA ecdsa.PrivateKey
|
||||
if err := jwk.Export(jwkKey, &rawECDSA); err != nil {
|
||||
return fmt.Errorf(`keyconv: failed to produce ecdh.PrivateKey or ecdsa.PrivateKey from %T: %w`, src, err)
|
||||
}
|
||||
src = &rawECDSA
|
||||
}
|
||||
}
|
||||
|
||||
switch src := src.(type) {
|
||||
case ecdh.PrivateKey:
|
||||
privECDH = &src
|
||||
case *ecdh.PrivateKey:
|
||||
privECDH = src
|
||||
case privECDHer:
|
||||
priv, err := src.ECDH()
|
||||
if err != nil {
|
||||
return fmt.Errorf(`keyconv: failed to convert ecdsa.PrivateKey to ecdh.PrivateKey: %w`, err)
|
||||
}
|
||||
privECDH = priv
|
||||
}
|
||||
|
||||
return blackmagic.AssignIfCompatible(dst, privECDH)
|
||||
}
|
||||
|
||||
type pubECDHer interface {
|
||||
ECDH() (*ecdh.PublicKey, error)
|
||||
}
|
||||
|
||||
func ECDHPublicKey(dst, src any) error {
|
||||
var pubECDH *ecdh.PublicKey
|
||||
if jwkKey, ok := src.(jwk.Key); ok {
|
||||
var rawECDH ecdh.PublicKey
|
||||
if err := jwk.Export(jwkKey, &rawECDH); err == nil {
|
||||
pubECDH = &rawECDH
|
||||
} else {
|
||||
// If we cannot export the key as an ecdh.PublicKey, we try to export it as an ecdsa.PublicKey
|
||||
var rawECDSA ecdsa.PublicKey
|
||||
if err := jwk.Export(jwkKey, &rawECDSA); err != nil {
|
||||
return fmt.Errorf(`keyconv: failed to produce ecdh.PublicKey or ecdsa.PublicKey from %T: %w`, src, err)
|
||||
}
|
||||
src = &rawECDSA
|
||||
}
|
||||
}
|
||||
|
||||
switch src := src.(type) {
|
||||
case ecdh.PublicKey:
|
||||
pubECDH = &src
|
||||
case *ecdh.PublicKey:
|
||||
pubECDH = src
|
||||
case pubECDHer:
|
||||
pub, err := src.ECDH()
|
||||
if err != nil {
|
||||
return fmt.Errorf(`keyconv: failed to convert ecdsa.PublicKey to ecdh.PublicKey: %w`, err)
|
||||
}
|
||||
pubECDH = pub
|
||||
}
|
||||
|
||||
return blackmagic.AssignIfCompatible(dst, pubECDH)
|
||||
}
|
||||
|
||||
// ecdhCurveToElliptic maps ECDH curves to elliptic curves
|
||||
func ecdhCurveToElliptic(ecdhCurve ecdh.Curve) (elliptic.Curve, error) {
|
||||
switch ecdhCurve {
|
||||
case ecdh.P256():
|
||||
return elliptic.P256(), nil
|
||||
case ecdh.P384():
|
||||
return elliptic.P384(), nil
|
||||
case ecdh.P521():
|
||||
return elliptic.P521(), nil
|
||||
default:
|
||||
return nil, fmt.Errorf(`keyconv: unsupported ECDH curve: %v`, ecdhCurve)
|
||||
}
|
||||
}
|
||||
|
||||
// ecdhPublicKeyToECDSA converts an ECDH public key to an ECDSA public key
|
||||
func ecdhPublicKeyToECDSA(ecdhPubKey *ecdh.PublicKey) (*ecdsa.PublicKey, error) {
|
||||
curve, err := ecdhCurveToElliptic(ecdhPubKey.Curve())
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
pubBytes := ecdhPubKey.Bytes()
|
||||
|
||||
// Parse the uncompressed point format (0x04 prefix + X + Y coordinates)
|
||||
if len(pubBytes) == 0 || pubBytes[0] != 0x04 {
|
||||
return nil, fmt.Errorf(`keyconv: invalid ECDH public key format`)
|
||||
}
|
||||
|
||||
keyLen := (len(pubBytes) - 1) / 2
|
||||
if len(pubBytes) != 1+2*keyLen {
|
||||
return nil, fmt.Errorf(`keyconv: invalid ECDH public key length`)
|
||||
}
|
||||
|
||||
x := new(big.Int).SetBytes(pubBytes[1 : 1+keyLen])
|
||||
y := new(big.Int).SetBytes(pubBytes[1+keyLen:])
|
||||
|
||||
return &ecdsa.PublicKey{
|
||||
Curve: curve,
|
||||
X: x,
|
||||
Y: y,
|
||||
}, nil
|
||||
}
|
||||
|
||||
func ECDHToECDSA(dst, src any) error {
|
||||
// convert ecdh.PublicKey to ecdsa.PublicKey, ecdh.PrivateKey to ecdsa.PrivateKey
|
||||
|
||||
// First, handle value types by converting to pointers
|
||||
switch s := src.(type) {
|
||||
case ecdh.PrivateKey:
|
||||
src = &s
|
||||
case ecdh.PublicKey:
|
||||
src = &s
|
||||
}
|
||||
|
||||
var privBytes []byte
|
||||
var pubkey *ecdh.PublicKey
|
||||
// Now handle the actual conversion with pointer types
|
||||
switch src := src.(type) {
|
||||
case *ecdh.PrivateKey:
|
||||
pubkey = src.PublicKey()
|
||||
privBytes = src.Bytes()
|
||||
case *ecdh.PublicKey:
|
||||
pubkey = src
|
||||
default:
|
||||
return fmt.Errorf(`keyconv: expected ecdh.PrivateKey, *ecdh.PrivateKey, ecdh.PublicKey, or *ecdh.PublicKey, got %T`, src)
|
||||
}
|
||||
|
||||
// convert the public key
|
||||
ecdsaPubKey, err := ecdhPublicKeyToECDSA(pubkey)
|
||||
if err != nil {
|
||||
return fmt.Errorf(`keyconv.ECDHToECDSA: failed to convert ECDH public key to ECDSA public key: %w`, err)
|
||||
}
|
||||
|
||||
// return if we were being asked to convert *ecdh.PublicKey
|
||||
if privBytes == nil {
|
||||
return blackmagic.AssignIfCompatible(dst, ecdsaPubKey)
|
||||
}
|
||||
|
||||
// Then create the private key with the public key embedded
|
||||
ecdsaPrivKey := &ecdsa.PrivateKey{
|
||||
D: new(big.Int).SetBytes(privBytes),
|
||||
PublicKey: *ecdsaPubKey,
|
||||
}
|
||||
|
||||
return blackmagic.AssignIfCompatible(dst, ecdsaPrivKey)
|
||||
}
|
||||
Reference in New Issue
Block a user