@@ -13,6 +13,7 @@ import (
1313 "errors"
1414 "fmt"
1515 "math"
16+ "strconv"
1617 "strings"
1718 "time"
1819 "unicode"
@@ -114,6 +115,48 @@ type CosignatureVerifier struct {
114115
115116var _ note.Verifier = & CosignatureVerifier {}
116117
118+ // NewCosignatureVerifier constructs a new [CosignatureVerifier] from a
119+ // c2sp.org/signed-note vkey string.
120+ func NewCosignatureVerifier (vkey string ) (* CosignatureVerifier , error ) {
121+ name , vkey , _ := strings .Cut (vkey , "+" )
122+ hash16 , key64 , _ := strings .Cut (vkey , "+" )
123+ hash , err1 := strconv .ParseUint (hash16 , 16 , 32 )
124+ key , err2 := base64 .StdEncoding .DecodeString (key64 )
125+ if len (hash16 ) != 8 || err1 != nil || err2 != nil || ! isValidName (name ) || len (key ) == 0 {
126+ return nil , errors .New ("malformed verifier id" )
127+ }
128+ if uint32 (hash ) != keyHash (name , key ) {
129+ return nil , errors .New ("invalid verifier hash" )
130+ }
131+ alg , key := key [0 ], key [1 :]
132+ if alg != algCosignatureV1 {
133+ return nil , errors .New ("unknown verifier algorithm" )
134+ }
135+ if len (key ) != ed25519 .PublicKeySize {
136+ return nil , errors .New ("malformed verifier public key" )
137+ }
138+ k := ed25519 .PublicKey (key )
139+ return & CosignatureVerifier {
140+ verifier : verifier {
141+ name : name ,
142+ hash : uint32 (hash ),
143+ verify : func (msg , sig []byte ) bool {
144+ if len (sig ) != 8 + ed25519 .SignatureSize {
145+ return false
146+ }
147+ t := binary .BigEndian .Uint64 (sig )
148+ sig = sig [8 :]
149+ m , err := formatCosignatureV1 (t , msg )
150+ if err != nil {
151+ return false
152+ }
153+ return ed25519 .Verify (k , m , sig )
154+ },
155+ },
156+ key : key ,
157+ }, nil
158+ }
159+
117160// String returns the vkey encoding of the verifier, according to
118161// c2sp.org/signed-note.
119162func (v * CosignatureVerifier ) String () string {
0 commit comments