File
Blob: spec/pki/token.go
| 1 | package pki |
| 2 | |
| 3 | import ( |
| 4 | "crypto/x509" |
| 5 | "crypto/x509/pkix" |
| 6 | "encoding/base64" |
| 7 | "errors" |
| 8 | "strconv" |
| 9 | "strings" |
| 10 | |
| 11 | "go.miragespace.co/specter/spec/protocol" |
| 12 | "go.miragespace.co/specter/util" |
| 13 | |
| 14 | "go.uber.org/zap/zapcore" |
| 15 | ) |
| 16 | |
| 17 | type TokenVersion string |
| 18 | |
| 19 | const ( |
| 20 | TokenSeparator string = ":" |
| 21 | TokenV1 TokenVersion = "v1" // CommonName: v1:clientID:oldToken before PKI is implemented, only the oldToken will be used |
| 22 | TokenV2 TokenVersion = "v2" // CommonName: v2:clientID:base64url(sha256(public key)) initial implementation of PKI, the entire CommonName will be used |
| 23 | ) |
| 24 | |
| 25 | type Identity struct { |
| 26 | Token []byte |
| 27 | ID uint64 |
| 28 | Version TokenVersion |
| 29 | } |
| 30 | |
| 31 | func (n *Identity) NodeIdentity() *protocol.Node { |
| 32 | return &protocol.Node{ |
| 33 | Id: n.ID, |
| 34 | Address: string(n.Token), |
| 35 | Rendezvous: true, |
| 36 | } |
| 37 | } |
| 38 | |
| 39 | func MakeSubjectV1(id uint64, token string) pkix.Name { |
| 40 | return pkix.Name{ |
| 41 | CommonName: strings.Join([]string{ |
| 42 | string(TokenV1), |
| 43 | strconv.FormatUint(id, 10), |
| 44 | token, |
| 45 | }, TokenSeparator), |
| 46 | } |
| 47 | } |
| 48 | |
| 49 | func MakeSubjectV2(id uint64, hash []byte) pkix.Name { |
| 50 | return pkix.Name{ |
| 51 | CommonName: strings.Join([]string{ |
| 52 | string(TokenV2), |
| 53 | strconv.FormatUint(id, 10), |
| 54 | base64.URLEncoding.EncodeToString(hash), |
| 55 | }, TokenSeparator), |
| 56 | } |
| 57 | } |
| 58 | |
| 59 | func ExtractCertificateIdentity(cert *x509.Certificate) (*Identity, error) { |
| 60 | cn := cert.Subject.CommonName |
| 61 | parts := strings.SplitN(cn, ":", 3) |
| 62 | |
| 63 | if len(parts) != 3 { |
| 64 | return nil, errors.New("pki: invalid subject format") |
| 65 | } |
| 66 | |
| 67 | switch parts[0] { |
| 68 | case string(TokenV1): |
| 69 | return &Identity{ |
| 70 | ID: util.Must(strconv.ParseUint(parts[1], 10, 64)), |
| 71 | Token: []byte(parts[2]), |
| 72 | Version: TokenV1, |
| 73 | }, nil |
| 74 | case string(TokenV2): |
| 75 | return &Identity{ |
| 76 | ID: util.Must(strconv.ParseUint(parts[1], 10, 64)), |
| 77 | Token: []byte(cn), |
| 78 | Version: TokenV2, |
| 79 | }, nil |
| 80 | default: |
| 81 | return nil, errors.New("pki: unknown subject in certificate") |
| 82 | } |
| 83 | } |
| 84 | |
| 85 | var _ zapcore.ObjectMarshaler = (*Identity)(nil) |
| 86 | |
| 87 | func (n *Identity) MarshalLogObject(enc zapcore.ObjectEncoder) error { |
| 88 | enc.AddUint64("id", n.ID) |
| 89 | enc.AddString("token", string(n.Token)) |
| 90 | enc.AddString("version", string(n.Version)) |
| 91 | return nil |
| 92 | } |