Federated via graft
2.0 kB
62 lines
1// Package activitypub exposes graft's mirrored repos over ActivityPub: one
2// actor per series (e.g. @federation-x@graft.cyberwild.org), so anyone on
3// the fediverse can follow a repo and see its mirrored commits, issues, and
4// patches show up as posts.
5package activitypub
6
7import (
8 "crypto/rand"
9 "crypto/rsa"
10 "crypto/x509"
11 "encoding/pem"
12 "fmt"
13)
14
15// GenerateKeyPair creates a new RSA-2048 keypair, PEM-encoded — RSA because
16// that's what HTTP Signatures (the scheme ActivityPub implementations like
17// Mastodon use to authenticate federated requests) expects in practice.
18func GenerateKeyPair() (privatePEM, publicPEM string, err error) {
19 key, err := rsa.GenerateKey(rand.Reader, 2048)
20 if err != nil {
21 return "", "", fmt.Errorf("generate rsa key: %w", err)
22 }
23
24 privBytes := x509.MarshalPKCS1PrivateKey(key)
25 priv := pem.EncodeToMemory(&pem.Block{Type: "RSA PRIVATE KEY", Bytes: privBytes})
26
27 pubBytes, err := x509.MarshalPKIXPublicKey(&key.PublicKey)
28 if err != nil {
29 return "", "", fmt.Errorf("marshal public key: %w", err)
30 }
31 pub := pem.EncodeToMemory(&pem.Block{Type: "PUBLIC KEY", Bytes: pubBytes})
32
33 return string(priv), string(pub), nil
34}
35
36// ParsePrivateKey decodes a PEM-encoded RSA private key, as stored via
37// GenerateKeyPair.
38func ParsePrivateKey(pemStr string) (*rsa.PrivateKey, error) {
39 block, _ := pem.Decode([]byte(pemStr))
40 if block == nil {
41 return nil, fmt.Errorf("no PEM block found in private key")
42 }
43 return x509.ParsePKCS1PrivateKey(block.Bytes)
44}
45
46// ParsePublicKey decodes a PEM-encoded RSA public key, e.g. one fetched
47// from a remote actor's publicKeyPem field.
48func ParsePublicKey(pemStr string) (*rsa.PublicKey, error) {
49 block, _ := pem.Decode([]byte(pemStr))
50 if block == nil {
51 return nil, fmt.Errorf("no PEM block found in public key")
52 }
53 key, err := x509.ParsePKIXPublicKey(block.Bytes)
54 if err != nil {
55 return nil, fmt.Errorf("parse public key: %w", err)
56 }
57 rsaKey, ok := key.(*rsa.PublicKey)
58 if !ok {
59 return nil, fmt.Errorf("public key is not RSA")
60 }
61 return rsaKey, nil
62}