@@ -13,6 +13,7 @@ import (
1313 "errors"
1414 "fmt"
1515 "strings"
16+ "strconv"
1617 "time"
1718 "unicode"
1819 "unicode/utf8"
@@ -81,9 +82,9 @@ func NewSignerForCosignatureV1(skey string) (*Signer, error) {
8182}
8283
8384// NewVerifierForCosignatureV1 constructs a new Verifier for timestamped
84- // cosignature/v1 signatures from a standard Ed25519 encoded verifier key.
85+ // cosignature/v1 signatures from either a standard Ed25519 encoded verifier key, or an Ed25519 CosignatureV1 key.
8586//
86- // (The returned Verifier has a different key hash from a non-timestamped one,
87+ // (In the case of passing a standard Ed25519 key, the returned Verifier has a different key hash from a non-timestamped one,
8788// meaning it will differ from the key hash in the input encoding.)
8889func NewVerifierForCosignatureV1 (vkey string ) (note.Verifier , error ) {
8990 name , vkey , _ := strings .Cut (vkey , "+" )
@@ -102,7 +103,7 @@ func NewVerifierForCosignatureV1(vkey string) (note.Verifier, error) {
102103 default :
103104 return nil , errVerifierAlg
104105
105- case algEd25519 :
106+ case algEd25519 , algEd25519CosignatureV1 :
106107 if len (key ) != 32 {
107108 return nil , errVerifierID
108109 }
@@ -113,6 +114,36 @@ func NewVerifierForCosignatureV1(vkey string) (note.Verifier, error) {
113114 return v , nil
114115}
115116
117+ // VKeyToCosignatureV1 converts a standard Ed25519 vkey to an Ed25519CosignatureV1 vkey.
118+ func VKeyToCosignatureV1 (vkey string ) (string , error ) {
119+ name , vkey , _ := strings .Cut (vkey , "+" )
120+ hash16 , key64 , _ := strings .Cut (vkey , "+" )
121+ algKey , err := base64 .StdEncoding .DecodeString (key64 )
122+ if len (hash16 ) != 8 || err != nil || ! isValidName (name ) || len (algKey ) == 0 {
123+ return "" , errVerifierID
124+ }
125+
126+ alg , key := algKey [0 ], algKey [1 :]
127+ if alg != algEd25519 {
128+ return "" , errVerifierAlg
129+ }
130+ hash , err := strconv .ParseUint (hash16 , 16 , 32 )
131+ if err != nil {
132+ return "" , errInvalidHash
133+ }
134+
135+ if uint32 (hash ) != keyHashEd25519 (name , algKey ) {
136+ return "" , errInvalidHash
137+ }
138+ if len (key ) != 32 {
139+ return "" , errVerifierID
140+ }
141+ pubKey := append ([]byte {algEd25519CosignatureV1 }, key ... )
142+ h := keyHashEd25519 (name , pubKey )
143+
144+ return fmt .Sprintf ("%s+%08x+%s" , name , h , base64 .StdEncoding .EncodeToString (pubKey )), nil
145+ }
146+
116147// CoSigV1Timestamp extracts the embedded timestamp from a CoSigV1 signature.
117148func CoSigV1Timestamp (s note.Signature ) (time.Time , error ) {
118149 r , err := base64 .StdEncoding .DecodeString (s .Base64 )
@@ -172,6 +203,7 @@ var (
172203 errSignerAlg = errors .New ("unknown signer algorithm" )
173204 errVerifierID = errors .New ("malformed verifier id" )
174205 errVerifierAlg = errors .New ("unknown verifier algorithm" )
206+ errInvalidHash = errors .New ("invalid key hash" )
175207 errMalformedSig = errors .New ("malformed signature" )
176208)
177209
0 commit comments