@@ -3,7 +3,11 @@ package auth
33import (
44 "bytes"
55 "context"
6+ "crypto/ecdsa"
67 "crypto/ed25519"
8+ "crypto/elliptic"
9+ "crypto/rand"
10+ "crypto/sha512"
711 "encoding/hex"
812 "encoding/json"
913 "fmt"
@@ -12,12 +16,24 @@ import (
1216 "time"
1317)
1418
19+ type CryptoAlgorithm string
20+
21+ const (
22+ AlgorithmEd25519 CryptoAlgorithm = "ed25519"
23+
24+ // ECDSA with NIST P-384 curve
25+ // public key is in compressed format
26+ // signature is in R || S format
27+ AlgorithmECDSAP384 CryptoAlgorithm = "ecdsap384"
28+ )
29+
1530// CryptoProvider provides common functionality for DNS and HTTP authentication
1631type CryptoProvider struct {
17- registryURL string
18- domain string
19- hexSeed string
20- authMethod string
32+ registryURL string
33+ domain string
34+ privateKey string
35+ cryptoAlgorithm CryptoAlgorithm
36+ authMethod string
2137}
2238
2339// GetToken retrieves the registry JWT token using cryptographic authentication
@@ -26,38 +42,65 @@ func (c *CryptoProvider) GetToken(ctx context.Context) (string, error) {
2642 return "" , fmt .Errorf ("%s domain is required" , c .authMethod )
2743 }
2844
29- if c .hexSeed == "" {
30- return "" , fmt .Errorf ("%s private key (hex seed ) is required" , c .authMethod )
45+ if c .privateKey == "" {
46+ return "" , fmt .Errorf ("%s private key (hex) is required" , c .authMethod )
3147 }
3248
33- // Decode hex seed to private key
34- seedBytes , err := hex .DecodeString (c .hexSeed )
49+ // Decode private key from hex
50+ privateKeyBytes , err := hex .DecodeString (c .privateKey )
3551 if err != nil {
36- return "" , fmt .Errorf ("invalid hex seed format: %w" , err )
52+ return "" , fmt .Errorf ("invalid hex private key format: %w" , err )
3753 }
3854
39- if len (seedBytes ) != ed25519 .SeedSize {
40- return "" , fmt .Errorf ("invalid seed length: expected %d bytes, got %d" , ed25519 .SeedSize , len (seedBytes ))
41- }
42-
43- privateKey := ed25519 .NewKeyFromSeed (seedBytes )
44-
4555 // Generate current timestamp
4656 timestamp := time .Now ().UTC ().Format (time .RFC3339 )
47-
48- // Sign the timestamp
49- signature := ed25519 .Sign (privateKey , []byte (timestamp ))
50- signedTimestamp := hex .EncodeToString (signature )
57+ signedTimestamp , err := c .signMessage (privateKeyBytes , []byte (timestamp ))
58+ if err != nil {
59+ return "" , fmt .Errorf ("failed to sign timestamp: %w" , err )
60+ }
61+ signedTimestampHex := hex .EncodeToString (signedTimestamp )
5162
5263 // Exchange signature for registry token
53- registryToken , err := c .exchangeTokenForRegistry (ctx , c .domain , timestamp , signedTimestamp )
64+ registryToken , err := c .exchangeTokenForRegistry (ctx , c .domain , timestamp , signedTimestampHex )
5465 if err != nil {
5566 return "" , fmt .Errorf ("failed to exchange %s signature: %w" , c .authMethod , err )
5667 }
5768
5869 return registryToken , nil
5970}
6071
72+ func (c * CryptoProvider ) signMessage (privateKeyBytes []byte , message []byte ) ([]byte , error ) {
73+ switch c .cryptoAlgorithm {
74+ case AlgorithmEd25519 :
75+ if len (privateKeyBytes ) != ed25519 .SeedSize {
76+ return nil , fmt .Errorf ("invalid seed length: expected %d bytes, got %d" , ed25519 .SeedSize , len (privateKeyBytes ))
77+ }
78+
79+ privateKey := ed25519 .NewKeyFromSeed (privateKeyBytes )
80+ signature := ed25519 .Sign (privateKey , message )
81+ return signature , nil
82+ case AlgorithmECDSAP384 :
83+ if len (privateKeyBytes ) != 48 {
84+ return nil , fmt .Errorf ("invalid seed length for ECDSA P-384: expected 48 bytes, got %d" , len (privateKeyBytes ))
85+ }
86+
87+ digest := sha512 .Sum384 (message )
88+ curve := elliptic .P384 ()
89+ privateKey , err := ecdsa .ParseRawPrivateKey (curve , privateKeyBytes )
90+ if err != nil {
91+ return nil , fmt .Errorf ("failed to parse ECDSA private key: %w" , err )
92+ }
93+ r , s , err := ecdsa .Sign (rand .Reader , privateKey , digest [:])
94+ if err != nil {
95+ return nil , fmt .Errorf ("failed to sign message: %w" , err )
96+ }
97+ signature := append (r .Bytes (), s .Bytes ()... )
98+ return signature , nil
99+ default :
100+ return nil , fmt .Errorf ("unsupported crypto algorithm: %s" , c .cryptoAlgorithm )
101+ }
102+ }
103+
61104// NeedsLogin always returns false for cryptographic auth since no interactive login is needed
62105func (c * CryptoProvider ) NeedsLogin () bool {
63106 return false
0 commit comments