86 lines
2.8 KiB
Go
86 lines
2.8 KiB
Go
package torutil
|
|
|
|
import (
|
|
"crypto"
|
|
"crypto/rsa"
|
|
"crypto/sha1"
|
|
"crypto/x509"
|
|
"encoding/base32"
|
|
"fmt"
|
|
"strings"
|
|
|
|
"git.openprivacy.ca/openprivacy/bine/torutil/ed25519"
|
|
"golang.org/x/crypto/sha3"
|
|
)
|
|
|
|
var serviceIDEncoding = base32.StdEncoding.WithPadding(base32.NoPadding)
|
|
|
|
// OnionServiceIDFromPrivateKey generates the onion service ID from the given
|
|
// private key. This panics if the private key is not a 1024-bit
|
|
// crypto/*rsa.PrivateKey or git.openprivacy.ca/openprivacy/bine/torutil/ed25519.KeyPair.
|
|
func OnionServiceIDFromPrivateKey(key crypto.PrivateKey) string {
|
|
switch k := key.(type) {
|
|
case *rsa.PrivateKey:
|
|
return OnionServiceIDFromV2PublicKey(&k.PublicKey)
|
|
case ed25519.KeyPair:
|
|
return OnionServiceIDFromV3PublicKey(k.PublicKey())
|
|
}
|
|
panic(fmt.Sprintf("Unrecognized private key type: %T", key))
|
|
}
|
|
|
|
// OnionServiceIDFromPublicKey generates the onion service ID from the given
|
|
// public key. This panics if the public key is not a 1024-bit
|
|
// crypto/*rsa.PublicKey or git.openprivacy.ca/openprivacy/bine/torutil/ed25519.PublicKey.
|
|
func OnionServiceIDFromPublicKey(key crypto.PublicKey) string {
|
|
switch k := key.(type) {
|
|
case *rsa.PublicKey:
|
|
return OnionServiceIDFromV2PublicKey(k)
|
|
case ed25519.PublicKey:
|
|
return OnionServiceIDFromV3PublicKey(k)
|
|
}
|
|
panic(fmt.Sprintf("Unrecognized public key type: %T", key))
|
|
}
|
|
|
|
// OnionServiceIDFromV2PublicKey generates a V2 service ID for the given
|
|
// RSA-1024 public key. Panics if not a 1024-bit key.
|
|
func OnionServiceIDFromV2PublicKey(key *rsa.PublicKey) string {
|
|
if key.N.BitLen() != 1024 {
|
|
panic("RSA key not 1024 bit")
|
|
}
|
|
h := sha1.New()
|
|
h.Write(x509.MarshalPKCS1PublicKey(key))
|
|
return strings.ToLower(serviceIDEncoding.EncodeToString(h.Sum(nil)[:10]))
|
|
}
|
|
|
|
// OnionServiceIDFromV3PublicKey generates a V3 service ID for the given
|
|
// ED25519 public key.
|
|
func OnionServiceIDFromV3PublicKey(key ed25519.PublicKey) string {
|
|
checkSum := sha3.Sum256(append(append([]byte(".onion checksum"), key...), 0x03))
|
|
var keyBytes [35]byte
|
|
copy(keyBytes[:], key)
|
|
keyBytes[32] = checkSum[0]
|
|
keyBytes[33] = checkSum[1]
|
|
keyBytes[34] = 0x03
|
|
return strings.ToLower(serviceIDEncoding.EncodeToString(keyBytes[:]))
|
|
}
|
|
|
|
// PublicKeyFromV3OnionServiceID returns a public key for the given service ID
|
|
// or an error if the service ID is invalid.
|
|
func PublicKeyFromV3OnionServiceID(id string) (ed25519.PublicKey, error) {
|
|
byts, err := serviceIDEncoding.DecodeString(strings.ToUpper(id))
|
|
if err != nil {
|
|
return nil, err
|
|
} else if len(byts) != 35 {
|
|
return nil, fmt.Errorf("Invalid id length")
|
|
} else if byts[34] != 0x03 {
|
|
return nil, fmt.Errorf("Invalid version")
|
|
}
|
|
// Do a checksum check
|
|
key := ed25519.PublicKey(byts[:32])
|
|
checkSum := sha3.Sum256(append(append([]byte(".onion checksum"), key...), 0x03))
|
|
if byts[32] != checkSum[0] || byts[33] != checkSum[1] {
|
|
return nil, fmt.Errorf("Invalid checksum")
|
|
}
|
|
return key, nil
|
|
}
|