github.com/zntrio/harp/v2@v2.0.9/pkg/container/identity/key/key.go (about) 1 // Licensed to Elasticsearch B.V. under one or more contributor 2 // license agreements. See the NOTICE file distributed with 3 // this work for additional information regarding copyright 4 // ownership. Elasticsearch B.V. licenses this file to you under 5 // the Apache License, Version 2.0 (the "License"); you may 6 // not use this file except in compliance with the License. 7 // You may obtain a copy of the License at 8 // 9 // http://www.apache.org/licenses/LICENSE-2.0 10 // 11 // Unless required by applicable law or agreed to in writing, 12 // software distributed under the License is distributed on an 13 // "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY 14 // KIND, either express or implied. See the License for the 15 // specific language governing permissions and limitations 16 // under the License. 17 18 package key 19 20 import ( 21 "crypto/ecdsa" 22 "crypto/ed25519" 23 "crypto/elliptic" 24 "crypto/sha512" 25 "encoding/base64" 26 "errors" 27 "fmt" 28 "math/big" 29 "strings" 30 31 "github.com/zntrio/harp/v2/pkg/sdk/security/crypto/extra25519" 32 ) 33 34 const ( 35 V1IdentityPublicKeyPrefix = "v1.ipk." 36 V2IdentityPublicKeyPrefix = "v2.ipk." 37 ) 38 39 // ----------------------------------------------------------------------------- 40 41 type Key struct { 42 version uint32 43 key interface{} 44 identity bool 45 public bool 46 } 47 48 func FromString(input string) (*Key, error) { 49 switch { 50 // Ed25519 public key 51 case strings.HasPrefix(input, V1IdentityPublicKeyPrefix): 52 // Decode public key 53 pk, err := base64.RawURLEncoding.DecodeString(input[7:]) 54 if err != nil { 55 return nil, fmt.Errorf("unable to decode public key: %w", err) 56 } 57 if len(pk) != ed25519.PublicKeySize { 58 return nil, errors.New("invalid public key size") 59 } 60 61 // Return wrapped key 62 return &Key{ 63 version: 1, 64 key: ed25519.PublicKey(pk), 65 identity: true, 66 public: true, 67 }, nil 68 69 // EC P-384 public key 70 case strings.HasPrefix(input, V2IdentityPublicKeyPrefix): 71 // Decode public key 72 pkRaw, err := base64.RawURLEncoding.DecodeString(input[7:]) 73 if err != nil { 74 return nil, fmt.Errorf("unable to decode public key: %w", err) 75 } 76 x, y := elliptic.UnmarshalCompressed(elliptic.P384(), pkRaw) 77 if x == nil || y == nil { 78 return nil, errors.New("unable to unmarshal the public key") 79 } 80 81 // Rebuild the public key 82 var pk ecdsa.PublicKey 83 pk.Curve = elliptic.P384() 84 pk.X = x 85 pk.Y = y 86 87 // Return wrapped key 88 return &Key{ 89 version: 2, 90 key: &pk, 91 identity: true, 92 public: true, 93 }, nil 94 95 // Unrecognized 96 default: 97 } 98 99 // Default to error 100 return nil, fmt.Errorf("unrecognized key %q", input) 101 } 102 103 // ----------------------------------------------------------------------------- 104 105 func (k *Key) Verify(message, signature []byte) bool { 106 switch keyRaw := k.key.(type) { 107 case *ecdsa.PublicKey: 108 // Unpack signature 109 r := new(big.Int).SetBytes(signature[:48]) 110 s := new(big.Int).SetBytes(signature[48:]) 111 112 // Compute digest 113 digest := sha512.Sum384(message) 114 115 // Verify signature 116 return ecdsa.Verify(keyRaw, digest[:], r, s) 117 case ed25519.PublicKey: 118 // Verify the signature 119 return ed25519.Verify(keyRaw, message, signature) 120 default: 121 } 122 123 return false 124 } 125 126 func (k *Key) String() string { 127 var payload []byte 128 switch keyRaw := k.key.(type) { 129 case *ecdsa.PublicKey: 130 payload = elliptic.MarshalCompressed(keyRaw.Curve, keyRaw.X, keyRaw.Y) 131 case ed25519.PublicKey: 132 payload = keyRaw 133 default: 134 return "" 135 } 136 137 return fmt.Sprintf("v%d.ipk.%s", k.version, base64.RawURLEncoding.EncodeToString(payload)) 138 } 139 140 func (k *Key) SealingKey() string { 141 var payload []byte 142 switch keyRaw := k.key.(type) { 143 case *ecdsa.PublicKey: 144 payload = elliptic.MarshalCompressed(keyRaw.Curve, keyRaw.X, keyRaw.Y) 145 case ed25519.PublicKey: 146 // Convert Ed25519 to X25519 147 var pkRaw [32]byte 148 if !extra25519.PublicKeyToCurve25519(&pkRaw, keyRaw) { 149 return "" 150 } 151 payload = pkRaw[:] 152 default: 153 return "" 154 } 155 156 return fmt.Sprintf("v%d.sk.%s", k.version, base64.RawURLEncoding.EncodeToString(payload)) 157 }