···2323 pubK256 *secp256k1secec.PublicKey
2424}
25252626+var _ PrivateKey = (*PrivateKeyK256)(nil)
2727+var _ PrivateKeyExportable = (*PrivateKeyK256)(nil)
2828+var _ PublicKey = (*PublicKeyK256)(nil)
2929+2630var k256Options = &secp256k1secec.ECDSAOptions{
2731 // Used to *verify* digest, not to re-hash
2832 Hash: crypto.SHA256,
···6468// Serializes the secret key material in to a raw binary format, which can be parsed by [ParsePrivateBytesK256].
6569//
6670// For K-256, this is the "compact" encoding and is 32 bytes long. There is no ASN.1 or other enclosing structure.
6767-func (k *PrivateKeyK256) Bytes() []byte {
7171+func (k PrivateKeyK256) Bytes() []byte {
6872 return k.privK256.Bytes()
6973}
70747175// Outputs the [PublicKey] corresponding to this [PrivateKeyK256]; it will be a [PublicKeyK256].
7272-func (k *PrivateKeyK256) Public() (PublicKey, error) {
7676+func (k PrivateKeyK256) Public() (PublicKey, error) {
7377 pub := PublicKeyK256{pubK256: k.privK256.PublicKey()}
7478 err := pub.ensureBytes()
7579 if err != nil {
···8589// Calling code is responsible for any string encoding of signatures (eg, hex or base64). For K-256, the signature is 64 bytes long.
8690//
8791// NIST ECDSA signatures can have a "malleability" issue, meaning that there are multiple valid signatures for the same content with the same signing key. This method always returns a "low-S" signature, as required by atproto.
8888-func (k *PrivateKeyK256) HashAndSign(content []byte) ([]byte, error) {
9292+func (k PrivateKeyK256) HashAndSign(content []byte) ([]byte, error) {
8993 hash := sha256.Sum256(content)
9094 return k.privK256.Sign(rand.Reader, hash[:], k256Options)
9195}
···170174func (k *PublicKeyK256) HashAndVerify(content, sig []byte) error {
171175 hash := sha256.Sum256(content)
172176 if !k.pubK256.Verify(hash[:], sig, k256Options) {
173173- return fmt.Errorf("crypto: invalid signature")
177177+ return ErrInvalidSignature
174178 }
175179 return nil
176180}
···190194// - encode bytes with base58btc
191195// - add "z" prefix to indicate encoding
192196// - add "did:key:" prefix
193193-func (k *PublicKeyK256) DidKey() string {
197197+func (k *PublicKeyK256) DIDKey() string {
194198 return "did:key:" + k.Multibase()
195199}
+30-32
atproto/crypto/keys.go
···11package crypto
2233import (
44+ "errors"
45 "fmt"
56 "strings"
6778 "github.com/mr-tron/base58"
89)
9101010-// Common interface for private keys of all the supported cryptographic systems in the atproto ecosystem, in a format which may or may not have secret key material directly available in memory to be exported as bytes.
1111+// Common interface for all the supported atproto cryptographic systems, when
1212+// secret key material may not be directly available to be exported as bytes.
1113type PrivateKey interface {
1214 Equal(other PrivateKey) bool
13151414- // Returns the public key for this private key. Verifies that the public
1515- // key is valid and will be possible to encode as bytes or a string later.
1616+ // If necessary, pre-verifies that the public key curve point is valid and
1717+ // will be possible to encode as bytes or a string later.
1618 Public() (PublicKey, error)
17191818- // First hashes the raw bytes, then signs the digest, returning a binary
1919- // signature. SHA-256 is the hash algorithm used, as specified by atproto.
2020- // This method always returns a "low-S" signature, as required by atproto.
2020+ // Hashes the raw bytes using SHA-256, then signs the digest bytes.
2121+ // Always returns a "low-S" signature (for elliptic curve systems where that is ambigious).
2122 HashAndSign(content []byte) ([]byte, error)
2223}
23242424-// Common interface for private keys of all the supported cryptographic systems in the atproto ecosystem, in a format which does have secret key material directly available in memory to be exported as bytes.
2525+// Common interface for all the supported atproto cryptographic systems, when
2626+// secret key material is directly available to be exported as bytes.
2527type PrivateKeyExportable interface {
2626- Equal(other PrivateKey) bool
2828+ PrivateKey
27292828- // Outputs an untyped (no multicodec) compact encoding of the secret key
2929- // material. The encoding format is curve-specific, and is generally
3030- // "compact" for private keys. Both P-256 and K-256 private keys end up 32
3131- // bytes long. There is no ASN.1 or other enclosing structure to the binary
3232- // encoding.
3030+ // Untyped (no multicodec) encoding of the secret key material.
3131+ // The encoding format is curve-specific, and is generally "compact" for private keys.
3232+ // No ASN.1 or other enclosing structure is applied to the bytes.
3333 Bytes() []byte
3434-3535- // Same as PrivateKey.Public()
3636- Public() (PublicKey, error)
3737-3838- // Same as PrivateKey.HashAndSign()
3939- HashAndSign(content []byte) ([]byte, error)
4034}
41354242-// Common interface for public keys of all the supported cryptographic systems in the atproto ecosystem.
3636+// Common interface for all the supported atproto cryptographic systems.
4337type PublicKey interface {
4438 Equal(other PublicKey) bool
45394646- // Outputs a compact byte serialization of this key.
4040+ // Compact byte serialization (for elliptic curve systems where encoding is ambigious).
4741 Bytes() []byte
48424949- // Hashes content bytes with SHA-256, then verifies the signature of the
5050- // digest.
4343+ // Hashes the raw bytes using SHA-256, then verifies the signature of the digest bytes.
5144 HashAndVerify(content, sig []byte) error
52455353- // String serialization of the public key using common parameters:
4646+ // String serialization of the key bytes using common parameters:
5447 // compressed byte serialization; multicode varint code prefix; base58btc
5548 // string encoding ("z" prefix)
5649 Multibase() string
57505858- // String serialization of the public key as did:key.
5959- DidKey() string
5151+ // String serialization of the key bytes as a did:key.
5252+ DIDKey() string
60536161- // Outputs a non-compact byte serialization of this key. This is not used
6262- // frequently, or directly in atproto, but some serializations and
6363- // encodings require it.
6464- // For curves with no compressed/uncompressed distinction, returns the same
5454+ // Non-compact byte serialization (for elliptic curve systems where
5555+ // encoding is ambigious)
5656+ //
5757+ // This is not used frequently, or directly in atproto, but some
5858+ // serializations and encodings require it.
5959+ //
6060+ // For systems with no compressed/uncompressed distinction, returns the same
6561 // value as Bytes().
6662 UncompressedBytes() []byte
6763}
68646969-// Parses a public key from multibase encoding, with multicodec indicating the key type.
6565+var ErrInvalidSignature = errors.New("crytographic signature invalid")
6666+6767+// Loads a public key from multibase string encoding, with multicodec indicating the key type.
7068func ParsePublicMultibase(encoded string) (PublicKey, error) {
7169 if len(encoded) < 2 || encoded[0] != 'z' {
7270 return nil, fmt.Errorf("crypto: not a multibase base58btc string")
···9290// Loads a [PublicKey] from did:key string serialization.
9391//
9492// The did:key format encodes the key type.
9595-func ParsePublicDidKey(didKey string) (PublicKey, error) {
9393+func ParsePublicDIDKey(didKey string) (PublicKey, error) {
9694 if !strings.HasPrefix(didKey, "did:key:z") {
9795 return nil, fmt.Errorf("string is not a DID key: %s", didKey)
9896 }
···88 "crypto/sha256"
99 "crypto/x509"
1010 "fmt"
1111+ "log"
1112 "math/big"
12131314 "github.com/mr-tron/base58"
···2324type PublicKeyP256 struct {
2425 pubP256 *ecdsa.PublicKey
2526}
2727+2828+var _ PrivateKey = (*PrivateKeyP256)(nil)
2929+var _ PrivateKeyExportable = (*PrivateKeyP256)(nil)
3030+var _ PublicKey = (*PublicKeyP256)(nil)
26312732// Creates a secure new cryptographic key from scratch.
2833func GeneratePrivateKeyP256() (*PrivateKeyP256, error) {
···5661 if err != nil {
5762 return nil, fmt.Errorf("invalid P-256/secp256r1 private key: %w", err)
5863 }
5959- priv := PrivateKeyP256{privP256: sk.(*ecdsa.PrivateKey)}
6464+ skECDSA, ok := sk.(*ecdsa.PrivateKey)
6565+ if !ok {
6666+ return nil, fmt.Errorf("expected ECDSA privatekey from internal encoding")
6767+ }
6868+ priv := PrivateKeyP256{privP256: skECDSA}
6069 err = priv.ensureBytes()
6170 if err != nil {
6271 return nil, err
···8594func (k *PrivateKeyP256) Bytes() []byte {
8695 skEcdh, err := k.privP256.ECDH()
8796 if err != nil {
8888- panic("unexpected failure to export P-256 private key, after being exportable at parse time")
9797+ log.Fatal("unexpected failure to export P-256 private key, after being exportable at parse time")
8998 }
9099 return skEcdh.Bytes()
91100}
···192201func (k *PublicKeyP256) UncompressedBytes() []byte {
193202 pkEcdh, err := k.pubP256.ECDH()
194203 if err != nil {
195195- panic("unexpected invalid P-256/secp256r1 public key, was verified at parse time")
204204+ log.Fatal("unexpected invalid P-256/secp256r1 public key, was verified at parse time")
196205 }
197206 return pkEcdh.Bytes()
198207}
···202211 return elliptic.MarshalCompressed(k.pubP256.Curve, k.pubP256.X, k.pubP256.Y)
203212}
204213205205-// First hashes the raw bytes, then verifies the digest, returning `nil` for valid signatures, or an error for any failure.
214214+// Hashes the raw bytes using SHA-256, then verifies the signature against the digest bytes.
206215//
207207-// SHA-256 is the hash algorithm used, as specified by atproto. Signing digests is the norm for ECDSA, and required by some backend implementations. This method does not "double hash", it simply has name which clarifies that hashing is happening.
216216+// Signing digests is the norm for ECDSA, and required by some backend implementations. This method does not "double hash", it simply has name which clarifies that hashing is happening.
208217//
209218// Calling code is responsible for any string decoding of signatures (eg, hex or base64) before calling this function.
210219//
···221230 s.SetBytes(sig[32:])
222231223232 if !ecdsa.Verify(k.pubP256, hash[:], r, s) {
224224- return fmt.Errorf("crypto: invalid signature")
233233+ return ErrInvalidSignature
225234 }
226235227236 // ensure that signature is low-S
228237 if !sigSIsLowS_P256(s) {
229229- return fmt.Errorf("crypto: invalid signature (high-S P-256)")
238238+ return ErrInvalidSignature
230239 }
231240232241 return nil
233242}
234243235235-// Returns a multibase string encoding of the public key, including a multicodec indicator and compressed curve bytes serialization
244244+// Multibase string encoding of the public key, including a multicodec indicator and compressed curve bytes serialization
236245func (k *PublicKeyP256) Multibase() string {
237246 kbytes := k.Bytes()
238247 // multicodec p256-pub, code 0x1200, varint-encoded bytes: [0x80, 0x24]
···240249 return "z" + base58.Encode(kbytes)
241250}
242251243243-// Returns a did:key string encoding of the public key, as would be encoded in a DID PLC operation:
252252+// did:key string encoding of the public key, as would be encoded in a DID PLC operation:
244253//
245254// - compressed / compacted binary representation
246255// - prefix with appropriate curve multicodec bytes
247256// - encode bytes with base58btc
248257// - add "z" prefix to indicate encoding
249258// - add "did:key:" prefix
250250-func (k *PublicKeyP256) DidKey() string {
259259+func (k *PublicKeyP256) DIDKey() string {
251260 return "did:key:" + k.Multibase()
252261}