New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
feat: Implement full support for ECDSA and RSA keys #270
Changes from all commits
File filter
Filter by extension
Conversations
Jump to
Diff view
Diff view
There are no files selected for viewing
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,46 @@ | ||
package data | ||
|
||
import ( | ||
"crypto" | ||
"crypto/x509" | ||
"encoding/json" | ||
"encoding/pem" | ||
"errors" | ||
"fmt" | ||
) | ||
|
||
type PKIXPublicKey struct { | ||
crypto.PublicKey | ||
} | ||
|
||
func (p *PKIXPublicKey) MarshalJSON() ([]byte, error) { | ||
bytes, err := x509.MarshalPKIXPublicKey(p.PublicKey) | ||
if err != nil { | ||
return nil, err | ||
} | ||
pemBytes := pem.EncodeToMemory(&pem.Block{ | ||
Type: "PUBLIC KEY", | ||
Bytes: bytes, | ||
}) | ||
return json.Marshal(string(pemBytes)) | ||
} | ||
|
||
func (p *PKIXPublicKey) UnmarshalJSON(b []byte) error { | ||
var pemValue string | ||
if err := json.Unmarshal(b, &pemValue); err != nil { | ||
return err | ||
} | ||
block, _ := pem.Decode([]byte(pemValue)) | ||
if block == nil { | ||
return errors.New("invalid PEM value") | ||
} | ||
if block.Type != "PUBLIC KEY" { | ||
return fmt.Errorf("invalid block type: %s", block.Type) | ||
} | ||
pub, err := x509.ParsePKIXPublicKey(block.Bytes) | ||
if err != nil { | ||
return err | ||
} | ||
p.PublicKey = pub | ||
return nil | ||
} |
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,42 @@ | ||
package data | ||
|
||
import ( | ||
"crypto/ecdsa" | ||
"crypto/x509" | ||
"encoding/json" | ||
"encoding/pem" | ||
|
||
. "gopkg.in/check.v1" | ||
) | ||
|
||
const ecdsaKey = `-----BEGIN PUBLIC KEY----- | ||
MFkwEwYHKoZIzj0CAQYIKoZIzj0DAQcDQgAEftgasQA68yvumeXZmcOTSIHKfbmx | ||
WT1oYuRF0Un3tKxnzip6xAYwlz0Dt96DUh+0P7BruHH2O6s4MiRR9/TuNw== | ||
-----END PUBLIC KEY----- | ||
` | ||
|
||
type PKIXSuite struct{} | ||
|
||
var _ = Suite(&PKIXSuite{}) | ||
|
||
func (PKIXSuite) TestMarshalJSON(c *C) { | ||
block, _ := pem.Decode([]byte(ecdsaKey)) | ||
key, err := x509.ParsePKIXPublicKey(block.Bytes) | ||
c.Assert(err, IsNil) | ||
k := PKIXPublicKey{PublicKey: key} | ||
buf, err := json.Marshal(&k) | ||
c.Assert(err, IsNil) | ||
var val string | ||
err = json.Unmarshal(buf, &val) | ||
c.Assert(err, IsNil) | ||
c.Assert(val, Equals, ecdsaKey) | ||
} | ||
|
||
func (PKIXSuite) TestUnmarshalJSON(c *C) { | ||
buf, err := json.Marshal(ecdsaKey) | ||
c.Assert(err, IsNil) | ||
var k PKIXPublicKey | ||
err = json.Unmarshal(buf, &k) | ||
c.Assert(err, IsNil) | ||
c.Assert(k.PublicKey, FitsTypeOf, (*ecdsa.PublicKey)(nil)) | ||
} |
Original file line number | Diff line number | Diff line change |
---|---|---|
|
@@ -3,69 +3,139 @@ package keys | |
import ( | ||
"crypto/ecdsa" | ||
"crypto/elliptic" | ||
"crypto/rand" | ||
"crypto/sha256" | ||
"encoding/asn1" | ||
"crypto/x509" | ||
"encoding/json" | ||
"encoding/pem" | ||
"errors" | ||
"math/big" | ||
"fmt" | ||
|
||
"github.com/theupdateframework/go-tuf/data" | ||
) | ||
|
||
func init() { | ||
VerifierMap.Store(data.KeyTypeECDSA_SHA2_P256, NewEcdsaVerifier) | ||
VerifierMap.Store(data.KeyTypeECDSA_SHA2_P256, newEcdsaVerifier) | ||
SignerMap.Store(data.KeyTypeECDSA_SHA2_P256, newEcdsaSigner) | ||
} | ||
|
||
func NewEcdsaVerifier() Verifier { | ||
return &p256Verifier{} | ||
func newEcdsaVerifier() Verifier { | ||
return &ecdsaVerifier{} | ||
} | ||
|
||
type ecdsaSignature struct { | ||
R, S *big.Int | ||
func newEcdsaSigner() Signer { | ||
return &ecdsaSigner{} | ||
} | ||
|
||
type p256Verifier struct { | ||
PublicKey data.HexBytes `json:"public"` | ||
type ecdsaVerifier struct { | ||
PublicKey *data.PKIXPublicKey `json:"public"` | ||
ecdsaKey *ecdsa.PublicKey | ||
key *data.PublicKey | ||
} | ||
|
||
func (p *p256Verifier) Public() string { | ||
return p.PublicKey.String() | ||
func (p *ecdsaVerifier) Public() string { | ||
r, _ := x509.MarshalPKIXPublicKey(p.ecdsaKey) | ||
return string(r) | ||
} | ||
|
||
func (p *p256Verifier) Verify(msg, sigBytes []byte) error { | ||
x, y := elliptic.Unmarshal(elliptic.P256(), p.PublicKey) | ||
k := &ecdsa.PublicKey{ | ||
Curve: elliptic.P256(), | ||
X: x, | ||
Y: y, | ||
} | ||
|
||
var sig ecdsaSignature | ||
if _, err := asn1.Unmarshal(sigBytes, &sig); err != nil { | ||
return err | ||
} | ||
|
||
func (p *ecdsaVerifier) Verify(msg, sigBytes []byte) error { | ||
hash := sha256.Sum256(msg) | ||
|
||
if !ecdsa.Verify(k, hash[:], sig.R, sig.S) { | ||
return errors.New("tuf: ecdsa signature verification failed") | ||
if !ecdsa.VerifyASN1(p.ecdsaKey, hash[:], sigBytes) { | ||
return errors.New("signature verification failed") | ||
} | ||
return nil | ||
} | ||
|
||
func (p *p256Verifier) MarshalPublicKey() *data.PublicKey { | ||
func (p *ecdsaVerifier) MarshalPublicKey() *data.PublicKey { | ||
return p.key | ||
} | ||
|
||
func (p *p256Verifier) UnmarshalPublicKey(key *data.PublicKey) error { | ||
func (p *ecdsaVerifier) UnmarshalPublicKey(key *data.PublicKey) error { | ||
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Question: To keep backwards compatibility with existing roots (at least for 1-2 releases), could we do something like rename the old I'm hoping to be able to still support old verifiers. For example, if TUF attempts to |
||
if err := json.Unmarshal(key.Value, p); err != nil { | ||
return err | ||
} | ||
x, _ := elliptic.Unmarshal(elliptic.P256(), p.PublicKey) | ||
if x == nil { | ||
return errors.New("tuf: invalid ecdsa public key point") | ||
ecdsaKey, ok := p.PublicKey.PublicKey.(*ecdsa.PublicKey) | ||
if !ok { | ||
return fmt.Errorf("invalid public key") | ||
} | ||
p.ecdsaKey = ecdsaKey | ||
p.key = key | ||
return nil | ||
} | ||
|
||
type ecdsaSigner struct { | ||
*ecdsa.PrivateKey | ||
} | ||
|
||
type ecdsaPrivateKeyValue struct { | ||
Private string `json:"private"` | ||
Public *data.PKIXPublicKey `json:"public"` | ||
} | ||
|
||
func (s *ecdsaSigner) PublicData() *data.PublicKey { | ||
keyValBytes, _ := json.Marshal(ecdsaVerifier{PublicKey: &data.PKIXPublicKey{PublicKey: s.Public()}}) | ||
return &data.PublicKey{ | ||
Type: data.KeyTypeECDSA_SHA2_P256, | ||
Scheme: data.KeySchemeECDSA_SHA2_P256, | ||
Algorithms: data.HashAlgorithms, | ||
Value: keyValBytes, | ||
} | ||
} | ||
|
||
func (s *ecdsaSigner) SignMessage(message []byte) ([]byte, error) { | ||
hash := sha256.Sum256(message) | ||
return ecdsa.SignASN1(rand.Reader, s.PrivateKey, hash[:]) | ||
} | ||
|
||
func (s *ecdsaSigner) MarshalPrivateKey() (*data.PrivateKey, error) { | ||
priv, err := x509.MarshalECPrivateKey(s.PrivateKey) | ||
if err != nil { | ||
return nil, err | ||
} | ||
pemKey := pem.EncodeToMemory(&pem.Block{Type: "EC PRIVATE KEY", Bytes: priv}) | ||
val, err := json.Marshal(ecdsaPrivateKeyValue{ | ||
Private: string(pemKey), | ||
Public: &data.PKIXPublicKey{PublicKey: s.Public()}, | ||
}) | ||
if err != nil { | ||
return nil, err | ||
} | ||
return &data.PrivateKey{ | ||
Type: data.KeyTypeECDSA_SHA2_P256, | ||
Scheme: data.KeySchemeECDSA_SHA2_P256, | ||
Algorithms: data.HashAlgorithms, | ||
Value: val, | ||
}, nil | ||
} | ||
|
||
func (s *ecdsaSigner) UnmarshalPrivateKey(key *data.PrivateKey) error { | ||
val := ecdsaPrivateKeyValue{} | ||
if err := json.Unmarshal(key.Value, &val); err != nil { | ||
return err | ||
} | ||
block, _ := pem.Decode([]byte(val.Private)) | ||
if block == nil { | ||
return errors.New("invalid PEM value") | ||
} | ||
if block.Type != "EC PRIVATE KEY" { | ||
return fmt.Errorf("invalid block type: %s", block.Type) | ||
} | ||
k, err := x509.ParseECPrivateKey(block.Bytes) | ||
if err != nil { | ||
return err | ||
} | ||
if k.Curve != elliptic.P256() { | ||
return errors.New("invalid ecdsa curve") | ||
} | ||
s.PrivateKey = k | ||
return nil | ||
} | ||
|
||
func GenerateEcdsaKey() (*ecdsaSigner, error) { | ||
privkey, err := ecdsa.GenerateKey(elliptic.P256(), rand.Reader) | ||
if err != nil { | ||
return nil, err | ||
} | ||
return &ecdsaSigner{privkey}, nil | ||
} |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
could you please document the other key type possibilities? it's a bit hard to discern in the code because they are consts.